diff --git a/.github/GITHUB_RULES.md b/.github/GITHUB_RULES.md deleted file mode 100644 index e81cbf29f3..0000000000 --- a/.github/GITHUB_RULES.md +++ /dev/null @@ -1,62 +0,0 @@ -### Contribute to ASDK's Friendly Reputation - -ASDK has earned its reputation as an exceptionally welcoming place for newbie & experienced developers alike through the extra time Scott takes to thank _everyone_ who posts a question, bug, feature request or PR, for their time and contribution to the project, no matter how large the contribution (or silly the question). - -###PR Reviewing - -Merge permissions granted to Scott Goodson (@appleguy), Michael Schneider (@maicki), Adlai Holler (@Adlai-Holler) - -**PR Type** | **Required Reviewers** ---- | --- -Documentation | Anyone -Bug Fix | 2 (external PR) or 1 (internal PR) of the following (Scott, Michael, Adlai, Levi) -Refactoring | 1-3 depending on size / author familiarity with feature -New API | Scott + component owner + 1 additional -Breaking API | Scott + component owner + 1 additional - -**Component** | **Experts For Reviewing** ---- | --- -ASTextNode + subclasses | Ricky / Oliver -ASImageNode + subclasses | Garrett / Scott / Michael -ASDataController / Table / Collection | Michael -ASRangeController | Scott -ASLayout | Huy -ASDisplayNode | Garret / Michael / Levi -ASVideoNode | #asvideonode channel - -###PR Merging - -BE CAUTIOUS, DON'T CAUSE A REGRESSION - -Try to include as much as possible: -- Description / Screenshots -- Motivation & Context -- Methods of testing / Sample app -- What type of change it is (bug fix, new feature, breaking change) -- Tag @hannahmbanana on any documentation needs* -- Title the PR with the component in brackets - e.g. "[ASTextNode] fix threading issues..." -- New files need to include the required Facebook licensing header info. -- For future viewers / potential contributors, try to describe why this PR is helpful / useful / awesome / makes an impact on the current or future community - -###What stays on GitHub vs goes to Ship? - -GitHub: -- active bugs -- active community discussions -- unresolved community questions -- open issue about slack channel -- open issue with list of “up-for-grabs” tasks to get involved - -Ship: -- feature requests -- documentation requests -- performance optimizations / refactoring - -Comment for moving to Ship: - -@\ The community is planning an exciting long term road map for the project and getting organized around how to deliver these feature requests. - -If you are interested in helping contribute to this component or any other, don’t hesitate to send us an email at AsyncDisplayKit@gmail.com or ping us on -ASDK's Slack channel. If you would like to contribute for a few weeks, we can also add you to our Ship bug tracker so that you can see what everyone is working on and actively coordinate with us. - -As always, keep filing issues and submitting pull requests here on Github and we will only move things to the new tracker if they require long term coordination. diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index c6e5d8efcc..c5ab71942b 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,7 +1,7 @@ // If you're looking for help, please consider joining our slack channel: -// http://asyncdisplaykit.org/slack +// http://asyncdisplaykit.org/slack (we'll be updating the name to Texture soon) // The more information you include, the faster we can help you out! // Please include: a sample project or screenshots, code snippets -// AsyncDisplayKit version, and/or backtraces for any crashes (> bt all). -// Please delete these lines before posting. Thanks! \ No newline at end of file +// Texture version, and/or backtraces for any crashes (> bt all). +// Please delete these lines before posting. Thanks! diff --git a/ASDK-Licenses/LICENSE b/ASDK-Licenses/LICENSE new file mode 100644 index 0000000000..507edbd628 --- /dev/null +++ b/ASDK-Licenses/LICENSE @@ -0,0 +1,30 @@ +BSD License + +For AsyncDisplayKit software + +Copyright (c) 2014-present, Facebook, Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name Facebook nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/PATENTS b/ASDK-Licenses/PATENTS similarity index 100% rename from PATENTS rename to ASDK-Licenses/PATENTS diff --git a/ASDKListKit/ASDKListKitTests/ASListKitTestAdapterDataSource.h b/ASDKListKit/ASDKListKitTests/ASListKitTestAdapterDataSource.h deleted file mode 100644 index cbace9a735..0000000000 --- a/ASDKListKit/ASDKListKitTests/ASListKitTestAdapterDataSource.h +++ /dev/null @@ -1,16 +0,0 @@ -// -// ASListKitTestAdapterDataSource.h -// AsyncDisplayKit -// -// Created by Adlai Holler on 12/25/16. -// Copyright © 2016 Facebook. All rights reserved. -// - -#import - -@interface ASListKitTestAdapterDataSource : NSObject - -// array of numbers which is then passed to -[IGListTestSection setItems:] -@property (nonatomic, strong) NSArray *objects; - -@end diff --git a/ASDKListKit/ASDKListKitTests/ASListKitTestAdapterDataSource.m b/ASDKListKit/ASDKListKitTests/ASListKitTestAdapterDataSource.m deleted file mode 100644 index e83dbf8870..0000000000 --- a/ASDKListKit/ASDKListKitTests/ASListKitTestAdapterDataSource.m +++ /dev/null @@ -1,30 +0,0 @@ -// -// ASListKitTestAdapterDataSource.m -// AsyncDisplayKit -// -// Created by Adlai Holler on 12/25/16. -// Copyright © 2016 Facebook. All rights reserved. -// - -#import "ASListKitTestAdapterDataSource.h" -#import "ASListTestSection.h" - -@implementation ASListKitTestAdapterDataSource - -- (NSArray *)objectsForListAdapter:(IGListAdapter *)listAdapter -{ - return self.objects; -} - -- (IGListSectionController *)listAdapter:(IGListAdapter *)listAdapter sectionControllerForObject:(id)object -{ - ASListTestSection *section = [[ASListTestSection alloc] init]; - return section; -} - -- (nullable UIView *)emptyViewForListAdapter:(IGListAdapter *)listAdapter -{ - return nil; -} - -@end diff --git a/ASDKListKit/ASDKListKitTests/ASListTestCellNode.h b/ASDKListKit/ASDKListKitTests/ASListTestCellNode.h deleted file mode 100644 index b94de37c62..0000000000 --- a/ASDKListKit/ASDKListKitTests/ASListTestCellNode.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// ASListTestCellNode.h -// AsyncDisplayKit -// -// Created by Adlai Holler on 12/25/16. -// Copyright © 2016 Facebook. All rights reserved. -// - -#import - -@interface ASListTestCellNode : ASCellNode - -@end diff --git a/ASDKListKit/ASDKListKitTests/ASListTestCellNode.m b/ASDKListKit/ASDKListKitTests/ASListTestCellNode.m deleted file mode 100644 index fd82e6dc4f..0000000000 --- a/ASDKListKit/ASDKListKitTests/ASListTestCellNode.m +++ /dev/null @@ -1,13 +0,0 @@ -// -// ASListTestCellNode.m -// AsyncDisplayKit -// -// Created by Adlai Holler on 12/25/16. -// Copyright © 2016 Facebook. All rights reserved. -// - -#import "ASListTestCellNode.h" - -@implementation ASListTestCellNode - -@end diff --git a/ASDKListKit/ASDKListKitTests/ASListTestObject.h b/ASDKListKit/ASDKListKitTests/ASListTestObject.h deleted file mode 100644 index 8b5cd23bb5..0000000000 --- a/ASDKListKit/ASDKListKitTests/ASListTestObject.h +++ /dev/null @@ -1,22 +0,0 @@ -// -// ASListTestObject.h -// AsyncDisplayKit -// -// Created by Adlai Holler on 12/25/16. -// Copyright © 2016 Facebook. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface ASListTestObject : NSObject - -- (instancetype)initWithKey:(id )key value:(id)value; - -@property (nonatomic, strong, readonly) id key; -@property (nonatomic, strong) id value; - -@end - -NS_ASSUME_NONNULL_END diff --git a/ASDKListKit/ASDKListKitTests/ASListTestSection.h b/ASDKListKit/ASDKListKitTests/ASListTestSection.h deleted file mode 100644 index 1d10ddbe5b..0000000000 --- a/ASDKListKit/ASDKListKitTests/ASListTestSection.h +++ /dev/null @@ -1,18 +0,0 @@ -// -// ASListTestSection.h -// AsyncDisplayKit -// -// Created by Adlai Holler on 12/25/16. -// Copyright © 2016 Facebook. All rights reserved. -// - -#import -#import - -@interface ASListTestSection : IGListSectionController - -@property (nonatomic) NSInteger itemCount; - -@property (nonatomic) NSInteger selectedItemIndex; - -@end diff --git a/ASDKListKit/ASDKListKitTests/ASListTestSupplementaryNode.h b/ASDKListKit/ASDKListKitTests/ASListTestSupplementaryNode.h deleted file mode 100644 index 55dcd6b70b..0000000000 --- a/ASDKListKit/ASDKListKitTests/ASListTestSupplementaryNode.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// ASListTestSupplementaryNode.h -// AsyncDisplayKit -// -// Created by Adlai Holler on 12/25/16. -// Copyright © 2016 Facebook. All rights reserved. -// - -#import - -@interface ASListTestSupplementaryNode : ASCellNode - -@end diff --git a/ASDKListKit/ASDKListKitTests/ASListTestSupplementaryNode.m b/ASDKListKit/ASDKListKitTests/ASListTestSupplementaryNode.m deleted file mode 100644 index f47f577ca6..0000000000 --- a/ASDKListKit/ASDKListKitTests/ASListTestSupplementaryNode.m +++ /dev/null @@ -1,13 +0,0 @@ -// -// ASListTestSupplementaryNode.m -// AsyncDisplayKit -// -// Created by Adlai Holler on 12/25/16. -// Copyright © 2016 Facebook. All rights reserved. -// - -#import "ASListTestSupplementaryNode.h" - -@implementation ASListTestSupplementaryNode - -@end diff --git a/ASDKListKit/ASDKListKitTests/ASListTestSupplementarySource.h b/ASDKListKit/ASDKListKitTests/ASListTestSupplementarySource.h deleted file mode 100644 index 55f00d4a5a..0000000000 --- a/ASDKListKit/ASDKListKitTests/ASListTestSupplementarySource.h +++ /dev/null @@ -1,20 +0,0 @@ -// -// ASListTestSupplementarySource.h -// AsyncDisplayKit -// -// Created by Adlai Holler on 12/25/16. -// Copyright © 2016 Facebook. All rights reserved. -// - -#import -#import - -@interface ASListTestSupplementarySource : NSObject - -@property (nonatomic, strong, readwrite) NSArray *supportedElementKinds; - -@property (nonatomic, weak) id collectionContext; - -@property (nonatomic, weak) IGListSectionController *sectionController; - -@end diff --git a/ASDKListKit/Podfile b/ASDKListKit/Podfile deleted file mode 100644 index 9204e224f9..0000000000 --- a/ASDKListKit/Podfile +++ /dev/null @@ -1,9 +0,0 @@ -source 'https://github.com/CocoaPods/Specs.git' - -platform :ios, '8.0' -target 'ASDKListKitTests' do - pod 'AsyncDisplayKit/IGListKit', :path => '..' - pod 'IGListKit', :git => 'https://github.com/Instagram/IGListKit', :commit => 'e9e09d7' - pod 'JGMethodSwizzler', :git => 'https://github.com/JonasGessner/JGMethodSwizzler', :branch => 'master' -end - diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index e91f8d8e8e..4d6a0a51c1 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -93,6 +93,8 @@ 34EFC7721B701D0300AD841F /* ASStackLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = ACF6ED171B17843500DA7C62 /* ASStackLayoutSpec.mm */; }; 34EFC7731B701D0700AD841F /* ASAbsoluteLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = ACF6ED181B17843500DA7C62 /* ASAbsoluteLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; 34EFC7741B701D0A00AD841F /* ASAbsoluteLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = ACF6ED191B17843500DA7C62 /* ASAbsoluteLayoutSpec.mm */; }; + 3917EBD41E9C2FC400D04A01 /* _ASCollectionReusableView.h in Headers */ = {isa = PBXBuildFile; fileRef = 3917EBD21E9C2FC400D04A01 /* _ASCollectionReusableView.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 3917EBD51E9C2FC400D04A01 /* _ASCollectionReusableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3917EBD31E9C2FC400D04A01 /* _ASCollectionReusableView.m */; }; 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 */; }; @@ -136,6 +138,7 @@ 690ED59B1E36D118000627C0 /* ASImageNode+tvOS.m in Sources */ = {isa = PBXBuildFile; fileRef = 690ED5951E36D118000627C0 /* ASImageNode+tvOS.m */; }; 692510141E74FB44003F2DD0 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 692510131E74FB44003F2DD0 /* Default-568h@2x.png */; }; 692BE8D71E36B65B00C86D87 /* ASLayoutSpecPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 692BE8D61E36B65B00C86D87 /* ASLayoutSpecPrivate.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 693A1DCA1ECC944E00D0C9D2 /* IGListAdapter+AsyncDisplayKit.h in Headers */ = {isa = PBXBuildFile; fileRef = CCE04B201E313EB9006AEBBB /* IGListAdapter+AsyncDisplayKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; 6947B0BE1E36B4E30007C478 /* ASStackUnpositionedLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 6947B0BC1E36B4E30007C478 /* ASStackUnpositionedLayout.h */; settings = {ATTRIBUTES = (Private, ); }; }; 6947B0C01E36B4E30007C478 /* ASStackUnpositionedLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6947B0BD1E36B4E30007C478 /* ASStackUnpositionedLayout.mm */; }; 6947B0C31E36B5040007C478 /* ASStackPositionedLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 6947B0C11E36B5040007C478 /* ASStackPositionedLayout.h */; settings = {ATTRIBUTES = (Private, ); }; }; @@ -146,8 +149,6 @@ 696F01EC1DD2AF450049FBD5 /* ASEventLog.h in Headers */ = {isa = PBXBuildFile; fileRef = 696F01EA1DD2AF450049FBD5 /* ASEventLog.h */; settings = {ATTRIBUTES = (Public, ); }; }; 696F01EE1DD2AF450049FBD5 /* ASEventLog.mm in Sources */ = {isa = PBXBuildFile; fileRef = 696F01EB1DD2AF450049FBD5 /* ASEventLog.mm */; }; 696FCB311D6E46050093471E /* ASBackgroundLayoutSpecSnapshotTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 696FCB301D6E46050093471E /* ASBackgroundLayoutSpecSnapshotTests.mm */; }; - 69708BA61D76386D005C3CF9 /* ASEqualityHashHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = 69708BA41D76386D005C3CF9 /* ASEqualityHashHelpers.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 69708BA81D76386D005C3CF9 /* ASEqualityHashHelpers.mm in Sources */ = {isa = PBXBuildFile; fileRef = 69708BA51D76386D005C3CF9 /* ASEqualityHashHelpers.mm */; }; 6977965F1D8AC8D3007E93D7 /* ASLayoutSpec+Subclasses.h in Headers */ = {isa = PBXBuildFile; fileRef = 6977965D1D8AC8D3007E93D7 /* ASLayoutSpec+Subclasses.h */; settings = {ATTRIBUTES = (Private, ); }; }; 697796611D8AC8D3007E93D7 /* ASLayoutSpec+Subclasses.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6977965E1D8AC8D3007E93D7 /* ASLayoutSpec+Subclasses.mm */; }; 697B315A1CFE4B410049936F /* ASEditableTextNodeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 697B31591CFE4B410049936F /* ASEditableTextNodeTests.m */; }; @@ -157,6 +158,7 @@ 698DFF441E36B6C9002891F1 /* ASStackLayoutSpecUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 698DFF431E36B6C9002891F1 /* ASStackLayoutSpecUtilities.h */; settings = {ATTRIBUTES = (Private, ); }; }; 698DFF471E36B7E9002891F1 /* ASLayoutSpecUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 698DFF461E36B7E9002891F1 /* ASLayoutSpecUtilities.h */; settings = {ATTRIBUTES = (Private, ); }; }; 69B225671D72535E00B25B22 /* ASDisplayNodeLayoutTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 69B225661D72535E00B25B22 /* ASDisplayNodeLayoutTests.mm */; }; + 69BCE3D91EC6513B007DCCAD /* ASDisplayNode+Layout.mm in Sources */ = {isa = PBXBuildFile; fileRef = 69BCE3D71EC6513B007DCCAD /* ASDisplayNode+Layout.mm */; }; 69CB62AC1CB8165900024920 /* _ASDisplayViewAccessiblity.h in Headers */ = {isa = PBXBuildFile; fileRef = 69CB62A91CB8165900024920 /* _ASDisplayViewAccessiblity.h */; settings = {ATTRIBUTES = (Private, ); }; }; 69CB62AE1CB8165900024920 /* _ASDisplayViewAccessiblity.mm in Sources */ = {isa = PBXBuildFile; fileRef = 69CB62AA1CB8165900024920 /* _ASDisplayViewAccessiblity.mm */; }; 69E0E8A71D356C9400627613 /* ASEqualityHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = 1950C4481A3BB5C1005C8279 /* ASEqualityHelpers.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -178,6 +180,12 @@ 8BBBAB8D1CEBAF1E00107FC6 /* ASDefaultPlaybackButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B0768B21CE752EC002E1453 /* ASDefaultPlaybackButton.m */; }; 8BDA5FC71CDBDF91007D13B2 /* ASVideoPlayerNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 8BDA5FC31CDBDDE1007D13B2 /* ASVideoPlayerNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8BDA5FC81CDBDF95007D13B2 /* ASVideoPlayerNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8BDA5FC41CDBDDE1007D13B2 /* ASVideoPlayerNode.mm */; }; + 9019FBBD1ED8061D00C45F72 /* ASYogaLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = 9019FBB91ED8061D00C45F72 /* ASYogaLayoutSpec.h */; }; + 9019FBBE1ED8061D00C45F72 /* ASYogaLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9019FBBA1ED8061D00C45F72 /* ASYogaLayoutSpec.mm */; }; + 9019FBBF1ED8061D00C45F72 /* ASYogaUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 9019FBBB1ED8061D00C45F72 /* ASYogaUtilities.h */; }; + 9019FBC01ED8061D00C45F72 /* ASYogaUtilities.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9019FBBC1ED8061D00C45F72 /* ASYogaUtilities.mm */; }; + 909C4C751F09C98B00D6B76F /* ASTextNode2.h in Headers */ = {isa = PBXBuildFile; fileRef = 909C4C731F09C98B00D6B76F /* ASTextNode2.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 909C4C761F09C98B00D6B76F /* ASTextNode2.mm in Sources */ = {isa = PBXBuildFile; fileRef = 909C4C741F09C98B00D6B76F /* ASTextNode2.mm */; }; 90FC784F1E4BFE1B00383C5A /* ASDisplayNode+Yoga.mm in Sources */ = {isa = PBXBuildFile; fileRef = 90FC784E1E4BFE1B00383C5A /* ASDisplayNode+Yoga.mm */; }; 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 */; }; @@ -302,14 +310,10 @@ 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 */; }; CC034A091E60BEB400626263 /* ASDisplayNode+Convenience.h in Headers */ = {isa = PBXBuildFile; fileRef = CC034A071E60BEB400626263 /* ASDisplayNode+Convenience.h */; settings = {ATTRIBUTES = (Public, ); }; }; CC034A0A1E60BEB400626263 /* ASDisplayNode+Convenience.m in Sources */ = {isa = PBXBuildFile; fileRef = CC034A081E60BEB400626263 /* ASDisplayNode+Convenience.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 */; }; + CC034A131E649F1300626263 /* AsyncDisplayKit+IGListKitMethods.h in Headers */ = {isa = PBXBuildFile; fileRef = CC034A111E649F1300626263 /* AsyncDisplayKit+IGListKitMethods.h */; settings = {ATTRIBUTES = (Public, ); }; }; 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 */; }; @@ -339,9 +343,17 @@ 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 */; }; + CC56013B1F06E9A700DC4FBE /* ASIntegerMap.h in Headers */ = {isa = PBXBuildFile; fileRef = CC5601391F06E9A700DC4FBE /* ASIntegerMap.h */; }; + CC56013C1F06E9A700DC4FBE /* ASIntegerMap.mm in Sources */ = {isa = PBXBuildFile; fileRef = CC56013A1F06E9A700DC4FBE /* ASIntegerMap.mm */; }; 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, ); }; }; + CC583AD61EF9BDBE00134156 /* ASTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = CC583AC21EF9BAB400134156 /* ASTestCase.m */; }; + CC583AD71EF9BDC100134156 /* NSInvocation+ASTestHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = CC583AC51EF9BAB400134156 /* NSInvocation+ASTestHelpers.m */; }; + CC583AD81EF9BDC300134156 /* OCMockObject+ASAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = CC583AC71EF9BAB400134156 /* OCMockObject+ASAdditions.m */; }; + CC583AD91EF9BDC600134156 /* ASDisplayNode+OCMock.m in Sources */ = {isa = PBXBuildFile; fileRef = CC583AC01EF9BAB400134156 /* ASDisplayNode+OCMock.m */; }; CC58AA4B1E398E1D002C8CB4 /* ASBlockTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = CC58AA4A1E398E1D002C8CB4 /* ASBlockTypes.h */; settings = {ATTRIBUTES = (Public, ); }; }; + CC6AA2DA1E9F03B900978E87 /* ASDisplayNode+Ancestry.h in Headers */ = {isa = PBXBuildFile; fileRef = CC6AA2D81E9F03B900978E87 /* ASDisplayNode+Ancestry.h */; settings = {ATTRIBUTES = (Public, ); }; }; + CC6AA2DB1E9F03B900978E87 /* ASDisplayNode+Ancestry.m in Sources */ = {isa = PBXBuildFile; fileRef = CC6AA2D91E9F03B900978E87 /* ASDisplayNode+Ancestry.m */; }; CC7FD9E11BB5F750005CCB2B /* ASPhotosFrameworkImageRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC7FD9E01BB5F750005CCB2B /* ASPhotosFrameworkImageRequestTests.m */; }; CC7FD9E21BB603FF005CCB2B /* ASPhotosFrameworkImageRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = CC7FD9DC1BB5E962005CCB2B /* ASPhotosFrameworkImageRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; CC87BB951DA8193C0090E380 /* ASCellNode+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = CC87BB941DA8193C0090E380 /* ASCellNode+Internal.h */; settings = {ATTRIBUTES = (Private, ); }; }; @@ -349,7 +361,47 @@ CC8B05D81D73979700F54286 /* ASTextNodePerformanceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC8B05D71D73979700F54286 /* ASTextNodePerformanceTests.m */; }; CC90E1F41E383C0400FED591 /* AsyncDisplayKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B35061DA1B010EDF0018CF92 /* AsyncDisplayKit.framework */; }; CCA221D31D6FA7EF00AF6A0F /* ASViewControllerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CCA221D21D6FA7EF00AF6A0F /* ASViewControllerTests.m */; }; + CCA282B41E9EA7310037E8B7 /* ASTipsController.h in Headers */ = {isa = PBXBuildFile; fileRef = CCA282B21E9EA7310037E8B7 /* ASTipsController.h */; }; + CCA282B51E9EA7310037E8B7 /* ASTipsController.m in Sources */ = {isa = PBXBuildFile; fileRef = CCA282B31E9EA7310037E8B7 /* ASTipsController.m */; }; + CCA282B81E9EA8E40037E8B7 /* AsyncDisplayKit+Tips.h in Headers */ = {isa = PBXBuildFile; fileRef = CCA282B61E9EA8E40037E8B7 /* AsyncDisplayKit+Tips.h */; settings = {ATTRIBUTES = (Public, ); }; }; + CCA282B91E9EA8E40037E8B7 /* AsyncDisplayKit+Tips.m in Sources */ = {isa = PBXBuildFile; fileRef = CCA282B71E9EA8E40037E8B7 /* AsyncDisplayKit+Tips.m */; }; + CCA282BC1E9EABDD0037E8B7 /* ASTipProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = CCA282BA1E9EABDD0037E8B7 /* ASTipProvider.h */; }; + CCA282BD1E9EABDD0037E8B7 /* ASTipProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = CCA282BB1E9EABDD0037E8B7 /* ASTipProvider.m */; }; + CCA282C01E9EAE010037E8B7 /* ASTip.h in Headers */ = {isa = PBXBuildFile; fileRef = CCA282BE1E9EAE010037E8B7 /* ASTip.h */; }; + CCA282C11E9EAE010037E8B7 /* ASTip.m in Sources */ = {isa = PBXBuildFile; fileRef = CCA282BF1E9EAE010037E8B7 /* ASTip.m */; }; + CCA282C41E9EAE630037E8B7 /* ASLayerBackingTipProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = CCA282C21E9EAE630037E8B7 /* ASLayerBackingTipProvider.h */; }; + CCA282C51E9EAE630037E8B7 /* ASLayerBackingTipProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = CCA282C31E9EAE630037E8B7 /* ASLayerBackingTipProvider.m */; }; + CCA282C81E9EB64B0037E8B7 /* ASDisplayNodeTipState.h in Headers */ = {isa = PBXBuildFile; fileRef = CCA282C61E9EB64B0037E8B7 /* ASDisplayNodeTipState.h */; }; + CCA282C91E9EB64B0037E8B7 /* ASDisplayNodeTipState.m in Sources */ = {isa = PBXBuildFile; fileRef = CCA282C71E9EB64B0037E8B7 /* ASDisplayNodeTipState.m */; }; + CCA282CC1E9EB73E0037E8B7 /* ASTipNode.h in Headers */ = {isa = PBXBuildFile; fileRef = CCA282CA1E9EB73E0037E8B7 /* ASTipNode.h */; }; + CCA282CD1E9EB73E0037E8B7 /* ASTipNode.m in Sources */ = {isa = PBXBuildFile; fileRef = CCA282CB1E9EB73E0037E8B7 /* ASTipNode.m */; }; + CCA282D01E9EBF6C0037E8B7 /* ASTipsWindow.h in Headers */ = {isa = PBXBuildFile; fileRef = CCA282CE1E9EBF6C0037E8B7 /* ASTipsWindow.h */; }; + CCA282D11E9EBF6C0037E8B7 /* ASTipsWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = CCA282CF1E9EBF6C0037E8B7 /* ASTipsWindow.m */; }; + CCA5F62E1EECC2A80060C137 /* ASAssert.m in Sources */ = {isa = PBXBuildFile; fileRef = CCA5F62D1EECC2A80060C137 /* ASAssert.m */; }; + CCB1F95A1EFB60A5009C7475 /* ASLog.m in Sources */ = {isa = PBXBuildFile; fileRef = CCB1F9591EFB60A5009C7475 /* ASLog.m */; }; + CCB1F95C1EFB6350009C7475 /* ASSignpost.h in Headers */ = {isa = PBXBuildFile; fileRef = CCB1F95B1EFB6316009C7475 /* ASSignpost.h */; }; CCB2F34D1D63CCC6004E6DE9 /* ASDisplayNodeSnapshotTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CCB2F34C1D63CCC6004E6DE9 /* ASDisplayNodeSnapshotTests.m */; }; + CCBBBF5D1EB161760069AA91 /* ASRangeManagingNode.h in Headers */ = {isa = PBXBuildFile; fileRef = CCBBBF5C1EB161760069AA91 /* ASRangeManagingNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; + CCCCCCD51EC3EF060087FE10 /* ASTextDebugOption.h in Headers */ = {isa = PBXBuildFile; fileRef = CCCCCCC31EC3EF060087FE10 /* ASTextDebugOption.h */; }; + CCCCCCD61EC3EF060087FE10 /* ASTextDebugOption.m in Sources */ = {isa = PBXBuildFile; fileRef = CCCCCCC41EC3EF060087FE10 /* ASTextDebugOption.m */; }; + CCCCCCD71EC3EF060087FE10 /* ASTextInput.h in Headers */ = {isa = PBXBuildFile; fileRef = CCCCCCC51EC3EF060087FE10 /* ASTextInput.h */; }; + CCCCCCD81EC3EF060087FE10 /* ASTextInput.m in Sources */ = {isa = PBXBuildFile; fileRef = CCCCCCC61EC3EF060087FE10 /* ASTextInput.m */; }; + CCCCCCD91EC3EF060087FE10 /* ASTextLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = CCCCCCC71EC3EF060087FE10 /* ASTextLayout.h */; }; + CCCCCCDA1EC3EF060087FE10 /* ASTextLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = CCCCCCC81EC3EF060087FE10 /* ASTextLayout.m */; }; + CCCCCCDB1EC3EF060087FE10 /* ASTextLine.h in Headers */ = {isa = PBXBuildFile; fileRef = CCCCCCC91EC3EF060087FE10 /* ASTextLine.h */; }; + CCCCCCDC1EC3EF060087FE10 /* ASTextLine.m in Sources */ = {isa = PBXBuildFile; fileRef = CCCCCCCA1EC3EF060087FE10 /* ASTextLine.m */; }; + CCCCCCDD1EC3EF060087FE10 /* ASTextAttribute.h in Headers */ = {isa = PBXBuildFile; fileRef = CCCCCCCC1EC3EF060087FE10 /* ASTextAttribute.h */; }; + CCCCCCDE1EC3EF060087FE10 /* ASTextAttribute.m in Sources */ = {isa = PBXBuildFile; fileRef = CCCCCCCD1EC3EF060087FE10 /* ASTextAttribute.m */; }; + CCCCCCDF1EC3EF060087FE10 /* ASTextRunDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = CCCCCCCE1EC3EF060087FE10 /* ASTextRunDelegate.h */; }; + CCCCCCE01EC3EF060087FE10 /* ASTextRunDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = CCCCCCCF1EC3EF060087FE10 /* ASTextRunDelegate.m */; }; + CCCCCCE11EC3EF060087FE10 /* ASTextUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = CCCCCCD11EC3EF060087FE10 /* ASTextUtilities.h */; }; + CCCCCCE21EC3EF060087FE10 /* ASTextUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = CCCCCCD21EC3EF060087FE10 /* ASTextUtilities.m */; }; + CCCCCCE31EC3EF060087FE10 /* NSParagraphStyle+ASText.h in Headers */ = {isa = PBXBuildFile; fileRef = CCCCCCD31EC3EF060087FE10 /* NSParagraphStyle+ASText.h */; }; + CCCCCCE41EC3EF060087FE10 /* NSParagraphStyle+ASText.m in Sources */ = {isa = PBXBuildFile; fileRef = CCCCCCD41EC3EF060087FE10 /* NSParagraphStyle+ASText.m */; }; + CCCCCCE71EC3F0FC0087FE10 /* NSAttributedString+ASText.h in Headers */ = {isa = PBXBuildFile; fileRef = CCCCCCE51EC3F0FC0087FE10 /* NSAttributedString+ASText.h */; }; + CCCCCCE81EC3F0FC0087FE10 /* NSAttributedString+ASText.m in Sources */ = {isa = PBXBuildFile; fileRef = CCCCCCE61EC3F0FC0087FE10 /* NSAttributedString+ASText.m */; }; + CCDD148B1EEDCD9D0020834E /* ASCollectionModernDataSourceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CCDD148A1EEDCD9D0020834E /* ASCollectionModernDataSourceTests.m */; }; + CCE4F9B31F0D60AC00062E4E /* ASIntegerMapTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CCE4F9B21F0D60AC00062E4E /* ASIntegerMapTests.m */; }; CCF18FF41D2575E300DF5895 /* NSIndexSet+ASHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = CC4981BA1D1C7F65004E13CC /* NSIndexSet+ASHelpers.h */; settings = {ATTRIBUTES = (Private, ); }; }; DB55C2671C641AE4004EDCF5 /* ASContextTransitioning.h in Headers */ = {isa = PBXBuildFile; fileRef = DB55C2651C641AE4004EDCF5 /* ASContextTransitioning.h */; settings = {ATTRIBUTES = (Public, ); }; }; DB7121BCD50849C498C886FB /* libPods-AsyncDisplayKitTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = EFA731F0396842FF8AB635EE /* libPods-AsyncDisplayKitTests.a */; }; @@ -372,9 +424,40 @@ DECBD6E81BE56E1900CF4905 /* ASButtonNode.h in Headers */ = {isa = PBXBuildFile; fileRef = DECBD6E51BE56E1900CF4905 /* ASButtonNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; DECBD6EA1BE56E1900CF4905 /* ASButtonNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = DECBD6E61BE56E1900CF4905 /* ASButtonNode.mm */; }; DEFAD8131CC48914000527C4 /* ASVideoNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = AEEC47E01C20C2DD00EC1693 /* ASVideoNode.mm */; }; + E51B78BF1F028ABF00E32604 /* ASLayoutFlatteningTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E51B78BD1F01A0EE00E32604 /* ASLayoutFlatteningTests.m */; }; + E54E81FC1EB357BD00FFE8E1 /* ASPageTable.h in Headers */ = {isa = PBXBuildFile; fileRef = E54E81FA1EB357BD00FFE8E1 /* ASPageTable.h */; }; + E54E81FD1EB357BD00FFE8E1 /* ASPageTable.m in Sources */ = {isa = PBXBuildFile; fileRef = E54E81FB1EB357BD00FFE8E1 /* ASPageTable.m */; }; E55D86331CA8A14000A0C26F /* ASLayoutElement.mm in Sources */ = {isa = PBXBuildFile; fileRef = E55D86311CA8A14000A0C26F /* ASLayoutElement.mm */; }; 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 */; }; + E5775AFC1F13CE9F00CAC9BC /* _ASCollectionGalleryLayoutItem.h in Headers */ = {isa = PBXBuildFile; fileRef = E5775AFB1F13CE9F00CAC9BC /* _ASCollectionGalleryLayoutItem.h */; }; + E5775AFE1F13CF7400CAC9BC /* _ASCollectionGalleryLayoutItem.mm in Sources */ = {isa = PBXBuildFile; fileRef = E5775AFD1F13CF7400CAC9BC /* _ASCollectionGalleryLayoutItem.mm */; }; + E5775B001F13D25400CAC9BC /* ASCollectionLayoutState+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = E5775AFF1F13D25400CAC9BC /* ASCollectionLayoutState+Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + E5775B021F16759300CAC9BC /* ASCollectionLayoutCache.h in Headers */ = {isa = PBXBuildFile; fileRef = E5775B011F16759300CAC9BC /* ASCollectionLayoutCache.h */; settings = {ATTRIBUTES = (Private, ); }; }; + E5775B041F16759F00CAC9BC /* ASCollectionLayoutCache.mm in Sources */ = {isa = PBXBuildFile; fileRef = E5775B031F16759F00CAC9BC /* ASCollectionLayoutCache.mm */; }; + E5855DEF1EBB4D83003639AE /* ASCollectionLayoutDefines.m in Sources */ = {isa = PBXBuildFile; fileRef = E5855DED1EBB4D83003639AE /* ASCollectionLayoutDefines.m */; }; + E5855DF01EBB4D83003639AE /* ASCollectionLayoutDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = E5855DEE1EBB4D83003639AE /* ASCollectionLayoutDefines.h */; settings = {ATTRIBUTES = (Private, ); }; }; + E58E9E421E941D74004CFC59 /* ASCollectionFlowLayoutDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = E58E9E3D1E941D74004CFC59 /* ASCollectionFlowLayoutDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E58E9E431E941D74004CFC59 /* ASCollectionFlowLayoutDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = E58E9E3E1E941D74004CFC59 /* ASCollectionFlowLayoutDelegate.m */; }; + E58E9E441E941D74004CFC59 /* ASCollectionLayoutContext.h in Headers */ = {isa = PBXBuildFile; fileRef = E58E9E3F1E941D74004CFC59 /* ASCollectionLayoutContext.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E58E9E451E941D74004CFC59 /* ASCollectionLayoutContext.m in Sources */ = {isa = PBXBuildFile; fileRef = E58E9E401E941D74004CFC59 /* ASCollectionLayoutContext.m */; }; + E58E9E461E941D74004CFC59 /* ASCollectionLayoutDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = E58E9E411E941D74004CFC59 /* ASCollectionLayoutDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E58E9E491E941DA5004CFC59 /* ASCollectionLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = E58E9E471E941DA5004CFC59 /* ASCollectionLayout.h */; settings = {ATTRIBUTES = (Private, ); }; }; + E58E9E4A1E941DA5004CFC59 /* ASCollectionLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = E58E9E481E941DA5004CFC59 /* ASCollectionLayout.mm */; }; + E5ABAC7B1E8564EE007AC15C /* ASRectTable.h in Headers */ = {isa = PBXBuildFile; fileRef = E5ABAC791E8564EE007AC15C /* ASRectTable.h */; }; + E5ABAC7C1E8564EE007AC15C /* ASRectTable.m in Sources */ = {isa = PBXBuildFile; fileRef = E5ABAC7A1E8564EE007AC15C /* ASRectTable.m */; }; + E5B077FF1E69F4EB00C24B5B /* ASElementMap.h in Headers */ = {isa = PBXBuildFile; fileRef = E5B077FD1E69F4EB00C24B5B /* ASElementMap.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E5B078001E69F4EB00C24B5B /* ASElementMap.m in Sources */ = {isa = PBXBuildFile; fileRef = E5B077FE1E69F4EB00C24B5B /* ASElementMap.m */; }; + E5B225281F1790D6001E1431 /* ASHashing.h in Headers */ = {isa = PBXBuildFile; fileRef = E5B225271F1790B5001E1431 /* ASHashing.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E5B225291F1790EE001E1431 /* ASHashing.m in Sources */ = {isa = PBXBuildFile; fileRef = E5B225261F1790B5001E1431 /* ASHashing.m */; }; + E5B2252E1F17E521001E1431 /* ASDispatch.m in Sources */ = {isa = PBXBuildFile; fileRef = E5B2252D1F17E521001E1431 /* ASDispatch.m */; }; + E5B5B9D11E9BAD9800A6B726 /* ASCollectionLayoutContext+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = E5B5B9D01E9BAD9800A6B726 /* ASCollectionLayoutContext+Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + E5C347B11ECB3D9200EC4BE4 /* ASBatchFetchingDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = E5C347B01ECB3D9200EC4BE4 /* ASBatchFetchingDelegate.h */; }; + E5C347B31ECB40AA00EC4BE4 /* ASTableNode+Beta.h in Headers */ = {isa = PBXBuildFile; fileRef = E5C347B21ECB40AA00EC4BE4 /* ASTableNode+Beta.h */; }; + E5E281741E71C833006B67C2 /* ASCollectionLayoutState.h in Headers */ = {isa = PBXBuildFile; fileRef = E5E281731E71C833006B67C2 /* ASCollectionLayoutState.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E5E281761E71C845006B67C2 /* ASCollectionLayoutState.mm in Sources */ = {isa = PBXBuildFile; fileRef = E5E281751E71C845006B67C2 /* ASCollectionLayoutState.mm */; }; + E5E2D72E1EA780C4005C24C6 /* ASCollectionGalleryLayoutDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = E5E2D72D1EA780C4005C24C6 /* ASCollectionGalleryLayoutDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E5E2D7301EA780DF005C24C6 /* ASCollectionGalleryLayoutDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = E5E2D72F1EA780DF005C24C6 /* ASCollectionGalleryLayoutDelegate.m */; }; F711994E1D20C21100568860 /* ASDisplayNodeExtrasTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F711994D1D20C21100568860 /* ASDisplayNodeExtrasTests.m */; }; /* End PBXBuildFile section */ @@ -440,7 +523,7 @@ 058D09DF195D050800B7D73C /* ASTextNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASTextNode.h; sourceTree = ""; }; 058D09E0195D050800B7D73C /* ASTextNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = ASTextNode.mm; sourceTree = ""; }; 058D09E2195D050800B7D73C /* _ASDisplayLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _ASDisplayLayer.h; sourceTree = ""; }; - 058D09E3195D050800B7D73C /* _ASDisplayLayer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = _ASDisplayLayer.mm; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; + 058D09E3195D050800B7D73C /* _ASDisplayLayer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = _ASDisplayLayer.mm; sourceTree = ""; }; 058D09E4195D050800B7D73C /* _ASDisplayView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _ASDisplayView.h; sourceTree = ""; }; 058D09E5195D050800B7D73C /* _ASDisplayView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = _ASDisplayView.mm; sourceTree = ""; }; 058D09E6195D050800B7D73C /* ASHighlightOverlayLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASHighlightOverlayLayer.h; sourceTree = ""; }; @@ -450,7 +533,7 @@ 058D09F5195D050800B7D73C /* NSMutableAttributedString+TextKitAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSMutableAttributedString+TextKitAdditions.h"; sourceTree = ""; }; 058D09F6195D050800B7D73C /* NSMutableAttributedString+TextKitAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSMutableAttributedString+TextKitAdditions.m"; sourceTree = ""; }; 058D09F8195D050800B7D73C /* _ASAsyncTransaction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _ASAsyncTransaction.h; sourceTree = ""; }; - 058D09F9195D050800B7D73C /* _ASAsyncTransaction.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = _ASAsyncTransaction.mm; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; + 058D09F9195D050800B7D73C /* _ASAsyncTransaction.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = _ASAsyncTransaction.mm; sourceTree = ""; }; 058D09FA195D050800B7D73C /* _ASAsyncTransactionContainer+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "_ASAsyncTransactionContainer+Private.h"; sourceTree = ""; }; 058D09FB195D050800B7D73C /* _ASAsyncTransactionContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _ASAsyncTransactionContainer.h; sourceTree = ""; }; 058D09FC195D050800B7D73C /* _ASAsyncTransactionContainer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = _ASAsyncTransactionContainer.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; @@ -533,6 +616,8 @@ 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 = ""; }; 29CDC2E11AAE70D000833CA4 /* ASBasicImageDownloaderContextTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = ASBasicImageDownloaderContextTests.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; + 3917EBD21E9C2FC400D04A01 /* _ASCollectionReusableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _ASCollectionReusableView.h; sourceTree = ""; }; + 3917EBD31E9C2FC400D04A01 /* _ASCollectionReusableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = _ASCollectionReusableView.m; sourceTree = ""; }; 3C9C128419E616EF00E942A0 /* ASTableViewTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = ASTableViewTests.mm; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; 464052191A3F83C40061C0BA /* ASDataController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = ASDataController.h; sourceTree = ""; }; 4640521A1A3F83C40061C0BA /* ASDataController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = ASDataController.mm; sourceTree = ""; }; @@ -583,8 +668,6 @@ 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; 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 = ""; }; @@ -595,7 +678,7 @@ 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 = ""; }; 69B225661D72535E00B25B22 /* ASDisplayNodeLayoutTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASDisplayNodeLayoutTests.mm; sourceTree = ""; }; - 69B225681D7265DA00B25B22 /* ASXCTExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASXCTExtensions.h; sourceTree = ""; }; + 69BCE3D71EC6513B007DCCAD /* ASDisplayNode+Layout.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "ASDisplayNode+Layout.mm"; sourceTree = ""; }; 69CB62A91CB8165900024920 /* _ASDisplayViewAccessiblity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _ASDisplayViewAccessiblity.h; sourceTree = ""; }; 69CB62AA1CB8165900024920 /* _ASDisplayViewAccessiblity.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = _ASDisplayViewAccessiblity.mm; sourceTree = ""; }; 69F10C851C84C35D0026140C /* ASRangeControllerUpdateRangeProtocol+Beta.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASRangeControllerUpdateRangeProtocol+Beta.h"; sourceTree = ""; }; @@ -618,6 +701,12 @@ 8B0768B21CE752EC002E1453 /* ASDefaultPlaybackButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASDefaultPlaybackButton.m; sourceTree = ""; }; 8BDA5FC31CDBDDE1007D13B2 /* ASVideoPlayerNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASVideoPlayerNode.h; sourceTree = ""; }; 8BDA5FC41CDBDDE1007D13B2 /* ASVideoPlayerNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASVideoPlayerNode.mm; sourceTree = ""; }; + 9019FBB91ED8061D00C45F72 /* ASYogaLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASYogaLayoutSpec.h; sourceTree = ""; }; + 9019FBBA1ED8061D00C45F72 /* ASYogaLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASYogaLayoutSpec.mm; sourceTree = ""; }; + 9019FBBB1ED8061D00C45F72 /* ASYogaUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASYogaUtilities.h; sourceTree = ""; }; + 9019FBBC1ED8061D00C45F72 /* ASYogaUtilities.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASYogaUtilities.mm; sourceTree = ""; }; + 909C4C731F09C98B00D6B76F /* ASTextNode2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASTextNode2.h; sourceTree = ""; }; + 909C4C741F09C98B00D6B76F /* ASTextNode2.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASTextNode2.mm; sourceTree = ""; }; 90FC784E1E4BFE1B00383C5A /* ASDisplayNode+Yoga.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "ASDisplayNode+Yoga.mm"; sourceTree = ""; }; 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 = ""; }; @@ -696,12 +785,8 @@ 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 = ""; }; CC034A071E60BEB400626263 /* ASDisplayNode+Convenience.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASDisplayNode+Convenience.h"; sourceTree = ""; }; CC034A081E60BEB400626263 /* ASDisplayNode+Convenience.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "ASDisplayNode+Convenience.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 = ""; }; @@ -735,8 +820,20 @@ 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 = ""; }; + CC5601391F06E9A700DC4FBE /* ASIntegerMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASIntegerMap.h; sourceTree = ""; }; + CC56013A1F06E9A700DC4FBE /* ASIntegerMap.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASIntegerMap.mm; sourceTree = ""; }; CC57EAF91E394EA40034C595 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + CC583AC01EF9BAB400134156 /* ASDisplayNode+OCMock.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "ASDisplayNode+OCMock.m"; sourceTree = ""; }; + CC583AC11EF9BAB400134156 /* ASTestCase.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ASTestCase.h; sourceTree = ""; }; + CC583AC21EF9BAB400134156 /* ASTestCase.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ASTestCase.m; sourceTree = ""; }; + CC583AC31EF9BAB400134156 /* ASXCTExtensions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ASXCTExtensions.h; sourceTree = ""; }; + CC583AC41EF9BAB400134156 /* NSInvocation+ASTestHelpers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSInvocation+ASTestHelpers.h"; sourceTree = ""; }; + CC583AC51EF9BAB400134156 /* NSInvocation+ASTestHelpers.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSInvocation+ASTestHelpers.m"; sourceTree = ""; }; + CC583AC61EF9BAB400134156 /* OCMockObject+ASAdditions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "OCMockObject+ASAdditions.h"; sourceTree = ""; }; + CC583AC71EF9BAB400134156 /* OCMockObject+ASAdditions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "OCMockObject+ASAdditions.m"; sourceTree = ""; }; CC58AA4A1E398E1D002C8CB4 /* ASBlockTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASBlockTypes.h; sourceTree = ""; }; + CC6AA2D81E9F03B900978E87 /* ASDisplayNode+Ancestry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "ASDisplayNode+Ancestry.h"; path = "Base/ASDisplayNode+Ancestry.h"; sourceTree = ""; }; + CC6AA2D91E9F03B900978E87 /* ASDisplayNode+Ancestry.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "ASDisplayNode+Ancestry.m"; path = "Base/ASDisplayNode+Ancestry.m"; 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 = ""; }; @@ -745,13 +842,53 @@ 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 = ""; }; CCA221D21D6FA7EF00AF6A0F /* ASViewControllerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASViewControllerTests.m; sourceTree = ""; }; + CCA282B21E9EA7310037E8B7 /* ASTipsController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASTipsController.h; sourceTree = ""; }; + CCA282B31E9EA7310037E8B7 /* ASTipsController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASTipsController.m; sourceTree = ""; }; + CCA282B61E9EA8E40037E8B7 /* AsyncDisplayKit+Tips.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "AsyncDisplayKit+Tips.h"; sourceTree = ""; }; + CCA282B71E9EA8E40037E8B7 /* AsyncDisplayKit+Tips.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "AsyncDisplayKit+Tips.m"; sourceTree = ""; }; + CCA282BA1E9EABDD0037E8B7 /* ASTipProvider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASTipProvider.h; sourceTree = ""; }; + CCA282BB1E9EABDD0037E8B7 /* ASTipProvider.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASTipProvider.m; sourceTree = ""; }; + CCA282BE1E9EAE010037E8B7 /* ASTip.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASTip.h; sourceTree = ""; }; + CCA282BF1E9EAE010037E8B7 /* ASTip.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASTip.m; sourceTree = ""; }; + CCA282C21E9EAE630037E8B7 /* ASLayerBackingTipProvider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASLayerBackingTipProvider.h; sourceTree = ""; }; + CCA282C31E9EAE630037E8B7 /* ASLayerBackingTipProvider.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASLayerBackingTipProvider.m; sourceTree = ""; }; + CCA282C61E9EB64B0037E8B7 /* ASDisplayNodeTipState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASDisplayNodeTipState.h; sourceTree = ""; }; + CCA282C71E9EB64B0037E8B7 /* ASDisplayNodeTipState.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASDisplayNodeTipState.m; sourceTree = ""; }; + CCA282CA1E9EB73E0037E8B7 /* ASTipNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASTipNode.h; sourceTree = ""; }; + CCA282CB1E9EB73E0037E8B7 /* ASTipNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASTipNode.m; sourceTree = ""; }; + CCA282CE1E9EBF6C0037E8B7 /* ASTipsWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASTipsWindow.h; sourceTree = ""; }; + CCA282CF1E9EBF6C0037E8B7 /* ASTipsWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASTipsWindow.m; sourceTree = ""; }; + CCA5F62D1EECC2A80060C137 /* ASAssert.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASAssert.m; sourceTree = ""; }; + CCB1F9591EFB60A5009C7475 /* ASLog.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASLog.m; sourceTree = ""; }; + CCB1F95B1EFB6316009C7475 /* ASSignpost.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ASSignpost.h; sourceTree = ""; }; CCB2F34C1D63CCC6004E6DE9 /* ASDisplayNodeSnapshotTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASDisplayNodeSnapshotTests.m; sourceTree = ""; }; + CCBBBF5C1EB161760069AA91 /* ASRangeManagingNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASRangeManagingNode.h; sourceTree = ""; }; CCBD05DE1E4147B000D18509 /* ASIGListAdapterBasedDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASIGListAdapterBasedDataSource.m; sourceTree = ""; }; CCBD05DF1E4147B000D18509 /* ASIGListAdapterBasedDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASIGListAdapterBasedDataSource.h; sourceTree = ""; }; + CCCCCCC31EC3EF060087FE10 /* ASTextDebugOption.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASTextDebugOption.h; sourceTree = ""; }; + CCCCCCC41EC3EF060087FE10 /* ASTextDebugOption.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASTextDebugOption.m; sourceTree = ""; }; + CCCCCCC51EC3EF060087FE10 /* ASTextInput.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASTextInput.h; sourceTree = ""; }; + CCCCCCC61EC3EF060087FE10 /* ASTextInput.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASTextInput.m; sourceTree = ""; }; + CCCCCCC71EC3EF060087FE10 /* ASTextLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASTextLayout.h; sourceTree = ""; }; + CCCCCCC81EC3EF060087FE10 /* ASTextLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASTextLayout.m; sourceTree = ""; }; + CCCCCCC91EC3EF060087FE10 /* ASTextLine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASTextLine.h; sourceTree = ""; }; + CCCCCCCA1EC3EF060087FE10 /* ASTextLine.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASTextLine.m; sourceTree = ""; }; + CCCCCCCC1EC3EF060087FE10 /* ASTextAttribute.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASTextAttribute.h; sourceTree = ""; }; + CCCCCCCD1EC3EF060087FE10 /* ASTextAttribute.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASTextAttribute.m; sourceTree = ""; }; + CCCCCCCE1EC3EF060087FE10 /* ASTextRunDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASTextRunDelegate.h; sourceTree = ""; }; + CCCCCCCF1EC3EF060087FE10 /* ASTextRunDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASTextRunDelegate.m; sourceTree = ""; }; + CCCCCCD11EC3EF060087FE10 /* ASTextUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASTextUtilities.h; sourceTree = ""; }; + CCCCCCD21EC3EF060087FE10 /* ASTextUtilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASTextUtilities.m; sourceTree = ""; }; + CCCCCCD31EC3EF060087FE10 /* NSParagraphStyle+ASText.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSParagraphStyle+ASText.h"; sourceTree = ""; }; + CCCCCCD41EC3EF060087FE10 /* NSParagraphStyle+ASText.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSParagraphStyle+ASText.m"; sourceTree = ""; }; + CCCCCCE51EC3F0FC0087FE10 /* NSAttributedString+ASText.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSAttributedString+ASText.h"; sourceTree = ""; }; + CCCCCCE61EC3F0FC0087FE10 /* NSAttributedString+ASText.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSAttributedString+ASText.m"; sourceTree = ""; }; + CCDD148A1EEDCD9D0020834E /* ASCollectionModernDataSourceTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASCollectionModernDataSourceTests.m; sourceTree = ""; }; CCE04B1E1E313EA7006AEBBB /* ASSectionController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASSectionController.h; sourceTree = ""; }; 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 = ""; }; + CCE4F9B21F0D60AC00062E4E /* ASIntegerMapTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASIntegerMapTests.m; 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 = ""; }; @@ -772,11 +909,42 @@ DEC146B51C37A16A004A0EE7 /* ASCollectionInternal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ASCollectionInternal.m; path = Details/ASCollectionInternal.m; sourceTree = ""; }; DECBD6E51BE56E1900CF4905 /* ASButtonNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASButtonNode.h; sourceTree = ""; }; DECBD6E61BE56E1900CF4905 /* ASButtonNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASButtonNode.mm; sourceTree = ""; }; + E51B78BD1F01A0EE00E32604 /* ASLayoutFlatteningTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASLayoutFlatteningTests.m; sourceTree = ""; }; E52405B21C8FEF03004DC8E7 /* ASLayoutTransition.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASLayoutTransition.mm; sourceTree = ""; }; E52405B41C8FEF16004DC8E7 /* ASLayoutTransition.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASLayoutTransition.h; sourceTree = ""; }; + E54E81FA1EB357BD00FFE8E1 /* ASPageTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASPageTable.h; sourceTree = ""; }; + E54E81FB1EB357BD00FFE8E1 /* ASPageTable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASPageTable.m; sourceTree = ""; }; E55D86311CA8A14000A0C26F /* ASLayoutElement.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASLayoutElement.mm; sourceTree = ""; }; 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 = ""; }; + E5775AFB1F13CE9F00CAC9BC /* _ASCollectionGalleryLayoutItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _ASCollectionGalleryLayoutItem.h; sourceTree = ""; }; + E5775AFD1F13CF7400CAC9BC /* _ASCollectionGalleryLayoutItem.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = _ASCollectionGalleryLayoutItem.mm; sourceTree = ""; }; + E5775AFF1F13D25400CAC9BC /* ASCollectionLayoutState+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASCollectionLayoutState+Private.h"; sourceTree = ""; }; + E5775B011F16759300CAC9BC /* ASCollectionLayoutCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCollectionLayoutCache.h; sourceTree = ""; }; + E5775B031F16759F00CAC9BC /* ASCollectionLayoutCache.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASCollectionLayoutCache.mm; sourceTree = ""; }; + E5855DED1EBB4D83003639AE /* ASCollectionLayoutDefines.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASCollectionLayoutDefines.m; sourceTree = ""; }; + E5855DEE1EBB4D83003639AE /* ASCollectionLayoutDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCollectionLayoutDefines.h; sourceTree = ""; }; + E58E9E3D1E941D74004CFC59 /* ASCollectionFlowLayoutDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCollectionFlowLayoutDelegate.h; sourceTree = ""; }; + E58E9E3E1E941D74004CFC59 /* ASCollectionFlowLayoutDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASCollectionFlowLayoutDelegate.m; sourceTree = ""; }; + E58E9E3F1E941D74004CFC59 /* ASCollectionLayoutContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCollectionLayoutContext.h; sourceTree = ""; }; + E58E9E401E941D74004CFC59 /* ASCollectionLayoutContext.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASCollectionLayoutContext.m; sourceTree = ""; }; + E58E9E411E941D74004CFC59 /* ASCollectionLayoutDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCollectionLayoutDelegate.h; sourceTree = ""; }; + E58E9E471E941DA5004CFC59 /* ASCollectionLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCollectionLayout.h; sourceTree = ""; }; + E58E9E481E941DA5004CFC59 /* ASCollectionLayout.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASCollectionLayout.mm; sourceTree = ""; }; + E5ABAC791E8564EE007AC15C /* ASRectTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASRectTable.h; sourceTree = ""; }; + E5ABAC7A1E8564EE007AC15C /* ASRectTable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASRectTable.m; sourceTree = ""; }; + E5B077FD1E69F4EB00C24B5B /* ASElementMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASElementMap.h; sourceTree = ""; }; + E5B077FE1E69F4EB00C24B5B /* ASElementMap.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASElementMap.m; sourceTree = ""; }; + E5B225261F1790B5001E1431 /* ASHashing.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ASHashing.m; sourceTree = ""; }; + E5B225271F1790B5001E1431 /* ASHashing.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ASHashing.h; sourceTree = ""; }; + E5B2252D1F17E521001E1431 /* ASDispatch.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ASDispatch.m; sourceTree = ""; }; + E5B5B9D01E9BAD9800A6B726 /* ASCollectionLayoutContext+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASCollectionLayoutContext+Private.h"; sourceTree = ""; }; + E5C347B01ECB3D9200EC4BE4 /* ASBatchFetchingDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASBatchFetchingDelegate.h; sourceTree = ""; }; + E5C347B21ECB40AA00EC4BE4 /* ASTableNode+Beta.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASTableNode+Beta.h"; sourceTree = ""; }; + E5E281731E71C833006B67C2 /* ASCollectionLayoutState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCollectionLayoutState.h; sourceTree = ""; }; + E5E281751E71C845006B67C2 /* ASCollectionLayoutState.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASCollectionLayoutState.mm; sourceTree = ""; }; + E5E2D72D1EA780C4005C24C6 /* ASCollectionGalleryLayoutDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCollectionGalleryLayoutDelegate.h; sourceTree = ""; }; + E5E2D72F1EA780DF005C24C6 /* ASCollectionGalleryLayoutDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = ASCollectionGalleryLayoutDelegate.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; 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 = ""; }; @@ -911,6 +1079,7 @@ 058D09DA195D050800B7D73C /* ASDisplayNode+Subclasses.h */, CC034A071E60BEB400626263 /* ASDisplayNode+Convenience.h */, CC034A081E60BEB400626263 /* ASDisplayNode+Convenience.m */, + 69BCE3D71EC6513B007DCCAD /* ASDisplayNode+Layout.mm */, 058D09DB195D050800B7D73C /* ASDisplayNodeExtras.h */, 058D09DC195D050800B7D73C /* ASDisplayNodeExtras.mm */, 0587F9BB1A7309ED00AFF0BA /* ASEditableTextNode.h */, @@ -924,6 +1093,8 @@ 0516FA3F1A1563D200B4EBED /* ASMultiplexImageNode.mm */, 68FC85DC1CE29AB700EDD713 /* ASNavigationController.h */, 68FC85DD1CE29AB700EDD713 /* ASNavigationController.m */, + CC6AA2D81E9F03B900978E87 /* ASDisplayNode+Ancestry.h */, + CC6AA2D91E9F03B900978E87 /* ASDisplayNode+Ancestry.m */, 055B9FA61A1C154B00035D6D /* ASNetworkImageNode.h */, 055B9FA71A1C154B00035D6D /* ASNetworkImageNode.mm */, 698371D91E4379CD00437585 /* ASNodeController+Beta.h */, @@ -932,6 +1103,7 @@ 25E327551C16819500A2170C /* ASPagerNode.m */, A2763D771CBDD57D00A9ADBD /* ASPINRemoteImageDownloader.h */, A2763D781CBDD57D00A9ADBD /* ASPINRemoteImageDownloader.m */, + CCBBBF5C1EB161760069AA91 /* ASRangeManagingNode.h */, ACE87A2B1D73696800D7FF06 /* ASSectionContext.h */, D785F6601A74327E00291744 /* ASScrollNode.h */, D785F6611A74327E00291744 /* ASScrollNode.mm */, @@ -939,6 +1111,7 @@ 68FC85E11CE29B7E00EDD713 /* ASTabBarController.m */, B0F880581BEAEC7500D17647 /* ASTableNode.h */, 9CFFC6C11CCAC768006A6476 /* ASTableNode.mm */, + E5C347B21ECB40AA00EC4BE4 /* ASTableNode+Beta.h */, 055F1A3219ABD3E3004DAFF1 /* ASTableView.h */, 055F1A3319ABD3E3004DAFF1 /* ASTableView.mm */, AC7A2C161BDE11DF0093FE1A /* ASTableViewInternal.h */, @@ -946,6 +1119,8 @@ 058D09DF195D050800B7D73C /* ASTextNode.h */, A373200E1C571B050011FC94 /* ASTextNode+Beta.h */, 058D09E0195D050800B7D73C /* ASTextNode.mm */, + 909C4C731F09C98B00D6B76F /* ASTextNode2.h */, + 909C4C741F09C98B00D6B76F /* ASTextNode2.mm */, ACC945A81BA9E7A0005E1FB8 /* ASViewController.h */, 9CFFC6BF1CCAC73C006A6476 /* ASViewController.mm */, 6BDC61F51978FEA400E50D21 /* AsyncDisplayKit.h */, @@ -978,13 +1153,15 @@ 058D09C5195D04C000B7D73C /* Tests */ = { isa = PBXGroup; children = ( + CC583ABF1EF9BAB400134156 /* Common */, + CCDD148A1EEDCD9D0020834E /* ASCollectionModernDataSourceTests.m */, CC034A0F1E60C9BF00626263 /* ASRectTableTests.m */, CC11F9791DB181180024D77B /* ASNetworkImageNodeTests.m */, CC051F1E1D7A286A006434CB /* ASCALayerTests.m */, + CCE4F9B21F0D60AC00062E4E /* ASIntegerMapTests.m */, CC8B05D71D73979700F54286 /* ASTextNodePerformanceTests.m */, CC8B05D41D73836400F54286 /* ASPerformanceTestContext.h */, CC8B05D51D73836400F54286 /* ASPerformanceTestContext.m */, - 69B225681D7265DA00B25B22 /* ASXCTExtensions.h */, CC54A81D1D7008B300296A24 /* ASDispatchTests.m */, CCA221D21D6FA7EF00AF6A0F /* ASViewControllerTests.m */, CC0AEEA31D66316E005D1C78 /* ASUICollectionViewTests.m */, @@ -1024,6 +1201,7 @@ 058D0A30195D057000B7D73C /* ASDisplayNodeTestsHelper.h */, 058D0A31195D057000B7D73C /* ASDisplayNodeTestsHelper.m */, 697B31591CFE4B410049936F /* ASEditableTextNodeTests.m */, + E51B78BD1F01A0EE00E32604 /* ASLayoutFlatteningTests.m */, 052EE0651A159FEF002C6279 /* ASMultiplexImageNodeTests.m */, 058D0A32195D057000B7D73C /* ASMutableAttributedStringBuilderTests.m */, 3C9C128419E616EF00E942A0 /* ASTableViewTests.mm */, @@ -1059,8 +1237,12 @@ 058D09E1195D050800B7D73C /* Details */ = { isa = PBXGroup; children = ( + CC5601391F06E9A700DC4FBE /* ASIntegerMap.h */, + CC56013A1F06E9A700DC4FBE /* ASIntegerMap.mm */, CC0F885E1E4280B800576FED /* _ASCollectionViewCell.h */, CC0F885D1E4280B800576FED /* _ASCollectionViewCell.m */, + 3917EBD21E9C2FC400D04A01 /* _ASCollectionReusableView.h */, + 3917EBD31E9C2FC400D04A01 /* _ASCollectionReusableView.m */, 058D09E2195D050800B7D73C /* _ASDisplayLayer.h */, 058D09E3195D050800B7D73C /* _ASDisplayLayer.mm */, 058D09E4195D050800B7D73C /* _ASDisplayView.h */, @@ -1073,12 +1255,15 @@ 054963481A1EA066000F8E56 /* ASBasicImageDownloader.mm */, 299DA1A71A828D2900162D41 /* ASBatchContext.h */, 299DA1A81A828D2900162D41 /* ASBatchContext.mm */, + E5C347B01ECB3D9200EC4BE4 /* ASBatchFetchingDelegate.h */, 68C215561DE10D330019C4BC /* ASCollectionViewLayoutInspector.h */, 68C215571DE10D330019C4BC /* ASCollectionViewLayoutInspector.m */, 205F0E1B1B373A2C007741D0 /* ASCollectionViewLayoutController.h */, 205F0E1C1B373A2C007741D0 /* ASCollectionViewLayoutController.m */, 696F01EA1DD2AF450049FBD5 /* ASEventLog.h */, 696F01EB1DD2AF450049FBD5 /* ASEventLog.mm */, + E5B225271F1790B5001E1431 /* ASHashing.h */, + E5B225261F1790B5001E1431 /* ASHashing.m */, 4640521B1A3F83C40061C0BA /* ASTableLayoutController.h */, 4640521C1A3F83C40061C0BA /* ASTableLayoutController.m */, 058D09E6195D050800B7D73C /* ASHighlightOverlayLayer.h */, @@ -1117,6 +1302,7 @@ 205F0E1F1B376416007741D0 /* CoreGraphics+ASConvenience.h */, 205F0E201B376416007741D0 /* CoreGraphics+ASConvenience.m */, 25B171EA1C12242700508A7A /* Data Controller */, + E5B077EB1E6843AF00C24B5B /* Collection Layout */, DBC452D91C5BF64600B16017 /* NSArray+Diffing.h */, DBC452DA1C5BF64600B16017 /* NSArray+Diffing.m */, CC4981BA1D1C7F65004E13CC /* NSIndexSet+ASHelpers.h */, @@ -1148,16 +1334,29 @@ 058D0A01195D050800B7D73C /* Private */ = { isa = PBXGroup; children = ( + CCA282CE1E9EBF6C0037E8B7 /* ASTipsWindow.h */, + CCA282CF1E9EBF6C0037E8B7 /* ASTipsWindow.m */, + CCA282C61E9EB64B0037E8B7 /* ASDisplayNodeTipState.h */, + CCA282C71E9EB64B0037E8B7 /* ASDisplayNodeTipState.m */, + CCA282CA1E9EB73E0037E8B7 /* ASTipNode.h */, + CCA282CB1E9EB73E0037E8B7 /* ASTipNode.m */, + CCA282BE1E9EAE010037E8B7 /* ASTip.h */, + CCA282BF1E9EAE010037E8B7 /* ASTip.m */, + CCA282C21E9EAE630037E8B7 /* ASLayerBackingTipProvider.h */, + CCA282C31E9EAE630037E8B7 /* ASLayerBackingTipProvider.m */, + CCA282BA1E9EABDD0037E8B7 /* ASTipProvider.h */, + CCA282BB1E9EABDD0037E8B7 /* ASTipProvider.m */, + CCA282B21E9EA7310037E8B7 /* ASTipsController.h */, + CCA282B31E9EA7310037E8B7 /* ASTipsController.m */, CC2F65EC1E5FFB1600DA57C9 /* ASMutableElementMap.h */, CC2F65ED1E5FFB1600DA57C9 /* ASMutableElementMap.m */, - CC0349FF1E5FAF9700626263 /* ASElementMap.h */, - CC034A001E5FAF9700626263 /* ASElementMap.m */, - CC034A0B1E60C3D500626263 /* ASRectTable.h */, - CC034A0C1E60C3D500626263 /* ASRectTable.m */, + E5ABAC791E8564EE007AC15C /* ASRectTable.h */, + E5ABAC7A1E8564EE007AC15C /* ASRectTable.m */, CC55A70F1E52A0F200594372 /* ASResponderChainEnumerator.h */, CC55A7101E52A0F200594372 /* ASResponderChainEnumerator.m */, 6947B0BB1E36B4E30007C478 /* Layout */, CCE04B2A1E313EDA006AEBBB /* Collection Data Adapter */, + E52F8AEE1EAE659600B5A912 /* Collection Layout */, 058D0A03195D050800B7D73C /* _ASCoreAnimationExtras.h */, 058D0A04195D050800B7D73C /* _ASCoreAnimationExtras.mm */, AC026B6D1BD57DBF00BBC17E /* _ASHierarchyChangeSet.h */, @@ -1181,6 +1380,7 @@ AEB7B0181C5962EA00662EF4 /* ASDefaultPlayButton.h */, AEB7B0191C5962EA00662EF4 /* ASDefaultPlayButton.m */, CC54A81B1D70077A00296A24 /* ASDispatch.h */, + E5B2252D1F17E521001E1431 /* ASDispatch.m */, 058D0A08195D050800B7D73C /* ASDisplayNode+AsyncDisplay.mm */, 058D0A09195D050800B7D73C /* ASDisplayNode+DebugTiming.h */, 058D0A0A195D050800B7D73C /* ASDisplayNode+DebugTiming.mm */, @@ -1205,6 +1405,7 @@ CC512B841DAC45C60054848E /* ASTableView+Undeprecated.h */, 83A7D9581D44542100BF333E /* ASWeakMap.h */, 83A7D9591D44542100BF333E /* ASWeakMap.m */, + CCCCCCC11EC3EF060087FE10 /* TextExperiment */, ); path = Private; sourceTree = ""; @@ -1213,10 +1414,13 @@ isa = PBXGroup; children = ( 058D0A43195D058D00B7D73C /* ASAssert.h */, + CCA5F62D1EECC2A80060C137 /* ASAssert.m */, 0516FA3A1A15563400B4EBED /* ASAvailability.h */, 058D0A44195D058D00B7D73C /* ASBaseDefines.h */, 1950C4481A3BB5C1005C8279 /* ASEqualityHelpers.h */, 0516FA3B1A15563400B4EBED /* ASLog.h */, + CCB1F9591EFB60A5009C7475 /* ASLog.m */, + CCB1F95B1EFB6316009C7475 /* ASSignpost.h */, ); path = Base; sourceTree = ""; @@ -1224,8 +1428,6 @@ 257754661BED245B00737CA5 /* TextKit */ = { isa = PBXGroup; children = ( - 69708BA41D76386D005C3CF9 /* ASEqualityHashHelpers.h */, - 69708BA51D76386D005C3CF9 /* ASEqualityHashHelpers.mm */, B30BF6501C5964B0004FCD53 /* ASLayoutManager.h */, B30BF6511C5964B0004FCD53 /* ASLayoutManager.m */, 257754BA1BEE458E00737CA5 /* ASTextKitComponents.h */, @@ -1267,6 +1469,8 @@ 4640521A1A3F83C40061C0BA /* ASDataController.mm */, E5711A2A1C840C81009619D4 /* ASCollectionElement.h */, E5711A2D1C840C96009619D4 /* ASCollectionElement.mm */, + E5B077FD1E69F4EB00C24B5B /* ASElementMap.h */, + E5B077FE1E69F4EB00C24B5B /* ASElementMap.m */, AC6145401D8AFAE8003D62A2 /* ASSection.h */, AC6145421D8AFD4F003D62A2 /* ASSection.m */, ); @@ -1339,10 +1543,78 @@ 9C49C36E1B853957000B0DD5 /* ASStackLayoutElement.h */, ACF6ED161B17843500DA7C62 /* ASStackLayoutSpec.h */, ACF6ED171B17843500DA7C62 /* ASStackLayoutSpec.mm */, + 9019FBB91ED8061D00C45F72 /* ASYogaLayoutSpec.h */, + 9019FBBA1ED8061D00C45F72 /* ASYogaLayoutSpec.mm */, + 9019FBBB1ED8061D00C45F72 /* ASYogaUtilities.h */, + 9019FBBC1ED8061D00C45F72 /* ASYogaUtilities.mm */, ); path = Layout; sourceTree = ""; }; + CC583ABF1EF9BAB400134156 /* Common */ = { + isa = PBXGroup; + children = ( + CC583AC01EF9BAB400134156 /* ASDisplayNode+OCMock.m */, + CC583AC11EF9BAB400134156 /* ASTestCase.h */, + CC583AC21EF9BAB400134156 /* ASTestCase.m */, + CC583AC31EF9BAB400134156 /* ASXCTExtensions.h */, + CC583AC41EF9BAB400134156 /* NSInvocation+ASTestHelpers.h */, + CC583AC51EF9BAB400134156 /* NSInvocation+ASTestHelpers.m */, + CC583AC61EF9BAB400134156 /* OCMockObject+ASAdditions.h */, + CC583AC71EF9BAB400134156 /* OCMockObject+ASAdditions.m */, + ); + path = Common; + sourceTree = ""; + }; + CCCCCCC11EC3EF060087FE10 /* TextExperiment */ = { + isa = PBXGroup; + children = ( + CCCCCCC21EC3EF060087FE10 /* Component */, + CCCCCCCB1EC3EF060087FE10 /* String */, + CCCCCCD01EC3EF060087FE10 /* Utility */, + ); + path = TextExperiment; + sourceTree = ""; + }; + CCCCCCC21EC3EF060087FE10 /* Component */ = { + isa = PBXGroup; + children = ( + CCCCCCC31EC3EF060087FE10 /* ASTextDebugOption.h */, + CCCCCCC41EC3EF060087FE10 /* ASTextDebugOption.m */, + CCCCCCC51EC3EF060087FE10 /* ASTextInput.h */, + CCCCCCC61EC3EF060087FE10 /* ASTextInput.m */, + CCCCCCC71EC3EF060087FE10 /* ASTextLayout.h */, + CCCCCCC81EC3EF060087FE10 /* ASTextLayout.m */, + CCCCCCC91EC3EF060087FE10 /* ASTextLine.h */, + CCCCCCCA1EC3EF060087FE10 /* ASTextLine.m */, + ); + path = Component; + sourceTree = ""; + }; + CCCCCCCB1EC3EF060087FE10 /* String */ = { + isa = PBXGroup; + children = ( + CCCCCCCC1EC3EF060087FE10 /* ASTextAttribute.h */, + CCCCCCCD1EC3EF060087FE10 /* ASTextAttribute.m */, + CCCCCCCE1EC3EF060087FE10 /* ASTextRunDelegate.h */, + CCCCCCCF1EC3EF060087FE10 /* ASTextRunDelegate.m */, + ); + path = String; + sourceTree = ""; + }; + CCCCCCD01EC3EF060087FE10 /* Utility */ = { + isa = PBXGroup; + children = ( + CCCCCCD11EC3EF060087FE10 /* ASTextUtilities.h */, + CCCCCCD21EC3EF060087FE10 /* ASTextUtilities.m */, + CCCCCCE51EC3F0FC0087FE10 /* NSAttributedString+ASText.h */, + CCCCCCE61EC3F0FC0087FE10 /* NSAttributedString+ASText.m */, + CCCCCCD31EC3EF060087FE10 /* NSParagraphStyle+ASText.h */, + CCCCCCD41EC3EF060087FE10 /* NSParagraphStyle+ASText.m */, + ); + path = Utility; + sourceTree = ""; + }; CCE04B1D1E313E99006AEBBB /* Collection Data Adapter */ = { isa = PBXGroup; children = ( @@ -1376,12 +1648,49 @@ DE89C1691DCEB9CC00D49D74 /* Debug */ = { isa = PBXGroup; children = ( + CCA282B61E9EA8E40037E8B7 /* AsyncDisplayKit+Tips.h */, + CCA282B71E9EA8E40037E8B7 /* AsyncDisplayKit+Tips.m */, 764D83D21C8EA515009B4FB8 /* AsyncDisplayKit+Debug.h */, 764D83D31C8EA515009B4FB8 /* AsyncDisplayKit+Debug.m */, ); path = Debug; sourceTree = ""; }; + E52F8AEE1EAE659600B5A912 /* Collection Layout */ = { + isa = PBXGroup; + children = ( + E58E9E471E941DA5004CFC59 /* ASCollectionLayout.h */, + E58E9E481E941DA5004CFC59 /* ASCollectionLayout.mm */, + E5775B011F16759300CAC9BC /* ASCollectionLayoutCache.h */, + E5775B031F16759F00CAC9BC /* ASCollectionLayoutCache.mm */, + E5B5B9D01E9BAD9800A6B726 /* ASCollectionLayoutContext+Private.h */, + E5855DEE1EBB4D83003639AE /* ASCollectionLayoutDefines.h */, + E5855DED1EBB4D83003639AE /* ASCollectionLayoutDefines.m */, + E5775AFF1F13D25400CAC9BC /* ASCollectionLayoutState+Private.h */, + E5775AFB1F13CE9F00CAC9BC /* _ASCollectionGalleryLayoutItem.h */, + E5775AFD1F13CF7400CAC9BC /* _ASCollectionGalleryLayoutItem.mm */, + ); + name = "Collection Layout"; + sourceTree = ""; + }; + E5B077EB1E6843AF00C24B5B /* Collection Layout */ = { + isa = PBXGroup; + children = ( + E58E9E3F1E941D74004CFC59 /* ASCollectionLayoutContext.h */, + E58E9E401E941D74004CFC59 /* ASCollectionLayoutContext.m */, + E5E281731E71C833006B67C2 /* ASCollectionLayoutState.h */, + E5E281751E71C845006B67C2 /* ASCollectionLayoutState.mm */, + E58E9E411E941D74004CFC59 /* ASCollectionLayoutDelegate.h */, + E58E9E3D1E941D74004CFC59 /* ASCollectionFlowLayoutDelegate.h */, + E58E9E3E1E941D74004CFC59 /* ASCollectionFlowLayoutDelegate.m */, + E5E2D72D1EA780C4005C24C6 /* ASCollectionGalleryLayoutDelegate.h */, + E5E2D72F1EA780DF005C24C6 /* ASCollectionGalleryLayoutDelegate.m */, + E54E81FA1EB357BD00FFE8E1 /* ASPageTable.h */, + E54E81FB1EB357BD00FFE8E1 /* ASPageTable.m */, + ); + name = "Collection Layout"; + sourceTree = ""; + }; FD40E2760492F0CAAEAD552D /* Pods */ = { isa = PBXGroup; children = ( @@ -1399,8 +1708,19 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + E5B225281F1790D6001E1431 /* ASHashing.h in Headers */, + CC034A131E649F1300626263 /* AsyncDisplayKit+IGListKitMethods.h in Headers */, + 693A1DCA1ECC944E00D0C9D2 /* IGListAdapter+AsyncDisplayKit.h in Headers */, + E5E2D72E1EA780C4005C24C6 /* ASCollectionGalleryLayoutDelegate.h in Headers */, + E58E9E461E941D74004CFC59 /* ASCollectionLayoutDelegate.h in Headers */, + E5E281741E71C833006B67C2 /* ASCollectionLayoutState.h in Headers */, + E5B077FF1E69F4EB00C24B5B /* ASElementMap.h in Headers */, + CCCCCCE31EC3EF060087FE10 /* NSParagraphStyle+ASText.h in Headers */, + E58E9E441E941D74004CFC59 /* ASCollectionLayoutContext.h in Headers */, + E58E9E421E941D74004CFC59 /* ASCollectionFlowLayoutDelegate.h in Headers */, 696F01EC1DD2AF450049FBD5 /* ASEventLog.h in Headers */, 690C35641E055C7B00069B91 /* ASDimensionInternal.h in Headers */, + 3917EBD41E9C2FC400D04A01 /* _ASCollectionReusableView.h in Headers */, 690C356B1E05680300069B91 /* ASDimensionDeprecated.h in Headers */, 683489281D70DE3400327501 /* ASDisplayNode+Deprecated.h in Headers */, 698371DB1E4379CD00437585 /* ASNodeController+Beta.h in Headers */, @@ -1416,19 +1736,23 @@ 68C215581DE10D330019C4BC /* ASCollectionViewLayoutInspector.h in Headers */, B35062411B010EFD0018CF92 /* _ASAsyncTransactionGroup.h in Headers */, B350620F1B010EFD0018CF92 /* _ASDisplayLayer.h in Headers */, + CCCCCCD71EC3EF060087FE10 /* ASTextInput.h in Headers */, B35062111B010EFD0018CF92 /* _ASDisplayView.h in Headers */, 9C55866C1BD54A3000B50E3A /* ASAsciiArtBoxCreator.h in Headers */, 509E68611B3AEDA0009B9150 /* ASAbstractLayoutController.h in Headers */, + CCA282B81E9EA8E40037E8B7 /* AsyncDisplayKit+Tips.h in Headers */, B35062571B010F070018CF92 /* ASAssert.h in Headers */, + CCBBBF5D1EB161760069AA91 /* ASRangeManagingNode.h in Headers */, B35062581B010F070018CF92 /* ASAvailability.h in Headers */, + 9019FBBF1ED8061D00C45F72 /* ASYogaUtilities.h in Headers */, DE84918D1C8FFF2B003D89E9 /* ASRunLoopQueue.h in Headers */, CC0F88621E4281E200576FED /* ASSectionController.h in Headers */, + CCB1F95C1EFB6350009C7475 /* ASSignpost.h in Headers */, A2763D7A1CBDD57D00A9ADBD /* ASPINRemoteImageDownloader.h in Headers */, 34EFC7611B701C9C00AD841F /* ASBackgroundLayoutSpec.h in Headers */, 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 */, @@ -1438,13 +1762,17 @@ 509E68631B3AEDB4009B9150 /* ASCollectionViewLayoutController.h in Headers */, B35061F71B010EFD0018CF92 /* ASCollectionViewProtocols.h in Headers */, 68FC85E31CE29B7E00EDD713 /* ASTabBarController.h in Headers */, + CC56013B1F06E9A700DC4FBE /* ASIntegerMap.h in Headers */, B35061FA1B010EFD0018CF92 /* ASControlNode+Subclasses.h in Headers */, + E54E81FC1EB357BD00FFE8E1 /* ASPageTable.h in Headers */, B35061F81B010EFD0018CF92 /* ASControlNode.h in Headers */, B35062171B010EFD0018CF92 /* ASDataController.h in Headers */, 34EFC75B1B701BAF00AD841F /* ASDimension.h in Headers */, 68FC85EA1CE29C7D00EDD713 /* ASVisibilityProtocols.h in Headers */, + CCCCCCD91EC3EF060087FE10 /* ASTextLayout.h in Headers */, A37320101C571B740011FC94 /* ASTextNode+Beta.h in Headers */, 9C70F2061CDA4F0C007D6C76 /* ASTraitCollection.h in Headers */, + CC6AA2DA1E9F03B900978E87 /* ASDisplayNode+Ancestry.h in Headers */, 8021EC1D1D2B00B100799119 /* UIImage+ASConvenience.h in Headers */, B35061FD1B010EFD0018CF92 /* ASDisplayNode+Subclasses.h in Headers */, B35061FB1B010EFD0018CF92 /* ASDisplayNode.h in Headers */, @@ -1465,11 +1793,14 @@ 698DFF471E36B7E9002891F1 /* ASLayoutSpecUtilities.h in Headers */, 9C70F20D1CDBE9CB007D6C76 /* ASDefaultPlayButton.h in Headers */, DE7EF4F81DFF77720082B84A /* ASDisplayNode+FrameworkSubclasses.h in Headers */, + CCCCCCD51EC3EF060087FE10 /* ASTextDebugOption.h in Headers */, CC034A091E60BEB400626263 /* ASDisplayNode+Convenience.h in Headers */, 254C6B7E1BF94DF4003EC431 /* ASTextKitTailTruncater.h in Headers */, B35062491B010EFD0018CF92 /* _ASCoreAnimationExtras.h in Headers */, 68EE0DBE1C1B4ED300BA1B99 /* ASMainSerialQueue.h in Headers */, + CCCCCCE11EC3EF060087FE10 /* ASTextUtilities.h in Headers */, B350624B1B010EFD0018CF92 /* _ASPendingState.h in Headers */, + E5C347B11ECB3D9200EC4BE4 /* ASBatchFetchingDelegate.h in Headers */, CC54A81C1D70079800296A24 /* ASDispatch.h in Headers */, B350624D1B010EFD0018CF92 /* _ASScopeTimer.h in Headers */, CC0F88631E4281E700576FED /* ASSupplementaryNodeSource.h in Headers */, @@ -1482,10 +1813,15 @@ 69CB62AC1CB8165900024920 /* _ASDisplayViewAccessiblity.h in Headers */, 254C6B7C1BF94DF4003EC431 /* ASTextKitRenderer+TextChecking.h in Headers */, 68AF37DB1CBEF4D80077BF76 /* ASImageNode+AnimatedImagePrivate.h in Headers */, + CCCCCCDD1EC3EF060087FE10 /* ASTextAttribute.h in Headers */, B35062461B010EFD0018CF92 /* ASBasicImageDownloaderInternal.h in Headers */, 044285081BAA63FE00D16268 /* ASBatchFetching.h in Headers */, AC026B701BD57DBF00BBC17E /* _ASHierarchyChangeSet.h in Headers */, CC87BB951DA8193C0090E380 /* ASCellNode+Internal.h in Headers */, + E5775B021F16759300CAC9BC /* ASCollectionLayoutCache.h in Headers */, + E5775B001F13D25400CAC9BC /* ASCollectionLayoutState+Private.h in Headers */, + E5855DF01EBB4D83003639AE /* ASCollectionLayoutDefines.h in Headers */, + E5B5B9D11E9BAD9800A6B726 /* ASCollectionLayoutContext+Private.h in Headers */, 9C8898BD1C738BB800D6B02E /* ASTextKitFontSizeAdjuster.h in Headers */, 254C6B791BF94DF4003EC431 /* ASTextKitEntityAttribute.h in Headers */, CC3B20841C3F76D600798563 /* ASPendingStateController.h in Headers */, @@ -1497,17 +1833,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 */, + E58E9E491E941DA5004CFC59 /* ASCollectionLayout.h in Headers */, 254C6B7F1BF94DF4003EC431 /* ASTextKitTruncating.h in Headers */, CC58AA4B1E398E1D002C8CB4 /* ASBlockTypes.h in Headers */, + CCA282BC1E9EABDD0037E8B7 /* ASTipProvider.h in Headers */, 6977965F1D8AC8D3007E93D7 /* ASLayoutSpec+Subclasses.h in Headers */, 692BE8D71E36B65B00C86D87 /* ASLayoutSpecPrivate.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 /* ASTwoDimensionalArrayUtils.h in Headers */, @@ -1520,8 +1856,8 @@ 698DFF441E36B6C9002891F1 /* ASStackLayoutSpecUtilities.h in Headers */, CCF18FF41D2575E300DF5895 /* NSIndexSet+ASHelpers.h in Headers */, 83A7D95C1D44548100BF333E /* ASWeakMap.h in Headers */, - 69708BA61D76386D005C3CF9 /* ASEqualityHashHelpers.h in Headers */, E5711A2C1C840C81009619D4 /* ASCollectionElement.h in Headers */, + 9019FBBD1ED8061D00C45F72 /* ASYogaLayoutSpec.h in Headers */, 6947B0BE1E36B4E30007C478 /* ASStackUnpositionedLayout.h in Headers */, CC4C2A771D88E3BF0039ACAB /* ASTraceEvent.h in Headers */, 254C6B7B1BF94DF4003EC431 /* ASTextKitRenderer+Positioning.h in Headers */, @@ -1533,31 +1869,44 @@ B35062211B010EFD0018CF92 /* ASLayoutRangeType.h in Headers */, CC2F65EE1E5FFB1600DA57C9 /* ASMutableElementMap.h in Headers */, 34EFC76A1B701CE600AD841F /* ASLayoutSpec.h in Headers */, + CCA282D01E9EBF6C0037E8B7 /* ASTipsWindow.h in Headers */, B350625C1B010F070018CF92 /* ASLog.h in Headers */, CC3B208A1C3F7A5400798563 /* ASWeakSet.h in Headers */, B35062041B010EFD0018CF92 /* ASMultiplexImageNode.h in Headers */, DECBD6E81BE56E1900CF4905 /* ASButtonNode.h in Headers */, B35062241B010EFD0018CF92 /* ASMutableAttributedStringBuilder.h in Headers */, B13CA0F81C519EBA00E031AB /* ASCollectionViewLayoutFacilitatorProtocol.h in Headers */, + 909C4C751F09C98B00D6B76F /* ASTextNode2.h in Headers */, B35062061B010EFD0018CF92 /* ASNetworkImageNode.h in Headers */, + CCA282C81E9EB64B0037E8B7 /* ASDisplayNodeTipState.h in Headers */, 34EFC76C1B701CED00AD841F /* ASOverlayLayoutSpec.h in Headers */, + E5ABAC7B1E8564EE007AC15C /* ASRectTable.h in Headers */, B35062261B010EFD0018CF92 /* ASRangeController.h in Headers */, 34EFC76E1B701CF400AD841F /* ASRatioLayoutSpec.h in Headers */, DB55C2671C641AE4004EDCF5 /* ASContextTransitioning.h in Headers */, + CCA282C41E9EAE630037E8B7 /* ASLayerBackingTipProvider.h in Headers */, + E5C347B31ECB40AA00EC4BE4 /* ASTableNode+Beta.h in Headers */, 6900C5F41E8072DA00BCD75C /* ASImageNode+Private.h in Headers */, 68B0277B1C1A79D60041016B /* ASDisplayNode+Beta.h in Headers */, B350622D1B010EFD0018CF92 /* ASScrollDirection.h in Headers */, 254C6B751BF94DF4003EC431 /* ASTextKitComponents.h in Headers */, B35062081B010EFD0018CF92 /* ASScrollNode.h in Headers */, + CCA282CC1E9EB73E0037E8B7 /* ASTipNode.h in Headers */, + E5775AFC1F13CE9F00CAC9BC /* _ASCollectionGalleryLayoutItem.h in Headers */, 25E327571C16819500A2170C /* ASPagerNode.h in Headers */, + CCCCCCDB1EC3EF060087FE10 /* ASTextLine.h in Headers */, 9C70F20E1CDBE9E5007D6C76 /* NSArray+Diffing.h in Headers */, + CCCCCCE71EC3F0FC0087FE10 /* NSAttributedString+ASText.h in Headers */, + CCCCCCDF1EC3EF060087FE10 /* ASTextRunDelegate.h in Headers */, 9C49C3701B853961000B0DD5 /* ASStackLayoutElement.h in Headers */, 34EFC7701B701CFA00AD841F /* ASStackLayoutDefines.h in Headers */, CC0F885C1E42807F00576FED /* ASCollectionViewFlowLayoutInspector.h in Headers */, 764D83D51C8EA515009B4FB8 /* AsyncDisplayKit+Debug.h in Headers */, CC7FD9E21BB603FF005CCB2B /* ASPhotosFrameworkImageRequest.h in Headers */, 254C6B761BF94DF4003EC431 /* ASTextNodeTypes.h in Headers */, + CCA282B41E9EA7310037E8B7 /* ASTipsController.h in Headers */, 34EFC7711B701CFF00AD841F /* ASStackLayoutSpec.h in Headers */, + CCA282C01E9EAE010037E8B7 /* ASTip.h in Headers */, 2767E9411BB19BD600EA9B77 /* ASViewController.h in Headers */, 92DD2FE81BF4D0A80074C9DD /* ASMapNode.h in Headers */, 9C6BB3B31B8CC9C200F13F52 /* ASAbsoluteLayoutElement.h in Headers */, @@ -1769,7 +2118,9 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + E51B78BF1F028ABF00E32604 /* ASLayoutFlatteningTests.m in Sources */, 29CDC2E21AAE70D000833CA4 /* ASBasicImageDownloaderContextTests.m in Sources */, + CC583AD71EF9BDC100134156 /* NSInvocation+ASTestHelpers.m in Sources */, CC051F1F1D7A286A006434CB /* ASCALayerTests.m in Sources */, 242995D31B29743C00090100 /* ASBasicImageDownloaderTests.m in Sources */, 296A0A351A951ABF005ACEAA /* ASBatchFetchingTests.m in Sources */, @@ -1784,14 +2135,17 @@ CCA221D31D6FA7EF00AF6A0F /* ASViewControllerTests.m in Sources */, 058D0A38195D057000B7D73C /* ASDisplayLayerTests.m in Sources */, 2538B6F31BC5D2A2003CA0B4 /* ASCollectionViewFlowLayoutInspectorTests.m in Sources */, + CC583AD61EF9BDBE00134156 /* ASTestCase.m in Sources */, 058D0A39195D057000B7D73C /* ASDisplayNodeAppearanceTests.m in Sources */, CCB2F34D1D63CCC6004E6DE9 /* ASDisplayNodeSnapshotTests.m in Sources */, AE6987C11DD04E1000B9E458 /* ASPagerNodeTests.m in Sources */, 058D0A3A195D057000B7D73C /* ASDisplayNodeTests.mm in Sources */, 696FCB311D6E46050093471E /* ASBackgroundLayoutSpecSnapshotTests.mm in Sources */, + CC583AD81EF9BDC300134156 /* OCMockObject+ASAdditions.m in Sources */, 69FEE53D1D95A9AF0086F066 /* ASLayoutElementStyleTests.m in Sources */, CC4981B31D1A02BE004E13CC /* ASTableViewThrashTests.m in Sources */, CC54A81E1D7008B300296A24 /* ASDispatchTests.m in Sources */, + CCE4F9B31F0D60AC00062E4E /* ASIntegerMapTests.m in Sources */, 058D0A3B195D057000B7D73C /* ASDisplayNodeTestsHelper.m in Sources */, 83A7D95E1D446A6E00BF333E /* ASWeakMapTests.m in Sources */, 056D21551ABCEF50001107EF /* ASImageNodeSnapshotTests.m in Sources */, @@ -1802,6 +2156,7 @@ 052EE0661A159FEF002C6279 /* ASMultiplexImageNodeTests.m in Sources */, 058D0A3C195D057000B7D73C /* ASMutableAttributedStringBuilderTests.m in Sources */, CC8B05D81D73979700F54286 /* ASTextNodePerformanceTests.m in Sources */, + CC583AD91EF9BDC600134156 /* ASDisplayNode+OCMock.m in Sources */, 697B315A1CFE4B410049936F /* ASEditableTextNodeTests.m in Sources */, ACF6ED611B178DC700DA7C62 /* ASOverlayLayoutSpecSnapshotTests.mm in Sources */, CC8B05D61D73836400F54286 /* ASPerformanceTestContext.m in Sources */, @@ -1809,6 +2164,7 @@ 69B225671D72535E00B25B22 /* ASDisplayNodeLayoutTests.mm in Sources */, ACF6ED621B178DC700DA7C62 /* ASRatioLayoutSpecSnapshotTests.mm in Sources */, 7AB338691C55B97B0055FDE8 /* ASRelativeLayoutSpecSnapshotTests.mm in Sources */, + CCDD148B1EEDCD9D0020834E /* ASCollectionModernDataSourceTests.m in Sources */, 254C6B541BF8FF2A003EC431 /* ASTextKitTests.mm in Sources */, 05EA6FE71AC0966E00E35788 /* ASSnapshotTestCase.m in Sources */, ACF6ED631B178DC700DA7C62 /* ASStackLayoutSpecSnapshotTests.mm in Sources */, @@ -1830,53 +2186,68 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + E5B225291F1790EE001E1431 /* ASHashing.m in Sources */, DEB8ED7C1DD003D300DBDE55 /* ASLayoutTransition.mm in Sources */, + CCA5F62E1EECC2A80060C137 /* ASAssert.m in Sources */, 9F98C0261DBE29E000476D92 /* ASControlTargetAction.m in Sources */, 9C70F2091CDABA36007D6C76 /* ASViewController.mm in Sources */, + 3917EBD51E9C2FC400D04A01 /* _ASCollectionReusableView.m in Sources */, + CCA282D11E9EBF6C0037E8B7 /* ASTipsWindow.m in Sources */, + CCCCCCE41EC3EF060087FE10 /* NSParagraphStyle+ASText.m in Sources */, 8BBBAB8D1CEBAF1E00107FC6 /* ASDefaultPlaybackButton.m in Sources */, B30BF6541C59D889004FCD53 /* ASLayoutManager.m in Sources */, 690C35671E0567C600069B91 /* ASDimensionDeprecated.mm in Sources */, 92DD2FE71BF4D0850074C9DD /* ASMapNode.mm in Sources */, + CCA282B91E9EA8E40037E8B7 /* AsyncDisplayKit+Tips.m in Sources */, 636EA1A51C7FF4EF00EE152F /* ASDefaultPlayButton.m in Sources */, B350623D1B010EFD0018CF92 /* _ASAsyncTransaction.mm in Sources */, 6947B0C51E36B5040007C478 /* ASStackPositionedLayout.mm in Sources */, B35062401B010EFD0018CF92 /* _ASAsyncTransactionContainer.m in Sources */, AC026B721BD57DBF00BBC17E /* _ASHierarchyChangeSet.mm in Sources */, B35062421B010EFD0018CF92 /* _ASAsyncTransactionGroup.m in Sources */, + CCA282BD1E9EABDD0037E8B7 /* ASTipProvider.m in Sources */, + 9019FBC01ED8061D00C45F72 /* ASYogaUtilities.mm in Sources */, B350624A1B010EFD0018CF92 /* _ASCoreAnimationExtras.mm in Sources */, 68EE0DC01C1B4ED300BA1B99 /* ASMainSerialQueue.mm in Sources */, B35062101B010EFD0018CF92 /* _ASDisplayLayer.mm in Sources */, 9C55866B1BD54A1900B50E3A /* ASAsciiArtBoxCreator.m in Sources */, B35062121B010EFD0018CF92 /* _ASDisplayView.mm in Sources */, DEFAD8131CC48914000527C4 /* ASVideoNode.mm in Sources */, + CCA282C11E9EAE010037E8B7 /* ASTip.m in Sources */, B350624C1B010EFD0018CF92 /* _ASPendingState.mm in Sources */, 698371DC1E4379CD00437585 /* ASNodeController+Beta.m in Sources */, - 69708BA81D76386D005C3CF9 /* ASEqualityHashHelpers.mm in Sources */, + CC6AA2DB1E9F03B900978E87 /* ASDisplayNode+Ancestry.m in Sources */, 509E68621B3AEDA5009B9150 /* ASAbstractLayoutController.mm in Sources */, 254C6B861BF94F8A003EC431 /* ASTextKitContext.mm in Sources */, DBDB83971C6E879900D0098C /* ASPagerFlowLayout.m in Sources */, + E5B078001E69F4EB00C24B5B /* ASElementMap.m in Sources */, 9C8898BC1C738BA800D6B02E /* ASTextKitFontSizeAdjuster.mm in Sources */, 690ED59B1E36D118000627C0 /* ASImageNode+tvOS.m in Sources */, + CCCCCCD81EC3EF060087FE10 /* ASTextInput.m in Sources */, 34EFC7621B701CA400AD841F /* ASBackgroundLayoutSpec.mm in Sources */, DE8BEAC41C2DF3FC00D57C12 /* ASDelegateProxy.m in Sources */, B35062141B010EFD0018CF92 /* ASBasicImageDownloader.mm in Sources */, B35062161B010EFD0018CF92 /* ASBatchContext.mm in Sources */, AC47D9421B3B891B00AAEE9D /* ASCellNode.mm in Sources */, + E58E9E451E941D74004CFC59 /* ASCollectionLayoutContext.m in Sources */, 34EFC7641B701CC600AD841F /* ASCenterLayoutSpec.mm in Sources */, 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 */, + E5775B041F16759F00CAC9BC /* ASCollectionLayoutCache.mm in Sources */, 9C70F20A1CDBE949007D6C76 /* ASTableNode.mm in Sources */, 69CB62AE1CB8165900024920 /* _ASDisplayViewAccessiblity.mm in Sources */, - CC034A021E5FAF9700626263 /* ASElementMap.m in Sources */, B35061F61B010EFD0018CF92 /* ASCollectionView.mm in Sources */, + CCA282C51E9EAE630037E8B7 /* ASLayerBackingTipProvider.m in Sources */, 509E68641B3AEDB7009B9150 /* ASCollectionViewLayoutController.m in Sources */, B35061F91B010EFD0018CF92 /* ASControlNode.mm in Sources */, 8021EC1F1D2B00B100799119 /* UIImage+ASConvenience.m in Sources */, B35062181B010EFD0018CF92 /* ASDataController.mm in Sources */, + CCB1F95A1EFB60A5009C7475 /* ASLog.m in Sources */, 767E7F8E1C90191D0066C000 /* AsyncDisplayKit+Debug.m in Sources */, + CCCCCCD61EC3EF060087FE10 /* ASTextDebugOption.m in Sources */, 34EFC75C1B701BD200AD841F /* ASDimension.mm in Sources */, B350624E1B010EFD0018CF92 /* ASDisplayNode+AsyncDisplay.mm in Sources */, 25E327591C16819500A2170C /* ASPagerNode.m in Sources */, @@ -1884,10 +2255,10 @@ 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 /* ASCollectionElement.mm in Sources */, B35062511B010EFD0018CF92 /* ASDisplayNode+UIViewBridge.mm in Sources */, + E5E281761E71C845006B67C2 /* ASCollectionLayoutState.mm in Sources */, B35061FC1B010EFD0018CF92 /* ASDisplayNode.mm in Sources */, B35061FF1B010EFD0018CF92 /* ASDisplayNodeExtras.mm in Sources */, B35062011B010EFD0018CF92 /* ASEditableTextNode.mm in Sources */, @@ -1899,25 +2270,34 @@ CC0F885F1E4280B800576FED /* _ASCollectionViewCell.m in Sources */, CC2F65EF1E5FFB1600DA57C9 /* ASMutableElementMap.m in Sources */, B35062541B010EFD0018CF92 /* ASImageNode+CGExtras.m in Sources */, + E58E9E4A1E941DA5004CFC59 /* ASCollectionLayout.mm in Sources */, 6947B0C01E36B4E30007C478 /* ASStackUnpositionedLayout.mm in Sources */, 68355B401CB57A69001D4E68 /* ASImageContainerProtocolCategories.m in Sources */, + E5855DEF1EBB4D83003639AE /* ASCollectionLayoutDefines.m in Sources */, B35062031B010EFD0018CF92 /* ASImageNode.mm in Sources */, 254C6B821BF94F8A003EC431 /* ASTextKitComponents.mm in Sources */, 34EFC7601B701C8B00AD841F /* ASInsetLayoutSpec.mm in Sources */, AC6145441D8AFD4F003D62A2 /* ASSection.m in Sources */, + E5775AFE1F13CF7400CAC9BC /* _ASCollectionGalleryLayoutItem.mm in Sources */, 34EFC75E1B701BF000AD841F /* ASInternalHelpers.m in Sources */, 34EFC7681B701CDE00AD841F /* ASLayout.mm in Sources */, DECBD6EA1BE56E1900CF4905 /* ASButtonNode.mm in Sources */, + CCCCCCE01EC3EF060087FE10 /* ASTextRunDelegate.m in Sources */, + CCCCCCDA1EC3EF060087FE10 /* ASTextLayout.m in Sources */, 254C6B841BF94F8A003EC431 /* ASTextNodeWordKerner.m in Sources */, + E5E2D7301EA780DF005C24C6 /* ASCollectionGalleryLayoutDelegate.m in Sources */, 34EFC76B1B701CEB00AD841F /* ASLayoutSpec.mm in Sources */, CC3B20861C3F76D600798563 /* ASPendingStateController.mm in Sources */, 254C6B8C1BF94F8A003EC431 /* ASTextKitTailTruncater.mm in Sources */, 6907C25A1DC4ECFE00374C66 /* ASObjectDescriptionHelpers.m in Sources */, B35062051B010EFD0018CF92 /* ASMultiplexImageNode.mm in Sources */, B35062251B010EFD0018CF92 /* ASMutableAttributedStringBuilder.m in Sources */, + 9019FBBE1ED8061D00C45F72 /* ASYogaLayoutSpec.mm in Sources */, B35062071B010EFD0018CF92 /* ASNetworkImageNode.mm in Sources */, 34EFC76D1B701CF100AD841F /* ASOverlayLayoutSpec.mm in Sources */, 044285101BAA64EC00D16268 /* ASTwoDimensionalArrayUtils.m in Sources */, + CCCCCCDE1EC3EF060087FE10 /* ASTextAttribute.m in Sources */, + CCA282B51E9EA7310037E8B7 /* ASTipsController.m in Sources */, B35062271B010EFD0018CF92 /* ASRangeController.mm in Sources */, 0442850A1BAA63FE00D16268 /* ASBatchFetching.m in Sources */, 68FC85E61CE29B9400EDD713 /* ASNavigationController.m in Sources */, @@ -1926,19 +2306,28 @@ 254C6B8B1BF94F8A003EC431 /* ASTextKitShadower.mm in Sources */, 254C6B851BF94F8A003EC431 /* ASTextKitAttributes.mm in Sources */, 90FC784F1E4BFE1B00383C5A /* ASDisplayNode+Yoga.mm in Sources */, + E5ABAC7C1E8564EE007AC15C /* ASRectTable.m in Sources */, + CCA282C91E9EB64B0037E8B7 /* ASDisplayNodeTipState.m in Sources */, 509E68601B3AED8E009B9150 /* ASScrollDirection.m in Sources */, B35062091B010EFD0018CF92 /* ASScrollNode.mm in Sources */, + 69BCE3D91EC6513B007DCCAD /* ASDisplayNode+Layout.mm in Sources */, 8BDA5FC81CDBDF95007D13B2 /* ASVideoPlayerNode.mm in Sources */, + E54E81FD1EB357BD00FFE8E1 /* ASPageTable.m in Sources */, 34EFC7721B701D0300AD841F /* ASStackLayoutSpec.mm in Sources */, 7AB338661C55B3420055FDE8 /* ASRelativeLayoutSpec.mm in Sources */, + E5B2252E1F17E521001E1431 /* ASDispatch.m in Sources */, 696F01EE1DD2AF450049FBD5 /* ASEventLog.mm in Sources */, 9C70F2051CDA4F06007D6C76 /* ASTraitCollection.m in Sources */, 83A7D95B1D44547700BF333E /* ASWeakMap.m in Sources */, CC034A0A1E60BEB400626263 /* ASDisplayNode+Convenience.m in Sources */, + E58E9E431E941D74004CFC59 /* ASCollectionFlowLayoutDelegate.m in Sources */, DE84918E1C8FFF9F003D89E9 /* ASRunLoopQueue.mm in Sources */, 68FC85E51CE29B7E00EDD713 /* ASTabBarController.m in Sources */, + CCCCCCDC1EC3EF060087FE10 /* ASTextLine.m in Sources */, 34EFC7741B701D0A00AD841F /* ASAbsoluteLayoutSpec.mm in Sources */, + CCCCCCE81EC3F0FC0087FE10 /* NSAttributedString+ASText.m in Sources */, 690C35621E055C5D00069B91 /* ASDimensionInternal.mm in Sources */, + 909C4C761F09C98B00D6B76F /* ASTextNode2.mm in Sources */, 68C2155A1DE10D330019C4BC /* ASCollectionViewLayoutInspector.m in Sources */, DB78412E1C6BCE1600A9E2B4 /* _ASTransitionContext.m in Sources */, B350620B1B010EFD0018CF92 /* ASTableView.mm in Sources */, @@ -1950,9 +2339,12 @@ 254C6B871BF94F8A003EC431 /* ASTextKitEntityAttribute.m in Sources */, 34566CB31BC1213700715E6B /* ASPhotosFrameworkImageRequest.m in Sources */, 254C6B831BF94F8A003EC431 /* ASTextKitCoreTextAdditions.m in Sources */, + CCCCCCE21EC3EF060087FE10 /* ASTextUtilities.m in Sources */, CC55A70E1E529FA200594372 /* UIResponder+AsyncDisplayKit.m in Sources */, + CC56013C1F06E9A700DC4FBE /* ASIntegerMap.mm in Sources */, 697796611D8AC8D3007E93D7 /* ASLayoutSpec+Subclasses.mm in Sources */, B350623B1B010EFD0018CF92 /* NSMutableAttributedString+TextKitAdditions.m in Sources */, + CCA282CD1E9EB73E0037E8B7 /* ASTipNode.m in Sources */, 044284FD1BAA365100D16268 /* UICollectionViewLayout+ASConvenience.m in Sources */, CC0F885B1E42807F00576FED /* ASCollectionViewFlowLayoutInspector.m in Sources */, 690ED5981E36D118000627C0 /* ASControlNode+tvOS.m in Sources */, @@ -2025,7 +2417,6 @@ ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_CODE_COVERAGE = NO; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BOOL_CONVERSION = YES; @@ -2045,7 +2436,6 @@ GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_GENERATE_TEST_COVERAGE_FILES = YES; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( @@ -2072,7 +2462,6 @@ ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_CODE_COVERAGE = NO; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BOOL_CONVERSION = YES; @@ -2091,7 +2480,6 @@ ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_GENERATE_TEST_COVERAGE_FILES = YES; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; @@ -2111,12 +2499,10 @@ baseConfigurationReference = FB07EABBCF28656C6297BC2D /* Pods-AsyncDisplayKitTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; - CLANG_ENABLE_CODE_COVERAGE = YES; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(DEVELOPER_FRAMEWORKS_DIR)", ); - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", @@ -2137,12 +2523,10 @@ baseConfigurationReference = D3779BCFF841AD3EB56537ED /* Pods-AsyncDisplayKitTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; - CLANG_ENABLE_CODE_COVERAGE = YES; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(DEVELOPER_FRAMEWORKS_DIR)", ); - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; GCC_PREPROCESSOR_DEFINITIONS = ( "$(inherited)", "COCOAPODS=1", @@ -2161,7 +2545,6 @@ isa = XCBuildConfiguration; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; - CLANG_ENABLE_CODE_COVERAGE = NO; CLANG_WARN_UNREACHABLE_CODE = YES; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; @@ -2170,17 +2553,18 @@ DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "Source/AsyncDisplayKit-Prefix.pch"; GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; 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 = Source/AsyncDisplayKit.modulemap; MTL_ENABLE_DEBUG_INFO = YES; - OTHER_CFLAGS = "-DMINIMAL_ASDK=1"; + OTHER_CFLAGS = ( + "-DMINIMAL_ASDK=1", + "-Wundef", + ); PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = AsyncDisplayKit; PROVISIONING_PROFILE_SPECIFIER = X834Q8SBVP/; @@ -2194,7 +2578,6 @@ isa = XCBuildConfiguration; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; - CLANG_ENABLE_CODE_COVERAGE = NO; CLANG_WARN_UNREACHABLE_CODE = YES; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; @@ -2204,16 +2587,17 @@ DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_PRECOMPILE_PREFIX_HEADER = YES; - 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 = Source/AsyncDisplayKit.modulemap; MTL_ENABLE_DEBUG_INFO = NO; - OTHER_CFLAGS = "-DMINIMAL_ASDK=1"; + OTHER_CFLAGS = ( + "-DMINIMAL_ASDK=1", + "-Wundef", + ); PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = AsyncDisplayKit; PROVISIONING_PROFILE_SPECIFIER = X834Q8SBVP/; @@ -2236,7 +2620,6 @@ ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_CODE_COVERAGE = NO; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BOOL_CONVERSION = YES; @@ -2256,7 +2639,6 @@ GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_GENERATE_TEST_COVERAGE_FILES = YES; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( @@ -2300,7 +2682,10 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = Source/AsyncDisplayKit.modulemap; MTL_ENABLE_DEBUG_INFO = YES; - OTHER_CFLAGS = "-DMINIMAL_ASDK=1"; + OTHER_CFLAGS = ( + "-DMINIMAL_ASDK=1", + "-Wundef", + ); PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = AsyncDisplayKit; PROVISIONING_PROFILE_SPECIFIER = X834Q8SBVP/; @@ -2315,12 +2700,10 @@ baseConfigurationReference = FB07EABBCF28656C6297BC2D /* Pods-AsyncDisplayKitTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; - CLANG_ENABLE_CODE_COVERAGE = YES; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(DEVELOPER_FRAMEWORKS_DIR)", ); - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", @@ -2417,7 +2800,6 @@ isa = XCBuildConfiguration; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; - CLANG_ENABLE_CODE_COVERAGE = NO; CLANG_WARN_UNREACHABLE_CODE = YES; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; @@ -2427,16 +2809,18 @@ DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "Source/AsyncDisplayKit-Prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = "PROFILE=1"; 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 = Source/AsyncDisplayKit.modulemap; MTL_ENABLE_DEBUG_INFO = NO; - OTHER_CFLAGS = "-DMINIMAL_ASDK=1"; + OTHER_CFLAGS = ( + "-DMINIMAL_ASDK=1", + "-Wundef", + ); PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = AsyncDisplayKit; PROVISIONING_PROFILE_SPECIFIER = X834Q8SBVP/; diff --git a/BUCK b/BUCK index c5d5e0178e..2527c2f053 100755 --- a/BUCK +++ b/BUCK @@ -88,7 +88,6 @@ asyncdisplaykit_library( for name in ['AsyncDisplayKit', 'AsyncDisplayKit-PINRemoteImage']: asyncdisplaykit_library( name = name, - additional_preprocessor_flags = ['-DPIN_REMOTE_IMAGE=1'], deps = [ '//Pods/PINRemoteImage:PINRemoteImage-PINCache', ], diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000000..b39354c6d4 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,55 @@ +## master + +* Add your own contributions to the next release on the line below this with your name. +- [ASTextNode2] Add initial implementation for link handling. [Scott Goodson](https://github.com/appleguy) [#396](https://github.com/TextureGroup/Texture/pull/396) +- [ASTextNode2] Provide compile flag to globally enable new implementation of ASTextNode: ASTEXTNODE_EXPERIMENT_GLOBAL_ENABLE. [Scott Goodson](https://github.com/appleguy) [#396](https://github.com/TextureGroup/Texture/pull/410) + - Add ASCollectionGalleryLayoutDelegate - an async collection layout that makes same-size collections (e.g photo galleries, pagers, etc) fast and lightweight! [Huy Nguyen](https://github.com/nguyenhuy/) [#76](https://github.com/TextureGroup/Texture/pull/76) + +##2.3.5 +- Fix an issue where inserting/deleting sections could lead to inconsistent supplementary element behavior. [Adlai Holler](https://github.com/Adlai-Holler) +- Overhaul logging and add activity tracing support. [Adlai Holler](https://github.com/Adlai-Holler) +- Fix a crash where scrolling a table view after entering editing mode could lead to bad internal states in the table. [Huy Nguyen](https://github.com/nguyenhuy) [#416](https://github.com/TextureGroup/Texture/pull/416/) +- Fix a crash in collection view that occurs if batch updates are performed while scrolling [Huy Nguyen](https://github.com/nguyenhuy) [#378](https://github.com/TextureGroup/Texture/issues/378) +- Some improvements in ASCollectionView [Huy Nguyen](https://github.com/nguyenhuy) [#407](https://github.com/TextureGroup/Texture/pull/407) + +##2.3.4 +- [Yoga] Rewrite YOGA_TREE_CONTIGUOUS mode with improved behavior and cleaner integration [Scott Goodson](https://github.com/appleguy) +- [ASTraitCollection] Convert ASPrimitiveTraitCollection from lock to atomic. [Scott Goodson](https://github.com/appleguy) +- Add a synchronous mode to ASCollectionNode, for colletion view data source debugging. [Hannah Troisi](https://github.com/hannahmbanana) +- [ASDisplayNode+Layout] Add check for orphaned nodes after layout transition to clean up. #336. [Scott Goodson](https://github.com/appleguy) +- Fixed an issue where GIFs with placeholders never had their placeholders uncover the GIF. [Garrett Moon](https://github.com/garrettmoon) +- [Yoga] Implement ASYogaLayoutSpec, a simplified integration strategy for Yoga-powered layout calculation. [Scott Goodson](https://github.com/appleguy) +- Fixed an issue where calls to setNeedsDisplay and setNeedsLayout would stop working on loaded nodes. [Garrett Moon](https://github.com/garrettmoon) +- Migrated unit tests to OCMock 3.4 (from 2.2) and improved the multiplex image node tests. [Adlai Holler](https://github.com/Adlai-Holler) +- Fix CollectionNode double-load issue. This should significantly improve performance in cases where a collection node has content immediately available on first layout i.e. not fetched from the network. [Adlai Holler](https://github.com/Adlai-Holler) +- Overhaul layout flattening algorithm [Huy Nguyen](https://github.com/nguyenhuy) [#395](https://github.com/TextureGroup/Texture/pull/395). + +## 2.3.3 +- [ASTextKitFontSizeAdjuster] Replace use of NSAttributedString's boundingRectWithSize:options:context: with NSLayoutManager's boundingRectForGlyphRange:inTextContainer: [Ricky Cancro](https://github.com/rcancro) +- Add support for IGListKit post-removal-of-IGListSectionType, in preparation for IGListKit 3.0.0 release. [Adlai Holler](https://github.com/Adlai-Holler) [#49](https://github.com/TextureGroup/Texture/pull/49) +- Fix `__has_include` check in ASLog.h [Philipp Smorygo](Philipp.Smorygo@jetbrains.com) +- Fix potential deadlock in ASControlNode [Garrett Moon](https://github.com/garrettmoon) +- [Yoga Beta] Improvements to the experimental support for Yoga layout [Scott Goodson](appleguy) +- Make cell node `indexPath` and `supplementaryElementKind` atomic so you can read from any thread. [Adlai-Holler](https://github.com/Adlai-Holler) [#49](https://github.com/TextureGroup/Texture/pull/74) +- Update the rasterization API and un-deprecate it. [Adlai Holler](https://github.com/Adlai-Holler)[#82](https://github.com/TextureGroup/Texture/pull/49) +- Simplified & optimized hashing code. [Adlai Holler](https://github.com/Adlai-Holler) [#86](https://github.com/TextureGroup/Texture/pull/86) +- Improve the performance & safety of ASDisplayNode subnodes. [Adlai Holler](https://github.com/Adlai-Holler) [#223](https://github.com/TextureGroup/Texture/pull/223) +- Move more properties from ASTableView, ASCollectionView to their respective node classes. [Adlai Holler](https://github.com/Adlai-Holler) +- Remove finalLayoutElement [Michael Schneider](https://github.com/maicki)[#96](https://github.com/TextureGroup/Texture/pull/96) +- Add ASPageTable - A map table for fast retrieval of objects within a certain page [Huy Nguyen](https://github.com/nguyenhuy) +- Add new public `-supernodes`, `-supernodesIncludingSelf`, and `-supernodeOfClass:includingSelf:` methods. [Adlai Holler](https://github.com/Adlai-Holler)[#246](https://github.com/TextureGroup/Texture/pull/246) +- Improve our handling supernode traversal to avoid loading layers and fix assertion failures you might hit in debug. [Adlai Holler](https://github.com/Adlai-Holler)[#246](https://github.com/TextureGroup/Texture/pull/246) +- [ASDisplayNode] Pass drawParameter in rendering context callbacks [Michael Schneider](https://github.com/maicki)[#248](https://github.com/TextureGroup/Texture/pull/248) +- [ASTextNode] Move to class method of drawRect:withParameters:isCancelled:isRasterizing: for drawing [Michael Schneider](https://github.com/maicki)[#232](https://github.com/TextureGroup/Texture/pull/232) +- [ASDisplayNode] Remove instance:-drawRect:withParameters:isCancelled:isRasterizing: (https://github.com/maicki)[#232](https://github.com/TextureGroup/Texture/pull/232) +- [ASTextNode] Add an experimental new implementation. See `+[ASTextNode setExperimentOptions:]`. [Adlai Holler](https://github.com/Adlai-Holler)[#259](https://github.com/TextureGroup/Texture/pull/259) +- [ASVideoNode] Added error reporing to ASVideoNode and it's delegate [#260](https://github.com/TextureGroup/Texture/pull/260) +- [ASCollectionNode] Fixed conversion of item index paths between node & view. [Adlai Holler](https://github.com/Adlai-Holler) [#262](https://github.com/TextureGroup/Texture/pull/262) +- [Layout] Extract layout implementation code into it's own subcategories [Michael Schneider](https://github.com/maicki)[#272](https://github.com/TextureGroup/Texture/pull/272) +- [Fix] Fix a potential crash when cell nodes that need layout are deleted during the same runloop. [Adlai Holler](https://github.com/Adlai-Holler) [#279](https://github.com/TextureGroup/Texture/pull/279) +- [Batch fetching] Add ASBatchFetchingDelegate that takes scroll velocity and remaining time into account [Huy Nguyen](https://github.com/nguyenhuy) [#281](https://github.com/TextureGroup/Texture/pull/281) +- [Fix] Fix a major regression in our image node contents caching. [Adlai Holler](https://github.com/Adlai-Holler) [#287](https://github.com/TextureGroup/Texture/pull/287) +- [Fix] Fixed a bug where ASVideoNodeDelegate error reporting callback would crash an app because of not responding to selector. [Sergey Petrachkov](https://github.com/Petrachkov) [#291](https://github.com/TextureGroup/Texture/issues/291) +- [IGListKit] Add IGListKit headers to public section of Xcode project [Michael Schneider](https://github.com/maicki)[#286](https://github.com/TextureGroup/Texture/pull/286) +- [Layout] Ensure -layout and -layoutDidFinish are called only if a node is loaded. [Huy Nguyen](https://github.com/nguyenhuy) [#285](https://github.com/TextureGroup/Texture/pull/285) +- [Layout Debugger] Small changes needed for the coming layout debugger [Huy Nguyen](https://github.com/nguyenhuy) [#337](https://github.com/TextureGroup/Texture/pull/337) diff --git a/CI/build.sh b/CI/build.sh new file mode 100755 index 0000000000..058cef0542 --- /dev/null +++ b/CI/build.sh @@ -0,0 +1,4 @@ +#!/bin/bash +set -eo pipefail + +./build.sh all \ No newline at end of file diff --git a/CI/exclude-from-build.json b/CI/exclude-from-build.json new file mode 100644 index 0000000000..abc6f913a6 --- /dev/null +++ b/CI/exclude-from-build.json @@ -0,0 +1,5 @@ +[ + "^plans/", + "^docs/", + "^CI/exclude-from-build.json$" +] \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2861dc49e5..02f3e2bd4f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,76 +1,270 @@ -# Contributing to AsyncDisplayKit -We want to make contributing to this project as easy and transparent as -possible, and actively welcome your pull requests. If you run into problems, -please open an issue on GitHub. +# Contribution Guidelines +Texture is the most actively developed open source project at [Pinterest](https://www.pinterest.com) and is used extensively to deliver a world-class Pinner experience. This document is setup to ensure contributing to the project is easy and transparent. + +# Questions + +If you are having difficulties using Texture or have a question about usage, please ask a +question in our [Slack channel](http://texturegroup.org/slack.html). **Please do not ask for help by filing Github issues.** + +# Core Team + +The Core Team reviews and helps iterate on the RFC Issues from the community at large and acting as the approver of these RFCs. Team members help drive Texture forward in a coherent direction consistent with the goal of creating the best possible general purpose UI framework for iOS. Team members will have merge permissions on the repository. + +Members of the core team are appointed based on their technical expertise and proven contribution to the community. The current core team members are: + +- Adlai Holler ([@](http://github.com/adlai-holler)[adlai-holler](http://github.com/adlai-holler)) +- Garrett Moon [(](https://github.com/garrettmoon)[@](https://github.com/garrettmoon)[garrett](https://github.com/garrettmoon)[moon](https://github.com/garrettmoon)) +- Huy Nguyen ([@](https://github.com/nguyenhuy)[nguyenhuy](https://github.com/nguyenhuy)) +- Michael Schneider ([@maicki](https://github.com/maicki)) +- Scott Goodson ([@appleguy](https://github.com/appleguy)) + +Over time, exceptional community members from a much more diverse background will be appointed based on their record of community involvement and contributions. + +# Issues + +Think you've found a bug or have a new feature to suggest? [Let us know](https://github.com/TextureGroup/Texture/issues/new)! + +## Where to Find Known Issues + +We use [GitHub Issues](https://github.com/texturegroup/texture/issues) for all bug tracking. We keep a close eye on this and try to make it clear when we have an internal fix in progress. Before filing a new issue, try to make sure your problem doesn't already exist. + +## Reporting New Issues +1. Update to the most recent master release if possible. We may have already fixed your bug. +2. Search for similar [issues](https://github.com/TextureGroup/Texture/issues). It's possible somebody has encountered this bug already. +3. Provide a reduced test case that specifically shows the problem. This demo should be fully operational with the exception of the bug you want to demonstrate. The more pared down, the better. If it is not possible to produce a test case, please make sure you provide very specific steps to reproduce the error. If we cannot reproduce it, and there is no other evidence to help us figure out a fix we will close the issue. +4. Your issue will be verified. The provided example will be tested for correctness. The Texture team will work with you until your issue can be verified. +5. Keep up to date with feedback from the Texture team on your issue. Your issue may be closed if it becomes stale. +6. If possible, submit a Pull Request with a failing test. Better yet, take a stab at fixing the bug yourself if you can! + +The more information you provide, the easier it is for us to validate that there is a bug and the faster we'll be able to take action. + +## Issues Triaging +- You might be requested to provide a reproduction or extra information. In that case, the issue will be labeled as **N****eeds More Info**. If we did not get any response after fourteen days, we will ping you to remind you about it. We might close the issue if we do not hear from you after two weeks since the original notice. +- If you submit a feature request as a GitHub issue, you will be invited to follow the instructions in this section otherwise the issue will be closed. +- Issues that become inactive will be labelled accordingly to inform the original poster and Texture contributors that the issue should be closed since the issue is no longer actionable. The issue can be reopened at a later time if needed, e.g. becomes actionable again. +- If possible, issues will be labeled to indicate the status or priority. For example, labels may have a prefix for Status: X, or Priority: X. Statuses may include: In Progress, On Hold. Priorities may include: P1, P2 or P3 (high to low priority). +# Requesting a Feature + +If you intend to change the public API, or make any non-trivial changes to the implementation, we recommend filing an RFC [issue](https://github.com/TextureGroup/Texture/issues/new) outlined below. This lets us reach an agreement on your proposal before you put significant effort into implementing it. + +If you're only fixing a bug, it's fine to submit a pull request right away, but we still recommend to file an issue detailing what you're fixing. This is helpful in case we don't accept that specific fix but want to keep track of the issue. + +## RFC Issue process +1. Texture has an RFC process for feature requests. To begin the discussion either gather feedback on the Texture Slack channel or draft an Texture RFC as a Github Issue. +2. The title of the GitHub RFC Issue should have `[RFC]` as prefix: `[RFC] Add new cool feature` +3. Provide a clear and detailed explanation of the feature you want and why it's important to add. Keep in mind that we want features that will be useful to the majority of our users and not just a small subset. If you're just targeting a minority of users, consider writing an add-on library for Texture. +4. If the feature is complex, consider writing an Texture RFC issue. Even if you can’t implement the feature yourself, consider writing an RFC issue. If we do end up accepting the feature, the issue provides the needed documentation for contributors to develop the feature according the specification accepted by the core team. We will tag accepted RFC issues with **Needs Volunteer**. +5. After discussing the feature you may choose to attempt a Pull Request. If you're at all able, start writing some code. We always have more work to do than time to do it. If you can write some code then that will speed the process along. + +In short, if you have an idea that would be nice to have, create an issue on the [TextureGroup](https://github.com/TextureGroup/Texture)[/](https://github.com/TextureGroup/Texture)[Texture](https://github.com/TextureGroup/Texture) repo. If you have a question about requesting a feature, start a discussion in our [Slack channel](http://texturegroup.org/slack.html). + +# Our Development Process + +All work on Texture happens directly on [GitHub](https://github.com/TextureGroup/Texture). Both core team members and external contributors send pull requests which go through the same review process. + +## `master` is under active development + +We will do our best to keep master in good shape, with tests passing at all times. But in order to move fast, we will make API changes that your application might not be compatible with. We will do our best to communicate these changes and version appropriately so you can lock into a specific version if need be. ## Pull Requests -1. Fork the repo and create your branch from `master`. -2. If you've added code that should be tested, add tests. -3. If you've changed APIs, update the documentation. -4. Ensure the test suite passes. -5. Make sure your code lints. -6. If you haven't already, complete the Contributor License Agreement ("CLA"). -7. Make sure that any new files conform to the correct file header style below -#### ASDK files header style +If you send a pull request, please do it against the master branch. We maintain stable branches for major versions separately but we don't accept pull requests to them directly. Instead, we cherry-pick non-breaking changes from master to the latest stable major version. -``` -// -// ASPagerFlowLayout.h -// 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. -// -``` +Before submitting a pull request, please make sure the following is done… -#### Example project files header style +1. Search GitHub for an open or closed [pull request](https://github.com/TextureGroup/Texture/pulls?utf8=✓&q=is%3Apr) that relates to your submission. You don't want to duplicate effort. +2. Fork the [repo](https://github.com/TextureGroup/Texture) and create your branch from master: + git checkout -b my-fix-branch master +3. Create your patch, including appropriate test cases. Please follow our Coding Guidelines. +4. Please make sure every commit message are meaningful so it that makes it clearer for people to review and easier to understand your intention +5. Ensure tests pass CI on GitHub for your Pull Request. +6. If you haven't already, sign the CLA. -``` +**Copyright Notice for files** +Copy and paste this to the top of your new file(s): +```objc // -// PhotoCellNode.m -// Sample +// ASDisplayNode.mm +// Texture // -// Created by Levi McCallum on 2/12/16. +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at // -// 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. +// http://www.apache.org/licenses/LICENSE-2.0 // ``` -## Contributor License Agreement ("CLA") -In order to accept your pull request, we need you to submit a CLA. You only need -to do this once to work on any of Facebook's open source projects. +If you’ve modified an existing file, change the header to: +```objc +// +// ASDisplayNode.mm +// Texture +// +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +``` -Complete your CLA here: +# Semantic Versioning -## Issues -We use GitHub issues to track public bugs. Please ensure your description is -clear and has sufficient instructions to be able to reproduce the issue. +Texture follows semantic versioning. We release patch versions for bug fixes, minor versions for new features (and rarely, clear and easy to fix breaking changes), and major versions for any major breaking changes. When we make breaking changes, we also introduce deprecation warnings in a minor version so that our users learn about the upcoming changes and migrate their code in advance. -Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe -disclosure of security bugs. In those cases, please go through the process -outlined on that page and do not file a public issue. +We tag every pull request with a label marking whether the change should go in the next patch, minor, or a major version. We release new versions pretty regularly usually every few weeks. The version will be a patch or minor version if it does not contain major new features or breaking API changes and a major version if it does. -## Getting Help -We use Slack for real-time debugging, community updates, and general talk about ASDK. Signup at http://asdk-slack-auto-invite.herokuapp.com or email AsyncDisplayKit(at)gmail.com to get an invite. +# Coding Guidelines +- Indent using 2 spaces (this conserves space in print and makes line wrapping less likely). Never indent with tabs. Be sure to set this preference in Xcode. +- Do your best to keep it around 120 characters line length +- End files with a newline. +- Don’t leave trailing whitespace. +- Space after `@property` declarations and conform to ordering of attributes +```objc +@property (nonatomic, readonly, assign, getter=isTracking) BOOL tracking; +@property (nonatomic, readwrite, strong, nullable) NSAttributedString *attributedText; +``` +- In method signatures, there should be a space after the method type (-/+ symbol). There should be a space between the method segments (matching Apple's style). Always include a keyword and be descriptive with the word before the argument which describes the argument. +```objc +@interface SomeClass +- (instancetype)initWithWidth:(CGFloat)width height:(CGFloat)height; +- (void)setExampleText:(NSString *)text image:(UIImage *)image; +- (void)sendAction:(SEL)aSelector to:(id)anObject forAllCells:(BOOL)flag; +- (id)viewWithTag:(NSInteger)tag; +@end +``` +- Internal methods should be prefixed with a `_` +```objc +- (void)_internalMethodWithParameter:(id)param; +``` +- Method braces and other braces (if/else/switch/while etc.) always open on the same line as the statement but close on a new line. +```objc +if (foo == bar) { + //.. +} else { + //.. +} +``` +- Method, `@interface` , and `@implementation` brackets on the following line +```objc +@implementation SomeClass +- (void)someMethod +{ + // Implementation +} +@end +``` +- Function brackets on the same line +```objc +static void someFunction() { + // Implementation +} +``` +- Operator goes with the variable name +```objc + NSAttributedString *attributedText = self.textNode.attributedText; +``` +- Locking + - Add a `_locked_` in front of the method name that needs to be called with a lock held +```objc +- (void)_locked_needsToBeCalledWithLock {} +``` + - Locking safety: + - It is fine for a `_locked_` method to call other `_locked_` methods. + - On the other hand, the following should not be done: + - Calling normal, unlocked methods inside a `_locked_` method + - Calling subclass hooks that are meant to be overridden by developers inside a `_locked_` method. + - Subclass hooks + - that are meant to be overwritten by users should not be called with a lock held. + - that are used internally the same conventions as above apply. +- There are multiple ways to acquire a lock: + 1. Explicitly call `.lock()` and `.unlock()` : +```objc +- (void)setContentSpacing:(CGFloat)contentSpacing +{ + __instanceLock__.lock(); + BOOL needsUpdate = (contentSpacing != _contentSpacing); + if (needsUpdate) { + _contentSpacing = contentSpacing; + } + __instanceLock__.unlock(); -## Coding Style -* 2 spaces for indentation rather than tabs + if (needsUpdate) { + [self setNeedsLayout]; + } +} -## License -By contributing to AsyncDisplayKit, you agree that your contributions will be -licensed under its BSD license. +- (CGFloat)contentSpacing +{ + CGFloat contentSpacing = 0.0; + __instanceLock__.lock(); + contentSpacing = _contentSpacing; + __instanceLock__.unlock(); + return contentSpacing; +} +``` + 2. Create an `ASDN::MutexLocker` : +```objc +- (void)setContentSpacing:(CGFloat)contentSpacing +{ + { + ASDN::MutexLocker l(__instanceLock__); + if (contentSpacing == _contentSpacing) { + return; + } + + _contentSpacing = contentSpacing; + } + + [self setNeedsLayout]; +} + +- (CGFloat)contentSpacing +{ + ASDN::MutexLocker l(__instanceLock__); + return _contentSpacing; +} +``` +- Nullability + - The adoption of annotations is straightforward. The standard we adopt is using the `NS_ASSUME_NONNULL_BEGIN` and `NS_ASSUME_NONNULL_END` on all headers. Then indicate nullability for the pointers that can be so. + - There is mostly no sense using nullability annotations outside of interface declarations. +```objc +// Properties +@property(nonatomic, strong, nullable) NSNumber *status + +// Methods +- (nullable NSNumber *)doSomethingWithString:(nullable NSString *)str; + +// Functions +NSString * _Nullable ASStringWithQuotesIfMultiword(NSString * _Nullable string); + +// Typedefs +typedef void (^RemoteCallback)(id _Nullable result, NSError * _Nullable error); + +// Block as parameter +- (void)reloadDataWithCompletion:(void (^ _Nullable)())completion; + +// Block as parameter with parameter and return value +- (void)convertObject:(id _Nonnull (^ _Nullable)(id _Nullable input))handler; + +// More complex pointer types +- (void)allElementsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode displaySet:(NSSet *__autoreleasing _Nullable *)displaySet preloadSet:(NSSet *__autoreleasing _Nullable *)preloadSet map:(ASElementMap *)map; +``` + +# Contributor License Agreement (CLA) + +Please sign our Contributor License Agreement (CLA) before sending pull requests. For any code changes to be accepted, the CLA must be signed. + +Complete your CLA [here](https://cla-assistant.io/TextureGroup/Texture) + +# License + +By contributing to Texture, you agree that your contributions will be licensed under its Apache 2 license. diff --git a/Cartfile b/Cartfile index 13f6329f33..93ba674369 100644 --- a/Cartfile +++ b/Cartfile @@ -1,2 +1,2 @@ -github "pinterest/PINRemoteImage" "3.0.0-beta.6" -github "pinterest/PINCache" "3.0.1-beta.2" +github "pinterest/PINRemoteImage" "3.0.0-beta.11" +github "pinterest/PINCache" "3.0.1-beta.5" diff --git a/Dangerfile b/Dangerfile new file mode 100644 index 0000000000..edd455d53d --- /dev/null +++ b/Dangerfile @@ -0,0 +1,101 @@ +source_pattern = /(\.m|\.mm|\.h)$/ + +# Sometimes it's a README fix, or something like that - which isn't relevant for +# including in a project's CHANGELOG for example +declared_trivial = github.pr_title.include? "#trivial" +has_changes_in_source_directory = !git.modified_files.grep(/Source/).empty? + +modified_source_files = git.modified_files.grep(source_pattern) +has_modified_source_files = !modified_source_files.empty? +added_source_files = git.added_files.grep(source_pattern) +has_added_source_files = !added_source_files.empty? + +# Make it more obvious that a PR is a work in progress and shouldn't be merged yet +warn("PR is classed as Work in Progress") if github.pr_title.include? "[WIP]" + +# Warn when there is a big PR +warn("This is a big PR, please consider splitting it up to ease code review.") if git.lines_of_code > 500 + +# Changelog entries are required for changes to source files. +no_changelog_entry = !git.modified_files.include?("CHANGELOG.md") +if has_changes_in_source_directory && no_changelog_entry && !declared_trivial + warn("Any source code changes should have an entry in CHANGELOG.md or have #trivial in their title.") +end + +def full_license(partial_license, filename) + license_header = <<-HEREDOC +// + HEREDOC + license_header += "// " + filename + "\n" + license_header += <<-HEREDOC +// Texture +// + HEREDOC + license_header += partial_license + return license_header +end + +def check_file_header(files_to_check, licenses) + repo_name = github.pr_json["base"]["repo"]["full_name"] + pr_number = github.pr_json["number"] + files = github.api.pull_request_files(repo_name, pr_number) + files.each do |file| + if files_to_check.include?(file["filename"]) + filename = File.basename(file["filename"]) + + data = "" + contents = github.api.get file["contents_url"] + open(contents["download_url"]) { |io| + data += io.read + } + + correct_license = false + licenses.each do |license| + license_header = full_license(license, filename) + if data.start_with?(license_header) + correct_license = true + end + end + + if correct_license == false + warn ("Please ensure license is correct for #{filename}: \n```\n" + full_license(licenses[0], filename) + "```") + end + + end + end +end + +# Ensure new files have proper header +new_source_license_header = <<-HEREDOC +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +HEREDOC + +if has_added_source_files + check_file_header(added_source_files, [new_source_license_header]) +end + +# Ensure modified files have proper header +modified_source_license_header = <<-HEREDOC +// 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 /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +HEREDOC + +if has_modified_source_files + check_file_header(modified_source_files, [modified_source_license_header, new_source_license_header]) +end \ No newline at end of file diff --git a/Gemfile b/Gemfile index 23a1d057af..38bfbc8e08 100644 --- a/Gemfile +++ b/Gemfile @@ -1,3 +1,4 @@ source 'https://rubygems.org' -gem 'slather' \ No newline at end of file +gem 'danger' +gem 'danger-slack' \ No newline at end of file diff --git a/InstrumentsTemplates/SystemTrace.tracetemplate b/InstrumentsTemplates/SystemTrace.tracetemplate new file mode 100644 index 0000000000..48cc3e6aef Binary files /dev/null and b/InstrumentsTemplates/SystemTrace.tracetemplate differ diff --git a/LICENSE b/LICENSE index 507edbd628..8ce6c68398 100644 --- a/LICENSE +++ b/LICENSE @@ -1,30 +1,187 @@ -BSD License +The Texture project was created by Pinterest as a continuation, under a +different name and license, of the AsyncDisplayKit codebase originally developed +by Facebook. AsyncDisplayKit was originally released by Facebook under a BSD +license and additional patent grant. Those BSD and patent licenses govern use +of code in Texture contributed prior to 4/13/2017 (the original AsyncDisplayKit +code), and copies of the licenses are included in the /ASDK-Licenses directory +of this source tree for reference. -For AsyncDisplayKit software +All code contributed to Texture after 4/13/2017 is released by Pinterest under +the Apache License, Version 2.0. -Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. + 1. Definitions. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. - * Neither the name Facebook nor the names of its contributors may be used to - endorse or promote products derived from this software without specific - prior written permission. + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/Podfile b/Podfile index 4e3fc26b2a..0d8cb0fa26 100644 --- a/Podfile +++ b/Podfile @@ -3,28 +3,28 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' target :'AsyncDisplayKitTests' do - pod 'OCMock', '~> 2.2' + pod 'OCMock', '~> 3.4' pod 'FBSnapshotTestCase/Core', '~> 2.1' pod 'JGMethodSwizzler', :git => 'https://github.com/JonasGessner/JGMethodSwizzler', :branch => 'master' # Only for buck build - pod 'PINRemoteImage', '3.0.0-beta.7' + pod 'PINRemoteImage', '3.0.0-beta.10' +end - #TODO CocoaPods plugin instead? - post_install do |installer| - require 'fileutils' +#TODO CocoaPods plugin instead? +post_install do |installer| + require 'fileutils' - # Assuming we're at the root dir - buck_files_dir = 'buck-files' - if File.directory?(buck_files_dir) - installer.pod_targets.flat_map do |pod_target| - pod_name = pod_target.pod_name - # Copy the file at buck-files/BUCK_pod_name to Pods/pod_name/BUCK, - # override existing file if needed - buck_file = buck_files_dir + '/BUCK_' + pod_name - if File.file?(buck_file) - FileUtils.cp(buck_file, 'Pods/' + pod_name + '/BUCK', :preserve => false) - end + # Assuming we're at the root dir + buck_files_dir = 'buck-files' + if File.directory?(buck_files_dir) + installer.pod_targets.flat_map do |pod_target| + pod_name = pod_target.pod_name + # Copy the file at buck-files/BUCK_pod_name to Pods/pod_name/BUCK, + # override existing file if needed + buck_file = buck_files_dir + '/BUCK_' + pod_name + if File.file?(buck_file) + FileUtils.cp(buck_file, 'Pods/' + pod_name + '/BUCK', :preserve => false) end end end diff --git a/README.md b/README.md index 5f42670c59..97cb293660 100644 --- a/README.md +++ b/README.md @@ -1,45 +1,47 @@ -![AsyncDisplayKit](https://github.com/AsyncDisplayKit/Documentation/raw/master/docs/static/images/logo.png) +## Coming from AsyncDisplayKit? Learn more [here](https://medium.com/@Pinterest_Engineering/introducing-texture-a-new-home-for-asyncdisplaykit-e7c003308f50) -[![Apps Using](https://img.shields.io/cocoapods/at/AsyncDisplayKit.svg?label=Apps%20Using%20ASDK&colorB=28B9FE)](http://cocoapods.org/pods/AsyncDisplayKit) -[![Downloads](https://img.shields.io/cocoapods/dt/AsyncDisplayKit.svg?label=Total%20Downloads&colorB=28B9FE)](http://cocoapods.org/pods/AsyncDisplayKit) +![Texture](https://github.com/texturegroup/texture/raw/master/docs/static/images/logo.png) -[![Platform](https://img.shields.io/badge/platforms-iOS%20%7C%20tvOS-orange.svg)](http://AsyncDisplayKit.org) -[![Languages](https://img.shields.io/badge/languages-ObjC%20%7C%20Swift-orange.svg)](http://AsyncDisplayKit.org) +[![Apps Using](https://img.shields.io/cocoapods/at/Texture.svg?label=Apps%20Using%20Texture&colorB=28B9FE)](http://cocoapods.org/pods/Texture) +[![Downloads](https://img.shields.io/cocoapods/dt/Texture.svg?label=Total%20Downloads&colorB=28B9FE)](http://cocoapods.org/pods/Texture) -[![Version](https://img.shields.io/cocoapods/v/AsyncDisplayKit.svg)](http://cocoapods.org/pods/AsyncDisplayKit) +[![Platform](https://img.shields.io/badge/platforms-iOS%20%7C%20tvOS-orange.svg)](http://texturegroup.org) +[![Languages](https://img.shields.io/badge/languages-ObjC%20%7C%20Swift-orange.svg)](http://texturegroup.org) + +[![Version](https://img.shields.io/cocoapods/v/Texture.svg)](http://cocoapods.org/pods/Texture) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-59C939.svg?style=flat)](https://github.com/Carthage/Carthage) -[![License](https://img.shields.io/cocoapods/l/AsyncDisplayKit.svg)](https://github.com/facebook/AsyncDisplayKit/blob/master/LICENSE) +[![License](https://img.shields.io/cocoapods/l/Texture.svg)](https://github.com/texturegroup/texture/blob/master/LICENSE) ## Installation -ASDK is available via CocoaPods or Carthage. See our [Installation](http://asyncdisplaykit.org/docs/installation.html) guide for instructions. +Texture is available via CocoaPods or Carthage. See our [Installation](http://texturegroup.org/docs/installation.html) guide for instructions. ## Performance Gains -AsyncDisplayKit's basic unit is the `node`. An ASDisplayNode is an abstraction over `UIView`, which in turn is an abstraction over `CALayer`. Unlike views, which can only be used on the main thread, nodes are thread-safe: you can instantiate and configure entire hierarchies of them in parallel on background threads. +Texture's basic unit is the `node`. An ASDisplayNode is an abstraction over `UIView`, which in turn is an abstraction over `CALayer`. Unlike views, which can only be used on the main thread, nodes are thread-safe: you can instantiate and configure entire hierarchies of them in parallel on background threads. To keep its user interface smooth and responsive, your app should render at 60 frames per second — the gold standard on iOS. This means the main thread has one-sixtieth of a second to push each frame. That's 16 milliseconds to execute all layout and drawing code! And because of system overhead, your code usually has less than ten milliseconds to run before it causes a frame drop. -AsyncDisplayKit lets you move image decoding, text sizing and rendering, layout, and other expensive UI operations off the main thread, to keep the main thread available to respond to user interaction. +Texture lets you move image decoding, text sizing and rendering, layout, and other expensive UI operations off the main thread, to keep the main thread available to respond to user interaction. ## Advanced Developer Features -As the framework has grown, many features have been added that can save developers tons of time by eliminating common boilerplate style structures common in modern iOS apps. If you've ever dealt with cell reuse bugs, tried to performantly preload data for a page or scroll style interface or even just tried to keep your app from dropping too many frames you can benefit from integrating ASDK. +As the framework has grown, many features have been added that can save developers tons of time by eliminating common boilerplate style structures common in modern iOS apps. If you've ever dealt with cell reuse bugs, tried to performantly preload data for a page or scroll style interface or even just tried to keep your app from dropping too many frames you can benefit from integrating Texture. ## Learn More -* Read the our [Getting Started](http://asyncdisplaykit.org/docs/getting-started.html) guide -* Get the [sample projects](https://github.com/facebook/AsyncDisplayKit/tree/master/examples) -* Browse the [API reference](http://asyncdisplaykit.org/appledocs.html) +* Read the our [Getting Started](http://texturegroup.org/docs/getting-started.html) guide +* Get the [sample projects](https://github.com/texturegroup/texture/tree/master/examples) +* Browse the [API reference](http://texturegroup.org/appledocs.html) ## Getting Help -We use Slack for real-time debugging, community updates, and general talk about ASDK. [Signup](http://asdk-slack-auto-invite.herokuapp.com) yourself or email AsyncDisplayKit(at)gmail.com to get an invite. +We use Slack for real-time debugging, community updates, and general talk about Texture. [Signup](http://asdk-slack-auto-invite.herokuapp.com) yourself or email textureframework@gmail.com to get an invite. ## Contributing -We welcome any contributions. See the [CONTRIBUTING](https://github.com/facebook/AsyncDisplayKit/blob/master/CONTRIBUTING.md) file for how to get involved. +We welcome any contributions. See the [CONTRIBUTING](https://github.com/texturegroup/texture/blob/master/CONTRIBUTING.md) file for how to get involved. ## License -AsyncDisplayKit is BSD-licensed. We also provide an additional patent grant. The files in the `/examples` directory are licensed under a separate license as specified in each file; documentation is licensed CC-BY-4.0. +The Texture project was created by Pinterest as a continuation, under a different name and license, of the AsyncDisplayKit codebase originally developed by Facebook. AsyncDisplayKit was originally released by Facebook under a BSD license and additional patent grant. Those BSD and patent licenses govern use of code in Texture contributed prior to 4/13/2017 (the original AsyncDisplayKit code), and copies of the licenses are included in the root directory of this source tree for reference. All code contributed to Texture after 4/13/2017 is released by Pinterest under an Apache 2.0 license. diff --git a/Source/ASBlockTypes.h b/Source/ASBlockTypes.h index c6aaefdcba..cf05a19377 100644 --- a/Source/ASBlockTypes.h +++ b/Source/ASBlockTypes.h @@ -1,9 +1,18 @@ // // ASBlockTypes.h -// AsyncDisplayKit +// Texture // -// Created by Adlai Holler on 1/25/17. -// Copyright © 2017 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 /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/ASButtonNode.h b/Source/ASButtonNode.h index 2f33173819..ac4ba54b0f 100644 --- a/Source/ASButtonNode.h +++ b/Source/ASButtonNode.h @@ -1,11 +1,18 @@ // // ASButtonNode.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/ASButtonNode.mm b/Source/ASButtonNode.mm index a092549cea..41e411caf8 100644 --- a/Source/ASButtonNode.mm +++ b/Source/ASButtonNode.mm @@ -1,11 +1,18 @@ // // ASButtonNode.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/ASCellNode.h b/Source/ASCellNode.h index 87b386e842..887793487e 100644 --- a/Source/ASCellNode.h +++ b/Source/ASCellNode.h @@ -1,11 +1,18 @@ // // ASCellNode.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK @@ -15,6 +22,7 @@ NS_ASSUME_NONNULL_BEGIN @class ASCellNode, ASTextNode; +@protocol ASRangeManagingNode; typedef NSUInteger ASCellNodeAnimation; @@ -82,7 +90,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; +@property (atomic, copy, readonly, nullable) NSString *supplementaryElementKind; /* * The layout attributes currently assigned to this node, if any. @@ -108,10 +116,24 @@ typedef NS_ENUM(NSUInteger, ASCellNodeVisibilityEvent) { /** * The current index path of this cell node, or @c nil if this node is * not a valid item inside a table node or collection node. - * - * @note This property must be accessed on the main thread. */ -@property (nonatomic, readonly, nullable) NSIndexPath *indexPath; +@property (atomic, readonly, nullable) NSIndexPath *indexPath; + +/** + * BETA: API is under development. We will attempt to provide an easy migration pathway for any changes. + * + * The view-model currently assigned to this node, if any. + * + * This property may be set off the main thread, but this method will never be invoked concurrently on the + */ +@property (atomic, nullable) id viewModel; + +/** + * Asks the node whether it can be updated to the given view model. + * + * The default implementation returns YES if the class matches that of the current view-model. + */ +- (BOOL)canUpdateToViewModel:(id)viewModel; /** * The backing view controller, or @c nil if the node wasn't initialized with backing view controller @@ -121,10 +143,9 @@ typedef NS_ENUM(NSUInteger, ASCellNodeVisibilityEvent) { /** - * The owning node (ASCollectionNode/ASTableNode) of this cell node, or @c nil if this node is - * not a valid item inside a table node or collection node or if those nodes are nil. + * The table- or collection-node that this cell is a member of, if any. */ -@property (weak, nonatomic, readonly, nullable) ASDisplayNode *owningNode; +@property (atomic, weak, readonly, nullable) id owningNode; /* * ASCellNode must forward touch events in order for UITableView and UICollectionView tap handling to work. Overriding diff --git a/Source/ASCellNode.mm b/Source/ASCellNode.mm index 2327bc83e5..655643bdb8 100644 --- a/Source/ASCellNode.mm +++ b/Source/ASCellNode.mm @@ -1,11 +1,18 @@ // // ASCellNode.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK @@ -39,12 +46,6 @@ ASDisplayNode *_viewControllerNode; UIViewController *_viewController; BOOL _suspendInteractionDelegate; - - struct { - unsigned int isTableNode:1; - unsigned int isCollectionNode:1; - } _owningNodeType; - } @end @@ -115,13 +116,6 @@ _viewControllerNode.frame = self.bounds; } -- (void)layoutDidFinish -{ - [super layoutDidFinish]; - - _viewControllerNode.frame = self.bounds; -} - - (void)_rootNodeDidInvalidateSize { if (_interactionDelegate != nil) { @@ -160,19 +154,6 @@ } } -- (void)setOwningNode:(ASDisplayNode *)owningNode -{ - _owningNode = owningNode; - - memset(&_owningNodeType, 0, sizeof(_owningNodeType)); - - if ([owningNode isKindOfClass:[ASTableNode class]]) { - _owningNodeType.isTableNode = 1; - } else if ([owningNode isKindOfClass:[ASCollectionNode class]]) { - _owningNodeType.isCollectionNode = 1; - } -} - - (void)__setSelectedFromUIKit:(BOOL)selected; { if (selected != _selected) { @@ -191,17 +172,14 @@ } } +- (BOOL)canUpdateToViewModel:(id)viewModel +{ + return [self.viewModel class] == [viewModel class]; +} + - (NSIndexPath *)indexPath { - ASDisplayNodeAssertMainThread(); - - if (_owningNodeType.isTableNode) { - return [(ASTableNode *)self.owningNode indexPathForNode:self]; - } else if (_owningNodeType.isCollectionNode) { - return [(ASCollectionNode *)self.owningNode indexPathForNode:self]; - } - - return nil; + return [self.owningNode indexPathForNode:self]; } - (UIViewController *)viewController @@ -339,13 +317,13 @@ if (ip != nil) { [result addObject:@{ @"indexPath" : ip }]; } - [result addObject:@{ @"collectionNode" : ASObjectDescriptionMakeTiny(owningNode) }]; + [result addObject:@{ @"collectionNode" : owningNode }]; } else if ([owningNode isKindOfClass:[ASTableNode class]]) { NSIndexPath *ip = [(ASTableNode *)owningNode indexPathForNode:self]; if (ip != nil) { [result addObject:@{ @"indexPath" : ip }]; } - [result addObject:@{ @"tableNode" : ASObjectDescriptionMakeTiny(owningNode) }]; + [result addObject:@{ @"tableNode" : owningNode }]; } else if ([scrollView isKindOfClass:[ASCollectionView class]]) { NSIndexPath *ip = [(ASCollectionView *)scrollView indexPathForNode:self]; @@ -370,6 +348,11 @@ return self.collectionElement.supplementaryElementKind; } +- (BOOL)supportsLayerBacking +{ + return NO; +} + @end diff --git a/Source/ASCollectionNode+Beta.h b/Source/ASCollectionNode+Beta.h index cc5bdb2269..8529ea0999 100644 --- a/Source/ASCollectionNode+Beta.h +++ b/Source/ASCollectionNode+Beta.h @@ -1,17 +1,25 @@ // // ASCollectionNode+Beta.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK #import -@protocol ASCollectionViewLayoutFacilitatorProtocol; +@protocol ASCollectionViewLayoutFacilitatorProtocol, ASCollectionLayoutDelegate, ASBatchFetchingDelegate; +@class ASElementMap; NS_ASSUME_NONNULL_BEGIN @@ -24,8 +32,36 @@ NS_ASSUME_NONNULL_BEGIN */ @property (strong, nonatomic, nullable) Class collectionViewClass; +/** + * The elements that are currently displayed. The "UIKit index space". Must be accessed on main thread. + */ +@property (strong, nonatomic, readonly) ASElementMap *visibleElements; + +@property (strong, readonly, nullable) id layoutDelegate; + +@property (nonatomic, weak) id batchFetchingDelegate; + +/** + * When this mode is enabled, ASCollectionView matches the timing of UICollectionView as closely as possible, + * ensuring that all reload and edit operations are performed on the main thread as blocking calls. + * + * This mode is useful for applications that are debugging issues with their collection view implementation. + * In particular, some applications do not properly conform to the API requirement of UICollectionView, and these + * applications may experience difficulties with ASCollectionView. Providing this mode allows for developers to + * work towards resolving technical debt in their collection view data source, while ramping up asynchronous + * collection layout. + * + * NOTE: Because this mode results in expensive operations like cell layout being performed on the main thread, + * it should be used as a tool to resolve data source conformance issues with Apple collection view API. + * + * @default defaults to NO. + */ +@property (nonatomic, assign) BOOL usesSynchronousDataLoading; + - (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout layoutFacilitator:(nullable id)layoutFacilitator; +- (instancetype)initWithLayoutDelegate:(id)layoutDelegate layoutFacilitator:(nullable id)layoutFacilitator; + - (void)beginUpdates ASDISPLAYNODE_DEPRECATED_MSG("Use -performBatchUpdates:completion: instead."); - (void)endUpdatesAnimated:(BOOL)animated ASDISPLAYNODE_DEPRECATED_MSG("Use -performBatchUpdates:completion: instead."); diff --git a/Source/ASCollectionNode.h b/Source/ASCollectionNode.h index cf4da39d06..1a6b9bda31 100644 --- a/Source/ASCollectionNode.h +++ b/Source/ASCollectionNode.h @@ -1,13 +1,18 @@ // // ASCollectionNode.h -// AsyncDisplayKit -// -// Created by Scott Goodson on 9/5/15. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK @@ -17,6 +22,7 @@ #import #import #import +#import @protocol ASCollectionViewLayoutFacilitatorProtocol; @protocol ASCollectionDelegate; @@ -29,7 +35,7 @@ NS_ASSUME_NONNULL_BEGIN * ASCollectionNode is a node based class that wraps an ASCollectionView. It can be used * as a subnode of another node, and provide room for many (great) features and improvements later on. */ -@interface ASCollectionNode : ASDisplayNode +@interface ASCollectionNode : ASDisplayNode - (instancetype)init NS_UNAVAILABLE; @@ -79,6 +85,13 @@ NS_ASSUME_NONNULL_BEGIN */ @property (weak, nonatomic) id dataSource; +/** + * The number of screens left to scroll before the delegate -collectionNode:beginBatchFetchingWithContext: is called. + * + * Defaults to two screenfuls. + */ +@property (nonatomic, assign) CGFloat leadingScreensForBatching; + /* * A Boolean value that determines whether the collection node will be flipped. * If the value of this property is YES, the first cell node will be at the bottom of the collection node (as opposed to the top by default). This is useful for chat/messaging apps. The default value is NO. @@ -98,6 +111,24 @@ NS_ASSUME_NONNULL_BEGIN */ @property (nonatomic, assign) BOOL allowsMultipleSelection; +/** + * The layout used to organize the node's items. + * + * @discussion Assigning a new layout object to this property causes the new layout to be applied (without animations) to the node’s items. + */ +@property (nonatomic, strong) UICollectionViewLayout *collectionViewLayout; + +/** + * Optional introspection object for the collection node's layout. + * + * @discussion Since supplementary and decoration nodes are controlled by the layout, this object + * is used as a bridge to provide information to the internal data controller about the existence of these views and + * their associated index paths. For collections using `UICollectionViewFlowLayout`, a default inspector + * implementation `ASCollectionViewFlowLayoutInspector` is created and set on this property by default. Custom + * collection layout subclasses will need to provide their own implementation of an inspector object for their + * supplementary elements to be compatible with `ASCollectionNode`'s supplementary node support. + */ +@property (nonatomic, weak) id layoutInspector; /** * Tuning parameters for a range type in full mode. @@ -158,6 +189,20 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)scrollToItemAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UICollectionViewScrollPosition)scrollPosition animated:(BOOL)animated; +/** + * Determines collection node's current scroll direction. Supports 2-axis collection nodes. + * + * @return a bitmask of ASScrollDirection values. + */ +@property (nonatomic, readonly) ASScrollDirection scrollDirection; + +/** + * Determines collection node's scrollable directions. + * + * @return a bitmask of ASScrollDirection values. + */ +@property (nonatomic, readonly) ASScrollDirection scrollableDirections; + #pragma mark - Editing /** @@ -375,6 +420,17 @@ NS_ASSUME_NONNULL_BEGIN */ - (nullable __kindof ASCellNode *)nodeForItemAtIndexPath:(NSIndexPath *)indexPath AS_WARN_UNUSED_RESULT; +/** + * Retrieves the view-model for the item at the given index path, if any. + * + * @param indexPath The index path of the requested item. + * + * @return The view-model for the given item, or @c nil if no item exists at the specified path or no view-model was provided. + * + * @warning This API is beta and subject to change. We'll try to provide an easy migration path. + */ +- (nullable id)viewModelForItemAtIndexPath:(NSIndexPath *)indexPath AS_WARN_UNUSED_RESULT; + /** * Retrieve the index path for the item with the given node. * @@ -460,6 +516,17 @@ NS_ASSUME_NONNULL_BEGIN */ - (NSInteger)numberOfSectionsInCollectionNode:(ASCollectionNode *)collectionNode; +/** + * --BETA-- + * Asks the data source for a view-model for the item at the given index path. + * + * @param collectionNode The sender. + * @param indexPath The index path of the item. + * + * @return An object that contains all the data for this item. + */ +- (nullable id)collectionNode:(ASCollectionNode *)collectionNode viewModelForItemAtIndexPath:(NSIndexPath *)indexPath; + /** * Similar to -collectionNode:nodeForItemAtIndexPath: * This method takes precedence over collectionNode:nodeForItemAtIndexPath: if implemented. @@ -735,7 +802,7 @@ NS_ASSUME_NONNULL_BEGIN * 4. Lastly, you must implement a method to provide the size for the cell. There are two ways this is done: * 4a. UICollectionViewFlowLayout (incl. ASPagerNode). Implement collectionNode:constrainedSizeForItemAtIndexPath:. - * 4b. Custom collection layouts. Set .view.layoutInspector and have it implement + * 4b. Custom collection layouts. Set .layoutInspector and have it implement collectionView:constrainedSizeForNodeAtIndexPath:. * * For an example of using this method with all steps above (including a custom layout, 4b.), diff --git a/Source/ASCollectionNode.mm b/Source/ASCollectionNode.mm index 900085eb0f..a9c063b1ff 100644 --- a/Source/ASCollectionNode.mm +++ b/Source/ASCollectionNode.mm @@ -1,27 +1,35 @@ // // ASCollectionNode.mm -// AsyncDisplayKit -// -// Created by Scott Goodson on 9/5/15. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK #import +#import #import #import #import +#import #import #import #import #import #import #import +#import #import #import #import @@ -34,10 +42,14 @@ @interface _ASCollectionPendingState : NSObject @property (weak, nonatomic) id delegate; @property (weak, nonatomic) id dataSource; +@property (strong, nonatomic) UICollectionViewLayout *collectionViewLayout; @property (nonatomic, assign) ASLayoutRangeMode rangeMode; @property (nonatomic, assign) BOOL allowsSelection; // default is YES @property (nonatomic, assign) BOOL allowsMultipleSelection; // default is NO @property (nonatomic, assign) BOOL inverted; //default is NO +@property (nonatomic, assign) BOOL usesSynchronousDataLoading; +@property (nonatomic, assign) CGFloat leadingScreensForBatching; +@property (weak, nonatomic) id layoutInspector; @end @implementation _ASCollectionPendingState @@ -102,6 +114,7 @@ { ASDN::RecursiveMutex _environmentStateLock; Class _collectionViewClass; + id _batchFetchingDelegate; } @property (nonatomic) _ASCollectionPendingState *pendingState; @end @@ -134,13 +147,21 @@ return [self initWithFrame:frame collectionViewLayout:layout layoutFacilitator:nil]; } +- (instancetype)initWithLayoutDelegate:(id)layoutDelegate layoutFacilitator:(id)layoutFacilitator +{ + return [self initWithFrame:CGRectZero collectionViewLayout:[[ASCollectionLayout alloc] initWithLayoutDelegate:layoutDelegate] layoutFacilitator:layoutFacilitator]; +} + - (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout layoutFacilitator:(id)layoutFacilitator { if (self = [super init]) { + // Must call the setter here to make sure pendingState is created and the layout is configured. + [self setCollectionViewLayout:layout]; + __weak __typeof__(self) weakSelf = self; [self setViewBlock:^{ __typeof__(self) strongSelf = weakSelf; - return [[[strongSelf collectionViewClass] alloc] _initWithFrame:frame collectionViewLayout:layout layoutFacilitator:layoutFacilitator eventLog:ASDisplayNodeGetEventLog(strongSelf)]; + return [[[strongSelf collectionViewClass] alloc] _initWithFrame:frame collectionViewLayout:strongSelf->_pendingState.collectionViewLayout layoutFacilitator:layoutFacilitator owningNode:strongSelf eventLog:ASDisplayNodeGetEventLog(strongSelf)]; }]; } return self; @@ -157,16 +178,20 @@ if (_pendingState) { _ASCollectionPendingState *pendingState = _pendingState; - self.pendingState = nil; - view.asyncDelegate = pendingState.delegate; - view.asyncDataSource = pendingState.dataSource; - view.inverted = pendingState.inverted; - view.allowsSelection = pendingState.allowsSelection; - view.allowsMultipleSelection = pendingState.allowsMultipleSelection; - + view.asyncDelegate = pendingState.delegate; + view.asyncDataSource = pendingState.dataSource; + view.inverted = pendingState.inverted; + view.allowsSelection = pendingState.allowsSelection; + view.allowsMultipleSelection = pendingState.allowsMultipleSelection; + view.usesSynchronousDataLoading = pendingState.usesSynchronousDataLoading; + view.layoutInspector = pendingState.layoutInspector; + self.pendingState = nil; + if (pendingState.rangeMode != ASLayoutRangeModeUnspecified) { [view.rangeController updateCurrentRangeWithMode:pendingState.rangeMode]; } + + // Don't need to set collectionViewLayout to the view as the layout was already used to init the view in view block. } } @@ -181,18 +206,21 @@ [self.rangeController clearContents]; } -- (void)didExitPreloadState -{ - [super didExitPreloadState]; - [self.rangeController clearPreloadedData]; -} - - (void)interfaceStateDidChange:(ASInterfaceState)newState fromState:(ASInterfaceState)oldState { [super interfaceStateDidChange:newState fromState:oldState]; [ASRangeController layoutDebugOverlayIfNeeded]; } +- (void)didEnterPreloadState +{ + [super didEnterPreloadState]; + // Intentionally allocate the view here and trigger a layout pass on it, which in turn will trigger the intial data load. + // We can get rid of this call later when ASDataController, ASRangeController and ASCollectionLayout can operate without the view. + // TODO (ASCL) If this node supports async layout, kick off the initial data load without allocating the view + [[self view] layoutIfNeeded]; +} + #if ASRangeControllerLoggingEnabled - (void)didEnterVisibleState { @@ -207,6 +235,12 @@ } #endif +- (void)didExitPreloadState +{ + [super didExitPreloadState]; + [self.rangeController clearPreloadedData]; +} + #pragma mark Setter / Getter // TODO: Implement this without the view. Then revisit ASLayoutElementCollectionTableSetTraitCollection @@ -250,6 +284,44 @@ } } +- (void)setLayoutInspector:(id)layoutInspector +{ + if ([self pendingState]) { + _pendingState.layoutInspector = layoutInspector; + } else { + ASDisplayNodeAssert([self isNodeLoaded], @"ASCollectionNode should be loaded if pendingState doesn't exist"); + self.view.layoutInspector = layoutInspector; + } +} + +- (id)layoutInspector +{ + if ([self pendingState]) { + return _pendingState.layoutInspector; + } else { + return self.view.layoutInspector; + } +} + +- (void)setLeadingScreensForBatching:(CGFloat)leadingScreensForBatching +{ + if ([self pendingState]) { + _pendingState.leadingScreensForBatching = leadingScreensForBatching; + } else { + ASDisplayNodeAssert([self isNodeLoaded], @"ASCollectionNode should be loaded if pendingState doesn't exist"); + self.view.leadingScreensForBatching = leadingScreensForBatching; + } +} + +- (CGFloat)leadingScreensForBatching +{ + if ([self pendingState]) { + return _pendingState.leadingScreensForBatching; + } else { + return self.view.leadingScreensForBatching; + } +} + - (void)setDelegate:(id )delegate { if ([self pendingState]) { @@ -341,6 +413,80 @@ } } +- (void)setCollectionViewLayout:(UICollectionViewLayout *)layout +{ + if ([self pendingState]) { + [self _configureCollectionViewLayout:layout]; + _pendingState.collectionViewLayout = layout; + } else { + [self _configureCollectionViewLayout:layout]; + self.view.collectionViewLayout = layout; + } +} + +- (UICollectionViewLayout *)collectionViewLayout +{ + if ([self pendingState]) { + return _pendingState.collectionViewLayout; + } else { + return self.view.collectionViewLayout; + } +} + +- (ASScrollDirection)scrollDirection +{ + return [self isNodeLoaded] ? self.view.scrollDirection : ASScrollDirectionNone; +} + +- (ASScrollDirection)scrollableDirections +{ + return [self isNodeLoaded] ? self.view.scrollableDirections : ASScrollDirectionNone; +} + +- (ASElementMap *)visibleElements +{ + ASDisplayNodeAssertMainThread(); + // TODO Own the data controller when view is not yet loaded + return self.dataController.visibleMap; +} + +- (id)layoutDelegate +{ + UICollectionViewLayout *layout = self.collectionViewLayout; + if ([layout isKindOfClass:[ASCollectionLayout class]]) { + return ((ASCollectionLayout *)layout).layoutDelegate; + } + return nil; +} + +- (void)setBatchFetchingDelegate:(id)batchFetchingDelegate +{ + _batchFetchingDelegate = batchFetchingDelegate; +} + +- (id)batchFetchingDelegate +{ + return _batchFetchingDelegate; +} + +- (BOOL)usesSynchronousDataLoading +{ + if ([self pendingState]) { + return _pendingState.usesSynchronousDataLoading; + } else { + return self.view.usesSynchronousDataLoading; + } +} + +- (void)setUsesSynchronousDataLoading:(BOOL)usesSynchronousDataLoading +{ + if ([self pendingState]) { + _pendingState.usesSynchronousDataLoading = usesSynchronousDataLoading; + } else { + self.view.usesSynchronousDataLoading = usesSynchronousDataLoading; + } +} + #pragma mark - Range Tuning - (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType @@ -447,6 +593,12 @@ return [self.dataController.pendingMap elementForItemAtIndexPath:indexPath].node; } +- (id)viewModelForItemAtIndexPath:(NSIndexPath *)indexPath +{ + [self reloadDataInitiallyIfNeeded]; + return [self.dataController.pendingMap elementForItemAtIndexPath:indexPath].viewModel; +} + - (NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode { return [self.dataController.pendingMap indexPathForElement:cellNode.collectionElement]; @@ -533,9 +685,17 @@ - (void)reloadDataWithCompletion:(void (^)())completion { ASDisplayNodeAssertMainThread(); - if (self.nodeLoaded) { - [self.view reloadDataWithCompletion:completion]; + if (!self.nodeLoaded) { + return; } + + [self performBatchUpdates:^{ + [self.view.changeSet reloadData]; + } completion:^(BOOL finished){ + if (completion) { + completion(); + } + }]; } - (void)reloadData @@ -543,14 +703,19 @@ [self reloadDataWithCompletion:nil]; } -- (void)relayoutItems -{ - [self.view relayoutItems]; -} - - (void)reloadDataImmediately { - [self.view reloadDataImmediately]; + ASDisplayNodeAssertMainThread(); + [self reloadData]; + [self waitUntilAllUpdatesAreCommitted]; +} + +- (void)relayoutItems +{ + ASDisplayNodeAssertMainThread(); + if (self.nodeLoaded) { + [self.view relayoutItems]; + } } - (void)beginUpdates @@ -663,6 +828,16 @@ ASLayoutElementCollectionTableSetTraitCollection(_environmentStateLock) return result; } +#pragma mark - Private methods + +- (void)_configureCollectionViewLayout:(UICollectionViewLayout *)layout +{ + if ([layout isKindOfClass:[ASCollectionLayout class]]) { + ASCollectionLayout *collectionLayout = (ASCollectionLayout *)layout; + collectionLayout.collectionNode = self; + } +} + @end #endif diff --git a/Source/ASCollectionView.h b/Source/ASCollectionView.h index b1e8e898fa..510a0bcaa2 100644 --- a/Source/ASCollectionView.h +++ b/Source/ASCollectionView.h @@ -1,11 +1,18 @@ // // ASCollectionView.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK @@ -31,13 +38,6 @@ NS_ASSUME_NONNULL_BEGIN /** * Asynchronous UICollectionView with Intelligent Preloading capabilities. * - * @discussion ASCollectionView is a true subclass of UICollectionView, meaning it is pointer-compatible - * with code that currently uses UICollectionView. - * - * The main difference is that asyncDataSource expects -nodeForItemAtIndexPath, an ASCellNode, and - * the sizeForItemAtIndexPath: method is eliminated (as are the performance problems caused by it). - * This is made possible because ASCellNodes can calculate their own size, and preload ahead of time. - * * @note ASCollectionNode is strongly recommended over ASCollectionView. This class exists for adoption convenience. */ @interface ASCollectionView : UICollectionView @@ -49,25 +49,6 @@ NS_ASSUME_NONNULL_BEGIN */ @property (nonatomic, weak, readonly) ASCollectionNode *collectionNode; -/** - * The number of screens left to scroll before the delegate -collectionView:beginBatchFetchingWithContext: is called. - * - * Defaults to two screenfuls. - */ -@property (nonatomic, assign) CGFloat leadingScreensForBatching; - -/** - * Optional introspection object for the collection view's layout. - * - * @discussion Since supplementary and decoration views are controlled by the collection view's layout, this object - * is used as a bridge to provide information to the internal data controller about the existence of these views and - * their associated index paths. For collection views using `UICollectionViewFlowLayout`, a default inspector - * implementation `ASCollectionViewFlowLayoutInspector` is created and set on this property by default. Custom - * collection view layout subclasses will need to provide their own implementation of an inspector object for their - * supplementary views to be compatible with `ASCollectionView`'s supplementary node support. - */ -@property (nonatomic, weak) id layoutInspector; - /** * Retrieves the node for the item at the given index path. * @@ -110,28 +91,47 @@ NS_ASSUME_NONNULL_BEGIN */ - (nullable id)contextForSection:(NSInteger)section AS_WARN_UNUSED_RESULT; +@end + +@interface ASCollectionView (Deprecated) + +/* + * A Boolean value that determines whether the nodes that the data source renders will be flipped. + */ +@property (nonatomic, assign) BOOL inverted ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode property instead."); + +/** + * The number of screens left to scroll before the delegate -collectionView:beginBatchFetchingWithContext: is called. + * + * Defaults to two screenfuls. + */ +@property (nonatomic, assign) CGFloat leadingScreensForBatching ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode property instead."); + +/** + * Optional introspection object for the collection view's layout. + * + * @discussion Since supplementary and decoration views are controlled by the collection view's layout, this object + * is used as a bridge to provide information to the internal data controller about the existence of these views and + * their associated index paths. For collection views using `UICollectionViewFlowLayout`, a default inspector + * implementation `ASCollectionViewFlowLayoutInspector` is created and set on this property by default. Custom + * collection view layout subclasses will need to provide their own implementation of an inspector object for their + * supplementary views to be compatible with `ASCollectionView`'s supplementary node support. + */ +@property (nonatomic, weak) id layoutInspector ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode property instead."); + /** * Determines collection view's current scroll direction. Supports 2-axis collection views. * * @return a bitmask of ASScrollDirection values. */ -@property (nonatomic, readonly) ASScrollDirection scrollDirection; +@property (nonatomic, readonly) ASScrollDirection scrollDirection ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode property instead."); /** * Determines collection view's scrollable directions. * * @return a bitmask of ASScrollDirection values. */ -@property (nonatomic, readonly) ASScrollDirection scrollableDirections; - -/* - * A Boolean value that determines whether the nodes that the data source renders will be flipped. - */ -@property (nonatomic, assign) BOOL inverted; - -@end - -@interface ASCollectionView (Deprecated) +@property (nonatomic, readonly) ASScrollDirection scrollableDirections ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode property instead."); /** * Forces the .contentInset to be UIEdgeInsetsZero. @@ -268,14 +268,14 @@ NS_ASSUME_NONNULL_BEGIN * the main thread. * @warning This method is substantially more expensive than UICollectionView's version. */ -- (void)reloadDataWithCompletion:(nullable void (^)())completion ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); +- (void)reloadDataWithCompletion:(nullable void (^)())completion AS_UNAVAILABLE("Use ASCollectionNode method instead."); /** * Reload everything from scratch, destroying the working range and all cached nodes. * * @warning This method is substantially more expensive than UICollectionView's version. */ -- (void)reloadData ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); +- (void)reloadData AS_UNAVAILABLE("Use ASCollectionNode method instead."); /** * Reload everything from scratch entirely on the main thread, destroying the working range and all cached nodes. @@ -283,7 +283,7 @@ NS_ASSUME_NONNULL_BEGIN * @warning This method is substantially more expensive than UICollectionView's version and will block the main thread * while all the cells load. */ -- (void)reloadDataImmediately ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode's -reloadDataWithCompletion: followed by -waitUntilAllUpdatesAreCommitted instead."); +- (void)reloadDataImmediately AS_UNAVAILABLE("Use ASCollectionNode method instead."); /** * Triggers a relayout of all nodes. diff --git a/Source/ASCollectionView.mm b/Source/ASCollectionView.mm index 850554ba49..55f0369419 100644 --- a/Source/ASCollectionView.mm +++ b/Source/ASCollectionView.mm @@ -1,21 +1,30 @@ // // ASCollectionView.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK #import -#import #import #import #import #import #import +#import +#import #import +#import #import #import #import @@ -24,16 +33,16 @@ #import #import #import -#import #import #import -#import +#import #import #import #import #import #import #import +#import /** * A macro to get self.collectionNode and assign it to a local variable, or return @@ -72,7 +81,7 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; #pragma mark - #pragma mark ASCollectionView. -@interface ASCollectionView () { +@interface ASCollectionView () { ASCollectionViewProxy *_proxyDataSource; ASCollectionViewProxy *_proxyDelegate; @@ -81,9 +90,11 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; ASCollectionViewLayoutController *_layoutController; id _defaultLayoutInspector; __weak id _layoutInspector; - NSMutableSet *_cellsForVisibilityUpdates; - NSMutableSet *_cellsForLayoutUpdates; + NSHashTable<_ASCollectionViewCell *> *_cellsForVisibilityUpdates; + NSHashTable *_cellsForLayoutUpdates; id _layoutFacilitator; + CGFloat _leadingScreensForBatching; + BOOL _inverted; NSUInteger _superBatchUpdateCount; BOOL _isDeallocating; @@ -94,6 +105,9 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; NSMutableSet *_registeredSupplementaryKinds; + // CountedSet because UIKit may display the same element in multiple cells e.g. during animations. + NSCountedSet *_visibleElements; + CGPoint _deceleratingVelocity; BOOL _zeroContentInsets; @@ -130,11 +144,6 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; * (0 sections) we always check at least once after each update (initial reload is the first update.) */ BOOL _hasEverCheckedForBatchFetchingDueToUpdate; - - /** - * The change set that we're currently building, if any. - */ - _ASHierarchyChangeSet *_changeSet; /** * Counter used to keep track of nested batch updates. @@ -146,6 +155,7 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; unsigned int scrollViewWillBeginDragging:1; unsigned int scrollViewDidEndDragging:1; unsigned int scrollViewWillEndDragging:1; + unsigned int scrollViewDidEndDecelerating:1; unsigned int collectionViewWillDisplayNodeForItem:1; unsigned int collectionViewWillDisplayNodeForItemDeprecated:1; unsigned int collectionViewDidEndDisplayingNodeForItem:1; @@ -192,6 +202,7 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; unsigned int collectionViewNumberOfItemsInSection:1; unsigned int collectionNodeNodeForItem:1; unsigned int collectionNodeNodeBlockForItem:1; + unsigned int viewModelForItem:1; unsigned int collectionNodeNodeForSupplementaryElement:1; unsigned int collectionNodeNodeBlockForSupplementaryElement:1; unsigned int collectionNodeSupplementaryElementKindsInSection:1; @@ -213,6 +224,8 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; unsigned int didChangeCollectionViewDataSource:1; unsigned int didChangeCollectionViewDelegate:1; } _layoutInspectorFlags; + + BOOL _hasDataControllerLayoutDelegate; } @end @@ -239,20 +252,20 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; - (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout { - return [self _initWithFrame:frame collectionViewLayout:layout layoutFacilitator:nil eventLog:nil]; + return [self _initWithFrame:frame collectionViewLayout:layout layoutFacilitator:nil owningNode:nil eventLog:nil]; } -- (instancetype)_initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout layoutFacilitator:(id)layoutFacilitator eventLog:(ASEventLog *)eventLog +- (instancetype)_initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout layoutFacilitator:(id)layoutFacilitator owningNode:(ASCollectionNode *)owningNode eventLog:(ASEventLog *)eventLog { if (!(self = [super initWithFrame:frame collectionViewLayout:layout])) return nil; - // Disable UICollectionView prefetching. + // Disable UICollectionView prefetching. Use super, because self disables this method. // Experiments done by Instagram show that this option being YES (default) // when unused causes a significant hit to scroll performance. // https://github.com/Instagram/IGListKit/issues/318 if (AS_AT_LEAST_IOS10) { - self.prefetchingEnabled = NO; + super.prefetchingEnabled = NO; } _layoutController = [[ASCollectionViewLayoutController alloc] initWithCollectionView:self]; @@ -262,9 +275,8 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; _rangeController.delegate = self; _rangeController.layoutController = _layoutController; - _dataController = [[ASDataController alloc] initWithDataSource:self eventLog:eventLog]; + _dataController = [[ASDataController alloc] initWithDataSource:self node:owningNode eventLog:eventLog]; _dataController.delegate = _rangeController; - _dataController.environmentDelegate = self; _batchContext = [[ASBatchContext alloc] init]; @@ -281,9 +293,10 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; super.dataSource = (id)_proxyDataSource; _registeredSupplementaryKinds = [NSMutableSet set]; + _visibleElements = [[NSCountedSet alloc] init]; - _cellsForVisibilityUpdates = [NSMutableSet set]; - _cellsForLayoutUpdates = [NSMutableSet set]; + _cellsForVisibilityUpdates = [NSHashTable hashTableWithOptions:NSHashTableObjectPointerPersonality]; + _cellsForLayoutUpdates = [NSHashTable hashTableWithOptions:NSHashTableObjectPointerPersonality]; self.backgroundColor = [UIColor whiteColor]; [self registerClass:[_ASCollectionViewCell class] forCellWithReuseIdentifier:kReuseIdentifier]; @@ -292,6 +305,8 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; _retainedLayer = self.layer; } + [self _configureCollectionViewLayout:layout]; + return self; } @@ -313,31 +328,23 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; #pragma mark - #pragma mark Overrides. -- (void)reloadDataWithCompletion:(void (^)())completion -{ - ASDisplayNodeAssertMainThread(); - - if (! _dataController.initialReloadDataHasBeenCalled) { - // If this is the first reload, forward to super immediately to prevent it from triggering more "initial" loads while our data controller is working. - _superIsPendingDataLoad = YES; - [super reloadData]; - } - - void (^batchUpdatesCompletion)(BOOL); - if (completion) { - batchUpdatesCompletion = ^(BOOL) { - completion(); - }; - } - - [self performBatchUpdates:^{ - [_changeSet reloadData]; - } completion:batchUpdatesCompletion]; -} - +/** + * This method is not available to be called by the public i.e. + * it should only be called by UICollectionView itself. UICollectionView + * does this e.g. during the first layout pass, or if you call -numberOfSections + * before its content is loaded. + */ - (void)reloadData { - [self reloadDataWithCompletion:nil]; + [super reloadData]; + + // UICollectionView calls -reloadData during first layoutSubviews and when the data source changes. + // This fires off the first load of cell nodes. + if (_asyncDataSource != nil && !self.dataController.initialReloadDataHasBeenCalled) { + [self performBatchUpdates:^{ + [_changeSet reloadData]; + } completion:nil]; + } } - (void)scrollToItemAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UICollectionViewScrollPosition)scrollPosition animated:(BOOL)animated @@ -347,13 +354,6 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; } } -- (void)reloadDataImmediately -{ - ASDisplayNodeAssertMainThread(); - [self reloadData]; - [self waitUntilAllUpdatesAreCommitted]; -} - - (void)relayoutItems { [_dataController relayoutAllNodes]; @@ -431,6 +431,7 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; _asyncDataSourceFlags.collectionNodeNodeForSupplementaryElement = [_asyncDataSource respondsToSelector:@selector(collectionNode:nodeForSupplementaryElementOfKind:atIndexPath:)]; _asyncDataSourceFlags.collectionNodeNodeBlockForSupplementaryElement = [_asyncDataSource respondsToSelector:@selector(collectionNode:nodeBlockForSupplementaryElementOfKind:atIndexPath:)]; _asyncDataSourceFlags.collectionNodeSupplementaryElementKindsInSection = [_asyncDataSource respondsToSelector:@selector(collectionNode:supplementaryElementKindsInSection:)]; + _asyncDataSourceFlags.viewModelForItem = [_asyncDataSource respondsToSelector:@selector(collectionNode:viewModelForItemAtIndexPath:)]; _asyncDataSourceFlags.interop = [_asyncDataSource conformsToProtocol:@protocol(ASCollectionDataSourceInterop)]; if (_asyncDataSourceFlags.interop) { @@ -482,6 +483,7 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; _asyncDelegateFlags.scrollViewDidScroll = [_asyncDelegate respondsToSelector:@selector(scrollViewDidScroll:)]; _asyncDelegateFlags.scrollViewWillEndDragging = [_asyncDelegate respondsToSelector:@selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:)]; + _asyncDelegateFlags.scrollViewDidEndDecelerating = [_asyncDelegate respondsToSelector:@selector(scrollViewDidEndDecelerating:)]; _asyncDelegateFlags.scrollViewWillBeginDragging = [_asyncDelegate respondsToSelector:@selector(scrollViewWillBeginDragging:)]; _asyncDelegateFlags.scrollViewDidEndDragging = [_asyncDelegate respondsToSelector:@selector(scrollViewDidEndDragging:willDecelerate:)]; _asyncDelegateFlags.collectionViewWillDisplayNodeForItem = [_asyncDelegate respondsToSelector:@selector(collectionView:willDisplayNode:forItemAtIndexPath:)]; @@ -532,10 +534,13 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; } } -- (void)setCollectionViewLayout:(UICollectionViewLayout *)collectionViewLayout +- (void)setCollectionViewLayout:(nonnull UICollectionViewLayout *)collectionViewLayout { + ASDisplayNodeAssertMainThread(); [super setCollectionViewLayout:collectionViewLayout]; + [self _configureCollectionViewLayout:collectionViewLayout]; + // Trigger recreation of layout inspector with new collection view layout if (_layoutInspector != nil) { _layoutInspector = nil; @@ -697,7 +702,7 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; if (indexPath.item == NSNotFound) { return indexPath; } else { - return [_dataController.visibleMap convertIndexPath:indexPath fromMap:_dataController.pendingMap]; + return [_dataController.pendingMap convertIndexPath:indexPath fromMap:_dataController.visibleMap]; } } @@ -744,8 +749,26 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; return visibleNodes; } +- (BOOL)usesSynchronousDataLoading +{ + return self.dataController.usesSynchronousDataLoading; +} + +- (void)setUsesSynchronousDataLoading:(BOOL)usesSynchronousDataLoading +{ + self.dataController.usesSynchronousDataLoading = usesSynchronousDataLoading; +} + #pragma mark Internal +- (void)_configureCollectionViewLayout:(nonnull UICollectionViewLayout *)layout +{ + _hasDataControllerLayoutDelegate = [layout conformsToProtocol:@protocol(ASDataControllerLayoutDelegate)]; + if (_hasDataControllerLayoutDelegate) { + _dataController.layoutDelegate = (id)layout; + } +} + /** Performing nested batch updates with super (e.g. resizing a cell node & updating collection view during same frame) can cause super to throw data integrity exceptions because it checks the data source counts before @@ -777,6 +800,8 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; if (_batchUpdateCount == 0) { _changeSet = [[_ASHierarchyChangeSet alloc] initWithOldData:[_dataController itemCountsFromDataSource]]; + _changeSet.rootActivity = as_activity_create("Perform async collection update", AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT); + _changeSet.submitActivity = as_activity_create("Submit changes for collection update", _changeSet.rootActivity, OS_ACTIVITY_FLAG_DEFAULT); } _batchUpdateCount++; } @@ -794,6 +819,7 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; if (_batchUpdateCount == 0) { _ASHierarchyChangeSet *changeSet = _changeSet; + // Nil out _changeSet before forwarding to _dataController to allow the change set to cause subsequent batch updates on the same run loop _changeSet = nil; changeSet.animated = animated; @@ -805,8 +831,13 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; { ASDisplayNodeAssertMainThread(); [self beginUpdates]; - if (updates) { - updates(); + as_activity_scope(_changeSet.rootActivity); + { + // Only include client code in the submit activity, the rest just lives in the root activity. + as_activity_scope(_changeSet.submitActivity); + if (updates) { + updates(); + } } [self endUpdatesAnimated:animated completion:completion]; } @@ -821,7 +852,7 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; { ASDisplayNodeAssert(elementKind != nil, @"A kind is needed for supplementary node registration"); [_registeredSupplementaryKinds addObject:elementKind]; - [self registerClass:[UICollectionReusableView class] forSupplementaryViewOfKind:elementKind withReuseIdentifier:kReuseIdentifier]; + [self registerClass:[_ASCollectionReusableView class] forSupplementaryViewOfKind:elementKind withReuseIdentifier:kReuseIdentifier]; } - (void)insertSections:(NSIndexSet *)sections @@ -921,7 +952,13 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath { ASDisplayNodeAssertMainThread(); - ASCellNode *cell = [self nodeForItemAtIndexPath:indexPath]; + ASCollectionElement *element = [_dataController.visibleMap elementForItemAtIndexPath:indexPath]; + if (element == nil) { + ASDisplayNodeAssert(NO, @"Unexpected nil element for collectionView:layout:sizeForItemAtIndexPath: %@, %@, %@", self, collectionViewLayout, indexPath); + return CGSizeZero; + } + + ASCellNode *cell = element.node; if (cell.shouldUseUIKitCell) { if ([_asyncDelegate respondsToSelector:@selector(collectionView:layout:sizeForItemAtIndexPath:)]) { CGSize size = [(id)_asyncDelegate collectionView:collectionView layout:collectionViewLayout sizeForItemAtIndexPath:indexPath]; @@ -929,38 +966,46 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; return size; } } - ASCollectionElement *e = [_dataController.visibleMap elementForItemAtIndexPath:indexPath]; - return [self sizeForElement:e]; + + return [self sizeForElement:element]; } - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout referenceSizeForHeaderInSection:(NSInteger)section { ASDisplayNodeAssertMainThread(); NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:section]; - ASCellNode *cell = [self supplementaryNodeForElementKind:UICollectionElementKindSectionHeader - atIndexPath:indexPath]; - if (cell.shouldUseUIKitCell && _asyncDelegateFlags.interop) { + ASCollectionElement *element = [_dataController.visibleMap supplementaryElementOfKind:UICollectionElementKindSectionHeader + atIndexPath:indexPath]; + if (element == nil) { + return CGSizeZero; + } + + if (element.node.shouldUseUIKitCell && _asyncDelegateFlags.interop) { if ([_asyncDelegate respondsToSelector:@selector(collectionView:layout:referenceSizeForHeaderInSection:)]) { return [(id)_asyncDelegate collectionView:collectionView layout:layout referenceSizeForHeaderInSection:section]; } } - ASCollectionElement *e = [_dataController.visibleMap supplementaryElementOfKind:UICollectionElementKindSectionHeader atIndexPath:indexPath]; - return [self sizeForElement:e]; + + return [self sizeForElement:element]; } - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout referenceSizeForFooterInSection:(NSInteger)section { ASDisplayNodeAssertMainThread(); NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:section]; - ASCellNode *cell = [self supplementaryNodeForElementKind:UICollectionElementKindSectionFooter - atIndexPath:indexPath]; - if (cell.shouldUseUIKitCell && _asyncDelegateFlags.interop) { + ASCollectionElement *element = [_dataController.visibleMap supplementaryElementOfKind:UICollectionElementKindSectionFooter + atIndexPath:indexPath]; + if (element == nil) { + return CGSizeZero; + } + + if (element.node.shouldUseUIKitCell && _asyncDelegateFlags.interop) { if ([_asyncDelegate respondsToSelector:@selector(collectionView:layout:referenceSizeForFooterInSection:)]) { return [(id)_asyncDelegate collectionView:collectionView layout:layout referenceSizeForFooterInSection:section]; } } - ASCollectionElement *e = [_dataController.visibleMap supplementaryElementOfKind:UICollectionElementKindSectionFooter atIndexPath:indexPath]; - return [self sizeForElement:e]; + + return [self sizeForElement:element]; } - (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath @@ -970,7 +1015,8 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; } UICollectionReusableView *view = nil; - ASCellNode *node = [_dataController.visibleMap supplementaryElementOfKind:kind atIndexPath:indexPath].node; + ASCollectionElement *element = [_dataController.visibleMap supplementaryElementOfKind:kind atIndexPath:indexPath]; + ASCellNode *node = element.node; BOOL shouldDequeueExternally = _asyncDataSourceFlags.interopViewForSupplementaryElement && (_asyncDataSourceFlags.interopAlwaysDequeue || node.shouldUseUIKitCell); if (shouldDequeueExternally) { @@ -980,7 +1026,11 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; ASDisplayNodeAssert(node != nil, @"Supplementary node should exist. Kind = %@, indexPath = %@, collectionDataSource = %@", kind, indexPath, self); view = [self dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:kReuseIdentifier forIndexPath:indexPath]; } - + + if (_ASCollectionReusableView *reusableView = ASDynamicCastStrict(view, _ASCollectionReusableView)) { + reusableView.element = element; + } + if (node) { [_rangeController configureContentView:view forCellNode:node]; } @@ -991,7 +1041,8 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { UICollectionViewCell *cell = nil; - ASCellNode *node = [self nodeForItemAtIndexPath:indexPath]; + ASCollectionElement *element = [_dataController.visibleMap elementForItemAtIndexPath:indexPath]; + ASCellNode *node = element.node; BOOL shouldDequeueExternally = _asyncDataSourceFlags.interopAlwaysDequeue || (_asyncDataSourceFlags.interop && node.shouldUseUIKitCell); if (shouldDequeueExternally) { @@ -1000,30 +1051,38 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; cell = [self dequeueReusableCellWithReuseIdentifier:kReuseIdentifier forIndexPath:indexPath]; } - ASDisplayNodeAssert(node != nil, @"Cell node should exist. indexPath = %@, collectionDataSource = %@", indexPath, self); + ASDisplayNodeAssert(element != nil, @"Element should exist. indexPath = %@, collectionDataSource = %@", indexPath, self); - if (_ASCollectionViewCell *asCell = ASDynamicCast(cell, _ASCollectionViewCell)) { - asCell.node = node; + if (_ASCollectionViewCell *asCell = ASDynamicCastStrict(cell, _ASCollectionViewCell)) { + asCell.element = element; [_rangeController configureContentView:cell.contentView forCellNode:node]; } return cell; } -- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(_ASCollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath +- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)rawCell forItemAtIndexPath:(NSIndexPath *)indexPath { if (_asyncDelegateFlags.interopWillDisplayCell) { - [(id )_asyncDelegate collectionView:collectionView willDisplayCell:cell forItemAtIndexPath:indexPath]; + [(id )_asyncDelegate collectionView:collectionView willDisplayCell:rawCell forItemAtIndexPath:indexPath]; } - // Since _ASCollectionViewCell is not available for subclassing, this is faster than isKindOfClass: - // We must exit early here, because only _ASCollectionViewCell implements the -node accessor method. - if ([cell class] != [_ASCollectionViewCell class]) { + _ASCollectionViewCell *cell = ASDynamicCastStrict(rawCell, _ASCollectionViewCell); + if (cell == nil) { [_rangeController setNeedsUpdate]; return; } - - ASCellNode *cellNode = [cell node]; + + ASCollectionElement *element = cell.element; + if (element) { + ASDisplayNodeAssertTrue([_dataController.visibleMap elementForItemAtIndexPath:indexPath] == element); + [_visibleElements addObject:element]; + } else { + ASDisplayNodeAssert(NO, @"Unexpected nil element for willDisplayCell: %@, %@, %@", rawCell, self, indexPath); + return; + } + + ASCellNode *cellNode = element.node; cellNode.scrollView = collectionView; // Update the selected background view in collectionView:willDisplayCell:forItemAtIndexPath: otherwise it could be to @@ -1054,26 +1113,33 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; [_rangeController setNeedsUpdate]; - if (ASSubclassOverridesSelector([ASCellNode class], [cellNode class], @selector(cellNodeVisibilityEvent:inScrollView:withCellFrame:))) { + if ([cell consumesCellNodeVisibilityEvents]) { [_cellsForVisibilityUpdates addObject:cell]; } } -- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(_ASCollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath +- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)rawCell forItemAtIndexPath:(NSIndexPath *)indexPath { if (_asyncDelegateFlags.interopDidEndDisplayingCell) { - [(id )_asyncDelegate collectionView:collectionView didEndDisplayingCell:cell forItemAtIndexPath:indexPath]; + [(id )_asyncDelegate collectionView:collectionView didEndDisplayingCell:rawCell forItemAtIndexPath:indexPath]; } - // Since _ASCollectionViewCell is not available for subclassing, this is faster than isKindOfClass: - // We must exit early here, because only _ASCollectionViewCell implements the -node accessor method. - if ([cell class] != [_ASCollectionViewCell class]) { + _ASCollectionViewCell *cell = ASDynamicCastStrict(rawCell, _ASCollectionViewCell); + if (cell == nil) { [_rangeController setNeedsUpdate]; return; } - - ASCellNode *cellNode = [cell node]; - ASDisplayNodeAssertNotNil(cellNode, @"Expected node associated with removed cell not to be nil."); + + // Retrieve the element from cell instead of visible map because at this point visible map could have been updated and no longer holds the element. + ASCollectionElement *element = cell.element; + if (element) { + [_visibleElements removeObject:element]; + } else { + ASDisplayNodeAssert(NO, @"Unexpected nil element for didEndDisplayingCell: %@, %@, %@", rawCell, self, indexPath); + return; + } + + ASCellNode *cellNode = element.node; if (_asyncDelegateFlags.collectionNodeDidEndDisplayingItem) { if (ASCollectionNode *collectionNode = self.collectionNode) { @@ -1094,21 +1160,58 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; cell.layoutAttributes = nil; } -- (void)collectionView:(UICollectionView *)collectionView willDisplaySupplementaryView:(UICollectionReusableView *)view forElementKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath +- (void)collectionView:(UICollectionView *)collectionView willDisplaySupplementaryView:(UICollectionReusableView *)rawView forElementKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath { + _ASCollectionReusableView *view = ASDynamicCastStrict(rawView, _ASCollectionReusableView); + if (view == nil) { + return; + } + + ASCollectionElement *element = view.element; + if (element) { + ASDisplayNodeAssertTrue([_dataController.visibleMap supplementaryElementOfKind:elementKind atIndexPath:indexPath] == view.element); + [_visibleElements addObject:element]; + } else { + ASDisplayNodeAssert(NO, @"Unexpected nil element for willDisplaySupplementaryView: %@, %@, %@", rawView, self, indexPath); + return; + } + + // Under iOS 10+, cells may be removed/re-added to the collection view without + // receiving prepareForReuse/applyLayoutAttributes, as an optimization for e.g. + // if the user is scrolling back and forth across a small set of items. + // In this case, we have to fetch the layout attributes manually. + // This may be possible under iOS < 10 but it has not been observed yet. + if (view.layoutAttributes == nil) { + view.layoutAttributes = [collectionView layoutAttributesForSupplementaryElementOfKind:elementKind atIndexPath:indexPath]; + } + if (_asyncDelegateFlags.collectionNodeWillDisplaySupplementaryElement) { GET_COLLECTIONNODE_OR_RETURN(collectionNode, (void)0); - ASCellNode *node = [self supplementaryNodeForElementKind:elementKind atIndexPath:indexPath]; + ASCellNode *node = element.node; ASDisplayNodeAssert([node.supplementaryElementKind isEqualToString:elementKind], @"Expected node for supplementary element to have kind '%@', got '%@'.", elementKind, node.supplementaryElementKind); [_asyncDelegate collectionNode:collectionNode willDisplaySupplementaryElementWithNode:node]; } } -- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingSupplementaryView:(UICollectionReusableView *)view forElementOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath +- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingSupplementaryView:(UICollectionReusableView *)rawView forElementOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath { + _ASCollectionReusableView *view = ASDynamicCastStrict(rawView, _ASCollectionReusableView); + if (view == nil) { + return; + } + + // Retrieve the element from cell instead of visible map because at this point visible map could have been updated and no longer holds the element. + ASCollectionElement *element = view.element; + if (element) { + [_visibleElements removeObject:element]; + } else { + ASDisplayNodeAssert(NO, @"Unexpected nil element for didEndDisplayingSupplementaryView: %@, %@, %@", rawView, self, indexPath); + return; + } + if (_asyncDelegateFlags.collectionNodeDidEndDisplayingSupplementaryElement) { GET_COLLECTIONNODE_OR_RETURN(collectionNode, (void)0); - ASCellNode *node = [self supplementaryNodeForElementKind:elementKind atIndexPath:indexPath]; + ASCellNode *node = element.node; ASDisplayNodeAssert([node.supplementaryElementKind isEqualToString:elementKind], @"Expected node for supplementary element to have kind '%@', got '%@'.", elementKind, node.supplementaryElementKind); [_asyncDelegate collectionNode:collectionNode didEndDisplayingSupplementaryElementWithNode:node]; } @@ -1290,11 +1393,9 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; [self _checkForBatchFetching]; } - for (_ASCollectionViewCell *collectionCell in _cellsForVisibilityUpdates) { + for (_ASCollectionViewCell *cell in _cellsForVisibilityUpdates) { // Only nodes that respond to the selector are added to _cellsForVisibilityUpdates - [[collectionCell node] cellNodeVisibilityEvent:ASCellNodeVisibilityEventVisibleRectChanged - inScrollView:scrollView - withCellFrame:collectionCell.frame]; + [cell cellNodeVisibilityEvent:ASCellNodeVisibilityEventVisibleRectChanged inScrollView:scrollView]; } if (_asyncDelegateFlags.scrollViewDidScroll) { [_asyncDelegate scrollViewDidScroll:scrollView]; @@ -1311,7 +1412,7 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; if (targetContentOffset != NULL) { ASDisplayNodeAssert(_batchContext != nil, @"Batch context should exist"); - [self _beginBatchFetchingIfNeededWithContentOffset:*targetContentOffset]; + [self _beginBatchFetchingIfNeededWithContentOffset:*targetContentOffset velocity:velocity]; } if (_asyncDelegateFlags.scrollViewWillEndDragging) { @@ -1319,12 +1420,19 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; } } +- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView +{ + _deceleratingVelocity = CGPointZero; + + if (_asyncDelegateFlags.scrollViewDidEndDecelerating) { + [_asyncDelegate scrollViewDidEndDecelerating:scrollView]; + } +} + - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { - for (_ASCollectionViewCell *collectionCell in _cellsForVisibilityUpdates) { - [[collectionCell node] cellNodeVisibilityEvent:ASCellNodeVisibilityEventWillBeginDragging - inScrollView:scrollView - withCellFrame:collectionCell.frame]; + for (_ASCollectionViewCell *cell in _cellsForVisibilityUpdates) { + [cell cellNodeVisibilityEvent:ASCellNodeVisibilityEventWillBeginDragging inScrollView:scrollView]; } if (_asyncDelegateFlags.scrollViewWillBeginDragging) { [_asyncDelegate scrollViewWillBeginDragging:scrollView]; @@ -1333,18 +1441,36 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate { - for (_ASCollectionViewCell *collectionCell in _cellsForVisibilityUpdates) { - [[collectionCell node] cellNodeVisibilityEvent:ASCellNodeVisibilityEventDidEndDragging - inScrollView:scrollView - withCellFrame:collectionCell.frame]; - } - if (_asyncDelegateFlags.scrollViewDidEndDragging) { - [_asyncDelegate scrollViewDidEndDragging:scrollView willDecelerate:decelerate]; - } + for (_ASCollectionViewCell *cell in _cellsForVisibilityUpdates) { + [cell cellNodeVisibilityEvent:ASCellNodeVisibilityEventDidEndDragging inScrollView:scrollView]; + } + if (_asyncDelegateFlags.scrollViewDidEndDragging) { + [_asyncDelegate scrollViewDidEndDragging:scrollView willDecelerate:decelerate]; + } } #pragma mark - Scroll Direction. +- (BOOL)inverted +{ + return _inverted; +} + +- (void)setInverted:(BOOL)inverted +{ + _inverted = inverted; +} + +- (void)setLeadingScreensForBatching:(CGFloat)leadingScreensForBatching +{ + _leadingScreensForBatching = leadingScreensForBatching; +} + +- (CGFloat)leadingScreensForBatching +{ + return _leadingScreensForBatching; +} + - (ASScrollDirection)scrollDirection { CGPoint scrollVelocity; @@ -1456,6 +1582,10 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; } } +- (id)batchFetchingDelegate{ + return self.collectionNode.batchFetchingDelegate; +} + - (void)_scheduleCheckForBatchFetchingForNumberOfChanges:(NSUInteger)changes { // Prevent fetching will continually trigger in a loop after reaching end of content and no new content was provided @@ -1477,22 +1607,24 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; return; } - [self _beginBatchFetchingIfNeededWithContentOffset:self.contentOffset]; + [self _beginBatchFetchingIfNeededWithContentOffset:self.contentOffset velocity:CGPointZero]; } -- (void)_beginBatchFetchingIfNeededWithContentOffset:(CGPoint)contentOffset +- (void)_beginBatchFetchingIfNeededWithContentOffset:(CGPoint)contentOffset velocity:(CGPoint)velocity { - if (ASDisplayShouldFetchBatchForScrollView(self, self.scrollDirection, self.scrollableDirections, contentOffset)) { + if (ASDisplayShouldFetchBatchForScrollView(self, self.scrollDirection, self.scrollableDirections, contentOffset, velocity)) { [self _beginBatchFetching]; } } - (void)_beginBatchFetching { + as_activity_create_for_scope("Batch fetch for collection node"); [_batchContext beginBatchFetching]; if (_asyncDelegateFlags.collectionNodeWillBeginBatchFetch) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ GET_COLLECTIONNODE_OR_RETURN(collectionNode, (void)0); + as_log_debug(ASCollectionLog(), "Beginning batch fetch for %@ with context %@", collectionNode, _batchContext); [_asyncDelegate collectionNode:collectionNode willBeginBatchFetchWithContext:_batchContext]; }); } else if (_asyncDelegateFlags.collectionViewWillBeginBatchFetch) { @@ -1505,9 +1637,18 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; } } - #pragma mark - ASDataControllerSource +- (id)dataController:(ASDataController *)dataController viewModelForItemAtIndexPath:(NSIndexPath *)indexPath +{ + if (!_asyncDataSourceFlags.viewModelForItem) { + return nil; + } + + GET_COLLECTIONNODE_OR_RETURN(collectionNode, nil); + return [_asyncDataSource collectionNode:collectionNode viewModelForItemAtIndexPath:indexPath]; +} + - (ASCellNodeBlock)dataController:(ASDataController *)dataController nodeBlockAtIndexPath:(NSIndexPath *)indexPath { ASCellNodeBlock block = nil; @@ -1544,7 +1685,7 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; return cell; }; } else { - ASDisplayNodeFailAssert(@"ASCollection could not get a node block for row at index path %@: %@, %@. If you are trying to display a UICollectionViewCell, make sure your dataSource conforms to the protocol!", indexPath, cell, block); + ASDisplayNodeFailAssert(@"ASCollection could not get a node block for item at index path %@: %@, %@. If you are trying to display a UICollectionViewCell, make sure your dataSource conforms to the protocol!", indexPath, cell, block); block = ^{ return [[ASCellNode alloc] init]; }; @@ -1560,7 +1701,7 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; if (node.interactionDelegate == nil) { node.interactionDelegate = strongSelf; } - if (_inverted) { + if (strongSelf.inverted) { node.transform = CATransform3DMakeScale(1, -1, 1) ; } return node; @@ -1568,11 +1709,6 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; return block; } -- (ASSizeRange)dataController:(ASDataController *)dataController constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath -{ - return [self.layoutInspector collectionView:self constrainedSizeForNodeAtIndexPath:indexPath]; -} - - (NSUInteger)dataController:(ASDataController *)dataController rowsInSection:(NSUInteger)section { if (_asyncDataSourceFlags.collectionNodeNumberOfItemsInSection) { @@ -1605,15 +1741,12 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; - (BOOL)dataController:(ASDataController *)dataController presentedSizeForElement:(ASCollectionElement *)element matchesSize:(CGSize)size { NSIndexPath *indexPath = [self indexPathForNode:element.node]; + if (indexPath == nil) { + ASDisplayNodeFailAssert(@"Data controller should not ask for presented size for element that is not presented."); + return YES; + } UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForItemAtIndexPath:indexPath]; - CGRect rect = attributes.frame; - return CGSizeEqualToSizeWithIn(rect.size, size, FLT_EPSILON); - -} - -- (id)dataControllerEnvironment -{ - return self.collectionNode; + return CGSizeEqualToSizeWithIn(attributes.size, size, FLT_EPSILON); } #pragma mark - ASDataControllerSource optional methods @@ -1667,6 +1800,11 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; } } +- (ASSizeRange)dataController:(ASDataController *)dataController constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath +{ + return [self.layoutInspector collectionView:self constrainedSizeForNodeAtIndexPath:indexPath]; +} + - (ASSizeRange)dataController:(ASDataController *)dataController constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { if (_layoutInspectorFlags.constrainedSizeForSupplementaryNodeOfKindAtIndexPath) { @@ -1736,37 +1874,9 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; return result; } -- (NSArray *)visibleElementsForRangeController:(ASRangeController *)rangeController +- (NSHashTable *)visibleElementsForRangeController:(ASRangeController *)rangeController { - if (CGRectIsEmpty(self.bounds)) { - return @[]; - } - - ASElementMap *map = _dataController.visibleMap; - NSMutableArray *result = [NSMutableArray array]; - - // Visible items - for (NSIndexPath *indexPath in self.indexPathsForVisibleItems) { - ASCollectionElement *element = [map elementForItemAtIndexPath:indexPath]; - if (element != nil) { - [result addObject:element]; - } else { - ASDisplayNodeFailAssert(@"Couldn't find 'visible' item at index path %@ in map %@", indexPath, map); - } - } - - // Visible supplementary elements - for (NSString *kind in map.supplementaryElementKinds) { - for (NSIndexPath *indexPath in [self asdk_indexPathsForVisibleSupplementaryElementsOfKind:kind]) { - ASCollectionElement *element = [map supplementaryElementOfKind:kind atIndexPath:indexPath]; - if (element != nil) { - [result addObject:element]; - } else { - ASDisplayNodeFailAssert(@"Couldn't find 'visible' supplementary element of kind %@ at index path %@ in map %@", kind, indexPath, map); - } - } - } - return result; + return ASPointerTableByFlatMapping(_visibleElements, id element, element); } - (ASElementMap *)elementMapForRangeController:(ASRangeController *)rangeController @@ -1821,24 +1931,30 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; } } -- (void)rangeController:(ASRangeController *)rangeController didUpdateWithChangeSet:(_ASHierarchyChangeSet *)changeSet +- (void)rangeController:(ASRangeController *)rangeController didUpdateWithChangeSet:(_ASHierarchyChangeSet *)changeSet updates:(dispatch_block_t)updates { ASDisplayNodeAssertMainThread(); if (!self.asyncDataSource || _superIsPendingDataLoad) { + updates(); [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) { + as_activity_scope(as_activity_create("Commit collection update", changeSet.rootActivity, OS_ACTIVITY_FLAG_DEFAULT)); + if (changeSet.includesReloadData) { _superIsPendingDataLoad = YES; + updates(); [super reloadData]; + as_log_debug(ASCollectionLog(), "Did reloadData %@", self.collectionNode); [changeSet executeCompletionHandlerWithFinished:YES]; } else { [_layoutFacilitator collectionViewWillPerformBatchUpdates]; __block NSUInteger numberOfUpdates = 0; [self _superPerformBatchUpdates:^{ + updates(); + for (_ASHierarchyItemChange *change in [changeSet itemChangesOfType:_ASHierarchyChangeTypeReload]) { [super reloadItemsAtIndexPaths:change.indexPaths]; numberOfUpdates++; @@ -1869,19 +1985,24 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; numberOfUpdates++; } } completion:^(BOOL finished){ + as_activity_scope(as_activity_create("Handle collection update completion", changeSet.rootActivity, OS_ACTIVITY_FLAG_DEFAULT)); + as_log_verbose(ASCollectionLog(), "Update animation finished %{public}@", self.collectionNode); // Flush any range changes that happened as part of the update animations ending. [_rangeController updateIfNeeded]; [self _scheduleCheckForBatchFetchingForNumberOfChanges:numberOfUpdates]; [changeSet executeCompletionHandlerWithFinished:finished]; }]; + as_log_debug(ASCollectionLog(), "Completed batch update %{public}@", self.collectionNode); // Flush any range changes that happened as part of submitting the update. + as_activity_scope(changeSet.rootActivity); [_rangeController updateIfNeeded]; } }); } #pragma mark - ASCellNodeDelegate + - (void)nodeSelectedStateDidChange:(ASCellNode *)node { NSIndexPath *indexPath = [self indexPathForNode:node]; @@ -1908,16 +2029,6 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; [self setNeedsLayout]; } -- (void)nodeDidRelayout:(ASCellNode *)node sizeChanged:(BOOL)sizeChanged -{ - ASDisplayNodeAssertMainThread(); - - if (!sizeChanged) { - return; - } - [self nodesDidRelayout:@[node]]; -} - - (void)nodesDidRelayout:(NSArray *)nodes { ASDisplayNodeAssertMainThread(); @@ -2020,6 +2131,10 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; */ - (void)layer:(CALayer *)layer didChangeBoundsWithOldValue:(CGRect)oldBounds newValue:(CGRect)newBounds { + if (_hasDataControllerLayoutDelegate) { + // Let the layout delegate handle bounds changes if it's available. + return; + } if (self.collectionViewLayout == nil) { return; } @@ -2050,6 +2165,16 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; #pragma mark - UICollectionView dead-end intercepts +- (void)setPrefetchDataSource:(id)prefetchDataSource +{ + return; +} + +- (void)setPrefetchingEnabled:(BOOL)prefetchingEnabled +{ + return; +} + #if ASDISPLAYNODE_ASSERTIONS_ENABLED // Remove implementations entirely for efficiency if not asserting. // intercepted due to not being supported by ASCollectionView (prevent bugs caused by usage) diff --git a/Source/ASCollectionViewLayoutFacilitatorProtocol.h b/Source/ASCollectionViewLayoutFacilitatorProtocol.h index 80f8d5fd88..5358e9cec8 100644 --- a/Source/ASCollectionViewLayoutFacilitatorProtocol.h +++ b/Source/ASCollectionViewLayoutFacilitatorProtocol.h @@ -1,11 +1,18 @@ // // ASCollectionViewLayoutFacilitatorProtocol.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK diff --git a/Source/ASCollectionViewProtocols.h b/Source/ASCollectionViewProtocols.h index 358b85d320..8779ee15b2 100644 --- a/Source/ASCollectionViewProtocols.h +++ b/Source/ASCollectionViewProtocols.h @@ -1,11 +1,18 @@ // // ASCollectionViewProtocols.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK diff --git a/Source/ASContextTransitioning.h b/Source/ASContextTransitioning.h index f013ff2134..8982211b9a 100644 --- a/Source/ASContextTransitioning.h +++ b/Source/ASContextTransitioning.h @@ -1,13 +1,18 @@ // // ASContextTransitioning.h -// AsyncDisplayKit -// -// Created by Levi McCallum on 2/4/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/ASControlNode+Subclasses.h b/Source/ASControlNode+Subclasses.h index bed6a1b33e..e9d7c4c5ab 100644 --- a/Source/ASControlNode+Subclasses.h +++ b/Source/ASControlNode+Subclasses.h @@ -1,11 +1,18 @@ // // ASControlNode+Subclasses.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/ASControlNode.h b/Source/ASControlNode.h index b5e9935e28..e08f0650f3 100644 --- a/Source/ASControlNode.h +++ b/Source/ASControlNode.h @@ -1,11 +1,18 @@ // // ASControlNode.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/ASControlNode.mm b/Source/ASControlNode.mm index cd20c7dab6..fdd1155e7a 100644 --- a/Source/ASControlNode.mm +++ b/Source/ASControlNode.mm @@ -1,11 +1,18 @@ // // ASControlNode.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -273,6 +280,11 @@ CGRect _ASControlNodeGetExpandedBounds(ASControlNode *controlNode); return YES; } +- (BOOL)supportsLayerBacking +{ + return super.supportsLayerBacking && !self.userInteractionEnabled; +} + #pragma mark - Action Messages - (void)addTarget:(id)target action:(SEL)action forControlEvents:(ASControlNodeEvent)controlEventMask @@ -415,34 +427,44 @@ CGRect _ASControlNodeGetExpandedBounds(ASControlNode *controlNode); - (void)sendActionsForControlEvents:(ASControlNodeEvent)controlEvents withEvent:(UIEvent *)event { + ASDisplayNodeAssertMainThread(); //We access self.view below, it's not safe to call this off of main. NSParameterAssert(controlEvents != 0); - ASDN::MutexLocker l(_controlLock); + NSMutableArray *resolvedEventTargetActionArray = [[NSMutableArray alloc] init]; + + _controlLock.lock(); // Enumerate the events in the mask, invoking the target-action pairs for each. _ASEnumerateControlEventsIncludedInMaskWithBlock(controlEvents, ^ (ASControlNodeEvent controlEvent) { - // Use a copy to itereate, the action perform could call remove causing a mutation crash. - NSMutableArray *eventTargetActionArray = [_controlEventDispatchTable[_ASControlNodeEventKeyForControlEvent(controlEvent)] copy]; - // Iterate on each target action pair - for (ASControlTargetAction *targetAction in eventTargetActionArray) { - SEL action = targetAction.action; - id responder = targetAction.target; + for (ASControlTargetAction *targetAction in _controlEventDispatchTable[_ASControlNodeEventKeyForControlEvent(controlEvent)]) { + ASControlTargetAction *resolvedTargetAction = [[ASControlTargetAction alloc] init]; + resolvedTargetAction.action = targetAction.action; + resolvedTargetAction.target = targetAction.target; // NSNull means that a nil target was set, so start at self and travel the responder chain - if (!responder && targetAction.createdWithNoTarget) { + if (!resolvedTargetAction.target && targetAction.createdWithNoTarget) { // if the target cannot perform the action, travel the responder chain to try to find something that does - responder = [self.view targetForAction:action withSender:self]; + resolvedTargetAction.target = [self.view targetForAction:resolvedTargetAction.action withSender:self]; } -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Warc-performSelector-leaks" - [responder performSelector:action withObject:self withObject:event]; -#pragma clang diagnostic pop + if (resolvedTargetAction.target) { + [resolvedEventTargetActionArray addObject:resolvedTargetAction]; + } } }); + + _controlLock.unlock(); + + //We don't want to hold the lock while calling out, we could potentially walk up the ownership tree causing a deadlock. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" + for (ASControlTargetAction *targetAction in resolvedEventTargetActionArray) { + [targetAction.target performSelector:targetAction.action withObject:self withObject:event]; + } +#pragma clang diagnostic pop } #pragma mark - Convenience diff --git a/Source/ASDisplayNode+Beta.h b/Source/ASDisplayNode+Beta.h index 19a1e679ea..8235c24c9f 100644 --- a/Source/ASDisplayNode+Beta.h +++ b/Source/ASDisplayNode+Beta.h @@ -1,11 +1,18 @@ // // ASDisplayNode+Beta.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -15,6 +22,7 @@ #if YOGA #import YOGA_HEADER_PATH + #import #endif NS_ASSUME_NONNULL_BEGIN @@ -129,11 +137,11 @@ typedef struct { + (void)setRangeModeForMemoryWarnings:(ASLayoutRangeMode)rangeMode; /** - * @abstract Whether to draw all descendant nodes' layers/views into this node's layer/view's backing store. + * @abstract Whether to draw all descendent nodes' contents into this node's layer's backing store. * * @discussion - * When set to YES, causes all descendant nodes' layers/views to be drawn directly into this node's layer/view's backing - * store. Defaults to NO. + * When called, causes all descendent nodes' contents to be drawn directly into this node's layer's backing + * store. * * If a node's descendants are static (never animated or never change attributes after creation) then that node is a * good candidate for rasterization. Rasterizing descendants has two main benefits: @@ -147,8 +155,11 @@ typedef struct { * * Note: this has nothing to do with -[CALayer shouldRasterize], which doesn't work with ASDisplayNode's asynchronous * rendering model. + * + * Note: You cannot add subnodes whose layers/views are already loaded to a rasterized node. + * Note: You cannot call this method after the receiver's layer/view is loaded. */ -@property (nonatomic, assign) BOOL shouldRasterizeDescendants ASDISPLAYNODE_DEPRECATED_MSG("Deprecated in version 2.2"); +- (void)enableSubtreeRasterization; @end @@ -160,12 +171,15 @@ extern void ASDisplayNodePerformBlockOnEveryYogaChild(ASDisplayNode * _Nullable @interface ASDisplayNode (Yoga) -@property (nonatomic, strong) NSArray *yogaChildren; -@property (nonatomic, strong) ASLayout *yogaCalculatedLayout; +@property (nonatomic, strong, nullable) NSArray *yogaChildren; - (void)addYogaChild:(ASDisplayNode *)child; - (void)removeYogaChild:(ASDisplayNode *)child; +- (void)semanticContentAttributeDidChange:(UISemanticContentAttribute)attribute; + +@property (nonatomic, assign) BOOL yogaLayoutInProgress; +@property (nonatomic, strong, nullable) ASLayout *yogaCalculatedLayout; // These methods should not normally be called directly. - (void)invalidateCalculatedYogaLayout; - (void)calculateLayoutFromYogaRoot:(ASSizeRange)rootConstrainedSize; @@ -174,8 +188,11 @@ extern void ASDisplayNodePerformBlockOnEveryYogaChild(ASDisplayNode * _Nullable @interface ASLayoutElementStyle (Yoga) -@property (nonatomic, assign, readwrite) ASStackLayoutDirection direction; -@property (nonatomic, assign, readwrite) CGFloat spacing; +- (YGNodeRef)yogaNodeCreateIfNeeded; +@property (nonatomic, assign, readonly) YGNodeRef yogaNode; + +@property (nonatomic, assign, readwrite) ASStackLayoutDirection flexDirection; +@property (nonatomic, assign, readwrite) YGDirection direction; @property (nonatomic, assign, readwrite) ASStackLayoutJustifyContent justifyContent; @property (nonatomic, assign, readwrite) ASStackLayoutAlignItems alignItems; @property (nonatomic, assign, readwrite) YGPositionType positionType; diff --git a/Source/ASDisplayNode+Convenience.h b/Source/ASDisplayNode+Convenience.h index b75947c0dd..b5806b0f7f 100644 --- a/Source/ASDisplayNode+Convenience.h +++ b/Source/ASDisplayNode+Convenience.h @@ -1,9 +1,18 @@ // // ASDisplayNode+Convenience.h -// AsyncDisplayKit +// Texture // -// Created by Adlai Holler on 2/24/17. -// Copyright © 2017 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 /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/ASDisplayNode+Convenience.m b/Source/ASDisplayNode+Convenience.m index a85b734338..4e47f78ace 100644 --- a/Source/ASDisplayNode+Convenience.m +++ b/Source/ASDisplayNode+Convenience.m @@ -1,9 +1,18 @@ // // ASDisplayNode+Convenience.m -// AsyncDisplayKit +// Texture // -// Created by Adlai Holler on 2/24/17. -// Copyright © 2017 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 /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "ASDisplayNode+Convenience.h" diff --git a/Source/ASDisplayNode+Deprecated.h b/Source/ASDisplayNode+Deprecated.h index 851d5259a9..b05390ea47 100644 --- a/Source/ASDisplayNode+Deprecated.h +++ b/Source/ASDisplayNode+Deprecated.h @@ -1,11 +1,18 @@ // // ASDisplayNode+Deprecated.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #pragma once diff --git a/Source/ASDisplayNode+Layout.mm b/Source/ASDisplayNode+Layout.mm new file mode 100644 index 0000000000..6b73c1bc5e --- /dev/null +++ b/Source/ASDisplayNode+Layout.mm @@ -0,0 +1,963 @@ +// +// ASDisplayNode+Layout.mm +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import +#import +#import +#import + +#import + +#pragma mark - +#pragma mark - ASDisplayNode (ASLayoutElement) + +@implementation ASDisplayNode (ASLayoutElement) + +#pragma mark + +- (BOOL)implementsLayoutMethod +{ + ASDN::MutexLocker l(__instanceLock__); + return (_methodOverrides & (ASDisplayNodeMethodOverrideLayoutSpecThatFits | + ASDisplayNodeMethodOverrideCalcLayoutThatFits | + ASDisplayNodeMethodOverrideCalcSizeThatFits)) != 0 || _layoutSpecBlock != nil; +} + + +- (ASLayoutElementStyle *)style +{ + ASDN::MutexLocker l(__instanceLock__); + if (_style == nil) { + _style = [[ASLayoutElementStyle alloc] init]; + } + return _style; +} + +- (ASLayoutElementType)layoutElementType +{ + return ASLayoutElementTypeDisplayNode; +} + +- (NSArray> *)sublayoutElements +{ + return self.subnodes; +} + +#pragma mark Measurement Pass + +- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize +{ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + // For now we just call the deprecated measureWithSizeRange: method to not break old API + return [self measureWithSizeRange:constrainedSize]; +#pragma clang diagnostic pop +} + +- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize +{ + return [self layoutThatFits:constrainedSize parentSize:constrainedSize.max]; +} + +- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize parentSize:(CGSize)parentSize +{ + ASDN::MutexLocker l(__instanceLock__); + + // If one or multiple layout transitions are in flight it still can happen that layout information is requested + // on other threads. As the pending and calculated layout to be updated in the layout transition in here just a + // layout calculation wil be performed without side effect + if ([self _isLayoutTransitionInvalid]) { + return [self calculateLayoutThatFits:constrainedSize restrictedToSize:self.style.size relativeToParentSize:parentSize]; + } + + ASLayout *layout = nil; + NSUInteger version = _layoutVersion; + if (_calculatedDisplayNodeLayout->isValid(constrainedSize, parentSize, version)) { + ASDisplayNodeAssertNotNil(_calculatedDisplayNodeLayout->layout, @"-[ASDisplayNode layoutThatFits:parentSize:] _calculatedDisplayNodeLayout->layout should not be nil! %@", self); + layout = _calculatedDisplayNodeLayout->layout; + } else if (_pendingDisplayNodeLayout != nullptr && _pendingDisplayNodeLayout->isValid(constrainedSize, parentSize, version)) { + ASDisplayNodeAssertNotNil(_pendingDisplayNodeLayout->layout, @"-[ASDisplayNode layoutThatFits:parentSize:] _pendingDisplayNodeLayout->layout should not be nil! %@", self); + layout = _pendingDisplayNodeLayout->layout; + } else { + // Create a pending display node layout for the layout pass + layout = [self calculateLayoutThatFits:constrainedSize + restrictedToSize:self.style.size + relativeToParentSize:parentSize]; + _pendingDisplayNodeLayout = std::make_shared(layout, constrainedSize, parentSize, version); + ASDisplayNodeAssertNotNil(layout, @"-[ASDisplayNode layoutThatFits:parentSize:] newly calculated layout should not be nil! %@", self); + } + + return layout ?: [ASLayout layoutWithLayoutElement:self size:{0, 0}]; +} + +#pragma mark ASLayoutElementStyleExtensibility + +ASLayoutElementStyleExtensibilityForwarding + +#pragma mark ASPrimitiveTraitCollection + +- (ASPrimitiveTraitCollection)primitiveTraitCollection +{ + return _primitiveTraitCollection.load(); +} + +- (void)setPrimitiveTraitCollection:(ASPrimitiveTraitCollection)traitCollection +{ + if (ASPrimitiveTraitCollectionIsEqualToASPrimitiveTraitCollection(traitCollection, _primitiveTraitCollection.load()) == NO) { + _primitiveTraitCollection = traitCollection; + ASDisplayNodeLogEvent(self, @"asyncTraitCollectionDidChange: %@", NSStringFromASPrimitiveTraitCollection(traitCollection)); + + [self asyncTraitCollectionDidChange]; + } +} + +- (ASTraitCollection *)asyncTraitCollection +{ + return [ASTraitCollection traitCollectionWithASPrimitiveTraitCollection:self.primitiveTraitCollection]; +} + +ASPrimitiveTraitCollectionDeprecatedImplementation + +#pragma mark - ASLayoutElementAsciiArtProtocol + +- (NSString *)asciiArtString +{ + return [ASLayoutSpec asciiArtStringForChildren:@[] parentName:[self asciiArtName]]; +} + +- (NSString *)asciiArtName +{ + NSMutableString *result = [NSMutableString stringWithCString:object_getClassName(self) encoding:NSASCIIStringEncoding]; + if (_debugName) { + [result appendFormat:@" (%@)", _debugName]; + } + return result; +} + +@end + +#pragma mark - +#pragma mark - ASDisplayNode (ASLayout) + +@implementation ASDisplayNode (ASLayout) + +- (void)setLayoutSpecBlock:(ASLayoutSpecBlock)layoutSpecBlock +{ + // For now there should never be an override of layoutSpecThatFits: and a layoutSpecBlock together. + ASDisplayNodeAssert(!(_methodOverrides & ASDisplayNodeMethodOverrideLayoutSpecThatFits), + @"Nodes with a .layoutSpecBlock must not also implement -layoutSpecThatFits:"); + ASDN::MutexLocker l(__instanceLock__); + _layoutSpecBlock = layoutSpecBlock; +} + +- (ASLayoutSpecBlock)layoutSpecBlock +{ + ASDN::MutexLocker l(__instanceLock__); + return _layoutSpecBlock; +} + +- (ASLayout *)calculatedLayout +{ + ASDN::MutexLocker l(__instanceLock__); + return _calculatedDisplayNodeLayout->layout; +} + +- (CGSize)calculatedSize +{ + ASDN::MutexLocker l(__instanceLock__); + if (_pendingDisplayNodeLayout != nullptr) { + return _pendingDisplayNodeLayout->layout.size; + } + return _calculatedDisplayNodeLayout->layout.size; +} + +- (ASSizeRange)constrainedSizeForCalculatedLayout +{ + ASDN::MutexLocker l(__instanceLock__); + if (_pendingDisplayNodeLayout != nullptr) { + return _pendingDisplayNodeLayout->constrainedSize; + } + return _calculatedDisplayNodeLayout->constrainedSize; +} + +@end + +#pragma mark - +#pragma mark - ASDisplayNode (ASLayoutElementStylability) + +@implementation ASDisplayNode (ASLayoutElementStylability) + +- (instancetype)styledWithBlock:(AS_NOESCAPE void (^)(__kindof ASLayoutElementStyle *style))styleBlock +{ + styleBlock(self.style); + return self; +} + +@end + +#pragma mark - +#pragma mark - ASDisplayNode (ASLayoutInternal) + +@implementation ASDisplayNode (ASLayoutInternal) + +/** + * @abstract Informs the root node that the intrinsic size of the receiver is no longer valid. + * + * @discussion The size of a root node is determined by each subnode. Calling invalidateSize will let the root node know + * that the intrinsic size of the receiver node is no longer valid and a resizing of the root node needs to happen. + */ +- (void)_setNeedsLayoutFromAbove +{ + as_activity_create_for_scope("Set needs layout from above"); + ASDisplayNodeAssertThreadAffinity(self); + + // Mark the node for layout in the next layout pass + [self setNeedsLayout]; + + __instanceLock__.lock(); + // Escalate to the root; entire tree must allow adjustments so the layout fits the new child. + // Much of the layout will be re-used as cached (e.g. other items in an unconstrained stack) + ASDisplayNode *supernode = _supernode; + __instanceLock__.unlock(); + + if (supernode) { + // Threading model requires that we unlock before calling a method on our parent. + [supernode _setNeedsLayoutFromAbove]; + } else { + // Let the root node method know that the size was invalidated + [self _rootNodeDidInvalidateSize]; + } +} + +- (void)_rootNodeDidInvalidateSize +{ + ASDisplayNodeAssertThreadAffinity(self); + ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__); + + __instanceLock__.lock(); + + // 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); + + // Figure out constrainedSize to use + ASSizeRange constrainedSize = ASSizeRangeMake(boundsSizeForLayout); + if (_pendingDisplayNodeLayout != nullptr) { + constrainedSize = _pendingDisplayNodeLayout->constrainedSize; + } else if (_calculatedDisplayNodeLayout->layout != nil) { + constrainedSize = _calculatedDisplayNodeLayout->constrainedSize; + } + + __instanceLock__.unlock(); + + // Perform a measurement pass to get the full tree layout, adapting to the child's new size. + ASLayout *layout = [self layoutThatFits:constrainedSize]; + + // Check if the returned layout has a different size than our current bounds. + if (CGSizeEqualToSize(boundsSizeForLayout, layout.size) == NO) { + // If so, inform our container we need an update (e.g Table, Collection, ViewController, etc). + [self displayNodeDidInvalidateSizeNewSize:layout.size]; + } +} + +- (void)displayNodeDidInvalidateSizeNewSize:(CGSize)size +{ + ASDisplayNodeAssertThreadAffinity(self); + ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__); + + // The default implementation of display node changes the size of itself to the new size + CGRect oldBounds = self.bounds; + CGSize oldSize = oldBounds.size; + CGSize newSize = size; + + if (! CGSizeEqualToSize(oldSize, newSize)) { + self.bounds = (CGRect){ oldBounds.origin, newSize }; + + // Frame's origin must be preserved. Since it is computed from bounds size, anchorPoint + // and position (see frame setter in ASDisplayNode+UIViewBridge), position needs to be adjusted. + CGPoint anchorPoint = self.anchorPoint; + CGPoint oldPosition = self.position; + CGFloat xDelta = (newSize.width - oldSize.width) * anchorPoint.x; + CGFloat yDelta = (newSize.height - oldSize.height) * anchorPoint.y; + self.position = CGPointMake(oldPosition.x + xDelta, oldPosition.y + yDelta); + } +} + +- (void)_locked_measureNodeWithBoundsIfNecessary:(CGRect)bounds +{ + // Check if we are a subnode in a layout transition. + // In this case no measurement is needed as it's part of the layout transition + if ([self _isLayoutTransitionInvalid]) { + return; + } + + CGSize boundsSizeForLayout = ASCeilSizeValues(bounds.size); + + // Prefer _pendingDisplayNodeLayout over _calculatedDisplayNodeLayout (if exists, it's the newest) + // If there is no _pending, check if _calculated is valid to reuse (avoiding recalculation below). + if (_pendingDisplayNodeLayout == nullptr) { + if (_calculatedDisplayNodeLayout->version >= _layoutVersion + && (_calculatedDisplayNodeLayout->requestedLayoutFromAbove == YES + || CGSizeEqualToSize(_calculatedDisplayNodeLayout->layout.size, boundsSizeForLayout))) { + return; + } + } + + as_activity_create_for_scope("Update node layout for current bounds"); + as_log_verbose(ASLayoutLog(), "Node %@, bounds size %@, calculatedSize %@, calculatedIsDirty %d", self, NSStringFromCGSize(boundsSizeForLayout), NSStringFromCGSize(_calculatedDisplayNodeLayout->layout.size), _calculatedDisplayNodeLayout->isDirty()); + // _calculatedDisplayNodeLayout is not reusable we need to transition to a new one + [self cancelLayoutTransition]; + + BOOL didCreateNewContext = NO; + ASLayoutElementContext *context = ASLayoutElementGetCurrentContext(); + if (context == nil) { + context = [[ASLayoutElementContext alloc] init]; + ASLayoutElementPushContext(context); + didCreateNewContext = YES; + } + + // Figure out previous and pending layouts for layout transition + std::shared_ptr nextLayout = _pendingDisplayNodeLayout; + #define layoutSizeDifferentFromBounds !CGSizeEqualToSize(nextLayout->layout.size, boundsSizeForLayout) + + // nextLayout was likely created by a call to layoutThatFits:, check if it is valid and can be applied. + // If our bounds size is different than it, or invalid, recalculate. Use #define to avoid nullptr-> + BOOL pendingLayoutApplicable = NO; + if (nextLayout == nullptr) { + as_log_verbose(ASLayoutLog(), "No pending layout."); + } else if (nextLayout->version < _layoutVersion) { + as_log_verbose(ASLayoutLog(), "Pending layout is stale."); + } else if (layoutSizeDifferentFromBounds) { + as_log_verbose(ASLayoutLog(), "Pending layout size %@ doesn't match bounds size.", NSStringFromCGSize(nextLayout->layout.size)); + } else { + as_log_verbose(ASLayoutLog(), "Using pending layout %@.", nextLayout->layout); + pendingLayoutApplicable = YES; + } + + if (!pendingLayoutApplicable) { + as_log_verbose(ASLayoutLog(), "Measuring with previous constrained size."); + // Use the last known constrainedSize passed from a parent during layout (if never, use bounds). + NSUInteger version = _layoutVersion; + ASSizeRange constrainedSize = [self _locked_constrainedSizeForLayoutPass]; + ASLayout *layout = [self calculateLayoutThatFits:constrainedSize + restrictedToSize:self.style.size + relativeToParentSize:boundsSizeForLayout]; + + nextLayout = std::make_shared(layout, constrainedSize, boundsSizeForLayout, version); + } + + if (didCreateNewContext) { + ASLayoutElementPopContext(); + } + + // If our new layout's desired size for self doesn't match current size, ask our parent to update it. + // This can occur for either pre-calculated or newly-calculated layouts. + if (nextLayout->requestedLayoutFromAbove == NO + && CGSizeEqualToSize(boundsSizeForLayout, nextLayout->layout.size) == NO) { + as_log_verbose(ASLayoutLog(), "Layout size doesn't match bounds size. Requesting layout from above."); + // The layout that we have specifies that this node (self) would like to be a different size + // than it currently is. Because that size has been computed within the constrainedSize, we + // expect that calling setNeedsLayoutFromAbove will result in our parent resizing us to this. + // However, in some cases apps may manually interfere with this (setting a different bounds). + // In this case, we need to detect that we've already asked to be resized to match this + // particular ASLayout object, and shouldn't loop asking again unless we have a different ASLayout. + nextLayout->requestedLayoutFromAbove = YES; + [self _setNeedsLayoutFromAbove]; + } + + // Prepare to transition to nextLayout + ASDisplayNodeAssertNotNil(nextLayout->layout, @"nextLayout->layout should not be nil! %@", self); + _pendingLayoutTransition = [[ASLayoutTransition alloc] initWithNode:self + pendingLayout:nextLayout + previousLayout:_calculatedDisplayNodeLayout]; + + // If a parent is currently executing a layout transition, perform our layout application after it. + if (ASHierarchyStateIncludesLayoutPending(_hierarchyState) == NO) { + // If no transition, apply our new layout immediately (common case). + [self _completePendingLayoutTransition]; + } +} + +- (ASSizeRange)_locked_constrainedSizeForLayoutPass +{ + // TODO: The logic in -_setNeedsLayoutFromAbove seems correct and doesn't use this method. + // logic seems correct. For what case does -this method need to do the CGSizeEqual checks? + // IF WE CAN REMOVE BOUNDS CHECKS HERE, THEN WE CAN ALSO REMOVE "REQUESTED FROM ABOVE" CHECK + + CGSize boundsSizeForLayout = ASCeilSizeValues(self.threadSafeBounds.size); + + // Checkout if constrained size of pending or calculated display node layout can be used + if (_pendingDisplayNodeLayout != nullptr + && (_pendingDisplayNodeLayout->requestedLayoutFromAbove + || CGSizeEqualToSize(_pendingDisplayNodeLayout->layout.size, boundsSizeForLayout))) { + // We assume the size from the last returned layoutThatFits: layout was applied so use the pending display node + // layout constrained size + return _pendingDisplayNodeLayout->constrainedSize; + } else if (_calculatedDisplayNodeLayout->layout != nil + && (_calculatedDisplayNodeLayout->requestedLayoutFromAbove + || CGSizeEqualToSize(_calculatedDisplayNodeLayout->layout.size, boundsSizeForLayout))) { + // We assume the _calculatedDisplayNodeLayout is still valid and the frame is not different + return _calculatedDisplayNodeLayout->constrainedSize; + } else { + // In this case neither the _pendingDisplayNodeLayout or the _calculatedDisplayNodeLayout constrained size can + // be reused, so the current bounds is used. This is usual the case if a frame was set manually that differs to + // the one returned from layoutThatFits: or layoutThatFits: was never called + return ASSizeRangeMake(boundsSizeForLayout); + } +} + +- (void)_layoutSublayouts +{ + ASDisplayNodeAssertThreadAffinity(self); + ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__); + + ASLayout *layout; + { + ASDN::MutexLocker l(__instanceLock__); + if (_calculatedDisplayNodeLayout->version < _layoutVersion) { + return; + } + layout = _calculatedDisplayNodeLayout->layout; + } + + for (ASDisplayNode *node in self.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; + } + } +} + +@end + +#pragma mark - +#pragma mark - ASDisplayNode (ASAutomatic Subnode Management) + +@implementation ASDisplayNode (ASAutomaticSubnodeManagement) + +#pragma mark Automatically Manages Subnodes + +- (BOOL)automaticallyManagesSubnodes +{ + ASDN::MutexLocker l(__instanceLock__); + return _automaticallyManagesSubnodes; +} + +- (void)setAutomaticallyManagesSubnodes:(BOOL)automaticallyManagesSubnodes +{ + ASDN::MutexLocker l(__instanceLock__); + _automaticallyManagesSubnodes = automaticallyManagesSubnodes; +} + +@end + +#pragma mark - +#pragma mark - ASDisplayNode (ASLayoutTransition) + +@implementation ASDisplayNode (ASLayoutTransition) + +- (BOOL)_isLayoutTransitionInvalid +{ + ASDN::MutexLocker l(__instanceLock__); + if (ASHierarchyStateIncludesLayoutPending(_hierarchyState)) { + ASLayoutElementContext *context = ASLayoutElementGetCurrentContext(); + if (context == nil || _pendingTransitionID != context.transitionID) { + return YES; + } + } + return NO; +} + +/// Starts a new transition and returns the transition id +- (int32_t)_startNewTransition +{ + static std::atomic gNextTransitionID; + int32_t newTransitionID = gNextTransitionID.fetch_add(1) + 1; + _transitionID = newTransitionID; + return newTransitionID; +} + +/// Returns NO if there was no transition to cancel/finish. +- (BOOL)_finishOrCancelTransition +{ + int32_t oldValue = _transitionID.exchange(ASLayoutElementContextInvalidTransitionID); + return oldValue != ASLayoutElementContextInvalidTransitionID; +} + +#pragma mark Layout Transition + +- (void)transitionLayoutWithAnimation:(BOOL)animated + shouldMeasureAsync:(BOOL)shouldMeasureAsync + measurementCompletion:(void(^)())completion +{ + ASDisplayNodeAssertMainThread(); + + [self setNeedsLayout]; + + [self transitionLayoutWithSizeRange:[self _locked_constrainedSizeForLayoutPass] + animated:animated + shouldMeasureAsync:shouldMeasureAsync + measurementCompletion:completion]; + +} + +- (void)transitionLayoutWithSizeRange:(ASSizeRange)constrainedSize + animated:(BOOL)animated + shouldMeasureAsync:(BOOL)shouldMeasureAsync + measurementCompletion:(void(^)())completion +{ + ASDisplayNodeAssertMainThread(); + as_activity_create_for_scope("Transition node layout"); + as_log_debug(ASLayoutLog(), "Transition layout for %@ sizeRange %@ anim %d asyncMeasure %d", self, NSStringFromASSizeRange(constrainedSize), animated, shouldMeasureAsync); + + if (constrainedSize.max.width <= 0.0 || constrainedSize.max.height <= 0.0) { + // Using CGSizeZero for the sizeRange can cause negative values in client layout code. + // Most likely called transitionLayout: without providing a size, before first layout pass. + as_log_verbose(ASLayoutLog(), "Ignoring transition due to bad size range."); + return; + } + + // Check if we are a subnode in a layout transition. + // In this case no measurement is needed as we're part of the layout transition. + if ([self _isLayoutTransitionInvalid]) { + return; + } + + { + ASDN::MutexLocker l(__instanceLock__); + ASDisplayNodeAssert(ASHierarchyStateIncludesLayoutPending(_hierarchyState) == NO, @"Can't start a transition when one of the supernodes is performing one."); + } + + // Every new layout transition has a transition id associated to check in subsequent transitions for cancelling + int32_t transitionID = [self _startNewTransition]; + as_log_verbose(ASLayoutLog(), "Transition ID is %d", transitionID); + // NOTE: This block captures self. It's cheaper than hitting the weak table. + asdisplaynode_iscancelled_block_t isCancelled = ^{ + BOOL result = (_transitionID != transitionID); + if (result) { + as_log_verbose(ASLayoutLog(), "Transition %d canceled, superseded by %d", transitionID, _transitionID.load()); + } + return result; + }; + + // Move all subnodes in layout pending state for this transition + ASDisplayNodePerformBlockOnEverySubnode(self, NO, ^(ASDisplayNode * _Nonnull node) { + ASDisplayNodeAssert(node->_transitionID == ASLayoutElementContextInvalidTransitionID, @"Can't start a transition when one of the subnodes is performing one."); + node.hierarchyState |= ASHierarchyStateLayoutPending; + node->_pendingTransitionID = transitionID; + }); + + // Transition block that executes the layout transition + void (^transitionBlock)(void) = ^{ + if (isCancelled()) { + return; + } + + // Perform a full layout creation pass with passed in constrained size to create the new layout for the transition + NSUInteger newLayoutVersion = _layoutVersion; + ASLayout *newLayout; + { + ASDN::MutexLocker l(__instanceLock__); + + ASLayoutElementContext *ctx = [[ASLayoutElementContext alloc] init]; + ctx.transitionID = transitionID; + ASLayoutElementPushContext(ctx); + + BOOL automaticallyManagesSubnodesDisabled = (self.automaticallyManagesSubnodes == NO); + self.automaticallyManagesSubnodes = YES; // Temporary flag for 1.9.x + newLayout = [self calculateLayoutThatFits:constrainedSize + restrictedToSize:self.style.size + relativeToParentSize:constrainedSize.max]; + if (automaticallyManagesSubnodesDisabled) { + self.automaticallyManagesSubnodes = NO; // Temporary flag for 1.9.x + } + + ASLayoutElementPopContext(); + } + + if (isCancelled()) { + return; + } + + ASPerformBlockOnMainThread(^{ + if (isCancelled()) { + return; + } + as_activity_create_for_scope("Commit layout transition"); + ASLayoutTransition *pendingLayoutTransition; + _ASTransitionContext *pendingLayoutTransitionContext; + { + // Grab __instanceLock__ here to make sure this transition isn't invalidated + // right after it passed the validation test and before it proceeds + ASDN::MutexLocker l(__instanceLock__); + + // Update calculated layout + auto previousLayout = _calculatedDisplayNodeLayout; + auto pendingLayout = std::make_shared(newLayout, + constrainedSize, + constrainedSize.max, + newLayoutVersion); + [self _locked_setCalculatedDisplayNodeLayout:pendingLayout]; + + // Setup pending layout transition for animation + _pendingLayoutTransition = pendingLayoutTransition = [[ASLayoutTransition alloc] initWithNode:self + pendingLayout:pendingLayout + previousLayout:previousLayout]; + // Setup context for pending layout transition. we need to hold a strong reference to the context + _pendingLayoutTransitionContext = pendingLayoutTransitionContext = [[_ASTransitionContext alloc] initWithAnimation:animated + layoutDelegate:_pendingLayoutTransition + completionDelegate:self]; + } + + // Apply complete layout transitions for all subnodes + { + as_activity_create_for_scope("Complete pending layout transitions for subtree"); + ASDisplayNodePerformBlockOnEverySubnode(self, NO, ^(ASDisplayNode * _Nonnull node) { + [node _completePendingLayoutTransition]; + node.hierarchyState &= (~ASHierarchyStateLayoutPending); + }); + } + + // Measurement pass completion + // Give the subclass a change to hook into before calling the completion block + [self _layoutTransitionMeasurementDidFinish]; + if (completion) { + completion(); + } + + // Apply the subnode insertion immediately to be able to animate the nodes + [pendingLayoutTransition applySubnodeInsertions]; + + // Kick off animating the layout transition + { + as_activity_create_for_scope("Animate layout transition"); + [self animateLayoutTransition:pendingLayoutTransitionContext]; + } + + // Mark transaction as finished + [self _finishOrCancelTransition]; + }); + }; + + // Start transition based on flag on current or background thread + if (shouldMeasureAsync) { + ASPerformBlockOnBackgroundThread(transitionBlock); + } else { + transitionBlock(); + } +} + +- (void)cancelLayoutTransition +{ + if ([self _finishOrCancelTransition]) { + // Tell subnodes to exit layout pending state and clear related properties + ASDisplayNodePerformBlockOnEverySubnode(self, NO, ^(ASDisplayNode * _Nonnull node) { + node.hierarchyState &= (~ASHierarchyStateLayoutPending); + }); + } +} + +- (void)setDefaultLayoutTransitionDuration:(NSTimeInterval)defaultLayoutTransitionDuration +{ + ASDN::MutexLocker l(__instanceLock__); + _defaultLayoutTransitionDuration = defaultLayoutTransitionDuration; +} + +- (NSTimeInterval)defaultLayoutTransitionDuration +{ + ASDN::MutexLocker l(__instanceLock__); + return _defaultLayoutTransitionDuration; +} + +- (void)setDefaultLayoutTransitionDelay:(NSTimeInterval)defaultLayoutTransitionDelay +{ + ASDN::MutexLocker l(__instanceLock__); + _defaultLayoutTransitionDelay = defaultLayoutTransitionDelay; +} + +- (NSTimeInterval)defaultLayoutTransitionDelay +{ + ASDN::MutexLocker l(__instanceLock__); + return _defaultLayoutTransitionDelay; +} + +- (void)setDefaultLayoutTransitionOptions:(UIViewAnimationOptions)defaultLayoutTransitionOptions +{ + ASDN::MutexLocker l(__instanceLock__); + _defaultLayoutTransitionOptions = defaultLayoutTransitionOptions; +} + +- (UIViewAnimationOptions)defaultLayoutTransitionOptions +{ + ASDN::MutexLocker l(__instanceLock__); + return _defaultLayoutTransitionOptions; +} + +#pragma mark + +/* + * Hook for subclasses to perform an animation based on the given ASContextTransitioning. By default a fade in and out + * animation is provided. + */ +- (void)animateLayoutTransition:(id)context +{ + if ([context isAnimated] == NO) { + [self _layoutSublayouts]; + [context completeTransition:YES]; + return; + } + + ASDisplayNode *node = self; + + NSAssert(node.isNodeLoaded == YES, @"Invalid node state"); + + NSArray *removedSubnodes = [context removedSubnodes]; + NSMutableArray *insertedSubnodes = [[context insertedSubnodes] mutableCopy]; + NSMutableArray *movedSubnodes = [NSMutableArray array]; + + NSMutableArray<_ASAnimatedTransitionContext *> *insertedSubnodeContexts = [NSMutableArray array]; + NSMutableArray<_ASAnimatedTransitionContext *> *removedSubnodeContexts = [NSMutableArray array]; + + for (ASDisplayNode *subnode in [context subnodesForKey:ASTransitionContextToLayoutKey]) { + if ([insertedSubnodes containsObject:subnode] == NO) { + // This is an existing subnode, check if it is resized, moved or both + CGRect fromFrame = [context initialFrameForNode:subnode]; + CGRect toFrame = [context finalFrameForNode:subnode]; + if (CGSizeEqualToSize(fromFrame.size, toFrame.size) == NO) { + [insertedSubnodes addObject:subnode]; + } + if (CGPointEqualToPoint(fromFrame.origin, toFrame.origin) == NO) { + [movedSubnodes addObject:subnode]; + } + } + } + + // Create contexts for inserted and removed subnodes + for (ASDisplayNode *insertedSubnode in insertedSubnodes) { + [insertedSubnodeContexts addObject:[_ASAnimatedTransitionContext contextForNode:insertedSubnode alpha:insertedSubnode.alpha]]; + } + for (ASDisplayNode *removedSubnode in removedSubnodes) { + [removedSubnodeContexts addObject:[_ASAnimatedTransitionContext contextForNode:removedSubnode alpha:removedSubnode.alpha]]; + } + + // Fade out inserted subnodes + for (ASDisplayNode *insertedSubnode in insertedSubnodes) { + insertedSubnode.frame = [context finalFrameForNode:insertedSubnode]; + insertedSubnode.alpha = 0; + } + + // Adjust groupOpacity for animation + BOOL originAllowsGroupOpacity = node.allowsGroupOpacity; + node.allowsGroupOpacity = YES; + + [UIView animateWithDuration:self.defaultLayoutTransitionDuration delay:self.defaultLayoutTransitionDelay options:self.defaultLayoutTransitionOptions animations:^{ + // Fade removed subnodes and views out + for (ASDisplayNode *removedSubnode in removedSubnodes) { + removedSubnode.alpha = 0; + } + + // Fade inserted subnodes in + for (_ASAnimatedTransitionContext *insertedSubnodeContext in insertedSubnodeContexts) { + insertedSubnodeContext.node.alpha = insertedSubnodeContext.alpha; + } + + // Update frame of self and moved subnodes + CGSize fromSize = [context layoutForKey:ASTransitionContextFromLayoutKey].size; + CGSize toSize = [context layoutForKey:ASTransitionContextToLayoutKey].size; + BOOL isResized = (CGSizeEqualToSize(fromSize, toSize) == NO); + if (isResized == YES) { + CGPoint position = node.frame.origin; + node.frame = CGRectMake(position.x, position.y, toSize.width, toSize.height); + } + for (ASDisplayNode *movedSubnode in movedSubnodes) { + movedSubnode.frame = [context finalFrameForNode:movedSubnode]; + } + } completion:^(BOOL finished) { + // Restore all removed subnode alpha values + for (_ASAnimatedTransitionContext *removedSubnodeContext in removedSubnodeContexts) { + removedSubnodeContext.node.alpha = removedSubnodeContext.alpha; + } + + // Restore group opacity + node.allowsGroupOpacity = originAllowsGroupOpacity; + + // Subnode removals are automatically performed + [context completeTransition:finished]; + }]; +} + +/** + * Hook for subclasses to clean up nodes after the transition happened. Furthermore this can be used from subclasses + * to manually perform deletions. + */ +- (void)didCompleteLayoutTransition:(id)context +{ + ASDisplayNodeAssertMainThread(); + + __instanceLock__.lock(); + ASLayoutTransition *pendingLayoutTransition = _pendingLayoutTransition; + __instanceLock__.unlock(); + + [pendingLayoutTransition applySubnodeRemovals]; +} + +/** + * Completes the pending layout transition immediately without going through the the Layout Transition Animation API + */ +- (void)_completePendingLayoutTransition +{ + __instanceLock__.lock(); + ASLayoutTransition *pendingLayoutTransition = _pendingLayoutTransition; + __instanceLock__.unlock(); + + if (pendingLayoutTransition != nil) { + [self _setCalculatedDisplayNodeLayout:pendingLayoutTransition.pendingLayout]; + [self _completeLayoutTransition:pendingLayoutTransition]; + } + [self _pendingLayoutTransitionDidComplete]; +} + +/** + * Can be directly called to commit the given layout transition immediately to complete without calling through to the + * Layout Transition Animation API + */ +- (void)_completeLayoutTransition:(ASLayoutTransition *)layoutTransition +{ + // Layout transition is not supported for nodes that do not have automatic subnode management enabled + if (layoutTransition == nil || self.automaticallyManagesSubnodes == NO) { + return; + } + + // Trampoline to the main thread if necessary + if (ASDisplayNodeThreadIsMain() || layoutTransition.isSynchronous == NO) { + [layoutTransition commitTransition]; + } else { + // Subnode insertions and removals need to happen always on the main thread if at least one subnode is already loaded + ASPerformBlockOnMainThread(^{ + [layoutTransition commitTransition]; + }); + } +} + +- (void)_assertSubnodeState +{ + // Verify that any orphaned nodes are removed. + // This can occur in rare cases if main thread layout is flushed while a background layout is calculating. + + if (self.automaticallyManagesSubnodes == NO) { + return; + } + + NSArray *subnodes = [self subnodes]; + NSArray *sublayouts = _calculatedDisplayNodeLayout->layout.sublayouts; + + auto currentSubnodes = [[NSHashTable alloc] initWithOptions:NSHashTableObjectPointerPersonality + capacity:subnodes.count]; + auto layoutSubnodes = [[NSHashTable alloc] initWithOptions:NSHashTableObjectPointerPersonality + capacity:sublayouts.count];; + for (ASDisplayNode *subnode in subnodes) { + [currentSubnodes addObject:subnode]; + } + + for (ASLayout *sublayout in sublayouts) { + id layoutElement = sublayout.layoutElement; + ASDisplayNodeAssert([layoutElement isKindOfClass:[ASDisplayNode class]], + @"All calculatedLayouts should be flattened and only contain nodes!"); + [layoutSubnodes addObject:(ASDisplayNode *)layoutElement]; + } + + // Verify that all subnodes that occur in the current ASLayout tree are present in .subnodes array. + if ([layoutSubnodes isSubsetOfHashTable:currentSubnodes] == NO) { + // Note: This should be converted to an assertion after confirming it is rare. + NSLog(@"Warning: node's layout includes subnodes that have not been added: node = %@, subnodes = %@, subnodes in layout = %@", self, currentSubnodes, layoutSubnodes); + } + + // Verify that everything in the .subnodes array is present in the ASLayout tree (and correct it if not). + [currentSubnodes minusHashTable:layoutSubnodes]; + for (ASDisplayNode *orphanedSubnode in currentSubnodes) { + NSLog(@"Automatically removing orphaned subnode %@, from parent %@", orphanedSubnode, self); + [orphanedSubnode removeFromSupernode]; + } +} + +- (void)_pendingLayoutTransitionDidComplete +{ + // This assertion introduces a breaking behavior for nodes that has ASM enabled but also manually manage some subnodes. + // Let's gate it behind YOGA flag and remove it right after a branch cut. +#if YOGA + [self _assertSubnodeState]; +#endif + + // Subclass hook + [self calculatedLayoutDidChange]; + + // Grab lock after calling out to subclass + ASDN::MutexLocker l(__instanceLock__); + + // We generate placeholders at measureWithSizeRange: time so that a node is guaranteed to have a placeholder ready to go. + // This is also because measurement is usually asynchronous, but placeholders need to be set up synchronously. + // First measurement is guaranteed to be before the node is onscreen, so we can create the image async. but still have it appear sync. + if (_placeholderEnabled && !_placeholderImage && [self _locked_displaysAsynchronously]) { + + // Zero-sized nodes do not require a placeholder. + ASLayout *layout = _calculatedDisplayNodeLayout->layout; + CGSize layoutSize = (layout ? layout.size : CGSizeZero); + if (layoutSize.width * layoutSize.height <= 0.0) { + return; + } + + // If we've displayed our contents, we don't need a placeholder. + // Contents is a thread-affined property and can't be read off main after loading. + if (self.isNodeLoaded) { + ASPerformBlockOnMainThread(^{ + if (self.contents == nil) { + _placeholderImage = [self placeholderImage]; + } + }); + } else { + if (self.contents == nil) { + _placeholderImage = [self placeholderImage]; + } + } + } + + // Cleanup pending layout transition + _pendingLayoutTransition = nil; +} + +- (void)_setCalculatedDisplayNodeLayout:(std::shared_ptr)displayNodeLayout +{ + ASDN::MutexLocker l(__instanceLock__); + [self _locked_setCalculatedDisplayNodeLayout:displayNodeLayout]; +} + +- (void)_locked_setCalculatedDisplayNodeLayout:(std::shared_ptr)displayNodeLayout +{ + ASDisplayNodeAssertTrue(displayNodeLayout->layout.layoutElement == self); + ASDisplayNodeAssertTrue(displayNodeLayout->layout.size.width >= 0.0); + ASDisplayNodeAssertTrue(displayNodeLayout->layout.size.height >= 0.0); + + // Flatten the layout if it wasn't done before (@see -calculateLayoutThatFits:). + if ([ASDisplayNode shouldStoreUnflattenedLayouts]) { + _unflattenedLayout = displayNodeLayout->layout; + displayNodeLayout->layout = [_unflattenedLayout filteredNodeLayoutTree]; + } + + _calculatedDisplayNodeLayout = displayNodeLayout; +} + +@end diff --git a/Source/ASDisplayNode+Subclasses.h b/Source/ASDisplayNode+Subclasses.h index 3d5e042494..9a5027b59e 100644 --- a/Source/ASDisplayNode+Subclasses.h +++ b/Source/ASDisplayNode+Subclasses.h @@ -1,11 +1,18 @@ // // ASDisplayNode+Subclasses.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -115,7 +122,7 @@ NS_ASSUME_NONNULL_BEGIN * * @warning Subclasses must not override this; it returns the last cached layout and is never expensive. */ -@property (nullable, nonatomic, readonly, assign) ASLayout *calculatedLayout; +@property (nullable, nonatomic, readonly, strong) ASLayout *calculatedLayout; #pragma mark - View Lifecycle /** @name View Lifecycle */ @@ -272,9 +279,9 @@ NS_ASSUME_NONNULL_BEGIN * * @note Called on the display queue and/or main queue (MUST BE THREAD SAFE) */ -+ (void)drawRect:(CGRect)bounds withParameters:(nullable id )parameters +/*+ (void)drawRect:(CGRect)bounds withParameters:(nullable id)parameters isCancelled:(AS_NOESCAPE asdisplaynode_iscancelled_block_t)isCancelledBlock - isRasterizing:(BOOL)isRasterizing; + isRasterizing:(BOOL)isRasterizing;*/ /** * @summary Delegate override to provide new layer contents as a UIImage. @@ -289,7 +296,7 @@ NS_ASSUME_NONNULL_BEGIN * * @note Called on the display queue and/or main queue (MUST BE THREAD SAFE) */ -+ (nullable UIImage *)displayWithParameters:(nullable id)parameters ++ (nullable UIImage *)displayWithParameters:(nullable id)parameters isCancelled:(AS_NOESCAPE asdisplaynode_iscancelled_block_t)isCancelledBlock; /** @@ -507,7 +514,7 @@ NS_ASSUME_NONNULL_BEGIN @end -#define ASDisplayNodeAssertThreadAffinity(viewNode) ASDisplayNodeAssert(!viewNode || ASDisplayNodeThreadIsMain() || !(viewNode).nodeLoaded, @"Incorrect display node thread affinity - this method should not be called off the main thread after the ASDisplayNode's view or layer have been created") -#define ASDisplayNodeCAssertThreadAffinity(viewNode) ASDisplayNodeCAssert(!viewNode || ASDisplayNodeThreadIsMain() || !(viewNode).nodeLoaded, @"Incorrect display node thread affinity - this method should not be called off the main thread after the ASDisplayNode's view or layer have been created") +#define ASDisplayNodeAssertThreadAffinity(viewNode) ASDisplayNodeAssert(!viewNode || ASMainThreadAssertionsAreDisabled() || ASDisplayNodeThreadIsMain() || !(viewNode).nodeLoaded, @"Incorrect display node thread affinity - this method should not be called off the main thread after the ASDisplayNode's view or layer have been created") +#define ASDisplayNodeCAssertThreadAffinity(viewNode) ASDisplayNodeCAssert(!viewNode || ASMainThreadAssertionsAreDisabled() || ASDisplayNodeThreadIsMain() || !(viewNode).nodeLoaded, @"Incorrect display node thread affinity - this method should not be called off the main thread after the ASDisplayNode's view or layer have been created") NS_ASSUME_NONNULL_END diff --git a/Source/ASDisplayNode+Yoga.mm b/Source/ASDisplayNode+Yoga.mm index d655a3daf2..cc41593a65 100644 --- a/Source/ASDisplayNode+Yoga.mm +++ b/Source/ASDisplayNode+Yoga.mm @@ -1,221 +1,46 @@ // // ASDisplayNode+Yoga.mm -// AsyncDisplayKit +// Texture // -// Created by Scott Goodson on 2/8/17. -// Copyright © 2017 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 /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import #if YOGA /* YOGA */ -#import -#import +#import +#import #import -#import +#import +#import +#import #import #define YOGA_LAYOUT_LOGGING 0 -extern void ASDisplayNodePerformBlockOnEveryYogaChild(ASDisplayNode * _Nullable node, void(^block)(ASDisplayNode *node)) -{ - if (node == nil) { - return; - } - block(node); - for (ASDisplayNode *child in [node yogaChildren]) { - ASDisplayNodePerformBlockOnEveryYogaChild(child, block); - } -} - -#pragma mark - Yoga Type Conversion Helpers - -YGAlign yogaAlignItems(ASStackLayoutAlignItems alignItems); -YGJustify yogaJustifyContent(ASStackLayoutJustifyContent justifyContent); -YGAlign yogaAlignSelf(ASStackLayoutAlignSelf alignSelf); -YGFlexDirection yogaFlexDirection(ASStackLayoutDirection direction); -float yogaFloatForCGFloat(CGFloat value); -float yogaDimensionToPoints(ASDimension dimension); -float yogaDimensionToPercent(ASDimension dimension); -ASDimension dimensionForEdgeWithEdgeInsets(YGEdge edge, ASEdgeInsets insets); -YGSize ASLayoutElementYogaMeasureFunc(YGNodeRef yogaNode, - float width, YGMeasureMode widthMode, - float height, YGMeasureMode heightMode); - -#define YGNODE_STYLE_SET_DIMENSION(yogaNode, property, dimension) \ - if (dimension.unit == ASDimensionUnitPoints) { \ - YGNodeStyleSet##property(yogaNode, yogaDimensionToPoints(dimension)); \ - } else if (dimension.unit == ASDimensionUnitFraction) { \ - YGNodeStyleSet##property##Percent(yogaNode, yogaDimensionToPercent(dimension)); \ - } else { \ - YGNodeStyleSet##property(yogaNode, YGUndefined); \ - }\ - -#define YGNODE_STYLE_SET_DIMENSION_WITH_EDGE(yogaNode, property, dimension, edge) \ - if (dimension.unit == ASDimensionUnitPoints) { \ - YGNodeStyleSet##property(yogaNode, edge, yogaDimensionToPoints(dimension)); \ - } else if (dimension.unit == ASDimensionUnitFraction) { \ - YGNodeStyleSet##property##Percent(yogaNode, edge, yogaDimensionToPercent(dimension)); \ - } else { \ - YGNodeStyleSet##property(yogaNode, edge, YGUndefined); \ - } \ - -#define YGNODE_STYLE_SET_FLOAT_WITH_EDGE(yogaNode, property, dimension, edge) \ - if (dimension.unit == ASDimensionUnitPoints) { \ - YGNodeStyleSet##property(yogaNode, edge, yogaDimensionToPoints(dimension)); \ - } else if (dimension.unit == ASDimensionUnitFraction) { \ - ASDisplayNodeAssert(NO, @"Unexpected Fraction value in applying ##property## values to YGNode"); \ - } else { \ - YGNodeStyleSet##property(yogaNode, edge, YGUndefined); \ - } \ - -YGAlign yogaAlignItems(ASStackLayoutAlignItems alignItems) -{ - switch (alignItems) { - case ASStackLayoutAlignItemsNotSet: return YGAlignAuto; - case ASStackLayoutAlignItemsStart: return YGAlignFlexStart; - case ASStackLayoutAlignItemsEnd: return YGAlignFlexEnd; - case ASStackLayoutAlignItemsCenter: return YGAlignCenter; - case ASStackLayoutAlignItemsStretch: return YGAlignStretch; - case ASStackLayoutAlignItemsBaselineFirst: return YGAlignBaseline; - // FIXME: WARNING, Yoga does not currently support last-baseline item alignment. - case ASStackLayoutAlignItemsBaselineLast: return YGAlignBaseline; - } -} - -YGJustify yogaJustifyContent(ASStackLayoutJustifyContent justifyContent) -{ - switch (justifyContent) { - case ASStackLayoutJustifyContentStart: return YGJustifyFlexStart; - case ASStackLayoutJustifyContentCenter: return YGJustifyCenter; - case ASStackLayoutJustifyContentEnd: return YGJustifyFlexEnd; - case ASStackLayoutJustifyContentSpaceBetween: return YGJustifySpaceBetween; - case ASStackLayoutJustifyContentSpaceAround: return YGJustifySpaceAround; - } -} - -YGAlign yogaAlignSelf(ASStackLayoutAlignSelf alignSelf) -{ - switch (alignSelf) { - case ASStackLayoutAlignSelfStart: return YGAlignFlexStart; - case ASStackLayoutAlignSelfCenter: return YGAlignCenter; - case ASStackLayoutAlignSelfEnd: return YGAlignFlexEnd; - case ASStackLayoutAlignSelfStretch: return YGAlignStretch; - case ASStackLayoutAlignSelfAuto: return YGAlignAuto; - } -} - -YGFlexDirection yogaFlexDirection(ASStackLayoutDirection direction) -{ - return direction == ASStackLayoutDirectionVertical ? YGFlexDirectionColumn : YGFlexDirectionRow; -} - -float yogaFloatForCGFloat(CGFloat value) -{ - if (value < CGFLOAT_MAX / 2) { - return value; - } else { - return YGUndefined; - } -} - -float yogaDimensionToPoints(ASDimension dimension) -{ - ASDisplayNodeCAssert(dimension.unit == ASDimensionUnitPoints, - @"Dimensions should not be type Fraction for this method: %f", dimension.value); - return yogaFloatForCGFloat(dimension.value); -} - -float yogaDimensionToPercent(ASDimension dimension) -{ - ASDisplayNodeCAssert(dimension.unit == ASDimensionUnitFraction, - @"Dimensions should not be type Points for this method: %f", dimension.value); - return 100.0 * yogaFloatForCGFloat(dimension.value); - -} - -ASDimension dimensionForEdgeWithEdgeInsets(YGEdge edge, ASEdgeInsets insets) -{ - switch (edge) { - case YGEdgeLeft: return insets.left; - case YGEdgeTop: return insets.top; - case YGEdgeRight: return insets.right; - case YGEdgeBottom: return insets.bottom; - default: ASDisplayNodeCAssert(NO, @"YGEdge other than ASEdgeInsets is not supported."); - return ASDimensionAuto; - } -} - -YGSize ASLayoutElementYogaMeasureFunc(YGNodeRef yogaNode, float width, YGMeasureMode widthMode, - float height, YGMeasureMode heightMode) -{ - id layoutElement = (__bridge id )YGNodeGetContext(yogaNode); - ASSizeRange sizeRange; - sizeRange.max = CGSizeMake(width, height); - sizeRange.min = sizeRange.max; - if (widthMode == YGMeasureModeAtMost) { - sizeRange.min.width = 0.0; - } - if (heightMode == YGMeasureModeAtMost) { - sizeRange.min.height = 0.0; - } - CGSize size = [[layoutElement layoutThatFits:sizeRange] size]; - return (YGSize){ .width = (float)size.width, .height = (float)size.height }; -} - #pragma mark - ASDisplayNode+Yoga @interface ASDisplayNode (YogaInternal) @property (nonatomic, weak) ASDisplayNode *yogaParent; -@property (nonatomic, assign) YGNodeRef yogaNode; +- (ASSizeRange)_locked_constrainedSizeForLayoutPass; @end @implementation ASDisplayNode (Yoga) -- (void)setYogaNode:(YGNodeRef)yogaNode -{ - _yogaNode = yogaNode; -} - -- (YGNodeRef)yogaNode -{ - if (_yogaNode == NULL) { - _yogaNode = YGNodeNew(); - } - return _yogaNode; -} - -- (void)setYogaParent:(ASDisplayNode *)yogaParent -{ - if (_yogaParent == yogaParent) { - return; - } - - YGNodeRef yogaNode = self.yogaNode; // Use property to assign Ref if needed. - YGNodeRef oldParentRef = YGNodeGetParent(yogaNode); - if (oldParentRef != NULL) { - YGNodeRemoveChild(oldParentRef, yogaNode); - } - - _yogaParent = yogaParent; - if (yogaParent) { - self.hierarchyState |= ASHierarchyStateYogaLayoutEnabled; - YGNodeRef newParentRef = yogaParent.yogaNode; - YGNodeInsertChild(newParentRef, yogaNode, YGNodeGetChildCount(newParentRef)); - } else { - self.hierarchyState &= ~ASHierarchyStateYogaLayoutEnabled; - } -} - -- (ASDisplayNode *)yogaParent -{ - return _yogaParent; -} - - (void)setYogaChildren:(NSArray *)yogaChildren { - for (ASDisplayNode *child in _yogaChildren) { + for (ASDisplayNode *child in [_yogaChildren copy]) { // Make sure to un-associate the YGNodeRef tree before replacing _yogaChildren // If this becomes a performance bottleneck, it can be optimized by not doing the NSArray removals here. [self removeYogaChild:child]; @@ -243,11 +68,10 @@ YGSize ASLayoutElementYogaMeasureFunc(YGNodeRef yogaNode, float width, YGMeasure // Clean up state in case this child had another parent. [self removeYogaChild:child]; - // YGNodeRef insertion is done in setParent: - child.yogaParent = self; [_yogaChildren addObject:child]; - self.hierarchyState |= ASHierarchyStateYogaLayoutEnabled; + // YGNodeRef insertion is done in setParent: + child.yogaParent = self; } - (void)removeYogaChild:(ASDisplayNode *)child @@ -255,15 +79,47 @@ YGSize ASLayoutElementYogaMeasureFunc(YGNodeRef yogaNode, float width, YGMeasure if (child == nil) { return; } - // YGNodeRef removal is done in setParent: - child.yogaParent = nil; + [_yogaChildren removeObjectIdenticalTo:child]; - if (_yogaChildren.count == 0 && self.yogaParent == nil) { - self.hierarchyState &= ~ASHierarchyStateYogaLayoutEnabled; + // YGNodeRef removal is done in setParent: + child.yogaParent = nil; +} + +- (void)semanticContentAttributeDidChange:(UISemanticContentAttribute)attribute +{ + if (AS_AT_LEAST_IOS9) { + UIUserInterfaceLayoutDirection layoutDirection = + [UIView userInterfaceLayoutDirectionForSemanticContentAttribute:attribute]; + self.style.direction = (layoutDirection == UIUserInterfaceLayoutDirectionLeftToRight + ? YGDirectionLTR : YGDirectionRTL); } } +- (void)setYogaParent:(ASDisplayNode *)yogaParent +{ + if (_yogaParent == yogaParent) { + return; + } + + YGNodeRef yogaNode = [self.style yogaNodeCreateIfNeeded]; + YGNodeRef oldParentRef = YGNodeGetParent(yogaNode); + if (oldParentRef != NULL) { + YGNodeRemoveChild(oldParentRef, yogaNode); + } + + _yogaParent = yogaParent; + if (yogaParent) { + YGNodeRef newParentRef = [yogaParent.style yogaNodeCreateIfNeeded]; + YGNodeInsertChild(newParentRef, yogaNode, YGNodeGetChildCount(newParentRef)); + } +} + +- (ASDisplayNode *)yogaParent +{ + return _yogaParent; +} + - (void)setYogaCalculatedLayout:(ASLayout *)yogaCalculatedLayout { _yogaCalculatedLayout = yogaCalculatedLayout; @@ -274,20 +130,30 @@ YGSize ASLayoutElementYogaMeasureFunc(YGNodeRef yogaNode, float width, YGMeasure return _yogaCalculatedLayout; } +- (void)setYogaLayoutInProgress:(BOOL)yogaLayoutInProgress +{ + setFlag(YogaLayoutInProgress, yogaLayoutInProgress); + [self updateYogaMeasureFuncIfNeeded]; +} + +- (BOOL)yogaLayoutInProgress +{ + return checkFlag(YogaLayoutInProgress); +} + - (ASLayout *)layoutForYogaNode { - YGNodeRef yogaNode = self.yogaNode; + YGNodeRef yogaNode = self.style.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. + YGNodeRef yogaNode = self.style.yogaNode; uint32_t childCount = YGNodeGetChildCount(yogaNode); ASDisplayNodeAssert(childCount == self.yogaChildren.count, @"Yoga tree should always be in sync with .yogaNodes array! %@", self.yogaChildren); @@ -300,109 +166,69 @@ YGSize ASLayoutElementYogaMeasureFunc(YGNodeRef yogaNode, float width, YGMeasure // 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 +- (void)updateYogaMeasureFuncIfNeeded { - // Manual size calculation via calculateSizeThatFits: - // This will be used for ASTextNode, as well as any other leaf node that has no layout spec. - if ((self.methodOverrides & ASDisplayNodeMethodOverrideLayoutSpecThatFits) == NO - && self.layoutSpecBlock == NULL && self.yogaChildren.count == 0) { - YGNodeRef yogaNode = self.yogaNode; // Use property to assign Ref if needed. - YGNodeSetContext(yogaNode, (__bridge void *)self); - YGNodeSetMeasureFunc(yogaNode, &ASLayoutElementYogaMeasureFunc); - } + // Size calculation via calculateSizeThatFits: or layoutSpecThatFits: + // This will be used for ASTextNode, as well as any other node that has no Yoga children + BOOL isLeafNode = (self.yogaChildren.count == 0); + BOOL definesCustomLayout = [self implementsLayoutMethod]; + + // We set the measure func only during layout. Otherwise, a cycle is created: + // The YGNodeRef Context will retain the ASDisplayNode, which retains the style, which owns the YGNodeRef. + BOOL shouldHaveMeasureFunc = (isLeafNode && definesCustomLayout && checkFlag(YogaLayoutInProgress)); + + ASLayoutElementYogaUpdateMeasureFunc(self.style.yogaNode, shouldHaveMeasureFunc ? self : nil); } - (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)) { + YGNodeRef yogaNode = self.style.yogaNode; + if (yogaNode && YGNodeGetMeasureFunc(yogaNode)) { YGNodeMarkDirty(yogaNode); } + self.yogaCalculatedLayout = nil; } - (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]); + ASDisplayNode *yogaParent = self.yogaParent; + + if (yogaParent) { + ASYogaLog(@"ESCALATING to Yoga root: %@", self); + // TODO(appleguy): Consider how to get the constrainedSize for the yogaRoot when escalating manually. + [yogaParent calculateLayoutFromYogaRoot:ASSizeRangeUnconstrained]; return; } + ASDN::MutexLocker l(__instanceLock__); + + // Prepare all children for the layout pass with the current Yoga tree configuration. ASDisplayNodePerformBlockOnEveryYogaChild(self, ^(ASDisplayNode * _Nonnull node) { - node.hierarchyState |= ASHierarchyStateYogaLayoutMeasuring; + node.yogaLayoutInProgress = YES; }); - YGNodeRef rootYogaNode = self.yogaNode; + if (ASSizeRangeEqualToSizeRange(rootConstrainedSize, ASSizeRangeUnconstrained)) { + rootConstrainedSize = [self _locked_constrainedSizeForLayoutPass]; + } + + ASYogaLog(@"CALCULATING at Yoga root with constraint = {%@, %@}: %@", + NSStringFromCGSize(rootConstrainedSize.min), NSStringFromCGSize(rootConstrainedSize.max), self); + + YGNodeRef rootYogaNode = self.style.yogaNode; // 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). + + // TODO(appleguy): Reconcile the self.style.*Size properties with rootConstrainedSize YGNodeStyleSetMinWidth (rootYogaNode, yogaFloatForCGFloat(rootConstrainedSize.min.width)); YGNodeStyleSetMinHeight(rootYogaNode, yogaFloatForCGFloat(rootConstrainedSize.min.height)); - ASDisplayNodePerformBlockOnEveryYogaChild(self, ^(ASDisplayNode * _Nonnull node) { - ASLayoutElementStyle *style = node.style; - YGNodeRef yogaNode = node.yogaNode; - - YGNodeStyleSetDirection (yogaNode, YGDirectionInherit); - - YGNodeStyleSetFlexWrap (yogaNode, style.flexWrap); - YGNodeStyleSetFlexGrow (yogaNode, style.flexGrow); - YGNodeStyleSetFlexShrink (yogaNode, style.flexShrink); - YGNODE_STYLE_SET_DIMENSION (yogaNode, FlexBasis, style.flexBasis); - - YGNodeStyleSetFlexDirection (yogaNode, yogaFlexDirection(style.direction)); - 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; - ASEdgeInsets border = style.border; - - YGEdge edge = YGEdgeLeft; - for (int i = 0; i < 4; i++) { - YGNODE_STYLE_SET_DIMENSION_WITH_EDGE(yogaNode, Position, dimensionForEdgeWithEdgeInsets(edge, position), edge); - YGNODE_STYLE_SET_DIMENSION_WITH_EDGE(yogaNode, Margin, dimensionForEdgeWithEdgeInsets(edge, margin), edge); - YGNODE_STYLE_SET_DIMENSION_WITH_EDGE(yogaNode, Padding, dimensionForEdgeWithEdgeInsets(edge, padding), edge); - YGNODE_STYLE_SET_FLOAT_WITH_EDGE(yogaNode, Border, dimensionForEdgeWithEdgeInsets(edge, border), edge); - edge = (edge == YGEdgeLeft ? YGEdgeTop : (edge == YGEdgeTop ? YGEdgeRight : YGEdgeBottom)); - } - - CGFloat aspectRatio = style.aspectRatio; - if (aspectRatio > FLT_EPSILON && aspectRatio < CGFLOAT_MAX / 2.0) { - YGNodeStyleSetAspectRatio(yogaNode, aspectRatio); - } - - // For the root node, we use rootConstrainedSize above. For children, consult the style for their size. - if (node != self) { - YGNODE_STYLE_SET_DIMENSION(yogaNode, Width, style.width); - YGNODE_STYLE_SET_DIMENSION(yogaNode, Height, style.height); - - YGNODE_STYLE_SET_DIMENSION(yogaNode, MinWidth, style.minWidth); - YGNODE_STYLE_SET_DIMENSION(yogaNode, MinHeight, style.minHeight); - - YGNODE_STYLE_SET_DIMENSION(yogaNode, MaxWidth, style.maxWidth); - YGNODE_STYLE_SET_DIMENSION(yogaNode, MaxHeight, style.maxHeight); - } - - [node setYogaMeasureFuncIfNeeded]; - - /* TODO(appleguy): STYLE SETTER METHODS LEFT TO IMPLEMENT - void YGNodeStyleSetFlexDirection(YGNodeRef node, YGFlexDirection flexDirection); - void YGNodeStyleSetOverflow(YGNodeRef node, YGOverflow overflow); - void YGNodeStyleSetFlex(YGNodeRef node, float flex); - */ - }); - // It is crucial to use yogaFloat... to convert CGFLOAT_MAX into YGUndefined here. YGNodeCalculateLayout(rootYogaNode, yogaFloatForCGFloat(rootConstrainedSize.max.width), @@ -411,10 +237,10 @@ YGSize ASLayoutElementYogaMeasureFunc(YGNodeRef yogaNode, float width, YGMeasure ASDisplayNodePerformBlockOnEveryYogaChild(self, ^(ASDisplayNode * _Nonnull node) { [node setupYogaCalculatedLayout]; - node.hierarchyState &= ~ASHierarchyStateYogaLayoutMeasuring; + node.yogaLayoutInProgress = NO; }); -#if YOGA_LAYOUT_LOGGING +#if YOGA_LAYOUT_LOGGING /* 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]) { @@ -429,7 +255,7 @@ YGSize ASLayoutElementYogaMeasureFunc(YGNodeRef yogaNode, float width, YGMeasure YGNodePrint(node.yogaNode, (YGPrintOptions)(YGPrintOptionsStyle | YGPrintOptionsLayout)); }); } -#endif +#endif /* YOGA_LAYOUT_LOGGING */ } @end diff --git a/Source/ASDisplayNode.h b/Source/ASDisplayNode.h index db59e257bb..a3bb069f61 100644 --- a/Source/ASDisplayNode.h +++ b/Source/ASDisplayNode.h @@ -1,11 +1,18 @@ // // ASDisplayNode.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #pragma once @@ -18,6 +25,7 @@ #import #import #import +#import NS_ASSUME_NONNULL_BEGIN @@ -49,7 +57,7 @@ typedef void (^ASDisplayNodeDidLoadBlock)(__kindof ASDisplayNode * node); /** * ASDisplayNode will / did render node content in context. */ -typedef void (^ASDisplayNodeContextModifier)(CGContextRef context); +typedef void (^ASDisplayNodeContextModifier)(CGContextRef context, id _Nullable drawParameters); /** * ASDisplayNode layout spec block. This block can be used instead of implementing layoutSpecThatFits: in subclass @@ -114,12 +122,16 @@ extern NSInteger const ASDefaultDrawingPriority; * */ -@interface ASDisplayNode : NSObject +@interface ASDisplayNode : NSObject + ++ (void)drawRect:(CGRect)bounds withParameters:(nullable id)parameters + isCancelled:(AS_NOESCAPE asdisplaynode_iscancelled_block_t)isCancelledBlock + isRasterizing:(BOOL)isRasterizing; /** @name Initializing a node object */ -/** +/** * @abstract Designated initializer. * * @return An ASDisplayNode instance whose view will be a subclass that enables asynchronous rendering, and passes @@ -178,8 +190,6 @@ extern NSInteger const ASDefaultDrawingPriority; * @param body The work to be performed when the node is loaded. * * @precondition The node is not already loaded. - * @note This will only be called the next time the node is loaded. If the node is later added to a subtree of a node - * that has `shouldRasterizeDescendants=YES`, and is unloaded, this block will not be called if it is loaded again. */ - (void)onDidLoad:(ASDisplayNodeDidLoadBlock)body; @@ -197,7 +207,7 @@ extern NSInteger const ASDefaultDrawingPriority; /** * Set the block that should be used to load this node's layer. * - * @param viewBlock The block that creates a layer for this node. + * @param layerBlock The block that creates a layer for this node. * * @precondition The node is not yet loaded. * @@ -210,8 +220,7 @@ extern NSInteger const ASDefaultDrawingPriority; * * @return NO if the node wraps a _ASDisplayView, YES otherwise. */ -@property (nonatomic, readonly, assign, getter=isSynchronous) BOOL synchronous; - +@property (atomic, readonly, assign, getter=isSynchronous) BOOL synchronous; /** @name Getting view and layer */ @@ -291,61 +300,6 @@ extern NSInteger const ASDefaultDrawingPriority; */ @property (nonatomic, class, copy) ASDisplayNodeNonFatalErrorBlock nonFatalErrorBlock; - -/** @name Managing dimensions */ - -/** - * @abstract Asks the node to return a layout based on given size range. - * - * @param constrainedSize The minimum and maximum sizes the receiver should fit in. - * - * @return An ASLayout instance defining the layout of the receiver (and its children, if the box layout model is used). - * - * @discussion Though this method does not set the bounds of the view, it does have side effects--caching both the - * constraint and the result. - * - * @warning Subclasses must not override this; it caches results from -calculateLayoutThatFits:. Calling this method may - * be expensive if result is not cached. - * - * @see [ASDisplayNode(Subclassing) calculateLayoutThatFits:] - */ -- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize; - -/** - * @abstract Provides a way to declare a block to provide an ASLayoutSpec without having to subclass ASDisplayNode and - * implement layoutSpecThatFits: - * - * @return A block that takes a constrainedSize ASSizeRange argument, and must return an ASLayoutSpec that includes all - * of the subnodes to position in the layout. This input-output relationship is identical to the subclass override - * method -layoutSpecThatFits: - * - * @warning Subclasses that implement -layoutSpecThatFits: must not also use .layoutSpecBlock. Doing so will trigger - * an exception. A future version of the framework may support using both, calling them serially, with the - * .layoutSpecBlock superseding any values set by the method override. - * - * @code ^ASLayoutSpec *(__kindof ASDisplayNode * _Nonnull node, ASSizeRange constrainedSize) {}; - */ -@property (nonatomic, readwrite, copy, nullable) ASLayoutSpecBlock layoutSpecBlock; - -/** - * @abstract Return the calculated size. - * - * @discussion Ideal for use by subclasses in -layout, having already prompted their subnodes to calculate their size by - * calling -measure: on them in -calculateLayoutThatFits. - * - * @return Size already calculated by -calculateLayoutThatFits:. - * - * @warning Subclasses must not override this; it returns the last cached measurement and is never expensive. - */ -@property (nonatomic, readonly, assign) CGSize calculatedSize; - -/** - * @abstract Return the constrained size range used for calculating layout. - * - * @return The minimum and maximum constrained sizes used by calculateLayoutThatFits:. - */ -@property (nonatomic, readonly, assign) ASSizeRange constrainedSizeForCalculatedLayout; - /** @name Managing the nodes hierarchy */ @@ -518,7 +472,7 @@ extern NSInteger const ASDefaultDrawingPriority; * @discussion Defaults to ASDefaultDrawingPriority. There may be multiple drawing threads, and some of them may * decide to perform operations in queued order (regardless of drawingPriority) */ -@property (nonatomic, assign) NSInteger drawingPriority; +@property (atomic, assign) NSInteger drawingPriority; /** @name Hit Testing */ @@ -592,12 +546,41 @@ extern NSInteger const ASDefaultDrawingPriority; */ - (CGRect)convertRect:(CGRect)rect fromNode:(nullable ASDisplayNode *)node AS_WARN_UNUSED_RESULT; +/** + * Whether or not the node would support having .layerBacked = YES. + */ +@property (nonatomic, readonly) BOOL supportsLayerBacking; + @end /** * Convenience methods for debugging. */ -@interface ASDisplayNode (Debugging) +@interface ASDisplayNode (Debugging) + +/** + * Set to YES to tell all ASDisplayNode instances to store their unflattened layouts. + * + * The layout can be accessed via `-unflattenedCalculatedLayout`. + * + * Flattened layouts use less memory and are faster to lookup. On the other hand, unflattened layouts are useful for debugging + * because they preserve original information. + */ ++ (void)setShouldStoreUnflattenedLayouts:(BOOL)shouldStore; + +/** + * Whether or not ASDisplayNode instances should store their unflattened layouts. + * + * The layout can be accessed via `-unflattenedCalculatedLayout`. + * + * Flattened layouts use less memory and are faster to lookup. On the other hand, unflattened layouts are useful for debugging + * because they preserve original information. + * + * Defaults to NO. + */ ++ (BOOL)shouldStoreUnflattenedLayouts; + +@property (nonatomic, strong, readonly, nullable) ASLayout *unflattenedCalculatedLayout; /** * @abstract Return a description of the node hierarchy. @@ -608,7 +591,6 @@ extern NSInteger const ASDefaultDrawingPriority; @end - /** * ## UIView bridge * @@ -639,29 +621,37 @@ extern NSInteger const ASDefaultDrawingPriority; */ - (void)setNeedsLayout; -@property (nonatomic, strong, nullable) id contents; // default=nil +/** + * Performs a layout pass on the node. Convenience for use whether the view / layer is loaded or not. Safe to call from a background thread. + */ +- (void)layoutIfNeeded; + +@property (nonatomic, assign) CGRect frame; // default=CGRectZero +@property (nonatomic, assign) CGRect bounds; // default=CGRectZero +@property (nonatomic, assign) CGPoint position; // default=CGPointZero +@property (nonatomic, assign) CGFloat alpha; // default=1.0f + @property (nonatomic, assign) BOOL clipsToBounds; // default==NO +@property (nonatomic, getter=isHidden) BOOL hidden; // default==NO @property (nonatomic, getter=isOpaque) BOOL opaque; // default==YES -@property (nonatomic, assign) BOOL allowsGroupOpacity; -@property (nonatomic, assign) BOOL allowsEdgeAntialiasing; -@property (nonatomic, assign) unsigned int edgeAntialiasingMask; // default==all values from CAEdgeAntialiasingMask +@property (nonatomic, strong, nullable) id contents; // default=nil +@property (nonatomic, assign) CGRect contentsRect; // default={0,0,1,1}. @see CALayer.h for details. +@property (nonatomic, assign) CGRect contentsCenter; // default={0,0,1,1}. @see CALayer.h for details. +@property (nonatomic, assign) CGFloat contentsScale; // default=1.0f. See @contentsScaleForDisplay for details. +@property (nonatomic, assign) CGFloat rasterizationScale; // default=1.0f. -@property (nonatomic, getter=isHidden) BOOL hidden; // default==NO -@property (nonatomic, assign) BOOL needsDisplayOnBoundsChange; // default==NO -@property (nonatomic, assign) BOOL autoresizesSubviews; // default==YES (undefined for layer-backed nodes) -@property (nonatomic, assign) UIViewAutoresizing autoresizingMask; // default==UIViewAutoresizingNone (undefined for layer-backed nodes) -@property (nonatomic, assign) CGFloat alpha; // default=1.0f -@property (nonatomic, assign) CGRect bounds; // default=CGRectZero -@property (nonatomic, assign) CGRect frame; // default=CGRectZero @property (nonatomic, assign) CGPoint anchorPoint; // default={0.5, 0.5} @property (nonatomic, assign) CGFloat zPosition; // default=0.0 -@property (nonatomic, assign) CGPoint position; // default=CGPointZero @property (nonatomic, assign) CGFloat cornerRadius; // default=0.0 -@property (nonatomic, assign) CGFloat contentsScale; // default=1.0f. See @contentsScaleForDisplay for more info @property (nonatomic, assign) CATransform3D transform; // default=CATransform3DIdentity @property (nonatomic, assign) CATransform3D subnodeTransform; // default=CATransform3DIdentity +@property (nonatomic, assign, getter=isUserInteractionEnabled) BOOL userInteractionEnabled; // default=YES (NO for layer-backed nodes) +#if TARGET_OS_IOS +@property (nonatomic, assign, getter=isExclusiveTouch) BOOL exclusiveTouch; // default=NO +#endif + /** * @abstract The node view's background color. * @@ -670,8 +660,8 @@ extern NSInteger const ASDefaultDrawingPriority; */ @property (nonatomic, strong, nullable) UIColor *backgroundColor; // default=nil -@property (nonatomic, strong, null_resettable) UIColor *tintColor; // default=Blue -- (void)tintColorDidChange; // Notifies the node when the tintColor has changed. +@property (nonatomic, strong, null_resettable) UIColor *tintColor; // default=Blue +- (void)tintColorDidChange; // Notifies the node when the tintColor has changed. /** * @abstract A flag used to determine how a node lays out its content when its bounds change. @@ -682,17 +672,23 @@ extern NSInteger const ASDefaultDrawingPriority; * contentMode for your content while it's being re-rendered. */ @property (nonatomic, assign) UIViewContentMode contentMode; // default=UIViewContentModeScaleToFill +@property (nonatomic, copy) NSString *contentsGravity; // Use .contentMode in preference when possible. +@property (nonatomic, assign) UISemanticContentAttribute semanticContentAttribute; // default=Unspecified -@property (nonatomic, assign, getter=isUserInteractionEnabled) BOOL userInteractionEnabled; // default=YES (NO for layer-backed nodes) -#if TARGET_OS_IOS -@property (nonatomic, assign, getter=isExclusiveTouch) BOOL exclusiveTouch; // default=NO -#endif -@property (nonatomic, assign, nullable) CGColorRef shadowColor; // default=opaque rgb black +@property (nonatomic, nullable) CGColorRef shadowColor; // default=opaque rgb black @property (nonatomic, assign) CGFloat shadowOpacity; // default=0.0 @property (nonatomic, assign) CGSize shadowOffset; // default=(0, -3) @property (nonatomic, assign) CGFloat shadowRadius; // default=3 @property (nonatomic, assign) CGFloat borderWidth; // default=0 -@property (nonatomic, assign, nullable) CGColorRef borderColor; // default=opaque rgb black +@property (nonatomic, nullable) CGColorRef borderColor; // default=opaque rgb black + +@property (nonatomic, assign) BOOL allowsGroupOpacity; +@property (nonatomic, assign) BOOL allowsEdgeAntialiasing; +@property (nonatomic, assign) unsigned int edgeAntialiasingMask; // default==all values from CAEdgeAntialiasingMask + +@property (nonatomic, assign) BOOL needsDisplayOnBoundsChange; // default==NO +@property (nonatomic, assign) BOOL autoresizesSubviews; // default==YES (undefined for layer-backed nodes) +@property (nonatomic, assign) UIViewAutoresizing autoresizingMask; // default==UIViewAutoresizingNone (undefined for layer-backed nodes) // UIResponder methods // By default these fall through to the underlying view, but can be overridden. @@ -740,7 +736,74 @@ extern NSInteger const ASDefaultDrawingPriority; @end -@interface ASDisplayNode (LayoutTransitioning) +@interface ASDisplayNode (ASLayoutElement) + +/** + * @abstract Asks the node to return a layout based on given size range. + * + * @param constrainedSize The minimum and maximum sizes the receiver should fit in. + * + * @return An ASLayout instance defining the layout of the receiver (and its children, if the box layout model is used). + * + * @discussion Though this method does not set the bounds of the view, it does have side effects--caching both the + * constraint and the result. + * + * @warning Subclasses must not override this; it caches results from -calculateLayoutThatFits:. Calling this method may + * be expensive if result is not cached. + * + * @see [ASDisplayNode(Subclassing) calculateLayoutThatFits:] + */ +- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize; + +@end + +@interface ASDisplayNode (ASLayoutElementStylability) + +@end + +@interface ASDisplayNode (ASLayout) + +/** @name Managing dimensions */ + +/** + * @abstract Provides a way to declare a block to provide an ASLayoutSpec without having to subclass ASDisplayNode and + * implement layoutSpecThatFits: + * + * @return A block that takes a constrainedSize ASSizeRange argument, and must return an ASLayoutSpec that includes all + * of the subnodes to position in the layout. This input-output relationship is identical to the subclass override + * method -layoutSpecThatFits: + * + * @warning Subclasses that implement -layoutSpecThatFits: must not also use .layoutSpecBlock. Doing so will trigger + * an exception. A future version of the framework may support using both, calling them serially, with the + * .layoutSpecBlock superseding any values set by the method override. + * + * @code ^ASLayoutSpec *(__kindof ASDisplayNode * _Nonnull node, ASSizeRange constrainedSize) {}; + */ +@property (nonatomic, readwrite, copy, nullable) ASLayoutSpecBlock layoutSpecBlock; + +/** + * @abstract Return the calculated size. + * + * @discussion Ideal for use by subclasses in -layout, having already prompted their subnodes to calculate their size by + * calling -measure: on them in -calculateLayoutThatFits. + * + * @return Size already calculated by -calculateLayoutThatFits:. + * + * @warning Subclasses must not override this; it returns the last cached measurement and is never expensive. + */ +@property (nonatomic, readonly, assign) CGSize calculatedSize; + +/** + * @abstract Return the constrained size range used for calculating layout. + * + * @return The minimum and maximum constrained sizes used by calculateLayoutThatFits:. + */ +@property (nonatomic, readonly, assign) ASSizeRange constrainedSizeForCalculatedLayout; + + +@end + +@interface ASDisplayNode (ASLayoutTransitioning) /** * @abstract The amount of time it takes to complete the default transition animation. Default is 0.2. @@ -814,7 +877,7 @@ extern NSInteger const ASDefaultDrawingPriority; /* * ASDisplayNode support for automatic subnode management. */ -@interface ASDisplayNode (AutomaticSubnodeManagement) +@interface ASDisplayNode (ASAutomaticSubnodeManagement) /** * @abstract A boolean that shows whether the node automatically inserts and removes nodes based on the presence or diff --git a/Source/ASDisplayNode.mm b/Source/ASDisplayNode.mm index 349af8321c..b7ecb1546c 100644 --- a/Source/ASDisplayNode.mm +++ b/Source/ASDisplayNode.mm @@ -1,15 +1,23 @@ // // ASDisplayNode.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import +#import #import #import #import @@ -33,29 +41,13 @@ #import #import #import +#import #import +#import #import #import #import - -/** - * Assert if the current thread owns a mutex. - * This assertion is useful when you want to indicate and enforce the locking policy/expectation of methods. - * To determine when and which methods acquired a (recursive) mutex (to debug deadlocks, for example), - * put breakpoints at some of these assertions. When the breakpoints hit, walk through stack trace frames - * and check ownership count of the mutex. - */ -#if CHECK_LOCKING_SAFETY - #define ASDisplayNodeAssertLockUnownedByCurrentThread(lock) ASDisplayNodeAssertFalse(lock.ownedByCurrentThread()) -#else - #define ASDisplayNodeAssertLockUnownedByCurrentThread(lock) -#endif - -#if ASDisplayNodeLoggingEnabled - #define LOG(...) NSLog(__VA_ARGS__) -#else - #define LOG(...) -#endif +#import // Conditionally time these scopes to our debug ivars (only exist in debug/profile builds) #if TIME_DISPLAYNODE_OPS @@ -65,12 +57,13 @@ #endif static ASDisplayNodeNonFatalErrorBlock _nonFatalErrorBlock = nil; +NSInteger const ASDefaultDrawingPriority = ASDefaultTransactionPriority; -// Forward declare CALayerDelegate protocol as the iOS 10 SDK moves CALayerDelegate from a formal delegate to a protocol. +// Forward declare CALayerDelegate protocol as the iOS 10 SDK moves CALayerDelegate from an informal delegate to a protocol. // We have to forward declare the protocol as this place otherwise it will not compile compiling with an Base SDK < iOS 10 @protocol CALayerDelegate; -@interface ASDisplayNode () +@interface ASDisplayNode () /** * See ASDisplayNodeInternal.h for ivars @@ -82,11 +75,10 @@ static ASDisplayNodeNonFatalErrorBlock _nonFatalErrorBlock = nil; @dynamic layoutElementType; -@synthesize debugName = _debugName; @synthesize threadSafeBounds = _threadSafeBounds; -@synthesize layoutSpecBlock = _layoutSpecBlock; static BOOL suppressesInvalidCollectionUpdateExceptions = NO; +static std::atomic_bool storesUnflattenedLayouts = ATOMIC_VAR_INIT(NO); + (BOOL)suppressesInvalidCollectionUpdateExceptions { @@ -105,9 +97,9 @@ BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector) // For classes like ASTableNode, ASCollectionNode, ASScrollNode and similar - we have to be sure to set certain properties // like setFrame: and setBackgroundColor: directly to the UIView and not apply it to the layer only. -BOOL ASDisplayNodeNeedsSpecialPropertiesHandlingForFlags(ASDisplayNodeFlags flags) +BOOL ASDisplayNodeNeedsSpecialPropertiesHandling(BOOL isSynchronous, BOOL isLayerBacked) { - return flags.synchronous && !flags.layerBacked; + return isSynchronous && !isLayerBacked; } _ASPendingState *ASDisplayNodeGetPendingState(ASDisplayNode *node) @@ -146,13 +138,11 @@ static struct ASDisplayNodeFlags GetASDisplayNodeFlags(Class c, ASDisplayNode *i flags.implementsImageDisplay = ([c respondsToSelector:@selector(displayWithParameters:isCancelled:)] ? 1 : 0); if (instance) { flags.implementsDrawParameters = ([instance respondsToSelector:@selector(drawParametersForAsyncLayer:)] ? 1 : 0); - flags.implementsInstanceDrawRect = ([instance respondsToSelector:@selector(drawRect:withParameters:isCancelled:isRasterizing:)] ? 1 : 0); - flags.implementsInstanceImageDisplay = ([instance respondsToSelector:@selector(displayWithParameters:isCancelled:)] ? 1 : 0); } else { flags.implementsDrawParameters = ([c instancesRespondToSelector:@selector(drawParametersForAsyncLayer:)] ? 1 : 0); - flags.implementsInstanceDrawRect = ([c instancesRespondToSelector:@selector(drawRect:withParameters:isCancelled:isRasterizing:)] ? 1 : 0); - flags.implementsInstanceImageDisplay = ([c instancesRespondToSelector:@selector(displayWithParameters:isCancelled:)] ? 1 : 0); } + + return flags; } @@ -183,6 +173,15 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) if (ASDisplayNodeSubclassOverridesSelector(c, @selector(layoutSpecThatFits:))) { overrides |= ASDisplayNodeMethodOverrideLayoutSpecThatFits; } + if (ASDisplayNodeSubclassOverridesSelector(c, @selector(calculateLayoutThatFits:)) || + ASDisplayNodeSubclassOverridesSelector(c, @selector(calculateLayoutThatFits: + restrictedToSize: + relativeToParentSize:))) { + overrides |= ASDisplayNodeMethodOverrideCalcLayoutThatFits; + } + if (ASDisplayNodeSubclassOverridesSelector(c, @selector(calculateSizeThatFits:))) { + overrides |= ASDisplayNodeMethodOverrideCalcSizeThatFits; + } if (ASDisplayNodeSubclassOverridesSelector(c, @selector(fetchData))) { overrides |= ASDisplayNodeMethodOverrideFetchData; } @@ -195,8 +194,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) + (void)initialize { - [super initialize]; - +#if ASDISPLAYNODE_ASSERTIONS_ENABLED if (self != [ASDisplayNode class]) { // Subclasses should never override these. Use unused to prevent warnings @@ -210,7 +208,20 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(layoutThatFits:parentSize:)), @"Subclass %@ must not override layoutThatFits:parentSize method. Instead override calculateLayoutThatFits:.", classString); ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(recursivelyClearContents)), @"Subclass %@ must not override recursivelyClearContents method.", classString); ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(recursivelyClearPreloadedData)), @"Subclass %@ must not override recursivelyClearFetchedData method.", classString); + } else { + // Check if subnodes where modified during the creation of the layout + __block IMP originalLayoutSpecThatFitsIMP = ASReplaceMethodWithBlock(self, @selector(_locked_layoutElementThatFits:), ^(ASDisplayNode *_self, ASSizeRange sizeRange) { + NSArray *oldSubnodes = _self.subnodes; + ASLayoutSpec *layoutElement = ((ASLayoutSpec *( *)(id, SEL, ASSizeRange))originalLayoutSpecThatFitsIMP)(_self, @selector(_locked_layoutElementThatFits:), sizeRange); + NSArray *subnodes = _self.subnodes; + ASDisplayNodeAssert(oldSubnodes.count == subnodes.count, @"Adding or removing nodes in layoutSpecBlock or layoutSpecThatFits: is not allowed and can cause unexpected behavior."); + for (NSInteger i = 0; i < oldSubnodes.count; i++) { + ASDisplayNodeAssert(oldSubnodes[i] == subnodes[i], @"Adding or removing nodes in layoutSpecBlock or layoutSpecThatFits: is not allowed and can cause unexpected behavior."); + } + return layoutElement; + }); } +#endif // Below we are pre-calculating values per-class and dynamically adding a method (_staticInitialize) to populate these values // when each instance is constructed. These values don't change for each class, so there is significant performance benefit @@ -232,24 +243,6 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) }); class_replaceMethod(self, @selector(_staticInitialize), staticInitialize, "v:@"); - - -#if DEBUG - // Check if subnodes where modified during the creation of the layout - if (self == [ASDisplayNode class]) { - __block IMP originalLayoutSpecThatFitsIMP = ASReplaceMethodWithBlock(self, @selector(_locked_layoutElementThatFits:), ^(ASDisplayNode *_self, ASSizeRange sizeRange) { - NSArray *oldSubnodes = _self.subnodes; - ASLayoutSpec *layoutElement = ((ASLayoutSpec *( *)(id, SEL, ASSizeRange))originalLayoutSpecThatFitsIMP)(_self, @selector(_locked_layoutElementThatFits:), sizeRange); - NSArray *subnodes = _self.subnodes; - ASDisplayNodeAssert(oldSubnodes.count == subnodes.count, @"Adding or removing nodes in layoutSpecBlock or layoutSpecThatFits: is not allowed and can cause unexpected behavior."); - for (NSInteger i = 0; i < oldSubnodes.count; i++) { - ASDisplayNodeAssert(oldSubnodes[i] == subnodes[i], @"Adding or removing nodes in layoutSpecBlock or layoutSpecThatFits: is not allowed and can cause unexpected behavior."); - } - return layoutElement; - }); - } -#endif - } + (void)load @@ -258,11 +251,6 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) ASScreenScale(); } -+ (BOOL)layerBackedNodesEnabled -{ - return YES; -} - + (Class)viewClass { return [_ASDisplayView class]; @@ -288,12 +276,16 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) _eventLog = [[ASEventLog alloc] initWithObject:self]; #endif + _viewClass = [self.class viewClass]; + _layerClass = [self.class layerClass]; _contentsScaleForDisplay = ASScreenScale(); + _drawingPriority = ASDefaultDrawingPriority; _primitiveTraitCollection = ASPrimitiveTraitCollectionMakeDefault(); _calculatedDisplayNodeLayout = std::make_shared(); _pendingDisplayNodeLayout = nullptr; + _layoutVersion = 1; _defaultLayoutTransitionDuration = 0.2; _defaultLayoutTransitionDelay = 0.0; @@ -322,7 +314,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) ASDisplayNodeAssert([viewClass isSubclassOfClass:[UIView class]], @"should initialize with a subclass of UIView"); _viewClass = viewClass; - _flags.synchronous = ![viewClass isSubclassOfClass:[_ASDisplayView class]]; + setFlag(Synchronous, ![viewClass isSubclassOfClass:[_ASDisplayView class]]); return self; } @@ -336,8 +328,8 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) ASDisplayNodeAssert([layerClass isSubclassOfClass:[CALayer class]], @"should initialize with a subclass of CALayer"); _layerClass = layerClass; - _flags.synchronous = ![layerClass isSubclassOfClass:[_ASDisplayLayer class]]; _flags.layerBacked = YES; + setFlag(Synchronous, ![layerClass isSubclassOfClass:[_ASDisplayLayer class]]); return self; } @@ -349,10 +341,9 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) - (instancetype)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock didLoadBlock:(ASDisplayNodeDidLoadBlock)didLoadBlock { - if (!(self = [super init])) + if (!(self = [self init])) { return nil; - - [self _initializeInstance]; + } [self setViewBlock:viewBlock]; if (didLoadBlock != nil) { @@ -369,10 +360,9 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) - (instancetype)initWithLayerBlock:(ASDisplayNodeLayerBlock)layerBlock didLoadBlock:(ASDisplayNodeDidLoadBlock)didLoadBlock { - if (!(self = [super init])) + if (!(self = [self init])) { return nil; - - [self _initializeInstance]; + } [self setLayerBlock:layerBlock]; if (didLoadBlock != nil) { @@ -388,7 +378,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) ASDisplayNodeAssertNotNil(viewBlock, @"should initialize with a valid block that returns a UIView"); _viewBlock = viewBlock; - _flags.synchronous = YES; + setFlag(Synchronous, YES); } - (void)setLayerBlock:(ASDisplayNodeLayerBlock)layerBlock @@ -397,8 +387,8 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) ASDisplayNodeAssertNotNil(layerBlock, @"should initialize with a valid block that returns a CALayer"); _layerBlock = layerBlock; - _flags.synchronous = YES; _flags.layerBacked = YES; + setFlag(Synchronous, YES); } - (void)onDidLoad:(ASDisplayNodeDidLoadBlock)body @@ -421,7 +411,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) _flags.isDeallocating = YES; // Synchronous nodes may not be able to call the hierarchy notifications, so only enforce for regular nodes. - ASDisplayNodeAssert(_flags.synchronous || !ASInterfaceStateIncludesVisible(_interfaceState), @"Node should always be marked invisible before deallocating. Node: %@", self); + ASDisplayNodeAssert(checkFlag(Synchronous) || !ASInterfaceStateIncludesVisible(_interfaceState), @"Node should always be marked invisible before deallocating. Node: %@", self); self.asyncLayer.asyncDelegate = nil; _view.asyncdisplaykit_node = nil; @@ -440,14 +430,6 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) [self _scheduleIvarsForMainDeallocation]; } - _subnodes = nil; - -#if YOGA - if (_yogaNode != NULL) { - YGNodeFree(_yogaNode); - } -#endif - // TODO: Remove this? If supernode isn't already nil, this method isn't dealloc-safe anyway. [self _setSupernode:nil]; } @@ -467,10 +449,12 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) for (Ivar ivar : ivars) { id value = object_getIvar(self, ivar); if (ASClassRequiresMainThreadDeallocation(object_getClass(value))) { - LOG(@"Trampolining ivar '%s' value %@ for main deallocation.", ivar_getName(ivar), value); + as_log_debug(ASMainThreadDeallocationLog(), "%@: Trampolining ivar '%s' value %@ for main deallocation.", self, ivar_getName(ivar), value); + //NSLog(@"%@: Trampolining ivar '%s' value %@ for main deallocation.", self, ivar_getName(ivar), value); ASPerformMainThreadDeallocation(value); } else { - LOG(@"Not trampolining ivar '%s' value %@.", ivar_getName(ivar), value); + as_log_debug(ASMainThreadDeallocationLog(), "%@: Not trampolining ivar '%s' value %@.", self, ivar_getName(ivar), value); + //NSLog(@"%@: Not trampolining ivar '%s' value %@.", self, ivar_getName(ivar), value); } } } @@ -527,16 +511,16 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) // If it's `id` we have to include it just in case. resultIvars[resultCount] = ivar; resultCount += 1; - LOG(@"Marking ivar '%s' for possible main deallocation due to type id", ivar_getName(ivar)); + as_log_debug(ASMainThreadDeallocationLog(), "%@: Marking ivar '%s' for possible main deallocation due to type id", self, ivar_getName(ivar)); } else { // If it's an ivar with a static type, check the type. Class c = ASGetClassFromType(type); if (ASClassRequiresMainThreadDeallocation(c)) { resultIvars[resultCount] = ivar; resultCount += 1; - LOG(@"Marking ivar '%s' for main deallocation due to class %@", ivar_getName(ivar), c); + as_log_debug(ASMainThreadDeallocationLog(), "%@: Marking ivar '%s' for main deallocation due to class %@", self, ivar_getName(ivar), c); } else { - LOG(@"Skipping ivar '%s' for main deallocation.", ivar_getName(ivar)); + as_log_debug(ASMainThreadDeallocationLog(), "%@: Skipping ivar '%s' for main deallocation.", self, ivar_getName(ivar)); } } } @@ -551,28 +535,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) return result; } -#pragma mark - Loading / Unloading - -- (void)__unloadNode -{ - ASDisplayNodeAssertMainThread(); - ASDisplayNodeAssert([self isNodeLoaded], @"Implementation shouldn't call __unloadNode if not loaded: %@", self); - ASDisplayNodeAssert(_flags.synchronous == NO, @"Node created using -initWithViewBlock:/-initWithLayerBlock: cannot be unloaded. Node: %@", self); - ASDN::MutexLocker l(__instanceLock__); - - if (_flags.layerBacked) { - _pendingViewState = [_ASPendingState pendingViewStateFromLayer:_layer]; - } else { - _pendingViewState = [_ASPendingState pendingViewStateFromView:_view]; - } - - [_view removeFromSuperview]; - _view = nil; - if (_flags.layerBacked) - _layer.delegate = nil; - [_layer removeFromSuperlayer]; - _layer = nil; -} +#pragma mark - Loading - (BOOL)_locked_shouldLoadViewOrLayer { @@ -589,14 +552,11 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) _viewBlock = nil; _viewClass = [view class]; } else { - if (!_viewClass) { - _viewClass = [self.class viewClass]; - } view = [[_viewClass alloc] init]; } // Special handling of wrapping UIKit components - if (_flags.synchronous) { + if (checkFlag(Synchronous)) { // UIImageView layers. More details on the flags if ([_viewClass isSubclassOfClass:[UIImageView class]]) { _flags.canClearContentsOfLayer = NO; @@ -608,6 +568,11 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) || [_viewClass isSubclassOfClass:[UIVisualEffectView class]]) { self.opaque = NO; } + + // CAEAGLLayer + if([[view.layer class] isSubclassOfClass:[CAEAGLLayer class]]){ + _flags.canClearContentsOfLayer = NO; + } } return view; @@ -625,18 +590,15 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) _layerBlock = nil; _layerClass = [layer class]; } else { - if (!_layerClass) { - _layerClass = [self.class layerClass]; - } layer = [[_layerClass alloc] init]; } return layer; } -- (void)_locked_loadViewOrLayerIsLayerBacked:(BOOL)isLayerBacked +- (void)_locked_loadViewOrLayer { - if (isLayerBacked) { + if (_flags.layerBacked) { TIME_SCOPED(_debugTimeToCreateView); _layer = [self _locked_layerToLoad]; static int ASLayerDelegateAssociationKey; @@ -666,6 +628,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__); ASDisplayNodeLogEvent(self, @"didLoad"); + as_log_verbose(ASNodeLog(), "didLoad %@", self); TIME_SCOPED(_debugTimeForDidLoad); [self didLoad]; @@ -726,7 +689,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) // Loading a view needs to happen on the main thread ASDisplayNodeAssertMainThread(); - [self _locked_loadViewOrLayerIsLayerBacked:isLayerBacked]; + [self _locked_loadViewOrLayer]; // FIXME: Ideally we'd call this as soon as the node receives -setNeedsLayout // but automatic subnode management would require us to modify the node tree @@ -761,20 +724,13 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) return _layer; } - BOOL isLayerBacked = _flags.layerBacked; - if (!isLayerBacked) { - // No need for the lock and call the view explicitly in case it needs to be loaded first - ASDN::MutexUnlocker u(__instanceLock__); - return self.view.layer; - } - if (![self _locked_shouldLoadViewOrLayer]) { return nil; } // Loading a layer needs to happen on the main thread ASDisplayNodeAssertMainThread(); - [self _locked_loadViewOrLayerIsLayerBacked:isLayerBacked]; + [self _locked_loadViewOrLayer]; // FIXME: Ideally we'd call this as soon as the node receives -setNeedsLayout // but automatic subnode management would require us to modify the node tree @@ -816,30 +772,25 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) - (BOOL)isSynchronous { - ASDN::MutexLocker l(__instanceLock__); - return _flags.synchronous; -} - -- (void)setSynchronous:(BOOL)flag -{ - ASDN::MutexLocker l(__instanceLock__); - _flags.synchronous = flag; + return checkFlag(Synchronous); } - (void)setLayerBacked:(BOOL)isLayerBacked { - if (![self.class layerBackedNodesEnabled]) { + // Only call this if assertions are enabled – it could be expensive. + ASDisplayNodeAssert(!isLayerBacked || self.supportsLayerBacking, @"Node %@ does not support layer backing.", self); + + ASDN::MutexLocker l(__instanceLock__); + if (_flags.layerBacked == isLayerBacked) { + return; + } + + if ([self _locked_isNodeLoaded]) { + ASDisplayNodeFailAssert(@"Cannot change layerBacked after view/layer has loaded."); return; } - ASDN::MutexLocker l(__instanceLock__); - ASDisplayNodeAssert(!_view && !_layer, @"Cannot change isLayerBacked after layer or view has loaded"); - ASDisplayNodeAssert(!_viewBlock && !_layerBlock, @"Cannot change isLayerBacked when a layer or view block is provided"); - ASDisplayNodeAssert(!_viewClass && !_layerClass, @"Cannot change isLayerBacked when a layer or view class is provided"); - - if (isLayerBacked != _flags.layerBacked && !_view && !_layer) { - _flags.layerBacked = isLayerBacked; - } + _flags.layerBacked = isLayerBacked; } - (BOOL)isLayerBacked @@ -848,6 +799,12 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) return _flags.layerBacked; } +- (BOOL)supportsLayerBacking +{ + ASDN::MutexLocker l(__instanceLock__); + return !checkFlag(Synchronous) && !_flags.viewEverHadAGestureRecognizerAttached && _viewClass == [_ASDisplayView class] && _layerClass == [_ASDisplayLayer class]; +} + - (BOOL)shouldAnimateSizeChanges { ASDN::MutexLocker l(__instanceLock__); @@ -863,6 +820,11 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) - (CGRect)threadSafeBounds { ASDN::MutexLocker l(__instanceLock__); + return [self _locked_threadSafeBounds]; +} + +- (CGRect)_locked_threadSafeBounds +{ return _threadSafeBounds; } @@ -872,53 +834,13 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) _threadSafeBounds = newBounds; } -#pragma mark - Layout - -#if DEBUG - #define AS_DEDUPE_LAYOUT_SPEC_TREE 1 -#endif - -// At most a layoutSpecBlock or one of the three layout methods is overridden -#define __ASDisplayNodeCheckForLayoutMethodOverrides \ - ASDisplayNodeAssert(_layoutSpecBlock != NULL || \ - ((ASDisplayNodeSubclassOverridesSelector(self.class, @selector(calculateSizeThatFits:)) ? 1 : 0) \ - + (ASDisplayNodeSubclassOverridesSelector(self.class, @selector(layoutSpecThatFits:)) ? 1 : 0) \ - + (ASDisplayNodeSubclassOverridesSelector(self.class, @selector(calculateLayoutThatFits:)) ? 1 : 0)) <= 1, \ - @"Subclass %@ must at least provide a layoutSpecBlock or override at most one of the three layout methods: calculateLayoutThatFits:, layoutSpecThatFits:, or calculateSizeThatFits:", NSStringFromClass(self.class)) - -#pragma mark - -- (ASLayoutElementStyle *)style +- (void)nodeViewDidAddGestureRecognizer { ASDN::MutexLocker l(__instanceLock__); - if (_style == nil) { - _style = [[ASLayoutElementStyle alloc] init]; - } - return _style; + _flags.viewEverHadAGestureRecognizerAttached = YES; } -- (ASLayoutElementType)layoutElementType -{ - return ASLayoutElementTypeDisplayNode; -} - -- (BOOL)canLayoutAsynchronous -{ - return !self.isNodeLoaded; -} - -- (NSArray> *)sublayoutElements -{ - return self.subnodes; -} - -- (instancetype)styledWithBlock:(AS_NOESCAPE void (^)(__kindof ASLayoutElementStyle *style))styleBlock -{ - styleBlock(self.style); - return self; -} - -ASLayoutElementFinalLayoutElementDefault +#pragma mark - (NSString *)debugName { @@ -934,45 +856,22 @@ ASLayoutElementFinalLayoutElementDefault } } -#pragma mark Measurement Pass +#pragma mark - Layout -- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize +// At most a layoutSpecBlock or one of the three layout methods is overridden +#define __ASDisplayNodeCheckForLayoutMethodOverrides \ + ASDisplayNodeAssert(_layoutSpecBlock != NULL || \ + ((ASDisplayNodeSubclassOverridesSelector(self.class, @selector(calculateSizeThatFits:)) ? 1 : 0) \ + + (ASDisplayNodeSubclassOverridesSelector(self.class, @selector(layoutSpecThatFits:)) ? 1 : 0) \ + + (ASDisplayNodeSubclassOverridesSelector(self.class, @selector(calculateLayoutThatFits:)) ? 1 : 0)) <= 1, \ + @"Subclass %@ must at least provide a layoutSpecBlock or override at most one of the three layout methods: calculateLayoutThatFits:, layoutSpecThatFits:, or calculateSizeThatFits:", NSStringFromClass(self.class)) + + +#pragma mark + +- (BOOL)canLayoutAsynchronous { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - // For now we just call the deprecated measureWithSizeRange: method to not break old API - return [self measureWithSizeRange:constrainedSize]; -#pragma clang diagnostic pop -} - -- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize parentSize:(CGSize)parentSize -{ - ASDN::MutexLocker l(__instanceLock__); - - // If one or multiple layout transitions are in flight it still can happen that layout information is requested - // on other threads. As the pending and calculated layout to be updated in the layout transition in here just a - // layout calculation wil be performed without side effect - if ([self _isLayoutTransitionInvalid]) { - return [self calculateLayoutThatFits:constrainedSize restrictedToSize:self.style.size relativeToParentSize:parentSize]; - } - - if (_calculatedDisplayNodeLayout->isValidForConstrainedSizeParentSize(constrainedSize, parentSize)) { - ASDisplayNodeAssertNotNil(_calculatedDisplayNodeLayout->layout, @"-[ASDisplayNode layoutThatFits:parentSize:] _calculatedDisplayNodeLayout->layout should not be nil! %@", self); - // Our calculated layout is suitable for this constrainedSize, so keep using it and - // invalidate any pending layout that has been generated in the past. - _pendingDisplayNodeLayout = nullptr; - return _calculatedDisplayNodeLayout->layout ?: [ASLayout layoutWithLayoutElement:self size:{0, 0}]; - } - - // Create a pending display node layout for the layout pass - _pendingDisplayNodeLayout = std::make_shared( - [self calculateLayoutThatFits:constrainedSize restrictedToSize:self.style.size relativeToParentSize:parentSize], - constrainedSize, - parentSize - ); - - ASDisplayNodeAssertNotNil(_pendingDisplayNodeLayout->layout, @"-[ASDisplayNode layoutThatFits:parentSize:] _pendingDisplayNodeLayout->layout should not be nil! %@", self); - return _pendingDisplayNodeLayout->layout ?: [ASLayout layoutWithLayoutElement:self size:{0, 0}]; + return !self.isNodeLoaded; } #pragma mark Layout Pass @@ -986,12 +885,9 @@ ASLayoutElementFinalLayoutElementDefault { ASDN::MutexLocker l(__instanceLock__); - // This will cause the next layout pass to compute a new layout instead of returning - // the cached layout in case the constrained or parent size did not change - _calculatedDisplayNodeLayout->invalidate(); - if (_pendingDisplayNodeLayout != nullptr) { - _pendingDisplayNodeLayout->invalidate(); - } + _layoutVersion++; + + _unflattenedLayout = nil; #if YOGA [self invalidateCalculatedYogaLayout]; @@ -1000,143 +896,44 @@ ASLayoutElementFinalLayoutElementDefault - (void)__layout { - ASDisplayNodeAssertMainThread(); + ASDisplayNodeAssertThreadAffinity(self); ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__); + BOOL loaded = NO; { ASDN::MutexLocker l(__instanceLock__); + loaded = [self _locked_isNodeLoaded]; CGRect bounds = _threadSafeBounds; 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); + as_log_debug(OS_LOG_DISABLED, "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) { + if (_transitionID != ASLayoutElementContextInvalidTransitionID) { 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]; -} - -/// Needs to be called with lock held -- (void)_locked_measureNodeWithBoundsIfNecessary:(CGRect)bounds -{ - // Check if we are a subnode in a layout transition. - // In this case no measurement is needed as it's part of the layout transition - if ([self _isLayoutTransitionInvalid]) { - return; - } + [self _layoutSublayouts]; - CGSize boundsSizeForLayout = ASCeilSizeValues(bounds.size); - - // Prefer _pendingDisplayNodeLayout over _calculatedDisplayNodeLayout (if exists, it's the newest) - // If there is no _pending, check if _calculated is valid to reuse (avoiding recalculation below). - if (_pendingDisplayNodeLayout == nullptr) { - if (_calculatedDisplayNodeLayout->isDirty() == NO - && (_calculatedDisplayNodeLayout->requestedLayoutFromAbove == YES - || CGSizeEqualToSize(_calculatedDisplayNodeLayout->layout.size, boundsSizeForLayout))) { - return; - } - } - - // _calculatedDisplayNodeLayout is not reusable we need to transition to a new one - [self cancelLayoutTransition]; - - BOOL didCreateNewContext = NO; - ASLayoutElementContext context = ASLayoutElementGetCurrentContext(); - if (ASLayoutElementContextIsNull(context)) { - context = ASLayoutElementContextMake(ASLayoutElementContextDefaultTransitionID); - ASLayoutElementSetCurrentContext(context); - didCreateNewContext = YES; - } - - // Figure out previous and pending layouts for layout transition - std::shared_ptr nextLayout = _pendingDisplayNodeLayout; - #define layoutSizeDifferentFromBounds !CGSizeEqualToSize(nextLayout->layout.size, boundsSizeForLayout) - - // nextLayout was likely created by a call to layoutThatFits:, check if is valid and can be applied. - // If our bounds size is different than it, or invalid, recalculate. Use #define to avoid nullptr-> - if (nextLayout == nullptr || nextLayout->isDirty() == YES || layoutSizeDifferentFromBounds) { - // Use the last known constrainedSize passed from a parent during layout (if never, use bounds). - ASSizeRange constrainedSize = [self _locked_constrainedSizeForLayoutPass]; - ASLayout *layout = [self calculateLayoutThatFits:constrainedSize - restrictedToSize:self.style.size - relativeToParentSize:boundsSizeForLayout]; - - nextLayout = std::make_shared(layout, constrainedSize, boundsSizeForLayout); - } - - if (didCreateNewContext) { - ASLayoutElementClearCurrentContext(); - } - - // If our new layout's desired size for self doesn't match current size, ask our parent to update it. - // This can occur for either pre-calculated or newly-calculated layouts. - if (nextLayout->requestedLayoutFromAbove == NO - && CGSizeEqualToSize(boundsSizeForLayout, nextLayout->layout.size) == NO) { - // The layout that we have specifies that this node (self) would like to be a different size - // than it currently is. Because that size has been computed within the constrainedSize, we - // expect that calling setNeedsLayoutFromAbove will result in our parent resizing us to this. - // However, in some cases apps may manually interfere with this (setting a different bounds). - // In this case, we need to detect that we've already asked to be resized to match this - // particular ASLayout object, and shouldn't loop asking again unless we have a different ASLayout. - nextLayout->requestedLayoutFromAbove = YES; - [self _setNeedsLayoutFromAbove]; - } - - // Prepare to transition to nextLayout - ASDisplayNodeAssertNotNil(nextLayout->layout, @"nextLayout->layout should not be nil! %@", self); - _pendingLayoutTransition = [[ASLayoutTransition alloc] initWithNode:self - pendingLayout:nextLayout - previousLayout:_calculatedDisplayNodeLayout]; - - // If a parent is currently executing a layout transition, perform our layout application after it. - if (ASHierarchyStateIncludesLayoutPending(_hierarchyState) == NO) { - // If no transition, apply our new layout immediately (common case). - [self _completePendingLayoutTransition]; - } -} - -- (ASSizeRange)_locked_constrainedSizeForLayoutPass -{ - // TODO: The logic in -_setNeedsLayoutFromAbove seems correct and doesn't use this method. - // logic seems correct. For what case does -this method need to do the CGSizeEqual checks? - // IF WE CAN REMOVE BOUNDS CHECKS HERE, THEN WE CAN ALSO REMOVE "REQUESTED FROM ABOVE" CHECK - - CGSize boundsSizeForLayout = ASCeilSizeValues(self.threadSafeBounds.size); - - // Checkout if constrained size of pending or calculated display node layout can be used - if (_pendingDisplayNodeLayout != nullptr - && (_pendingDisplayNodeLayout->requestedLayoutFromAbove - || CGSizeEqualToSize(_pendingDisplayNodeLayout->layout.size, boundsSizeForLayout))) { - // We assume the size from the last returned layoutThatFits: layout was applied so use the pending display node - // layout constrained size - return _pendingDisplayNodeLayout->constrainedSize; - } else if (_calculatedDisplayNodeLayout->layout != nil - && (_calculatedDisplayNodeLayout->requestedLayoutFromAbove - || CGSizeEqualToSize(_calculatedDisplayNodeLayout->layout.size, boundsSizeForLayout))) { - // We assume the _calculatedDisplayNodeLayout is still valid and the frame is not different - return _calculatedDisplayNodeLayout->constrainedSize; - } else { - // In this case neither the _pendingDisplayNodeLayout or the _calculatedDisplayNodeLayout constrained size can - // be reused, so the current bounds is used. This is usual the case if a frame was set manually that differs to - // the one returned from layoutThatFits: or layoutThatFits: was never called - return ASSizeRangeMake(boundsSizeForLayout); + // Per API contract, `-layout` and `-layoutDidFinish` are called only if the node is loaded. + if (loaded) { + ASPerformBlockOnMainThread(^{ + [self layout]; + [self layoutDidFinish]; + }); } } @@ -1145,6 +942,7 @@ ASLayoutElementFinalLayoutElementDefault // Hook for subclasses ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__); + ASDisplayNodeAssertTrue(self.isNodeLoaded); } #pragma mark Calculation @@ -1153,9 +951,28 @@ ASLayoutElementFinalLayoutElementDefault restrictedToSize:(ASLayoutElementSize)size relativeToParentSize:(CGSize)parentSize { + // Use a pthread specific to mark when this method is called re-entrant on same thread. + // We only want one calculateLayout signpost interval per thread. + // This is fast enough to do it unconditionally. + auto key = ASPthreadStaticKey(NULL); + BOOL isRootCall = (pthread_getspecific(key) == NULL); + as_activity_scope_verbose(as_activity_create("Calculate node layout", AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT)); + as_log_verbose(ASLayoutLog(), "Calculating layout for %@ sizeRange %@", self, NSStringFromASSizeRange(constrainedSize)); + if (isRootCall) { + pthread_setspecific(key, kCFBooleanTrue); + ASSignpostStart(ASSignpostCalculateLayout); + } + ASSizeRange styleAndParentSize = ASLayoutElementSizeResolve(self.style.size, parentSize); const ASSizeRange resolvedRange = ASSizeRangeIntersect(constrainedSize, styleAndParentSize); - return [self calculateLayoutThatFits:resolvedRange]; + ASLayout *result = [self calculateLayoutThatFits:resolvedRange]; + as_log_verbose(ASLayoutLog(), "Calculated layout %@", result); + + if (isRootCall) { + pthread_setspecific(key, NULL); + ASSignpostEnd(ASSignpostCalculateLayout); + } + return result; } - (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize @@ -1164,21 +981,33 @@ ASLayoutElementFinalLayoutElementDefault ASDN::MutexLocker l(__instanceLock__); -#if YOGA /* YOGA */ - if (ASHierarchyStateIncludesYogaLayoutEnabled(_hierarchyState) == YES && - ASHierarchyStateIncludesYogaLayoutMeasuring(_hierarchyState) == NO) { +#if YOGA + // There are several cases where Yoga could arrive here: + // - This node is not in a Yoga tree: it has neither a yogaParent nor yogaChildren. + // - This node is a Yoga tree root: it has no yogaParent, but has yogaChildren. + // - This node is a Yoga tree node: it has both a yogaParent and yogaChildren. + // - This node is a Yoga tree leaf: it has a yogaParent, but no yogaChidlren. + // If we're a leaf node, we are probably being called by a measure function and proceed as normal. + // If we're a yoga root or tree node, initiate a new Yoga calculation pass from root. + YGNodeRef yogaNode = _style.yogaNode; + BOOL hasYogaParent = (_yogaParent != nil); + BOOL hasYogaChildren = (_yogaChildren.count > 0); + BOOL usesYoga = (yogaNode != NULL && (hasYogaParent || hasYogaChildren)); + if (usesYoga && (_yogaParent == nil || _yogaChildren.count > 0)) { + // This node has some connection to a Yoga tree. ASDN::MutexUnlocker ul(__instanceLock__); - [self calculateLayoutFromYogaRoot:constrainedSize]; - } - if (ASHierarchyStateIncludesYogaLayoutEnabled(_hierarchyState) == YES && self.yogaCalculatedLayout) { - return self.yogaCalculatedLayout; + if (self.yogaLayoutInProgress == NO) { + [self calculateLayoutFromYogaRoot:constrainedSize]; + } + ASDisplayNodeAssert(_yogaCalculatedLayout, @"Yoga node should have a non-nil layout at this stage: %@", self); + return _yogaCalculatedLayout; } + ASYogaLog(@"PROCEEDING past Yoga check to calculate ASLayout for: %@", self); #endif /* YOGA */ - + // Manual size calculation via calculateSizeThatFits: - if (((_methodOverrides & ASDisplayNodeMethodOverrideLayoutSpecThatFits) || - (_layoutSpecBlock != NULL)) == NO) { + if (_layoutSpecBlock == NULL && (_methodOverrides & ASDisplayNodeMethodOverrideLayoutSpecThatFits) == 0) { CGSize size = [self calculateSizeThatFits:constrainedSize.max]; ASDisplayNodeLogEvent(self, @"calculatedSize: %@", NSStringFromCGSize(size)); return [ASLayout layoutWithLayoutElement:self size:ASSizeRangeClamp(constrainedSize, size) sublayouts:nil]; @@ -1192,13 +1021,19 @@ ASLayoutElementFinalLayoutElementDefault // Get layout element from the node id layoutElement = [self _locked_layoutElementThatFits:constrainedSize]; +#if ASEnableVerboseLogging + for (NSString *asciiLine in [[layoutElement asciiArtString] componentsSeparatedByString:@"\n"]) { + as_log_verbose(ASLayoutLog(), "%@", asciiLine); + } +#endif + // Certain properties are necessary to set on an element of type ASLayoutSpec if (layoutElement.layoutElementType == ASLayoutElementTypeLayoutSpec) { ASLayoutSpec *layoutSpec = (ASLayoutSpec *)layoutElement; #if AS_DEDUPE_LAYOUT_SPEC_TREE - NSSet *duplicateElements = [layoutSpec findDuplicatedElementsInSubtree]; + NSHashTable *duplicateElements = [layoutSpec findDuplicatedElementsInSubtree]; if (duplicateElements.count > 0) { ASDisplayNodeFailAssert(@"Node %@ returned a layout spec that contains the same elements in multiple positions. Elements: %@", self, duplicateElements); // Use an empty layout spec to avoid crashes @@ -1213,7 +1048,6 @@ ASLayoutElementFinalLayoutElementDefault // Manually propagate the trait collection here so that any layoutSpec children of layoutSpec will get a traitCollection { - ASDN::SumScopeTimer t(_layoutSpecTotalTime, measureLayoutSpec); ASTraitCollectionPropagateDown(layoutElement, self.primitiveTraitCollection); } @@ -1238,18 +1072,20 @@ ASLayoutElementFinalLayoutElementDefault } ASDisplayNodeLogEvent(self, @"computedLayout: %@", layout); - return [layout filteredNodeLayoutTree]; + // Return the (original) unflattened layout if it needs to be stored. The layout will be flattened later on (@see _locked_setCalculatedDisplayNodeLayout:). + // Otherwise, flatten it right away. + if (! [ASDisplayNode shouldStoreUnflattenedLayouts]) { + layout = [layout filteredNodeLayoutTree]; + } + + return layout; } - (CGSize)calculateSizeThatFits:(CGSize)constrainedSize { __ASDisplayNodeCheckForLayoutMethodOverrides; -#if ASDISPLAYNODE_ASSERTIONS_ENABLED - if (ASIsCGSizeValidForSize(constrainedSize) == NO) { - NSLog(@"Cannot calculate size of node: constrainedSize is infinite and node does not override -calculateSizeThatFits: or specify a preferredSize. Try setting style.preferredSize. Node: %@", [self displayNodeRecursiveDescription]); - } -#endif + ASDisplayNodeLogEvent(self, @"calculateSizeThatFits: with constrainedSize: %@", NSStringFromCGSize(constrainedSize)); return ASIsCGSizeValidForSize(constrainedSize) ? constrainedSize : CGSizeZero; } @@ -1282,551 +1118,19 @@ ASLayoutElementFinalLayoutElementDefault return [[ASLayoutSpec alloc] init]; } -- (void)setLayoutSpecBlock:(ASLayoutSpecBlock)layoutSpecBlock -{ - // For now there should never be an override of layoutSpecThatFits: / layoutElementThatFits: and a layoutSpecBlock - ASDisplayNodeAssert(!(_methodOverrides & ASDisplayNodeMethodOverrideLayoutSpecThatFits), @"Overwriting layoutSpecThatFits: and providing a layoutSpecBlock block is currently not supported"); - - ASDN::MutexLocker l(__instanceLock__); - _layoutSpecBlock = layoutSpecBlock; -} - -- (ASLayoutSpecBlock)layoutSpecBlock -{ - ASDN::MutexLocker l(__instanceLock__); - return _layoutSpecBlock; -} - -- (ASLayout *)calculatedLayout -{ - ASDN::MutexLocker l(__instanceLock__); - return _calculatedDisplayNodeLayout->layout; -} - -- (void)_setCalculatedDisplayNodeLayout:(std::shared_ptr)displayNodeLayout -{ - ASDN::MutexLocker l(__instanceLock__); - [self _locked_setCalculatedDisplayNodeLayout:displayNodeLayout]; -} - -- (void)_locked_setCalculatedDisplayNodeLayout:(std::shared_ptr)displayNodeLayout -{ - ASDisplayNodeAssertTrue(displayNodeLayout->layout.layoutElement == self); - ASDisplayNodeAssertTrue(displayNodeLayout->layout.size.width >= 0.0); - ASDisplayNodeAssertTrue(displayNodeLayout->layout.size.height >= 0.0); - - _calculatedDisplayNodeLayout = displayNodeLayout; -} - - -- (CGSize)calculatedSize -{ - ASDN::MutexLocker l(__instanceLock__); - if (_pendingDisplayNodeLayout != nullptr) { - return _pendingDisplayNodeLayout->layout.size; - } - return _calculatedDisplayNodeLayout->layout.size; -} - -- (ASSizeRange)constrainedSizeForCalculatedLayout -{ - ASDN::MutexLocker l(__instanceLock__); - if (_pendingDisplayNodeLayout != nullptr) { - return _pendingDisplayNodeLayout->constrainedSize; - } - return _calculatedDisplayNodeLayout->constrainedSize; -} - -/** - * @abstract Informs the root node that the intrinsic size of the receiver is no longer valid. - * - * @discussion The size of a root node is determined by each subnode. Calling invalidateSize will let the root node know - * that the intrinsic size of the receiver node is no longer valid and a resizing of the root node needs to happen. - */ -- (void)_setNeedsLayoutFromAbove -{ - ASDisplayNodeAssertThreadAffinity(self); - - // Mark the node for layout in the next layout pass - [self setNeedsLayout]; - - __instanceLock__.lock(); - // Escalate to the root; entire tree must allow adjustments so the layout fits the new child. - // Much of the layout will be re-used as cached (e.g. other items in an unconstrained stack) - ASDisplayNode *supernode = _supernode; - __instanceLock__.unlock(); - - if (supernode) { - // Threading model requires that we unlock before calling a method on our parent. - [supernode _setNeedsLayoutFromAbove]; - } else { - // Let the root node method know that the size was invalidated - [self _rootNodeDidInvalidateSize]; - } -} - -- (void)_rootNodeDidInvalidateSize -{ - ASDisplayNodeAssertThreadAffinity(self); - ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__); - - __instanceLock__.lock(); - - // 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); - - // Figure out constrainedSize to use - ASSizeRange constrainedSize = ASSizeRangeMake(boundsSizeForLayout); - if (_pendingDisplayNodeLayout != nullptr) { - constrainedSize = _pendingDisplayNodeLayout->constrainedSize; - } else if (_calculatedDisplayNodeLayout->layout != nil) { - constrainedSize = _calculatedDisplayNodeLayout->constrainedSize; - } - - __instanceLock__.unlock(); - - // Perform a measurement pass to get the full tree layout, adapting to the child's new size. - ASLayout *layout = [self layoutThatFits:constrainedSize]; - - // Check if the returned layout has a different size than our current bounds. - if (CGSizeEqualToSize(boundsSizeForLayout, layout.size) == NO) { - // If so, inform our container we need an update (e.g Table, Collection, ViewController, etc). - [self displayNodeDidInvalidateSizeNewSize:layout.size]; - } -} - -- (void)displayNodeDidInvalidateSizeNewSize:(CGSize)size -{ - ASDisplayNodeAssertThreadAffinity(self); - ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__); - - // The default implementation of display node changes the size of itself to the new size - CGRect oldBounds = self.bounds; - CGSize oldSize = oldBounds.size; - CGSize newSize = size; - - if (! CGSizeEqualToSize(oldSize, newSize)) { - self.bounds = (CGRect){ oldBounds.origin, newSize }; - - // Frame's origin must be preserved. Since it is computed from bounds size, anchorPoint - // and position (see frame setter in ASDisplayNode+UIViewBridge), position needs to be adjusted. - CGPoint anchorPoint = self.anchorPoint; - CGPoint oldPosition = self.position; - CGFloat xDelta = (newSize.width - oldSize.width) * anchorPoint.x; - CGFloat yDelta = (newSize.height - oldSize.height) * anchorPoint.y; - self.position = CGPointMake(oldPosition.x + xDelta, oldPosition.y + yDelta); - } -} - - (void)layout { - [self _layoutSublayouts]; -} - -- (void)_layoutSublayouts -{ + // Hook for subclasses ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__); - - ASLayout *layout; - NSArray *subnodes; - { - ASDN::MutexLocker l(__instanceLock__); - if (_calculatedDisplayNodeLayout->isDirty() || _subnodes.count == 0) { - return; - } - layout = _calculatedDisplayNodeLayout->layout; - subnodes = [_subnodes copy]; - } - - 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; - } - } -} - -#pragma mark Automatically Manages Subnodes - -- (BOOL)automaticallyManagesSubnodes -{ - ASDN::MutexLocker l(__instanceLock__); - return _automaticallyManagesSubnodes; -} - -- (void)setAutomaticallyManagesSubnodes:(BOOL)automaticallyManagesSubnodes -{ - ASDN::MutexLocker l(__instanceLock__); - _automaticallyManagesSubnodes = automaticallyManagesSubnodes; + ASDisplayNodeAssertTrue(self.isNodeLoaded); } #pragma mark Layout Transition -- (void)transitionLayoutWithAnimation:(BOOL)animated - shouldMeasureAsync:(BOOL)shouldMeasureAsync - measurementCompletion:(void(^)())completion -{ - ASDisplayNodeAssertMainThread(); - - [self setNeedsLayout]; - - [self transitionLayoutWithSizeRange:[self _locked_constrainedSizeForLayoutPass] - animated:animated - shouldMeasureAsync:shouldMeasureAsync - measurementCompletion:completion]; - -} - -- (void)transitionLayoutWithSizeRange:(ASSizeRange)constrainedSize - animated:(BOOL)animated - shouldMeasureAsync:(BOOL)shouldMeasureAsync - measurementCompletion:(void(^)())completion -{ - ASDisplayNodeAssertMainThread(); - - if (constrainedSize.max.width <= 0.0 || constrainedSize.max.height <= 0.0) { - // Using CGSizeZero for the sizeRange can cause negative values in client layout code. - // Most likely called transitionLayout: without providing a size, before first layout pass. - return; - } - - // Check if we are a subnode in a layout transition. - // In this case no measurement is needed as we're part of the layout transition. - if ([self _isLayoutTransitionInvalid]) { - return; - } - - { - ASDN::MutexLocker l(__instanceLock__); - ASDisplayNodeAssert(ASHierarchyStateIncludesLayoutPending(_hierarchyState) == NO, @"Can't start a transition when one of the supernodes is performing one."); - } - - // Every new layout transition has a transition id associated to check in subsequent transitions for cancelling - int32_t transitionID = [self _startNewTransition]; - - // Move all subnodes in layout pending state for this transition - ASDisplayNodePerformBlockOnEverySubnode(self, NO, ^(ASDisplayNode * _Nonnull node) { - ASDisplayNodeAssert([node _isTransitionInProgress] == NO, @"Can't start a transition when one of the subnodes is performing one."); - node.hierarchyState |= ASHierarchyStateLayoutPending; - node.pendingTransitionID = transitionID; - }); - - // Transition block that executes the layout transition - void (^transitionBlock)(void) = ^{ - if ([self _shouldAbortTransitionWithID:transitionID]) { - return; - } - - // Perform a full layout creation pass with passed in constrained size to create the new layout for the transition - ASLayout *newLayout; - { - ASDN::MutexLocker l(__instanceLock__); - - ASLayoutElementSetCurrentContext(ASLayoutElementContextMake(transitionID)); - - BOOL automaticallyManagesSubnodesDisabled = (self.automaticallyManagesSubnodes == NO); - self.automaticallyManagesSubnodes = YES; // Temporary flag for 1.9.x - newLayout = [self calculateLayoutThatFits:constrainedSize - restrictedToSize:self.style.size - relativeToParentSize:constrainedSize.max]; - if (automaticallyManagesSubnodesDisabled) { - self.automaticallyManagesSubnodes = NO; // Temporary flag for 1.9.x - } - - ASLayoutElementClearCurrentContext(); - } - - if ([self _shouldAbortTransitionWithID:transitionID]) { - return; - } - - ASPerformBlockOnMainThread(^{ - ASLayoutTransition *pendingLayoutTransition; - _ASTransitionContext *pendingLayoutTransitionContext; - { - // Grab __instanceLock__ here to make sure this transition isn't invalidated - // right after it passed the validation test and before it proceeds - ASDN::MutexLocker l(__instanceLock__); - - if ([self _locked_shouldAbortTransitionWithID:transitionID]) { - return; - } - - // Update calculated layout - auto previousLayout = _calculatedDisplayNodeLayout; - auto pendingLayout = std::make_shared( - newLayout, - constrainedSize, - constrainedSize.max - ); - [self _locked_setCalculatedDisplayNodeLayout:pendingLayout]; - - // Setup pending layout transition for animation - _pendingLayoutTransition = pendingLayoutTransition = [[ASLayoutTransition alloc] initWithNode:self - pendingLayout:pendingLayout - previousLayout:previousLayout]; - // Setup context for pending layout transition. we need to hold a strong reference to the context - _pendingLayoutTransitionContext = pendingLayoutTransitionContext = [[_ASTransitionContext alloc] initWithAnimation:animated - layoutDelegate:_pendingLayoutTransition - completionDelegate:self]; - } - - // Apply complete layout transitions for all subnodes - ASDisplayNodePerformBlockOnEverySubnode(self, NO, ^(ASDisplayNode * _Nonnull node) { - [node _completePendingLayoutTransition]; - node.hierarchyState &= (~ASHierarchyStateLayoutPending); - }); - - // Measurement pass completion - // Give the subclass a change to hook into before calling the completion block - [self _layoutTransitionMeasurementDidFinish]; - if (completion) { - completion(); - } - - // Apply the subnode insertion immediately to be able to animate the nodes - [pendingLayoutTransition applySubnodeInsertions]; - - // Kick off animating the layout transition - [self animateLayoutTransition:pendingLayoutTransitionContext]; - - // Mark transaction as finished - [self _finishOrCancelTransition]; - }); - }; - - // Start transition based on flag on current or background thread - if (shouldMeasureAsync) { - ASPerformBlockOnBackgroundThread(transitionBlock); - } else { - transitionBlock(); - } -} - -- (void)cancelLayoutTransition -{ - __instanceLock__.lock(); - BOOL transitionInProgress = _transitionInProgress; - __instanceLock__.unlock(); - - if (transitionInProgress) { - // Cancel transition in progress - [self _finishOrCancelTransition]; - - // Tell subnodes to exit layout pending state and clear related properties - ASDisplayNodePerformBlockOnEverySubnode(self, NO, ^(ASDisplayNode * _Nonnull node) { - node.hierarchyState &= (~ASHierarchyStateLayoutPending); - }); - } -} - -- (BOOL)_isTransitionInProgress -{ - ASDN::MutexLocker l(__instanceLock__); - return _transitionInProgress; -} - -- (BOOL)_isLayoutTransitionInvalid -{ - ASDN::MutexLocker l(__instanceLock__); - if (ASHierarchyStateIncludesLayoutPending(_hierarchyState)) { - ASLayoutElementContext context = ASLayoutElementGetCurrentContext(); - if (ASLayoutElementContextIsNull(context) || _pendingTransitionID != context.transitionID) { - return YES; - } - } - return NO; -} - -/// Starts a new transition and returns the transition id -- (int32_t)_startNewTransition -{ - ASDN::MutexLocker l(__instanceLock__); - _transitionInProgress = YES; - _transitionID = OSAtomicAdd32(1, &_transitionID); - return _transitionID; -} - - (void)_layoutTransitionMeasurementDidFinish { - // No-Op in ASDisplayNode -} - -- (void)_finishOrCancelTransition -{ - ASDN::MutexLocker l(__instanceLock__); - _transitionInProgress = NO; -} - -- (void)setPendingTransitionID:(int32_t)pendingTransitionID -{ - ASDN::MutexLocker l(__instanceLock__); - ASDisplayNodeAssertTrue(_pendingTransitionID < pendingTransitionID); - _pendingTransitionID = pendingTransitionID; -} - -- (int32_t)pendingTransitionID -{ - ASDN::MutexLocker l(__instanceLock__); - return _pendingTransitionID; -} - -- (BOOL)_shouldAbortTransitionWithID:(int32_t)transitionID -{ - ASDN::MutexLocker l(__instanceLock__); - return [self _locked_shouldAbortTransitionWithID:transitionID]; -} - -- (BOOL)_locked_shouldAbortTransitionWithID:(int32_t)transitionID -{ - return (!_transitionInProgress || _transitionID != transitionID); -} - -- (void)setDefaultLayoutTransitionDuration:(NSTimeInterval)defaultLayoutTransitionDuration -{ - ASDN::MutexLocker l(__instanceLock__); - _defaultLayoutTransitionDuration = defaultLayoutTransitionDuration; -} - -- (NSTimeInterval)defaultLayoutTransitionDuration -{ - ASDN::MutexLocker l(__instanceLock__); - return _defaultLayoutTransitionDuration; -} - -- (void)setDefaultLayoutTransitionDelay:(NSTimeInterval)defaultLayoutTransitionDelay -{ - ASDN::MutexLocker l(__instanceLock__); - _defaultLayoutTransitionDelay = defaultLayoutTransitionDelay; -} - -- (NSTimeInterval)defaultLayoutTransitionDelay -{ - ASDN::MutexLocker l(__instanceLock__); - return _defaultLayoutTransitionDelay; -} - -- (void)setDefaultLayoutTransitionOptions:(UIViewAnimationOptions)defaultLayoutTransitionOptions -{ - ASDN::MutexLocker l(__instanceLock__); - _defaultLayoutTransitionOptions = defaultLayoutTransitionOptions; -} - -- (UIViewAnimationOptions)defaultLayoutTransitionOptions -{ - ASDN::MutexLocker l(__instanceLock__); - return _defaultLayoutTransitionOptions; -} - -#pragma mark - -/* - * Hook for subclasses to perform an animation based on the given ASContextTransitioning. By default a fade in and out - * animation is provided. - */ -- (void)animateLayoutTransition:(id)context -{ - if ([context isAnimated] == NO) { - [self _layoutSublayouts]; - [context completeTransition:YES]; - return; - } - - ASDisplayNode *node = self; - - NSAssert(node.isNodeLoaded == YES, @"Invalid node state"); - - NSArray *removedSubnodes = [context removedSubnodes]; - NSMutableArray *insertedSubnodes = [[context insertedSubnodes] mutableCopy]; - NSMutableArray *movedSubnodes = [NSMutableArray array]; - - NSMutableArray<_ASAnimatedTransitionContext *> *insertedSubnodeContexts = [NSMutableArray array]; - NSMutableArray<_ASAnimatedTransitionContext *> *removedSubnodeContexts = [NSMutableArray array]; - - for (ASDisplayNode *subnode in [context subnodesForKey:ASTransitionContextToLayoutKey]) { - if ([insertedSubnodes containsObject:subnode] == NO) { - // This is an existing subnode, check if it is resized, moved or both - CGRect fromFrame = [context initialFrameForNode:subnode]; - CGRect toFrame = [context finalFrameForNode:subnode]; - if (CGSizeEqualToSize(fromFrame.size, toFrame.size) == NO) { - [insertedSubnodes addObject:subnode]; - } - if (CGPointEqualToPoint(fromFrame.origin, toFrame.origin) == NO) { - [movedSubnodes addObject:subnode]; - } - } - } - - // Create contexts for inserted and removed subnodes - for (ASDisplayNode *insertedSubnode in insertedSubnodes) { - [insertedSubnodeContexts addObject:[_ASAnimatedTransitionContext contextForNode:insertedSubnode alpha:insertedSubnode.alpha]]; - } - for (ASDisplayNode *removedSubnode in removedSubnodes) { - [removedSubnodeContexts addObject:[_ASAnimatedTransitionContext contextForNode:removedSubnode alpha:removedSubnode.alpha]]; - } - - // Fade out inserted subnodes - for (ASDisplayNode *insertedSubnode in insertedSubnodes) { - insertedSubnode.frame = [context finalFrameForNode:insertedSubnode]; - insertedSubnode.alpha = 0; - } - - // Adjust groupOpacity for animation - BOOL originAllowsGroupOpacity = node.allowsGroupOpacity; - node.allowsGroupOpacity = YES; - - [UIView animateWithDuration:self.defaultLayoutTransitionDuration delay:self.defaultLayoutTransitionDelay options:self.defaultLayoutTransitionOptions animations:^{ - // Fade removed subnodes and views out - for (ASDisplayNode *removedSubnode in removedSubnodes) { - removedSubnode.alpha = 0; - } - - // Fade inserted subnodes in - for (_ASAnimatedTransitionContext *insertedSubnodeContext in insertedSubnodeContexts) { - insertedSubnodeContext.node.alpha = insertedSubnodeContext.alpha; - } - - // Update frame of self and moved subnodes - CGSize fromSize = [context layoutForKey:ASTransitionContextFromLayoutKey].size; - CGSize toSize = [context layoutForKey:ASTransitionContextToLayoutKey].size; - BOOL isResized = (CGSizeEqualToSize(fromSize, toSize) == NO); - if (isResized == YES) { - CGPoint position = node.frame.origin; - node.frame = CGRectMake(position.x, position.y, toSize.width, toSize.height); - } - for (ASDisplayNode *movedSubnode in movedSubnodes) { - movedSubnode.frame = [context finalFrameForNode:movedSubnode]; - } - } completion:^(BOOL finished) { - // Restore all removed subnode alpha values - for (_ASAnimatedTransitionContext *removedSubnodeContext in removedSubnodeContexts) { - removedSubnodeContext.node.alpha = removedSubnodeContext.alpha; - } - - // Restore group opacity - node.allowsGroupOpacity = originAllowsGroupOpacity; - - // Subnode removals are automatically performed - [context completeTransition:finished]; - }]; -} - -/** - * Hook for subclasses to clean up nodes after the transition happened. Furthermore this can be used from subclasses - * to manually perform deletions. - */ -- (void)didCompleteLayoutTransition:(id)context -{ - __instanceLock__.lock(); - ASLayoutTransition *pendingLayoutTransition = _pendingLayoutTransition; - __instanceLock__.unlock(); - - [pendingLayoutTransition applySubnodeRemovals]; + // Hook for subclasses - No-Op in ASDisplayNode } #pragma mark <_ASTransitionContextCompletionDelegate> @@ -1846,86 +1150,9 @@ ASLayoutElementFinalLayoutElementDefault [self _pendingLayoutTransitionDidComplete]; } -/** - * Completes the pending layout transition immediately without going through the the Layout Transition Animation API - */ -- (void)_completePendingLayoutTransition -{ - __instanceLock__.lock(); - ASLayoutTransition *pendingLayoutTransition = _pendingLayoutTransition; - __instanceLock__.unlock(); - - if (pendingLayoutTransition != nil) { - [self _setCalculatedDisplayNodeLayout:pendingLayoutTransition.pendingLayout]; - [self _completeLayoutTransition:pendingLayoutTransition]; - } - [self _pendingLayoutTransitionDidComplete]; -} - -/** - * Can be directly called to commit the given layout transition immediately to complete without calling through to the - * Layout Transition Animation API - */ -- (void)_completeLayoutTransition:(ASLayoutTransition *)layoutTransition -{ - // Layout transition is not supported for nodes that are not have automatic subnode management enabled - if (layoutTransition == nil || self.automaticallyManagesSubnodes == NO) { - return; - } - - // Trampoline to the main thread if necessary - if (ASDisplayNodeThreadIsMain() || layoutTransition.isSynchronous == NO) { - [layoutTransition commitTransition]; - } else { - // Subnode insertions and removals need to happen always on the main thread if at least one subnode is already loaded - ASPerformBlockOnMainThread(^{ - [layoutTransition commitTransition]; - }); - } -} - -- (void)_pendingLayoutTransitionDidComplete -{ - // Subclass hook - [self calculatedLayoutDidChange]; - - // Grab lock after calling out to subclass - ASDN::MutexLocker l(__instanceLock__); - - // We generate placeholders at measureWithSizeRange: time so that a node is guaranteed to have a placeholder ready to go. - // This is also because measurement is usually asynchronous, but placeholders need to be set up synchronously. - // First measurement is guaranteed to be before the node is onscreen, so we can create the image async. but still have it appear sync. - if (_placeholderEnabled && !_placeholderImage && [self _locked_displaysAsynchronously]) { - - // Zero-sized nodes do not require a placeholder. - ASLayout *layout = _calculatedDisplayNodeLayout->layout; - CGSize layoutSize = (layout ? layout.size : CGSizeZero); - if (layoutSize.width * layoutSize.height <= 0.0) { - return; - } - - // If we've displayed our contents, we don't need a placeholder. - // Contents is a thread-affined property and can't be read off main after loading. - if (self.isNodeLoaded) { - ASPerformBlockOnMainThread(^{ - if (self.contents == nil) { - _placeholderImage = [self placeholderImage]; - } - }); - } else { - if (self.contents == nil) { - _placeholderImage = [self placeholderImage]; - } - } - } - - // Cleanup pending layout transition - _pendingLayoutTransition = nil; -} - - (void)calculatedLayoutDidChange { - // subclass override + // Subclass override } #pragma mark - Display @@ -1944,7 +1171,7 @@ NSString * const ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp = @"AS */ - (BOOL)_locked_displaysAsynchronously { - return _flags.synchronous == NO && _flags.displaysAsynchronously; + return checkFlag(Synchronous) == NO && _flags.displaysAsynchronously; } - (void)setDisplaysAsynchronously:(BOOL)displaysAsynchronously @@ -1954,7 +1181,7 @@ NSString * const ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp = @"AS ASDN::MutexLocker l(__instanceLock__); // Can't do this for synchronous nodes (using layers that are not _ASDisplayLayer and so we can't control display prevention/cancel) - if (_flags.synchronous) { + if (checkFlag(Synchronous)) { return; } @@ -1967,63 +1194,46 @@ NSString * const ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp = @"AS self._locked_asyncLayer.displaysAsynchronously = displaysAsynchronously; } -- (BOOL)shouldRasterizeDescendants +- (BOOL)rasterizesSubtree { ASDN::MutexLocker l(__instanceLock__); - ASDisplayNodeAssert(!((_hierarchyState & ASHierarchyStateRasterized) && _flags.shouldRasterizeDescendants), - @"Subnode of a rasterized node should not have redundant shouldRasterizeDescendants enabled"); - return _flags.shouldRasterizeDescendants; + return _flags.rasterizesSubtree; } -- (void)setShouldRasterizeDescendants:(BOOL)shouldRasterize +- (void)enableSubtreeRasterization { - ASDisplayNodeAssertThreadAffinity(self); - BOOL rasterizedFromSelfOrAncestor = NO; - { - ASDN::MutexLocker l(__instanceLock__); - - if (_flags.shouldRasterizeDescendants == shouldRasterize) - return; - - _flags.shouldRasterizeDescendants = shouldRasterize; - rasterizedFromSelfOrAncestor = shouldRasterize || ASHierarchyStateIncludesRasterized(_hierarchyState); + ASDN::MutexLocker l(__instanceLock__); + // Already rasterized from self. + if (_flags.rasterizesSubtree) { + return; } - - if (self.isNodeLoaded) { - // Recursively tear down or build up subnodes. - // TODO: When disabling rasterization, preserve rasterized backing store as placeholderImage - // while the newly materialized subtree finishes rendering. Then destroy placeholderImage to save memory. - [self recursivelyClearContents]; - - ASDisplayNodePerformBlockOnEverySubnode(self, NO, ^(ASDisplayNode *node) { - if (rasterizedFromSelfOrAncestor) { - [node enterHierarchyState:ASHierarchyStateRasterized]; - if (node.isNodeLoaded) { - [node __unloadNode]; - } - } else { - [node exitHierarchyState:ASHierarchyStateRasterized]; - // We can avoid eagerly loading this node. We will load it on-demand as usual. - } - }); - if (!rasterizedFromSelfOrAncestor) { - // If we are not going to rasterize at all, go ahead and set up our view hierarchy. - [self _addSubnodeViewsAndLayers]; - } - - if (ASInterfaceStateIncludesVisible(self.interfaceState)) { - // TODO: Change this to recursivelyEnsureDisplay - but need a variant that does not skip - // nodes that have shouldBypassEnsureDisplay set (such as image nodes) so they are rasterized. - [self recursivelyDisplayImmediately]; - } - } else { - ASDisplayNodePerformBlockOnEverySubnode(self, NO, ^(ASDisplayNode *node) { - if (rasterizedFromSelfOrAncestor) { - [node enterHierarchyState:ASHierarchyStateRasterized]; - } else { - [node exitHierarchyState:ASHierarchyStateRasterized]; - } - }); + + // If rasterized from above, bail. + if (ASHierarchyStateIncludesRasterized(_hierarchyState)) { + ASDisplayNodeFailAssert(@"Subnode of a rasterized node should not have redundant -enableSubtreeRasterization."); + return; + } + + // Ensure not loaded. + if ([self _locked_isNodeLoaded]) { + ASDisplayNodeFailAssert(@"Cannot call %@ on loaded node: %@", NSStringFromSelector(_cmd), self); + return; + } + + // Ensure no loaded subnodes + ASDisplayNode *loadedSubnode = ASDisplayNodeFindFirstSubnode(self, ^BOOL(ASDisplayNode * _Nonnull node) { + return node.nodeLoaded; + }); + if (loadedSubnode != nil) { + ASDisplayNodeFailAssert(@"Cannot call %@ on node %@ with loaded subnode %@", NSStringFromSelector(_cmd), self, loadedSubnode); + return; + } + + _flags.rasterizesSubtree = YES; + + // Tell subnodes that now they're in a rasterized hierarchy (while holding lock!) + for (ASDisplayNode *subnode in _subnodes) { + [subnode enterHierarchyState:ASHierarchyStateRasterized]; } } @@ -2048,7 +1258,7 @@ NSString * const ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp = @"AS - (void)displayImmediately { ASDisplayNodeAssertMainThread(); - ASDisplayNodeAssert(!_flags.synchronous, @"this method is designed for asynchronous mode only"); + ASDisplayNodeAssert(!checkFlag(Synchronous), @"this method is designed for asynchronous mode only"); [self.asyncLayer displayImmediately]; } @@ -2069,7 +1279,7 @@ NSString * const ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp = @"AS BOOL nowDisplay = ASInterfaceStateIncludesDisplay(_interfaceState); // FIXME: This should not need to recursively display, so create a non-recursive variant. // The semantics of setNeedsDisplay (as defined by CALayer behavior) are not recursive. - if (_layer != nil && !_flags.synchronous && nowDisplay && [self _implementsDisplay]) { + if (_layer != nil && !checkFlag(Synchronous) && nowDisplay && [self _implementsDisplay]) { shouldScheduleForDisplay = YES; } } @@ -2097,6 +1307,7 @@ NSString * const ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp = @"AS }]; }); + as_log_verbose(ASDisplayLog(), "%s %@", sel_getName(_cmd), node); [renderQueue enqueue:node]; } @@ -2105,8 +1316,7 @@ NSString * const ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp = @"AS { ASDN::MutexLocker l(__instanceLock__); - return _flags.implementsDrawRect || _flags.implementsImageDisplay || _flags.shouldRasterizeDescendants || - _flags.implementsInstanceDrawRect || _flags.implementsInstanceImageDisplay; + return _flags.implementsDrawRect || _flags.implementsImageDisplay || _flags.rasterizesSubtree; } // Track that a node will be displayed as part of the current node hierarchy. @@ -2288,7 +1498,7 @@ static void _recursivelySetDisplaySuspended(ASDisplayNode *node, CALayer *layer, // Set the flag on the node. If this is a pure layer (no node) then this has no effect (plain layers don't support preventing/cancelling display). node.displaySuspended = flag; - if (layer && !node.shouldRasterizeDescendants) { + if (layer && !node.rasterizesSubtree) { // If there is a layer, recurse down the layer hierarchy to set the flag on descendants. This will cover both layer-based and node-based children. for (CALayer *sublayer in layer.sublayers) { _recursivelySetDisplaySuspended(nil, sublayer, flag); @@ -2313,7 +1523,7 @@ static void _recursivelySetDisplaySuspended(ASDisplayNode *node, CALayer *layer, __instanceLock__.lock(); // Can't do this for synchronous nodes (using layers that are not _ASDisplayLayer and so we can't control display prevention/cancel) - if (_flags.synchronous || _flags.displaySuspended == flag) { + if (checkFlag(Synchronous) || _flags.displaySuspended == flag) { __instanceLock__.unlock(); return; } @@ -2337,36 +1547,6 @@ static void _recursivelySetDisplaySuspended(ASDisplayNode *node, CALayer *layer, } } -NSInteger const ASDefaultDrawingPriority = ASDefaultTransactionPriority; -static const char *ASDisplayNodeDrawingPriorityKey = "ASDrawingPriority"; - -- (void)setDrawingPriority:(NSInteger)drawingPriority -{ - ASDisplayNodeAssertThreadAffinity(self); - ASDN::MutexLocker l(__instanceLock__); - - if (drawingPriority == ASDefaultDrawingPriority) { - _flags.hasCustomDrawingPriority = NO; - objc_setAssociatedObject(self, ASDisplayNodeDrawingPriorityKey, nil, OBJC_ASSOCIATION_ASSIGN); - } else { - _flags.hasCustomDrawingPriority = YES; - objc_setAssociatedObject(self, ASDisplayNodeDrawingPriorityKey, @(drawingPriority), OBJC_ASSOCIATION_RETAIN); - } -} - -- (NSInteger)drawingPriority -{ - ASDisplayNodeAssertThreadAffinity(self); - ASDN::MutexLocker l(__instanceLock__); - - if (!_flags.hasCustomDrawingPriority) { - return ASDefaultDrawingPriority; - } else { - return [objc_getAssociatedObject(self, ASDisplayNodeDrawingPriorityKey) integerValue]; - } -} - - #pragma mark <_ASDisplayLayerDelegate> - (void)willDisplayAsyncLayer:(_ASDisplayLayer *)layer asynchronously:(BOOL)asynchronously @@ -2617,8 +1797,8 @@ ASDISPLAYNODE_INLINE BOOL canUseViewAPI(ASDisplayNode *node, ASDisplayNode *subn } /// Returns if node is a member of a rasterized tree -ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) { - return (node.shouldRasterizeDescendants || (node.hierarchyState & ASHierarchyStateRasterized)); +ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) { + return (node.rasterizesSubtree || (node.hierarchyState & ASHierarchyStateRasterized)); } // NOTE: This method must be dealloc-safe (should not retain self). @@ -2655,8 +1835,8 @@ ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) { : oldSupernode.hierarchyState); // Rasterized state - BOOL parentWasOrIsRasterized = (newSupernode ? newSupernode.shouldRasterizeDescendants - : oldSupernode.shouldRasterizeDescendants); + BOOL parentWasOrIsRasterized = (newSupernode ? newSupernode.rasterizesSubtree + : oldSupernode.rasterizesSubtree); if (parentWasOrIsRasterized) { stateToEnterOrExit |= ASHierarchyStateRasterized; } @@ -2668,15 +1848,14 @@ ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) { // This is especially important as with automatic subnode management, adding subnodes can happen while a transition // is in fly if (ASHierarchyStateIncludesLayoutPending(stateToEnterOrExit)) { - int32_t pendingTransitionId = newSupernode.pendingTransitionID; + int32_t pendingTransitionId = newSupernode->_pendingTransitionID; if (pendingTransitionId != ASLayoutElementContextInvalidTransitionID) { { - ASDN::MutexLocker l(__instanceLock__); _pendingTransitionID = pendingTransitionId; // Propagate down the new pending transition id ASDisplayNodePerformBlockOnEverySubnode(self, NO, ^(ASDisplayNode * _Nonnull node) { - node.pendingTransitionID = pendingTransitionId; + node->_pendingTransitionID = pendingTransitionId; }); } } @@ -2710,7 +1889,12 @@ ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) { - (NSArray *)subnodes { ASDN::MutexLocker l(__instanceLock__); - return ([_subnodes copy] ?: @[]); + if (_cachedSubnodes == nil) { + _cachedSubnodes = [_subnodes copy]; + } else { + ASDisplayNodeAssert(ASObjectIsEqual(_cachedSubnodes, _subnodes), @"Expected _subnodes and _cachedSubnodes to have the same contents."); + } + return _cachedSubnodes ?: @[]; } /* @@ -2726,6 +1910,7 @@ ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) { - (void)_insertSubnode:(ASDisplayNode *)subnode atSubnodeIndex:(NSInteger)subnodeIndex sublayerIndex:(NSInteger)sublayerIndex andRemoveSubnode:(ASDisplayNode *)oldSubnode { ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__); + as_log_verbose(ASNodeLog(), "Insert subnode %@ at index %zd of %@ and remove subnode %@", subnode, subnodeIndex, self, oldSubnode); if (subnode == nil || subnode == self) { ASDisplayNodeFailAssert(@"Cannot insert a nil subnode or self as subnode"); @@ -2742,6 +1927,12 @@ ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) { return; } + BOOL isRasterized = subtreeIsRasterized(self); + if (isRasterized && subnode.nodeLoaded) { + ASDisplayNodeFailAssert(@"Cannot add loaded node %@ to rasterized subtree of node %@", ASObjectDescriptionMakeTiny(subnode), ASObjectDescriptionMakeTiny(self)); + return; + } + __instanceLock__.lock(); NSUInteger subnodesCount = _subnodes.count; __instanceLock__.unlock(); @@ -2765,21 +1956,17 @@ ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) { _subnodes = [[NSMutableArray alloc] init]; } [_subnodes insertObject:subnode atIndex:subnodeIndex]; + _cachedSubnodes = nil; __instanceLock__.unlock(); // This call will apply our .hierarchyState to the new subnode. // If we are a managed hierarchy, as in ASCellNode trees, it will also apply our .interfaceState. [subnode _setSupernode:self]; - // If this subnode will be rasterized, update its hierarchy state & enter hierarchy if needed - if (nodeIsInRasterizedTree(self)) { - ASDisplayNodePerformBlockOnEveryNodeBFS(subnode, ^(ASDisplayNode * _Nonnull node) { - [node enterHierarchyState:ASHierarchyStateRasterized]; - if (node.isNodeLoaded) { - [node __unloadNode]; - } - }); - if (self.isInHierarchy) { + // If this subnode will be rasterized, enter hierarchy if needed + // TODO: Move this into _setSupernode: ? + if (isRasterized) { + if (self.inHierarchy) { [subnode __enterHierarchy]; } } else if (self.nodeLoaded) { @@ -2824,9 +2011,8 @@ ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) { - (void)addSubnode:(ASDisplayNode *)subnode { - ASDisplayNodeLogEvent(self, @"addSubnode: %@", subnode); - // TODO: 2.0 Conversion: Reenable and fix within product code - //ASDisplayNodeAssert(self.automaticallyManagesSubnodes == NO, @"Attempt to manually add subnode to node with automaticallyManagesSubnodes=YES. Node: %@", subnode); + ASDisplayNodeLogEvent(self, @"addSubnode: %@ with automaticallyManagesSubnodes: %@", + subnode, self.automaticallyManagesSubnodes ? @"YES" : @"NO"); [self _addSubnode:subnode]; } @@ -2876,9 +2062,8 @@ ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) { - (void)replaceSubnode:(ASDisplayNode *)oldSubnode withSubnode:(ASDisplayNode *)replacementSubnode { - ASDisplayNodeLogEvent(self, @"replaceSubnode: %@ withSubnode:%@", oldSubnode, replacementSubnode); - // TODO: 2.0 Conversion: Reenable and fix within product code - //ASDisplayNodeAssert(self.automaticallyManagesSubnodes == NO, @"Attempt to manually replace old node with replacement node to node with automaticallyManagesSubnodes=YES. Old Node: %@, replacement node: %@", oldSubnode, replacementSubnode); + ASDisplayNodeLogEvent(self, @"replaceSubnode: %@ withSubnode: %@ with automaticallyManagesSubnodes: %@", + oldSubnode, replacementSubnode, self.automaticallyManagesSubnodes ? @"YES" : @"NO"); [self _replaceSubnode:oldSubnode withSubnode:replacementSubnode]; } @@ -2908,7 +2093,7 @@ ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) { // Don't bother figuring out the sublayerIndex if in a rasterized subtree, because there are no layers in the // hierarchy and none of this could possibly work. - if (nodeIsInRasterizedTree(self) == NO) { + if (subtreeIsRasterized(self) == NO) { if (_layer) { sublayerIndex = [_layer.sublayers indexOfObjectIdenticalTo:oldSubnode.layer]; ASDisplayNodeAssert(sublayerIndex != NSNotFound, @"Somehow oldSubnode's supernode is self, yet we could not find it in our layers to replace"); @@ -2924,9 +2109,8 @@ ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) { - (void)insertSubnode:(ASDisplayNode *)subnode belowSubnode:(ASDisplayNode *)below { - ASDisplayNodeLogEvent(self, @"insertSubnode: %@ belowSubnode:%@", subnode, below); - // TODO: 2.0 Conversion: Reenable and fix within product code - //ASDisplayNodeAssert(self.automaticallyManagesSubnodes == NO, @"Attempt to manually insert subnode to node with automaticallyManagesSubnodes=YES. Node: %@", subnode); + ASDisplayNodeLogEvent(self, @"insertSubnode: %@ belowSubnode: %@ with automaticallyManagesSubnodes: %@", + subnode, below, self.automaticallyManagesSubnodes ? @"YES" : @"NO"); [self _insertSubnode:subnode belowSubnode:below]; } @@ -2954,7 +2138,7 @@ ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) { // Don't bother figuring out the sublayerIndex if in a rasterized subtree, because there are no layers in the // hierarchy and none of this could possibly work. - if (nodeIsInRasterizedTree(self) == NO) { + if (subtreeIsRasterized(self) == NO) { if (_layer) { belowSublayerIndex = [_layer.sublayers indexOfObjectIdenticalTo:below.layer]; ASDisplayNodeAssert(belowSublayerIndex != NSNotFound, @"Somehow below's supernode is self, yet we could not find it in our layers to reference"); @@ -2988,9 +2172,8 @@ ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) { - (void)insertSubnode:(ASDisplayNode *)subnode aboveSubnode:(ASDisplayNode *)above { - ASDisplayNodeLogEvent(self, @"insertSubnode: %@ abodeSubnode: %@", subnode, above); - // TODO: 2.0 Conversion: Reenable and fix within product code - //ASDisplayNodeAssert(self.automaticallyManagesSubnodes == NO, @"Attempt to manually insert subnode to node with automaticallyManagesSubnodes=YES. Node: %@", subnode); + ASDisplayNodeLogEvent(self, @"insertSubnode: %@ abodeSubnode: %@ with automaticallyManagesSubnodes: %@", + subnode, above, self.automaticallyManagesSubnodes ? @"YES" : @"NO"); [self _insertSubnode:subnode aboveSubnode:above]; } @@ -3018,7 +2201,7 @@ ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) { // Don't bother figuring out the sublayerIndex if in a rasterized subtree, because there are no layers in the // hierarchy and none of this could possibly work. - if (nodeIsInRasterizedTree(self) == NO) { + if (subtreeIsRasterized(self) == NO) { if (_layer) { aboveSublayerIndex = [_layer.sublayers indexOfObjectIdenticalTo:above.layer]; ASDisplayNodeAssert(aboveSublayerIndex != NSNotFound, @"Somehow above's supernode is self, yet we could not find it in our layers to replace"); @@ -3050,9 +2233,8 @@ ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) { - (void)insertSubnode:(ASDisplayNode *)subnode atIndex:(NSInteger)idx { - ASDisplayNodeLogEvent(self, @"insertSubnode: %@ atIndex: %td", subnode, idx); - // TODO: 2.0 Conversion: Reenable and fix within product code - //ASDisplayNodeAssert(self.automaticallyManagesSubnodes == NO, @"Attempt to manually insert subnode to node with automaticallyManagesSubnodes=YES. Node: %@", subnode); + ASDisplayNodeLogEvent(self, @"insertSubnode: %@ atIndex: %td with automaticallyManagesSubnodes: %@", + subnode, idx, self.automaticallyManagesSubnodes ? @"YES" : @"NO"); [self _insertSubnode:subnode atIndex:idx]; } @@ -3076,7 +2258,7 @@ ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) { // Don't bother figuring out the sublayerIndex if in a rasterized subtree, because there are no layers in the // hierarchy and none of this could possibly work. - if (nodeIsInRasterizedTree(self) == NO) { + if (subtreeIsRasterized(self) == NO) { // Account for potentially having other subviews if (_layer && idx == 0) { sublayerIndex = 0; @@ -3105,6 +2287,7 @@ ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) { __instanceLock__.lock(); [_subnodes removeObjectIdenticalTo:subnode]; + _cachedSubnodes = nil; __instanceLock__.unlock(); [subnode _setSupernode:nil]; @@ -3112,9 +2295,8 @@ ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) { - (void)removeFromSupernode { - // TODO: 2.0 Conversion: Reenable and fix within product code - //ASDisplayNodeAssert(self.supernode.automaticallyManagesSubnodes == NO, @"Attempt to manually remove subnode from node with automaticallyManagesSubnodes=YES. Node: %@", self); - + ASDisplayNodeLogEvent(self, @"removeFromSupernode with automaticallyManagesSubnodes: %@", + self.automaticallyManagesSubnodes ? @"YES" : @"NO"); [self _removeFromSupernode]; } @@ -3154,6 +2336,12 @@ ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) { - (void)_removeFromSupernode:(ASDisplayNode *)supernode view:(UIView *)view layer:(CALayer *)layer { + // Note: we continue even if supernode is nil to ensure view/layer are removed from hierarchy. + + if (supernode != nil) { + as_log_verbose(ASNodeLog(), "Remove %@ from supernode %@", self, supernode); + } + // Clear supernode's reference to us before removing the view from the hierarchy, as _ASDisplayView // will trigger us to clear our _supernode pointer in willMoveToSuperview:nil. // This may result in removing the last strong reference, triggering deallocation after this method. @@ -3251,7 +2439,7 @@ ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) { if (_placeholderImage) { BOOL stretchable = !UIEdgeInsetsEqualToEdgeInsets(_placeholderImage.capInsets, UIEdgeInsetsZero); if (stretchable) { - ASDisplayNodeSetupLayerContentsWithResizableImage(_placeholderLayer, _placeholderImage); + ASDisplayNodeSetResizableContents(_placeholderLayer, _placeholderImage); } else { _placeholderLayer.contentsScale = self.contentsScale; _placeholderLayer.contents = (id)_placeholderImage.CGImage; @@ -3402,7 +2590,7 @@ ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) { // Entered rasterization state. if (newState & ASHierarchyStateRasterized) { - ASDisplayNodeAssert(_flags.synchronous == NO, @"Node created using -initWithViewBlock:/-initWithLayerBlock: cannot be added to subtree of node with shouldRasterizeDescendants=YES. Node: %@", self); + ASDisplayNodeAssert(checkFlag(Synchronous) == NO, @"Node created using -initWithViewBlock:/-initWithLayerBlock: cannot be added to subtree of node with subtree rasterization enabled. Node: %@", self); } // Entered or exited range managed state. @@ -3505,7 +2693,7 @@ ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) { if (interfaceState == ASInterfaceStateNone) { return; // This method is a no-op with a 0-bitfield argument, so don't bother recursing. } - ASDisplayNodeLogEvent(self, @"%@ %@", NSStringFromSelector(_cmd), NSStringFromASInterfaceState(interfaceState)); + ASDisplayNodeLogEvent(self, @"%s %@", sel_getName(_cmd), NSStringFromASInterfaceState(interfaceState)); ASDisplayNodePerformBlockOnEveryNode(nil, self, YES, ^(ASDisplayNode *node) { node.interfaceState &= (~interfaceState); }); @@ -3513,6 +2701,8 @@ ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) { - (void)recursivelySetInterfaceState:(ASInterfaceState)newInterfaceState { + as_activity_create_for_scope("Recursively set interface state"); + // Instead of each node in the recursion assuming it needs to schedule itself for display, // setInterfaceState: skips this when handling range-managed nodes (our whole subtree has this set). // If our range manager intends for us to be displayed right now, and didn't before, get started! @@ -3636,7 +2826,14 @@ ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) { } } - ASDisplayNodeLogEvent(self, @"interfaceStateDidChange: %@, old: %@", NSStringFromASInterfaceState(newState), NSStringFromASInterfaceState(oldState)); + // Log this change, unless it's just the node going from {} -> {Measure} because that change happens + // for all cell nodes and it isn't currently meaningful. + BOOL measureChangeOnly = ((oldState | newState) == ASInterfaceStateMeasureLayout); + if (!measureChangeOnly) { + as_log_verbose(ASNodeLog(), "%s %@ %@", sel_getName(_cmd), NSStringFromASInterfaceStateChange(oldState, newState), self); + } + + ASDisplayNodeLogEvent(self, @"interfaceStateDidChange: %@", NSStringFromASInterfaceStateChange(oldState, newState)); [self interfaceStateDidChange:newState fromState:oldState]; } @@ -3666,6 +2863,9 @@ ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) { ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__); [_interfaceStateDelegate didEnterVisibleState]; +#if AS_ENABLE_TIPS + [ASTipsController.shared nodeDidAppear:self]; +#endif } - (void)didExitVisibleState @@ -3896,7 +3096,7 @@ ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) { if (_flags.layerBacked) { [_pendingViewState applyToLayer:self.layer]; } else { - BOOL specialPropertiesHandling = ASDisplayNodeNeedsSpecialPropertiesHandlingForFlags(_flags); + BOOL specialPropertiesHandling = ASDisplayNodeNeedsSpecialPropertiesHandling(checkFlag(Synchronous), _flags.layerBacked); [_pendingViewState applyToView:self.view withSpecialPropertiesHandling:specialPropertiesHandling]; } @@ -3982,9 +3182,19 @@ ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) { - (NSMutableArray *)propertiesForDescription { NSMutableArray *result = [NSMutableArray array]; - if (self.debugName.length > 0) { - [result addObject:@{ @"debugName" : ASStringWithQuotesIfMultiword(self.debugName) }]; + ASPushMainThreadAssertionsDisabled(); + + NSString *debugName = self.debugName; + if (debugName.length > 0) { + [result addObject:@{ (id)kCFNull : ASStringWithQuotesIfMultiword(debugName) }]; } + + NSString *axId = self.accessibilityIdentifier; + if (axId.length > 0) { + [result addObject:@{ (id)kCFNull : ASStringWithQuotesIfMultiword(axId) }]; + } + + ASPopMainThreadAssertionsDisabled(); return result; } @@ -3995,7 +3205,10 @@ ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) { if (self.debugName.length > 0) { [result addObject:@{ @"debugName" : ASStringWithQuotesIfMultiword(self.debugName)}]; } - + if (self.accessibilityIdentifier.length > 0) { + [result addObject:@{ @"axId": ASStringWithQuotesIfMultiword(self.accessibilityIdentifier) }]; + } + CGRect windowFrame = [self _frameInWindow]; if (CGRectIsNull(windowFrame) == NO) { [result addObject:@{ @"frameInWindow" : [NSValue valueWithCGRect:windowFrame] }]; @@ -4025,10 +3238,9 @@ ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) { [result addObject:@{ @"alpha" : @(_pendingViewState.alpha) }]; [result addObject:@{ @"frame" : [NSValue valueWithCGRect:_pendingViewState.frame] }]; } - #ifndef MINIMAL_ASDK - // Check supernode so that if we are cell node we don't find self. - ASCellNode *cellNode = ASDisplayNodeFindFirstSupernodeOfClass(self.supernode, [ASCellNode class]); + // Check supernode so that if we are a cell node we don't find self. + ASCellNode *cellNode = [self supernodeOfClass:[ASCellNode class] includingSelf:NO]; if (cellNode != nil) { [result addObject:@{ @"cellNode" : ASObjectDescriptionMakeTiny(cellNode) }]; } @@ -4065,7 +3277,10 @@ ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) { - (NSString *)debugDescription { - return ASObjectDescriptionMake(self, [self propertiesForDebugDescription]); + ASPushMainThreadAssertionsDisabled(); + auto result = ASObjectDescriptionMake(self, [self propertiesForDebugDescription]); + ASPopMainThreadAssertionsDisabled(); + return result; } // This should only be called for debugging. It's not thread safe and it doesn't assert. @@ -4089,53 +3304,13 @@ ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) { } } -#pragma mark - NSFastEnumeration - -- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id __unsafe_unretained _Nullable [_Nonnull])buffer count:(NSUInteger)len -{ - return [self.subnodes countByEnumeratingWithState:state objects:buffer count:len]; -} - -#pragma mark - ASPrimitiveTraitCollection - -- (ASPrimitiveTraitCollection)primitiveTraitCollection -{ - ASDN::MutexLocker l(__instanceLock__); - return _primitiveTraitCollection; -} - -- (void)setPrimitiveTraitCollection:(ASPrimitiveTraitCollection)traitCollection -{ - __instanceLock__.lock(); - if (ASPrimitiveTraitCollectionIsEqualToASPrimitiveTraitCollection(traitCollection, _primitiveTraitCollection) == NO) { - _primitiveTraitCollection = traitCollection; - ASDisplayNodeLogEvent(self, @"asyncTraitCollectionDidChange: %@", NSStringFromASPrimitiveTraitCollection(traitCollection)); - __instanceLock__.unlock(); - - [self asyncTraitCollectionDidChange]; - return; - } - - __instanceLock__.unlock(); -} - -- (ASTraitCollection *)asyncTraitCollection -{ - ASDN::MutexLocker l(__instanceLock__); - return [ASTraitCollection traitCollectionWithASPrimitiveTraitCollection:self.primitiveTraitCollection]; -} +#pragma mark - Trait Collection Hooks - (void)asyncTraitCollectionDidChange { // Subclass override } -ASPrimitiveTraitCollectionDeprecatedImplementation - -#pragma mark - ASLayoutElementStyleExtensibility - -ASLayoutElementStyleExtensibilityForwarding - #if TARGET_OS_TV #pragma mark - UIFocusEnvironment Protocol (tvOS) @@ -4169,31 +3344,28 @@ ASLayoutElementStyleExtensibilityForwarding } #endif -#pragma mark - Deprecated - -// This methods cannot be moved into the category ASDisplayNode (Deprecated). So they need to be declared in ASDisplayNode until removed - -- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize -{ - return [self layoutThatFits:constrainedSize parentSize:constrainedSize.max]; -} - -- (BOOL)usesImplicitHierarchyManagement -{ - return self.automaticallyManagesSubnodes; -} - -- (void)setUsesImplicitHierarchyManagement:(BOOL)enabled -{ - self.automaticallyManagesSubnodes = enabled; -} - @end #pragma mark - ASDisplayNode (Debugging) @implementation ASDisplayNode (Debugging) ++ (void)setShouldStoreUnflattenedLayouts:(BOOL)shouldStore +{ + storesUnflattenedLayouts.store(shouldStore); +} + ++ (BOOL)shouldStoreUnflattenedLayouts +{ + return storesUnflattenedLayouts.load(); +} + +- (ASLayout *)unflattenedCalculatedLayout +{ + ASDN::MutexLocker l(__instanceLock__); + return _unflattenedLayout; +} + - (NSString *)displayNodeRecursiveDescription { return [self _recursiveDescriptionHelperWithIndent:@""]; @@ -4208,22 +3380,6 @@ ASLayoutElementStyleExtensibilityForwarding return subtree; } -#pragma mark - ASLayoutElementAsciiArtProtocol - -- (NSString *)asciiArtString -{ - return [ASLayoutSpec asciiArtStringForChildren:@[] parentName:[self asciiArtName]]; -} - -- (NSString *)asciiArtName -{ - NSString *string = NSStringFromClass([self class]); - if (_debugName) { - string = [string stringByAppendingString:[NSString stringWithFormat:@"\"%@\"",_debugName]]; - } - return string; -} - @end #pragma mark - ASDisplayNode UIKit / CA Categories @@ -4331,6 +3487,16 @@ static const char *ASDisplayNodeAssociatedNodeKey = "ASAssociatedNode"; return isPoints ? CGSizeMake(size.width.value, size.height.value) : CGSizeZero; } +- (BOOL)usesImplicitHierarchyManagement +{ + return self.automaticallyManagesSubnodes; +} + +- (void)setUsesImplicitHierarchyManagement:(BOOL)enabled +{ + self.automaticallyManagesSubnodes = enabled; +} + - (CGSize)measure:(CGSize)constrainedSize { return [self layoutThatFits:ASSizeRangeMake(CGSizeZero, constrainedSize)].size; diff --git a/Source/ASDisplayNodeExtras.h b/Source/ASDisplayNodeExtras.h index c8e6c9483a..a5cc344bcb 100644 --- a/Source/ASDisplayNodeExtras.h +++ b/Source/ASDisplayNodeExtras.h @@ -1,11 +1,18 @@ // // ASDisplayNodeExtras.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -20,13 +27,21 @@ * for the nodes will be set to `MYButtonNode.titleNode` and `MYButtonNode.countNode`. */ #if DEBUG + #define ASSetDebugName(node, format, ...) node.debugName = [NSString stringWithFormat:format, __VA_ARGS__] #define ASSetDebugNames(...) _ASSetDebugNames(self.class, @"" # __VA_ARGS__, __VA_ARGS__, nil) #else + #define ASSetDebugName(node, name) #define ASSetDebugNames(...) #endif /// For deallocation of objects on the main thread across multiple run loops. +#ifdef __cplusplus +extern "C" { +#endif extern void ASPerformMainThreadDeallocation(_Nullable id object); +#ifdef __cplusplus +} +#endif // Because inline methods can't be extern'd and need to be part of the translation unit of code // that compiles with them to actually inline, we both declare and define these in the header. @@ -71,6 +86,30 @@ __unused static NSString * _Nonnull NSStringFromASInterfaceState(ASInterfaceStat return [NSString stringWithFormat:@"{ %@ }", [states componentsJoinedByString:@" | "]]; } +#define INTERFACE_STATE_DELTA(Name) ({ \ + if ((oldState & ASInterfaceState##Name) != (newState & ASInterfaceState##Name)) { \ + [changes appendFormat:@"%c%s ", (newState & ASInterfaceState##Name ? '+' : '-'), #Name]; \ + } \ +}) + +/// e.g. { +Visible, -Preload } (although that should never actually happen.) +/// NOTE: Changes to MeasureLayout state don't really mean anything so we omit them for now. +__unused static NSString * _Nonnull NSStringFromASInterfaceStateChange(ASInterfaceState oldState, ASInterfaceState newState) +{ + if (oldState == newState) { + return @"{ }"; + } + + NSMutableString *changes = [NSMutableString stringWithString:@"{ "]; + INTERFACE_STATE_DELTA(Preload); + INTERFACE_STATE_DELTA(Display); + INTERFACE_STATE_DELTA(Visible); + [changes appendString:@"}"]; + return changes; +} + +#undef INTERFACE_STATE_DELTA + NS_ASSUME_NONNULL_BEGIN ASDISPLAYNODE_EXTERN_C_BEGIN @@ -91,7 +130,7 @@ extern ASDisplayNode * _Nullable ASLayerToDisplayNode(CALayer * _Nullable layer) extern ASDisplayNode * _Nullable ASViewToDisplayNode(UIView * _Nullable view) AS_WARN_UNUSED_RESULT; /** - Given a node, returns the root of the node heirarchy (where supernode == nil) + Given a node, returns the root of the node hierarchy (where supernode == nil) */ extern ASDisplayNode *ASDisplayNodeUltimateParentOfNode(ASDisplayNode *node) AS_WARN_UNUSED_RESULT; @@ -118,12 +157,12 @@ extern void ASDisplayNodePerformBlockOnEverySubnode(ASDisplayNode *node, BOOL tr /** Given a display node, traverses up the layer tree hierarchy, returning the first display node that passes block. */ -extern ASDisplayNode * _Nullable ASDisplayNodeFindFirstSupernode(ASDisplayNode * _Nullable node, BOOL (^block)(ASDisplayNode *node)) AS_WARN_UNUSED_RESULT; +extern ASDisplayNode * _Nullable ASDisplayNodeFindFirstSupernode(ASDisplayNode * _Nullable node, BOOL (^block)(ASDisplayNode *node)) AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED_MSG("Use the `supernodes` property instead."); /** Given a display node, traverses up the layer tree hierarchy, returning the first display node of kind class. */ -extern __kindof ASDisplayNode * _Nullable ASDisplayNodeFindFirstSupernodeOfClass(ASDisplayNode *start, Class c) AS_WARN_UNUSED_RESULT; +extern __kindof ASDisplayNode * _Nullable ASDisplayNodeFindFirstSupernodeOfClass(ASDisplayNode *start, Class c) AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED_MSG("Use the `supernodeOfClass:includingSelf:` method instead."); /** * Given a layer, find the window it lives in, if any. diff --git a/Source/ASDisplayNodeExtras.mm b/Source/ASDisplayNodeExtras.mm index 13f8c7b797..279687662a 100644 --- a/Source/ASDisplayNodeExtras.mm +++ b/Source/ASDisplayNodeExtras.mm @@ -1,16 +1,24 @@ // // ASDisplayNodeExtras.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import #import #import +#import #import #import @@ -90,7 +98,7 @@ extern void ASDisplayNodePerformBlockOnEveryNode(CALayer * _Nullable layer, ASDi layer = node.layer; } - if (traverseSublayers && layer && node.shouldRasterizeDescendants == NO) { + if (traverseSublayers && layer && node.rasterizesSubtree == NO) { /// NOTE: The docs say `sublayers` returns a copy, but it does not. /// See: http://stackoverflow.com/questions/14854480/collection-calayerarray-0x1ed8faa0-was-mutated-while-being-enumerated for (CALayer *sublayer in [[layer sublayers] copy]) { @@ -131,24 +139,21 @@ extern void ASDisplayNodePerformBlockOnEverySubnode(ASDisplayNode *node, BOOL tr ASDisplayNode *ASDisplayNodeFindFirstSupernode(ASDisplayNode *node, BOOL (^block)(ASDisplayNode *node)) { - CALayer *layer = node.layer; - - while (layer) { - node = ASLayerToDisplayNode(layer); - if (block(node)) { - return node; + // This function has historically started with `self` but the name suggests + // that it wouldn't. Perhaps we should change the behavior. + for (ASDisplayNode *ancestor in node.supernodesIncludingSelf) { + if (block(ancestor)) { + return ancestor; } - layer = layer.superlayer; } - return nil; } __kindof ASDisplayNode *ASDisplayNodeFindFirstSupernodeOfClass(ASDisplayNode *start, Class c) { - return ASDisplayNodeFindFirstSupernode(start, ^(ASDisplayNode *n) { - return [n isKindOfClass:c]; - }); + // This function has historically started with `self` but the name suggests + // that it wouldn't. Perhaps we should change the behavior. + return [start supernodeOfClass:c includingSelf:YES]; } static void _ASCollectDisplayNodes(NSMutableArray *array, CALayer *layer) diff --git a/Source/ASEditableTextNode.h b/Source/ASEditableTextNode.h index 71d4a89da1..db310f06b9 100644 --- a/Source/ASEditableTextNode.h +++ b/Source/ASEditableTextNode.h @@ -1,11 +1,18 @@ // // ASEditableTextNode.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/ASEditableTextNode.mm b/Source/ASEditableTextNode.mm index dfd3b79d57..fd35ea5e1f 100644 --- a/Source/ASEditableTextNode.mm +++ b/Source/ASEditableTextNode.mm @@ -1,11 +1,18 @@ // // ASEditableTextNode.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -306,6 +313,11 @@ [super setLayerBacked:layerBacked]; } +- (BOOL)supportsLayerBacking +{ + return NO; +} + #pragma mark - Configuration @synthesize delegate = _delegate; diff --git a/Source/ASImageNode+AnimatedImage.mm b/Source/ASImageNode+AnimatedImage.mm index 39a0401063..11117f1947 100644 --- a/Source/ASImageNode+AnimatedImage.mm +++ b/Source/ASImageNode+AnimatedImage.mm @@ -1,13 +1,18 @@ // // ASImageNode+AnimatedImage.mm -// AsyncDisplayKit -// -// Created by Garrett Moon on 3/22/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -24,6 +29,7 @@ #import #import +#define ASAnimatedImageDebug 0 #ifndef MINIMAL_ASDK @interface ASNetworkImageNode (Private) @@ -49,6 +55,7 @@ NSString *const ASAnimatedImageDefaultRunLoopMode = NSRunLoopCommonModes; return; } + id previousAnimatedImage = _animatedImage; _animatedImage = animatedImage; if (animatedImage != nil) { @@ -69,6 +76,13 @@ NSString *const ASAnimatedImageDefaultRunLoopMode = NSRunLoopCommonModes; }; } } + + [self animatedImageSet:_animatedImage previousAnimatedImage:previousAnimatedImage]; +} + +- (void)animatedImageSet:(id )newAnimatedImage previousAnimatedImage:(id )previousAnimatedImage +{ + //Subclasses may override } - (id )animatedImage @@ -301,6 +315,7 @@ NSString *const ASAnimatedImageDefaultRunLoopMode = NSRunLoopCommonModes; self.lastDisplayLinkFire = 0; } else { self.contents = (__bridge id)frameImage; + [self displayDidFinish]; } } diff --git a/Source/ASImageNode.h b/Source/ASImageNode.h index 8f144e8dcb..5d3e09718c 100644 --- a/Source/ASImageNode.h +++ b/Source/ASImageNode.h @@ -1,11 +1,18 @@ // // ASImageNode.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -143,6 +150,9 @@ typedef UIImage * _Nullable (^asimagenode_modification_block_t)(UIImage *image); * * @discussion Set this to an object which conforms to ASAnimatedImageProtocol * to have the ASImageNode playback an animated image. + * @warning this method should not be overridden, it may not always be called as + * another method is used internally. If you need to know when the animatedImage + * is set, override @c animatedImageSet:previousAnimatedImage: */ @property (nullable, nonatomic, strong) id animatedImage; @@ -163,6 +173,15 @@ typedef UIImage * _Nullable (^asimagenode_modification_block_t)(UIImage *image); */ @property (nonatomic, strong) NSString *animatedImageRunLoopMode; +/** + * @abstract Method called when animated image has been set + * + * @discussion This method is for subclasses to override so they can know if an animated image + * has been set on the node. + * @warning this method is called with the node's lock held. + */ +- (void)animatedImageSet:(id )newAnimatedImage previousAnimatedImage:(id )previousAnimatedImage; + @end @interface ASImageNode (Unavailable) diff --git a/Source/ASImageNode.mm b/Source/ASImageNode.mm index 1bd30540fd..a56333d3c2 100644 --- a/Source/ASImageNode.mm +++ b/Source/ASImageNode.mm @@ -1,11 +1,18 @@ // // ASImageNode.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -25,7 +32,7 @@ #import #import #import -#import +#import #import #import #import @@ -35,32 +42,45 @@ #include -struct ASImageNodeDrawParameters { - BOOL opaque; - CGRect bounds; - CGFloat contentsScale; - UIColor *backgroundColor; - UIViewContentMode contentMode; - BOOL cropEnabled; - BOOL forceUpscaling; - CGSize forcedSize; - CGRect cropRect; - CGRect cropDisplayBounds; - asimagenode_modification_block_t imageModificationBlock; -}; +typedef void (^ASImageNodeDrawParametersBlock)(ASWeakMapEntry *entry); + +@interface ASImageNodeDrawParameters : NSObject { +@package + UIImage *_image; + BOOL _opaque; + CGRect _bounds; + CGFloat _contentsScale; + UIColor *_backgroundColor; + UIViewContentMode _contentMode; + BOOL _cropEnabled; + BOOL _forceUpscaling; + CGSize _forcedSize; + CGRect _cropRect; + CGRect _cropDisplayBounds; + asimagenode_modification_block_t _imageModificationBlock; + ASDisplayNodeContextModifier _willDisplayNodeContentWithRenderingContext; + ASDisplayNodeContextModifier _didDisplayNodeContentWithRenderingContext; + ASImageNodeDrawParametersBlock _didDrawBlock; +} + +@end + +@implementation ASImageNodeDrawParameters + +@end /** * Contains all data that is needed to generate the content bitmap. */ -@interface ASImageNodeContentsKey : NSObject {} +@interface ASImageNodeContentsKey : NSObject @property (nonatomic, strong) UIImage *image; @property CGSize backingSize; @property CGRect imageDrawRect; @property BOOL isOpaque; @property (nonatomic, strong) UIColor *backgroundColor; -@property (nonatomic, copy) ASDisplayNodeContextModifier preContextBlock; -@property (nonatomic, copy) ASDisplayNodeContextModifier postContextBlock; +@property (nonatomic, copy) ASDisplayNodeContextModifier willDisplayNodeContentWithRenderingContext; +@property (nonatomic, copy) ASDisplayNodeContextModifier didDisplayNodeContentWithRenderingContext; @property (nonatomic, copy) asimagenode_modification_block_t imageModificationBlock; @end @@ -84,8 +104,8 @@ struct ASImageNodeDrawParameters { && CGRectEqualToRect(_imageDrawRect, other.imageDrawRect) && _isOpaque == other.isOpaque && [_backgroundColor isEqual:other.backgroundColor] - && _preContextBlock == other.preContextBlock - && _postContextBlock == other.postContextBlock + && _willDisplayNodeContentWithRenderingContext == other.willDisplayNodeContentWithRenderingContext + && _didDisplayNodeContentWithRenderingContext == other.didDisplayNodeContentWithRenderingContext && _imageModificationBlock == other.imageModificationBlock; } else { return NO; @@ -94,25 +114,29 @@ struct ASImageNodeDrawParameters { - (NSUInteger)hash { - NSUInteger subhashes[] = { - // Profiling shows that the work done in UIImage's `hash` is on the order of 0.005ms on an A5 processor - // and isn't proportional to the size of the image. - [_image hash], - - // TODO: Hashing the floats in a CGRect or CGSize is tricky. Equality of floats is - // fuzzy, but it's a 100% requirement that two equal values must produce an identical hash value. - // Until there's a robust solution for hashing floats, leave all float values out of the hash. - // This may lead to a greater number of isEqual comparisons but does not comprimise correctness. - //AS::hash()(_backingSize), - //AS::hash()(_imageDrawRect), - - AS::hash()(_isOpaque), - [_backgroundColor hash], - AS::hash()((void*)_preContextBlock), - AS::hash()((void*)_postContextBlock), - AS::hash()((void*)_imageModificationBlock), +#pragma clang diagnostic push +#pragma clang diagnostic warning "-Wpadded" + struct { + NSUInteger imageHash; + CGSize backingSize; + CGRect imageDrawRect; + NSInteger isOpaque; + NSUInteger backgroundColorHash; + void *willDisplayNodeContentWithRenderingContext; + void *didDisplayNodeContentWithRenderingContext; + void *imageModificationBlock; +#pragma clang diagnostic pop + } data = { + _image.hash, + _backingSize, + _imageDrawRect, + _isOpaque, + _backgroundColor.hash, + (void *)_willDisplayNodeContentWithRenderingContext, + (void *)_didDisplayNodeContentWithRenderingContext, + (void *)_imageModificationBlock }; - return ASIntegerArrayHash(subhashes, sizeof(subhashes) / sizeof(subhashes[0])); + return ASHashBytes(&data, sizeof(data)); } @end @@ -128,7 +152,6 @@ struct ASImageNodeDrawParameters { void (^_displayCompletionBlock)(BOOL canceled); // Drawing - ASImageNodeDrawParameters _drawParameter; ASTextNode *_debugLabelNode; // Cropping. @@ -142,17 +165,7 @@ struct ASImageNodeDrawParameters { @synthesize image = _image; @synthesize imageModificationBlock = _imageModificationBlock; -#pragma mark - NSObject - -+ (void)initialize -{ - [super initialize]; - - if (self != [ASImageNode class]) { - // Prevent custom drawing in subclasses - ASDisplayNodeAssert(!ASSubclassOverridesClassSelector([ASImageNode class], self, @selector(displayWithParameters:isCancelled:)), @"Subclass %@ must not override displayWithParameters:isCancelled: method. Custom drawing in %@ subclass is not supported.", NSStringFromClass(self), NSStringFromClass([ASImageNode class])); - } -} +#pragma mark - Lifecycle - (instancetype)init { @@ -185,6 +198,8 @@ struct ASImageNodeDrawParameters { [self invalidateAnimatedImage]; } +#pragma mark - Placeholder + - (UIImage *)placeholderImage { // FIXME: Replace this implementation with reusable CALayers that have .backgroundColor set. @@ -251,7 +266,7 @@ struct ASImageNodeDrawParameters { } return; } - + } else { self.contents = nil; } @@ -282,63 +297,61 @@ struct ASImageNodeDrawParameters { { ASDN::MutexLocker l(__instanceLock__); - _drawParameter = { - .bounds = self.bounds, - .opaque = self.opaque, - .contentsScale = self.contentsScaleForDisplay, - .backgroundColor = self.backgroundColor, - .contentMode = self.contentMode, - .cropEnabled = _cropEnabled, - .forceUpscaling = _forceUpscaling, - .forcedSize = _forcedSize, - .cropRect = _cropRect, - .cropDisplayBounds = _cropDisplayBounds, - .imageModificationBlock = _imageModificationBlock + ASImageNodeDrawParameters *drawParameters = [[ASImageNodeDrawParameters alloc] init]; + drawParameters->_image = [self _locked_Image]; + drawParameters->_bounds = [self threadSafeBounds]; + drawParameters->_opaque = self.opaque; + drawParameters->_contentsScale = _contentsScaleForDisplay; + drawParameters->_backgroundColor = self.backgroundColor; + drawParameters->_contentMode = self.contentMode; + drawParameters->_cropEnabled = _cropEnabled; + drawParameters->_forceUpscaling = _forceUpscaling; + drawParameters->_forcedSize = _forcedSize; + drawParameters->_cropRect = _cropRect; + drawParameters->_cropDisplayBounds = _cropDisplayBounds; + drawParameters->_imageModificationBlock = _imageModificationBlock; + drawParameters->_willDisplayNodeContentWithRenderingContext = _willDisplayNodeContentWithRenderingContext; + drawParameters->_didDisplayNodeContentWithRenderingContext = _didDisplayNodeContentWithRenderingContext; + + // Hack for now to retain the weak entry that was created while this drawing happened + drawParameters->_didDrawBlock = ^(ASWeakMapEntry *entry){ + ASDN::MutexLocker l(__instanceLock__); + _weakCacheEntry = entry; }; - return nil; + return drawParameters; } -- (NSDictionary *)debugLabelAttributes ++ (UIImage *)displayWithParameters:(id)parameter isCancelled:(asdisplaynode_iscancelled_block_t)isCancelled { - return @{ - NSFontAttributeName: [UIFont systemFontOfSize:15.0], - NSForegroundColorAttributeName: [UIColor redColor] - }; -} + ASImageNodeDrawParameters *drawParameter = (ASImageNodeDrawParameters *)parameter; -- (UIImage *)displayWithParameters:(id *)parameter isCancelled:(asdisplaynode_iscancelled_block_t)isCancelled -{ - UIImage *image = self.image; + UIImage *image = drawParameter->_image; if (image == nil) { return nil; } - if (_displayWithoutProcessing) { + if (true) { return image; } - __instanceLock__.lock(); - ASImageNodeDrawParameters drawParameter = _drawParameter; - __instanceLock__.unlock(); - - CGRect drawParameterBounds = drawParameter.bounds; - BOOL forceUpscaling = drawParameter.forceUpscaling; - CGSize forcedSize = drawParameter.forcedSize; - BOOL cropEnabled = drawParameter.cropEnabled; - BOOL isOpaque = drawParameter.opaque; - UIColor *backgroundColor = drawParameter.backgroundColor; - UIViewContentMode contentMode = drawParameter.contentMode; - CGFloat contentsScale = drawParameter.contentsScale; - CGRect cropDisplayBounds = drawParameter.cropDisplayBounds; - CGRect cropRect = drawParameter.cropRect; - asimagenode_modification_block_t imageModificationBlock = drawParameter.imageModificationBlock; + CGRect drawParameterBounds = drawParameter->_bounds; + BOOL forceUpscaling = drawParameter->_forceUpscaling; + CGSize forcedSize = drawParameter->_forcedSize; + BOOL cropEnabled = drawParameter->_cropEnabled; + BOOL isOpaque = drawParameter->_opaque; + UIColor *backgroundColor = drawParameter->_backgroundColor; + UIViewContentMode contentMode = drawParameter->_contentMode; + CGFloat contentsScale = drawParameter->_contentsScale; + CGRect cropDisplayBounds = drawParameter->_cropDisplayBounds; + CGRect cropRect = drawParameter->_cropRect; + asimagenode_modification_block_t imageModificationBlock = drawParameter->_imageModificationBlock; + ASDisplayNodeContextModifier willDisplayNodeContentWithRenderingContext = drawParameter->_willDisplayNodeContentWithRenderingContext; + ASDisplayNodeContextModifier didDisplayNodeContentWithRenderingContext = drawParameter->_didDisplayNodeContentWithRenderingContext; BOOL hasValidCropBounds = cropEnabled && !CGRectIsEmpty(cropDisplayBounds); CGRect bounds = (hasValidCropBounds ? cropDisplayBounds : drawParameterBounds); - ASDisplayNodeContextModifier preContextBlock = self.willDisplayNodeContentWithRenderingContext; - ASDisplayNodeContextModifier postContextBlock = self.didDisplayNodeContentWithRenderingContext; ASDisplayNodeAssert(contentsScale > 0, @"invalid contentsScale at display time"); @@ -355,19 +368,6 @@ struct ASImageNodeDrawParameters { CGSize imageSizeInPixels = CGSizeMake(imageSize.width * image.scale, imageSize.height * image.scale); CGSize boundsSizeInPixels = CGSizeMake(std::floor(bounds.size.width * contentsScale), std::floor(bounds.size.height * contentsScale)); - if (_debugLabelNode) { - CGFloat pixelCountRatio = (imageSizeInPixels.width * imageSizeInPixels.height) / (boundsSizeInPixels.width * boundsSizeInPixels.height); - if (pixelCountRatio != 1.0) { - NSString *scaleString = [NSString stringWithFormat:@"%.2fx", pixelCountRatio]; - _debugLabelNode.attributedText = [[NSAttributedString alloc] initWithString:scaleString attributes:[self debugLabelAttributes]]; - _debugLabelNode.hidden = NO; - [self setNeedsLayout]; - } else { - _debugLabelNode.hidden = YES; - _debugLabelNode.attributedText = nil; - } - } - BOOL contentModeSupported = contentMode == UIViewContentModeScaleAspectFill || contentMode == UIViewContentModeScaleAspectFit || contentMode == UIViewContentModeCenter; @@ -412,22 +412,25 @@ struct ASImageNodeDrawParameters { contentsKey.imageDrawRect = imageDrawRect; contentsKey.isOpaque = isOpaque; contentsKey.backgroundColor = backgroundColor; - contentsKey.preContextBlock = preContextBlock; - contentsKey.postContextBlock = postContextBlock; + contentsKey.willDisplayNodeContentWithRenderingContext = willDisplayNodeContentWithRenderingContext; + contentsKey.didDisplayNodeContentWithRenderingContext = didDisplayNodeContentWithRenderingContext; contentsKey.imageModificationBlock = imageModificationBlock; if (isCancelled()) { return nil; } - ASWeakMapEntry *entry = [self.class contentsForkey:contentsKey isCancelled:(asdisplaynode_iscancelled_block_t)isCancelled]; - if (entry == nil) { // If nil, we were cancelled. + ASWeakMapEntry *entry = [self.class contentsForkey:contentsKey + drawParameters:parameter + isCancelled:isCancelled]; + // If nil, we were cancelled. + if (entry == nil) { return nil; } - __instanceLock__.lock(); - _weakCacheEntry = entry; // Retain so that the entry remains in the weak cache - __instanceLock__.unlock(); + if (drawParameter->_didDrawBlock) { + drawParameter->_didDrawBlock(entry); + } return entry.value; } @@ -435,7 +438,7 @@ struct ASImageNodeDrawParameters { static ASWeakMap *cache = nil; static ASDN::Mutex cacheLock; -+ (ASWeakMapEntry *)contentsForkey:(ASImageNodeContentsKey *)key isCancelled:(asdisplaynode_iscancelled_block_t)isCancelled ++ (ASWeakMapEntry *)contentsForkey:(ASImageNodeContentsKey *)key drawParameters:(id)drawParameters isCancelled:(asdisplaynode_iscancelled_block_t)isCancelled { { ASDN::MutexLocker l(cacheLock); @@ -444,13 +447,12 @@ static ASDN::Mutex cacheLock; } ASWeakMapEntry *entry = [cache entryForKey:key]; if (entry != nil) { - // cache hit return entry; } } // cache miss - UIImage *contents = [self createContentsForkey:key isCancelled:isCancelled]; + UIImage *contents = [self createContentsForkey:key drawParameters:drawParameters isCancelled:isCancelled]; if (contents == nil) { // If nil, we were cancelled return nil; } @@ -461,7 +463,7 @@ static ASDN::Mutex cacheLock; } } -+ (UIImage *)createContentsForkey:(ASImageNodeContentsKey *)key isCancelled:(asdisplaynode_iscancelled_block_t)isCancelled ++ (UIImage *)createContentsForkey:(ASImageNodeContentsKey *)key drawParameters:(id)drawParameters isCancelled:(asdisplaynode_iscancelled_block_t)isCancelled { // The following `UIGraphicsBeginImageContextWithOptions` call will sometimes take take longer than 5ms on an // A5 processor for a 400x800 backingSize. @@ -477,8 +479,8 @@ static ASDN::Mutex cacheLock; BOOL contextIsClean = YES; CGContextRef context = UIGraphicsGetCurrentContext(); - if (context && key.preContextBlock) { - key.preContextBlock(context); + if (context && key.willDisplayNodeContentWithRenderingContext) { + key.willDisplayNodeContentWithRenderingContext(context, drawParameters); contextIsClean = NO; } @@ -509,8 +511,8 @@ static ASDN::Mutex cacheLock; [image drawInRect:key.imageDrawRect blendMode:blendMode alpha:1]; } - if (context && key.postContextBlock) { - key.postContextBlock(context); + if (context && key.didDisplayNodeContentWithRenderingContext) { + key.didDisplayNodeContentWithRenderingContext(context, drawParameters); } // The following `UIGraphicsGetImageFromCurrentImageContext` call will commonly take more than 20ms on an @@ -524,7 +526,7 @@ static ASDN::Mutex cacheLock; UIGraphicsEndImageContext(); - if (key.imageModificationBlock != NULL) { + if (key.imageModificationBlock) { result = key.imageModificationBlock(result); } @@ -538,7 +540,25 @@ static ASDN::Mutex cacheLock; __instanceLock__.lock(); void (^displayCompletionBlock)(BOOL canceled) = _displayCompletionBlock; UIImage *image = _image; + BOOL hasDebugLabel = (_debugLabelNode != nil); __instanceLock__.unlock(); + + // Update the debug label if necessary + if (hasDebugLabel) { + // For debugging purposes we don't care about locking for now + CGSize imageSize = image.size; + CGSize imageSizeInPixels = CGSizeMake(imageSize.width * image.scale, imageSize.height * image.scale); + CGSize boundsSizeInPixels = CGSizeMake(std::floor(self.bounds.size.width * self.contentsScale), std::floor(self.bounds.size.height * self.contentsScale)); + CGFloat pixelCountRatio = (imageSizeInPixels.width * imageSizeInPixels.height) / (boundsSizeInPixels.width * boundsSizeInPixels.height); + if (pixelCountRatio != 1.0) { + NSString *scaleString = [NSString stringWithFormat:@"%.2fx", pixelCountRatio]; + _debugLabelNode.attributedText = [[NSAttributedString alloc] initWithString:scaleString attributes:[self debugLabelAttributes]]; + _debugLabelNode.hidden = NO; + } else { + _debugLabelNode.hidden = YES; + _debugLabelNode.attributedText = nil; + } + } // If we've got a block to perform after displaying, do it. if (image && displayCompletionBlock) { @@ -699,6 +719,15 @@ static ASDN::Mutex cacheLock; _debugLabelNode.frame = (CGRect) {debugLabelOrigin, debugLabelSize}; } } + +- (NSDictionary *)debugLabelAttributes +{ + return @{ + NSFontAttributeName: [UIFont systemFontOfSize:15.0], + NSForegroundColorAttributeName: [UIColor redColor] + }; +} + @end #pragma mark - Extras diff --git a/Source/ASMapNode.h b/Source/ASMapNode.h index c2d13f24f3..d2ff45139b 100644 --- a/Source/ASMapNode.h +++ b/Source/ASMapNode.h @@ -1,11 +1,18 @@ // // ASMapNode.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK diff --git a/Source/ASMapNode.mm b/Source/ASMapNode.mm index 690c94f7bb..8d1cc2886d 100644 --- a/Source/ASMapNode.mm +++ b/Source/ASMapNode.mm @@ -1,15 +1,24 @@ // // ASMapNode.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK +#import + #if TARGET_OS_IOS #import @@ -427,6 +436,12 @@ } } } + +- (BOOL)supportsLayerBacking +{ + return NO; +} + @end #endif diff --git a/Source/ASMultiplexImageNode.h b/Source/ASMultiplexImageNode.h index d32cce8b03..cf8ebdc7a2 100644 --- a/Source/ASMultiplexImageNode.h +++ b/Source/ASMultiplexImageNode.h @@ -1,11 +1,18 @@ // // ASMultiplexImageNode.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK diff --git a/Source/ASMultiplexImageNode.mm b/Source/ASMultiplexImageNode.mm index 7dba9f37df..7df6351234 100644 --- a/Source/ASMultiplexImageNode.mm +++ b/Source/ASMultiplexImageNode.mm @@ -1,11 +1,18 @@ // // ASMultiplexImageNode.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK #import @@ -17,8 +24,9 @@ #import #import #import +#import -#if PIN_REMOTE_IMAGE +#if AS_PIN_REMOTE_IMAGE #import #else #import @@ -172,7 +180,7 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent - (instancetype)init { -#if PIN_REMOTE_IMAGE +#if AS_PIN_REMOTE_IMAGE return [self initWithCache:[ASPINRemoteImageDownloader sharedDownloader] downloader:[ASPINRemoteImageDownloader sharedDownloader]]; #else return [self initWithCache:nil downloader:[ASBasicImageDownloader sharedImageDownloader]]; @@ -250,7 +258,7 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent - (BOOL)placeholderShouldPersist { - return (self.image == nil && self.imageIdentifiers.count > 0); + return (self.image == nil && self.animatedImage == nil && self.imageIdentifiers.count > 0); } /* displayWillStart in ASNetworkImageNode has a very similar implementation. Changes here are likely necessary @@ -443,7 +451,7 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent // Grab the best possible image we can load right now. id bestImmediatelyAvailableImageIdentifier = nil; UIImage *bestImmediatelyAvailableImage = [self _bestImmediatelyAvailableImageFromDataSource:&bestImmediatelyAvailableImageIdentifier]; - ASMultiplexImageNodeLogDebug(@"[%p] Best immediately available image identifier is %@", self, bestImmediatelyAvailableImageIdentifier); + as_log_verbose(ASImageLoadingLog(), "%@ Best immediately available image identifier is %@", self, bestImmediatelyAvailableImageIdentifier); // Load it. This kicks off cache fetching/downloading, as appropriate. [self _finishedLoadingImage:bestImmediatelyAvailableImage forIdentifier:bestImmediatelyAvailableImageIdentifier error:nil]; @@ -573,6 +581,8 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent return; } + as_activity_create_for_scope("Load next image for multiplex image node"); + as_log_verbose(ASImageLoadingLog(), "Loading image for %@ ident: %@", self, nextImageIdentifier); self.loadingImageIdentifier = nextImageIdentifier; __weak __typeof__(self) weakSelf = self; @@ -588,13 +598,11 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent [strongSelf _finishedLoadingImage:image forIdentifier:imageIdentifier error:error]; }; - ASMultiplexImageNodeLogDebug(@"[%p] Loading next image, ident: %@", self, nextImageIdentifier); - // Ask our data-source if it's got this image. if (_dataSourceFlags.image) { UIImage *image = [_dataSource multiplexImageNode:self imageForImageIdentifier:nextImageIdentifier]; if (image) { - ASMultiplexImageNodeLogDebug(@"[%p] Acquired next image (%@) from data-source", self, nextImageIdentifier); + as_log_verbose(ASImageLoadingLog(), "Acquired image from data source for %@ ident: %@", self, nextImageIdentifier); finishedLoadingBlock(image, nextImageIdentifier, nil); return; } @@ -603,7 +611,7 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent NSURL *nextImageURL = (_dataSourceFlags.URL) ? [_dataSource multiplexImageNode:self URLForImageIdentifier:nextImageIdentifier] : nil; // If we fail to get a URL for the image, we have no source and can't proceed. if (!nextImageURL) { - ASMultiplexImageNodeLogError(@"[%p] Could not acquire URL for next image (%@). Bailing.", self, nextImageIdentifier); + as_log_error(ASImageLoadingLog(), "Could not acquire URL %@ ident: (%@)", self, nextImageIdentifier); finishedLoadingBlock(nil, nil, [NSError errorWithDomain:ASMultiplexImageNodeErrorDomain code:ASMultiplexImageNodeErrorCodeNoSourceForImage userInfo:nil]); return; } @@ -613,14 +621,14 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent if ([[nextImageURL scheme] isEqualToString:kAssetsLibraryURLScheme]) { // Load the asset. [self _loadALAssetWithIdentifier:nextImageIdentifier URL:nextImageURL completion:^(UIImage *downloadedImage, NSError *error) { - ASMultiplexImageNodeCLogDebug(@"[%p] Acquired next image (%@) from asset library", weakSelf, nextImageIdentifier); + as_log_verbose(ASImageLoadingLog(), "Acquired image from assets library for %@ %@", weakSelf, nextImageIdentifier); finishedLoadingBlock(downloadedImage, nextImageIdentifier, error); }]; } // Likewise, if it's a iOS 8 Photo asset, we need to fetch it accordingly. else if (ASPhotosFrameworkImageRequest *request = [ASPhotosFrameworkImageRequest requestWithURL:nextImageURL]) { [self _loadPHAssetWithRequest:request identifier:nextImageIdentifier completion:^(UIImage *image, NSError *error) { - ASMultiplexImageNodeCLogDebug(@"[%p] Acquired next image (%@) from Photos Framework", weakSelf, nextImageIdentifier); + as_log_verbose(ASImageLoadingLog(), "Acquired image from Photos for %@ %@", weakSelf, nextImageIdentifier); finishedLoadingBlock(image, nextImageIdentifier, error); }]; } @@ -635,7 +643,7 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent // If we had a cache-hit, we're done. if (imageFromCache) { - ASMultiplexImageNodeCLogDebug(@"[%p] Acquired next image (%@) from cache", strongSelf, nextImageIdentifier); + as_log_verbose(ASImageLoadingLog(), "Acquired image from cache for %@ id: %@ img: %@", strongSelf, nextImageIdentifier, imageFromCache); finishedLoadingBlock(imageFromCache, nextImageIdentifier, nil); return; } @@ -648,7 +656,12 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent // Otherwise, we've got to download it. [strongSelf _downloadImageWithIdentifier:nextImageIdentifier URL:nextImageURL completion:^(UIImage *downloadedImage, NSError *error) { - ASMultiplexImageNodeCLogDebug(@"[%p] Acquired next image (%@) from download", strongSelf, nextImageIdentifier); + __typeof__(self) strongSelf = weakSelf; + if (downloadedImage) { + as_log_verbose(ASImageLoadingLog(), "Acquired image from download for %@ id: %@ img: %@", strongSelf, nextImageIdentifier, downloadedImage); + } else { + as_log_error(ASImageLoadingLog(), "Error downloading image for %@ id: %@ err: %@", strongSelf, nextImageIdentifier, error); + } finishedLoadingBlock(downloadedImage, nextImageIdentifier, error); }]; }]; @@ -844,7 +857,7 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent // We explicitly perform this check because our datasource often doesn't give back immediately available images, even though we might have downloaded one already. // Because we seed this call with bestImmediatelyAvailableImageFromDataSource, we must be careful not to trample an existing image. if (image || imageIdentifierCount == 0) { - ASMultiplexImageNodeLogDebug(@"[%p] loaded -> displaying (%@, %@)", self, imageIdentifier, image); + as_log_verbose(ASImageLoadingLog(), "[%p] loaded -> displaying (%@, %@)", self, imageIdentifier, image); id previousIdentifier = self.loadedImageIdentifier; UIImage *previousImage = self.image; diff --git a/Source/ASNavigationController.h b/Source/ASNavigationController.h index 418ca7fc9d..98e3b8cb23 100644 --- a/Source/ASNavigationController.h +++ b/Source/ASNavigationController.h @@ -1,13 +1,18 @@ // // ASNavigationController.h -// AsyncDisplayKit -// -// Created by Garrett Moon on 4/27/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK diff --git a/Source/ASNavigationController.m b/Source/ASNavigationController.m index ec016e4a76..85a2104381 100644 --- a/Source/ASNavigationController.m +++ b/Source/ASNavigationController.m @@ -1,16 +1,23 @@ // // ASNavigationController.m -// AsyncDisplayKit -// -// Created by Garrett Moon on 4/27/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK #import +#import +#import @implementation ASNavigationController { @@ -60,39 +67,53 @@ ASVisibilityDepthImplementation; - (NSArray *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated { + as_activity_create_for_scope("Pop multiple from ASNavigationController"); NSArray *viewControllers = [super popToViewController:viewController animated:animated]; + as_log_info(ASNodeLog(), "Popped %@ to %@, removing %@", self, viewController, ASGetDescriptionValueString(viewControllers)); + [self visibilityDepthDidChange]; return viewControllers; } - (NSArray *)popToRootViewControllerAnimated:(BOOL)animated { + as_activity_create_for_scope("Pop to root of ASNavigationController"); NSArray *viewControllers = [super popToRootViewControllerAnimated:animated]; + as_log_info(ASNodeLog(), "Popped view controllers %@ from %@", ASGetDescriptionValueString(viewControllers), self); + [self visibilityDepthDidChange]; return viewControllers; } - (void)setViewControllers:(NSArray *)viewControllers { + // NOTE: As of now this method calls through to setViewControllers:animated: so no need to log/activity here. + [super setViewControllers:viewControllers]; [self visibilityDepthDidChange]; } - (void)setViewControllers:(NSArray *)viewControllers animated:(BOOL)animated { + as_activity_create_for_scope("Set view controllers of ASNavigationController"); + as_log_info(ASNodeLog(), "Set view controllers of %@ to %@ animated: %d", self, ASGetDescriptionValueString(viewControllers), animated); [super setViewControllers:viewControllers animated:animated]; [self visibilityDepthDidChange]; } - (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated { + as_activity_create_for_scope("Push view controller on ASNavigationController"); + as_log_info(ASNodeLog(), "Pushing %@ onto %@", viewController, self); [super pushViewController:viewController animated:animated]; [self visibilityDepthDidChange]; } - (UIViewController *)popViewControllerAnimated:(BOOL)animated { + as_activity_create_for_scope("Pop view controller from ASNavigationController"); UIViewController *viewController = [super popViewControllerAnimated:animated]; + as_log_info(ASNodeLog(), "Popped %@ from %@", viewController, self); [self visibilityDepthDidChange]; return viewController; } diff --git a/Source/ASNetworkImageNode.h b/Source/ASNetworkImageNode.h index c9ed7426c3..a19ba8d4cc 100644 --- a/Source/ASNetworkImageNode.h +++ b/Source/ASNetworkImageNode.h @@ -1,11 +1,18 @@ // // ASNetworkImageNode.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK diff --git a/Source/ASNetworkImageNode.mm b/Source/ASNetworkImageNode.mm index 12ee999744..f593b233ef 100755 --- a/Source/ASNetworkImageNode.mm +++ b/Source/ASNetworkImageNode.mm @@ -1,16 +1,24 @@ // // ASNetworkImageNode.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK #import +#import #import #import #import @@ -19,8 +27,9 @@ #import #import #import +#import -#if PIN_REMOTE_IMAGE +#if AS_PIN_REMOTE_IMAGE #import #endif @@ -59,6 +68,7 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0}; unsigned int downloaderImplementsSetProgress:1; unsigned int downloaderImplementsSetPriority:1; unsigned int downloaderImplementsAnimatedImage:1; + unsigned int downloaderImplementsCancelWithResume:1; } _downloaderFlags; // Immutable and set on init only. We don't need to lock in this case. @@ -86,6 +96,7 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0}; _downloaderFlags.downloaderImplementsSetProgress = [downloader respondsToSelector:@selector(setProgressImageBlock:callbackQueue:withDownloadIdentifier:)]; _downloaderFlags.downloaderImplementsSetPriority = [downloader respondsToSelector:@selector(setPriority:withDownloadIdentifier:)]; _downloaderFlags.downloaderImplementsAnimatedImage = [downloader respondsToSelector:@selector(animatedImageWithData:)]; + _downloaderFlags.downloaderImplementsCancelWithResume = [downloader respondsToSelector:@selector(cancelImageDownloadWithResumePossibilityForIdentifier:)]; _cacheFlags.cacheSupportsClearing = [cache respondsToSelector:@selector(clearFetchedImageFromCacheWithURL:)]; _cacheFlags.cacheSupportsSynchronousFetch = [cache respondsToSelector:@selector(synchronouslyFetchedCachedImageWithURL:)]; @@ -99,7 +110,7 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0}; - (instancetype)init { -#if PIN_REMOTE_IMAGE +#if AS_PIN_REMOTE_IMAGE return [self initWithCache:[ASPINRemoteImageDownloader sharedDownloader] downloader:[ASPINRemoteImageDownloader sharedDownloader]]; #else return [self initWithCache:nil downloader:[ASBasicImageDownloader sharedImageDownloader]]; @@ -108,7 +119,7 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0}; - (void)dealloc { - [self _cancelImageDownload]; + [self _cancelImageDownloadWithResumePossibility:NO]; } #pragma mark - Public methods -- must lock @@ -128,7 +139,7 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0}; 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."); _URL = nil; - [self _locked_cancelDownloadAndClearImage]; + [self _locked_cancelDownloadAndClearImageWithResumePossibility:NO]; } [self _locked__setImage:image]; @@ -156,15 +167,15 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0}; { ASDN::MutexLocker l(__instanceLock__); - ASDisplayNodeAssert(_imageWasSetExternally == NO, @"Setting a URL to an ASNetworkImageNode after setting an image changes its behavior from an ASImageNode to an ASNetworkImageNode. If this is what you want, set the image to nil first."); - - _imageWasSetExternally = NO; - if (ASObjectIsEqual(URL, _URL)) { return; } - [self _locked_cancelImageDownload]; + ASDisplayNodeAssert(_imageWasSetExternally == NO, @"Setting a URL to an ASNetworkImageNode after setting an image changes its behavior from an ASImageNode to an ASNetworkImageNode. If this is what you want, set the image to nil first."); + + _imageWasSetExternally = NO; + + [self _locked_cancelImageDownloadWithResumePossibility:NO]; _imageLoaded = NO; @@ -300,7 +311,7 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0}; - (BOOL)placeholderShouldPersist { ASDN::MutexLocker l(__instanceLock__); - return (self.image == nil && _URL != nil); + return (self.image == nil && self.animatedImage == nil && _URL != nil); } /* displayWillStart in ASMultiplexImageNode has a very similar implementation. Changes here are likely necessary @@ -386,7 +397,7 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0}; return; } - [self _cancelDownloadAndClearImage]; + [self _cancelDownloadAndClearImageWithResumePossibility:YES]; } - (void)didEnterPreloadState @@ -401,17 +412,16 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0}; - (void)handleProgressImage:(UIImage *)progressImage progress:(CGFloat)progress downloadIdentifier:(nullable id)downloadIdentifier { - __instanceLock__.lock(); + ASDN::MutexLocker l(__instanceLock__); // Getting a result back for a different download identifier, download must not have been successfully canceled if (ASObjectIsEqual(_downloadIdentifier, downloadIdentifier) == NO && downloadIdentifier != nil) { return; } + as_log_verbose(ASImageLoadingLog(), "Received progress image for %@ q: %.2g id: %@", self, progress, progressImage); [self _locked_setCurrentImageQuality:progress]; [self _locked__setImage:progressImage]; - - __instanceLock__.unlock(); } - (void)_updateProgressImageBlockOnDownloaderIfNeeded @@ -436,12 +446,14 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0}; // Unbind from the previous download. if (oldDownloadIDForProgressBlock != nil) { + as_log_verbose(ASImageLoadingLog(), "Disabled progress images for %@ id: %@", self, oldDownloadIDForProgressBlock); [_downloader setProgressImageBlock:nil callbackQueue:dispatch_get_main_queue() withDownloadIdentifier:oldDownloadIDForProgressBlock]; } // Bind to the current download. if (newDownloadIDForProgressBlock != nil) { __weak __typeof(self) weakSelf = self; + as_log_verbose(ASImageLoadingLog(), "Enabled progress images for %@ id: %@", self, newDownloadIDForProgressBlock); [_downloader setProgressImageBlock:^(UIImage * _Nonnull progressImage, CGFloat progress, id _Nullable downloadIdentifier) { [weakSelf handleProgressImage:progressImage progress:progress downloadIdentifier:downloadIdentifier]; } callbackQueue:dispatch_get_main_queue() withDownloadIdentifier:newDownloadIDForProgressBlock]; @@ -470,15 +482,15 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0}; } } -- (void)_cancelDownloadAndClearImage +- (void)_cancelDownloadAndClearImageWithResumePossibility:(BOOL)storeResume { ASDN::MutexLocker l(__instanceLock__); - [self _locked_cancelDownloadAndClearImage]; + [self _locked_cancelDownloadAndClearImageWithResumePossibility:storeResume]; } -- (void)_locked_cancelDownloadAndClearImage +- (void)_locked_cancelDownloadAndClearImageWithResumePossibility:(BOOL)storeResume { - [self _locked_cancelImageDownload]; + [self _locked_cancelImageDownloadWithResumePossibility:storeResume]; // Destruction of bigger images on the main thread can be expensive // and can take some time, so we dispatch onto a bg queue to @@ -500,25 +512,32 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0}; if (_cacheFlags.cacheSupportsClearing) { if (_URL != nil) { + as_log_verbose(ASImageLoadingLog(), "Clearing cached image for %@ url: %@", self, _URL); [_cache clearFetchedImageFromCacheWithURL:_URL]; } } } -- (void)_cancelImageDownload +- (void)_cancelImageDownloadWithResumePossibility:(BOOL)storeResume { ASDN::MutexLocker l(__instanceLock__); - [self _locked_cancelImageDownload]; + [self _locked_cancelImageDownloadWithResumePossibility:storeResume]; } -- (void)_locked_cancelImageDownload +- (void)_locked_cancelImageDownloadWithResumePossibility:(BOOL)storeResume { if (!_downloadIdentifier) { return; } if (_downloadIdentifier) { - [_downloader cancelImageDownloadForIdentifier:_downloadIdentifier]; + if (storeResume && _downloaderFlags.downloaderImplementsCancelWithResume) { + as_log_verbose(ASImageLoadingLog(), "Canceling image download w resume for %@ id: %@", self, _downloadIdentifier); + [_downloader cancelImageDownloadWithResumePossibilityForIdentifier:_downloadIdentifier]; + } else { + as_log_verbose(ASImageLoadingLog(), "Canceling image download no resume for %@ id: %@", self, _downloadIdentifier); + [_downloader cancelImageDownloadForIdentifier:_downloadIdentifier]; + } } _downloadIdentifier = nil; @@ -539,7 +558,8 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0}; ASDN::MutexLocker l(__instanceLock__); url = _URL; } - + + downloadIdentifier = [_downloader downloadImageWithURL:url callbackQueue:dispatch_get_main_queue() downloadProgress:NULL @@ -548,6 +568,7 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0}; finished(imageContainer, error, downloadIdentifier); } }]; + as_log_verbose(ASImageLoadingLog(), "Downloading image for %@ url: %@", self, url); { ASDN::MutexLocker l(__instanceLock__); @@ -563,6 +584,7 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0}; if (cancelAndReattempt) { if (downloadIdentifier != nil) { + as_log_verbose(ASImageLoadingLog(), "Canceling image download no resume for %@ id: %@", self, downloadIdentifier); [_downloader cancelImageDownloadForIdentifier:downloadIdentifier]; } [self _downloadImageWithCompletion:finished]; @@ -643,13 +665,15 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0}; }); } else { __weak __typeof__(self) weakSelf = self; - void (^finished)(id , NSError *, id downloadIdentifier) = ^(id imageContainer, NSError *error, id downloadIdentifier) { + auto finished = ^(id imageContainer, NSError *error, id downloadIdentifier) { __typeof__(self) strongSelf = weakSelf; if (strongSelf == nil) { return; } + as_log_verbose(ASImageLoadingLog(), "Downloaded image for %@ img: %@ url: %@", self, [imageContainer asdk_image], url); + // Grab the lock for the rest of the block ASDN::MutexLocker l(strongSelf->__instanceLock__); @@ -691,6 +715,7 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0}; _cacheUUID = cacheUUID; __instanceLock__.unlock(); + as_log_verbose(ASImageLoadingLog(), "Decaching image for %@ url: %@", self, URL); [_cache cachedImageWithURL:URL callbackQueue:dispatch_get_main_queue() completion:^(id imageContainer) { @@ -706,6 +731,7 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0}; if ([imageContainer asdk_image] == nil && _downloader != nil) { [self _downloadImageWithCompletion:finished]; } else { + as_log_verbose(ASImageLoadingLog(), "Decached image for %@ img: %@ url: %@", self, [imageContainer asdk_image], URL); finished(imageContainer, nil, nil); } }]; diff --git a/Source/ASNodeController+Beta.h b/Source/ASNodeController+Beta.h index 05a859792f..7c75c5eec1 100644 --- a/Source/ASNodeController+Beta.h +++ b/Source/ASNodeController+Beta.h @@ -1,18 +1,35 @@ // -// ASNodeController.h -// AsyncDisplayKit +// ASNodeController+Beta.h +// Texture // -// Created by Hannah Troisi for Scott Goodson on 1/27/17. -// Copyright © 2017 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 /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import #import // for ASInterfaceState protocol +// Until an ASNodeController can be provided in place of an ASCellNode, some apps may prefer to have +// nodes keep their controllers alive (and a weak reference from controller to node) +#define INVERT_NODE_CONTROLLER_OWNERSHIP 0 + /* ASNodeController is currently beta and open to change in the future */ @interface ASNodeController<__covariant DisplayNodeType : ASDisplayNode *> : NSObject +#if INVERT_NODE_CONTROLLER_OWNERSHIP +@property (nonatomic, weak) DisplayNodeType node; +#else @property (nonatomic, strong) DisplayNodeType node; +#endif - (void)loadNode; diff --git a/Source/ASNodeController+Beta.m b/Source/ASNodeController+Beta.m index 2fd01497bd..02efdc2250 100644 --- a/Source/ASNodeController+Beta.m +++ b/Source/ASNodeController+Beta.m @@ -1,15 +1,45 @@ // -// ASNodeController.mm -// AsyncDisplayKit +// ASNodeController+Beta.m +// Texture // -// Created by Hannah Troisi for Scott Goodson on 1/27/17. -// Copyright © 2017 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 /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "ASNodeController+Beta.h" - #import "ASDisplayNode+FrameworkPrivate.h" +#if INVERT_NODE_CONTROLLER_OWNERSHIP + +@interface ASDisplayNode (ASNodeController) +@property (nonatomic, strong) ASNodeController *asdkNodeController; +@end + +@implementation ASDisplayNode (ASNodeController) + +- (ASNodeController *)asdkNodeController +{ + return objc_getAssociatedObject(self, @selector(asdkNodeController)); +} + +- (void)setAsdkNodeController:(ASNodeController *)asdkNodeController +{ + objc_setAssociatedObject(self, @selector(asdkNodeController), asdkNodeController, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +@end + +#endif + @implementation ASNodeController @synthesize node = _node; @@ -40,6 +70,9 @@ { _node = node; _node.interfaceStateDelegate = self; +#if INVERT_NODE_CONTROLLER_OWNERSHIP + _node.asdkNodeController = self; +#endif } // subclass overrides diff --git a/Source/ASPagerFlowLayout.h b/Source/ASPagerFlowLayout.h index a73cf9b0fc..69362b559b 100644 --- a/Source/ASPagerFlowLayout.h +++ b/Source/ASPagerFlowLayout.h @@ -1,13 +1,18 @@ // // ASPagerFlowLayout.h -// AsyncDisplayKit -// -// Created by Levi McCallum on 2/12/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK diff --git a/Source/ASPagerFlowLayout.m b/Source/ASPagerFlowLayout.m index 4d8b6128c3..a2f06bee72 100644 --- a/Source/ASPagerFlowLayout.m +++ b/Source/ASPagerFlowLayout.m @@ -1,13 +1,18 @@ // // ASPagerFlowLayout.m -// AsyncDisplayKit -// -// Created by Levi McCallum on 2/12/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK @@ -21,6 +26,7 @@ @end +//TODO make this an ASCollectionViewLayout @implementation ASPagerFlowLayout - (ASCollectionView *)asCollectionView diff --git a/Source/ASPagerNode.h b/Source/ASPagerNode.h index f4c019cd1a..61e16bb6b0 100644 --- a/Source/ASPagerNode.h +++ b/Source/ASPagerNode.h @@ -1,13 +1,18 @@ // // ASPagerNode.h -// AsyncDisplayKit -// -// Created by Levi McCallum on 12/7/15. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK diff --git a/Source/ASPagerNode.m b/Source/ASPagerNode.m index a396cc0879..5bac4c352b 100644 --- a/Source/ASPagerNode.m +++ b/Source/ASPagerNode.m @@ -1,13 +1,18 @@ // // ASPagerNode.m -// AsyncDisplayKit -// -// Created by Levi McCallum on 12/7/15. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK diff --git a/Source/ASRangeManagingNode.h b/Source/ASRangeManagingNode.h new file mode 100644 index 0000000000..2716efbe4f --- /dev/null +++ b/Source/ASRangeManagingNode.h @@ -0,0 +1,35 @@ +// +// ASRangeManagingNode.h +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +@class ASCellNode; + +NS_ASSUME_NONNULL_BEGIN + +/** + * Basically ASTableNode or ASCollectionNode. + */ +@protocol ASRangeManagingNode + +/** + * Retrieve the index path for the given node, if it's a member of this container. + * + * @param node The node. + * @return The index path, or nil if the node is not part of this container. + */ +- (nullable NSIndexPath *)indexPathForNode:(ASCellNode *)node; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Source/ASRunLoopQueue.h b/Source/ASRunLoopQueue.h index 2e50444f3d..7ff563f409 100644 --- a/Source/ASRunLoopQueue.h +++ b/Source/ASRunLoopQueue.h @@ -1,13 +1,18 @@ // // ASRunLoopQueue.h -// AsyncDisplayKit -// -// Created by Rahul Malik on 3/7/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -46,6 +51,8 @@ AS_SUBCLASSING_RESTRICTED + (instancetype)sharedDeallocationQueue; +- (void)test_drain; + - (void)releaseObjectInBackground:(id)object; @end diff --git a/Source/ASRunLoopQueue.mm b/Source/ASRunLoopQueue.mm index 1374e79bf9..004d4abb97 100644 --- a/Source/ASRunLoopQueue.mm +++ b/Source/ASRunLoopQueue.mm @@ -1,19 +1,27 @@ // // ASRunLoopQueue.mm -// AsyncDisplayKit -// -// Created by Rahul Malik on 3/7/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // +#import +#import +#import #import #import -#import - +#import +#import #import #import #import @@ -72,16 +80,20 @@ static void runLoopSourceCallback(void *info) { return; } // The scope below is entered while already locked. @autorelease is crucial here; see PR 2890. + NSInteger count; @autoreleasepool { #if ASRunLoopQueueLoggingEnabled NSLog(@"ASDeallocQueue Processing: %lu objects destroyed", weakSelf->_queue.size()); #endif // Sometimes we release 10,000 objects at a time. Don't hold the lock while releasing. std::deque currentQueue = weakSelf->_queue; + count = currentQueue.size(); + ASSignpostStartCustom(ASSignpostDeallocQueueDrain, self, count); weakSelf->_queue = std::deque(); weakSelf->_queueLock.unlock(); currentQueue.clear(); } + ASSignpostEndCustom(ASSignpostDeallocQueueDrain, self, count, ASSignpostColorDefault); }); CFRunLoopRef runloop = CFRunLoopGetCurrent(); @@ -137,6 +149,29 @@ static void runLoopSourceCallback(void *info) { _thread = nil; } +- (void)test_drain +{ + [self performSelector:@selector(_test_drain) onThread:_thread withObject:nil waitUntilDone:YES]; +} + +- (void)_test_drain +{ + while (true) { + @autoreleasepool { + _queueLock.lock(); + std::deque currentQueue = _queue; + _queue = std::deque(); + _queueLock.unlock(); + + if (currentQueue.empty()) { + return; + } else { + currentQueue.clear(); + } + } + } +} + - (void)_stop { CFRunLoopStop(CFRunLoopGetCurrent()); @@ -157,7 +192,10 @@ static void runLoopSourceCallback(void *info) { CFRunLoopObserverRef _runLoopObserver; NSPointerArray *_internalQueue; // Use NSPointerArray so we can decide __strong or __weak per-instance. ASDN::RecursiveMutex _internalQueueLock; - + + // In order to not pollute the top-level activities, each queue has 1 root activity. + os_activity_t _rootActivity; + #if ASRunLoopQueueLoggingEnabled NSTimer *_runloopQueueLoggingTimer; #endif @@ -167,8 +205,69 @@ static void runLoopSourceCallback(void *info) { @end +#if AS_KDEBUG_ENABLE +/** + * This is real, private CA API. Valid as of iOS 10. + */ +typedef enum { + kCATransactionPhasePreLayout, + kCATransactionPhasePreCommit, + kCATransactionPhasePostCommit, +} CATransactionPhase; + +@interface CATransaction (Private) ++ (void)addCommitHandler:(void(^)(void))block forPhase:(CATransactionPhase)phase; ++ (int)currentState; +@end +#endif + @implementation ASRunLoopQueue +#if AS_KDEBUG_ENABLE ++ (void)load +{ + [self registerCATransactionObservers]; +} + ++ (void)registerCATransactionObservers +{ + static BOOL privateCAMethodsExist; + static dispatch_block_t preLayoutHandler; + static dispatch_block_t preCommitHandler; + static dispatch_block_t postCommitHandler; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + privateCAMethodsExist = [CATransaction respondsToSelector:@selector(addCommitHandler:forPhase:)]; + privateCAMethodsExist &= [CATransaction respondsToSelector:@selector(currentState)]; + if (!privateCAMethodsExist) { + NSLog(@"Private CA methods are gone."); + } + preLayoutHandler = ^{ + ASSignpostStartCustom(ASSignpostCATransactionLayout, 0, [CATransaction currentState]); + }; + preCommitHandler = ^{ + int state = [CATransaction currentState]; + ASSignpostEndCustom(ASSignpostCATransactionLayout, 0, state, ASSignpostColorDefault); + ASSignpostStartCustom(ASSignpostCATransactionCommit, 0, state); + }; + postCommitHandler = ^{ + ASSignpostEndCustom(ASSignpostCATransactionCommit, 0, [CATransaction currentState], ASSignpostColorDefault); + // Can't add new observers inside an observer. rdar://problem/31253952 + dispatch_async(dispatch_get_main_queue(), ^{ + [self registerCATransactionObservers]; + }); + }; + }); + + if (privateCAMethodsExist) { + [CATransaction addCommitHandler:preLayoutHandler forPhase:kCATransactionPhasePreLayout]; + [CATransaction addCommitHandler:preCommitHandler forPhase:kCATransactionPhasePreCommit]; + [CATransaction addCommitHandler:postCommitHandler forPhase:kCATransactionPhasePostCommit]; + } +} + +#endif // AS_KDEBUG_ENABLE + - (instancetype)initWithRunLoop:(CFRunLoopRef)runloop retainObjects:(BOOL)retainsObjects handler:(void (^)(id _Nullable, BOOL))handlerBlock { if (self = [super init]) { @@ -178,6 +277,15 @@ static void runLoopSourceCallback(void *info) { _queueConsumer = handlerBlock; _batchSize = 1; _ensureExclusiveMembership = YES; + + // We don't want to pollute the top-level app activities with run loop batches, so we create one top-level + // activity per queue, and each batch activity joins that one instead. + _rootActivity = as_activity_create("Process run loop queue items", OS_ACTIVITY_NONE, OS_ACTIVITY_FLAG_DEFAULT); + { + // Log a message identifying this queue into the queue's root activity. + as_activity_scope_verbose(_rootActivity); + as_log_verbose(ASDisplayLog(), "Created run loop queue: %@", self); + } // Self is guaranteed to outlive the observer. Without the high cost of a weak pointer, // __unsafe_unretained allows us to avoid flagging the memory cycle detector. @@ -246,7 +354,7 @@ static void runLoopSourceCallback(void *info) { return; } - ASProfilingSignpostStart(0, self); + ASSignpostStart(ASSignpostRunLoopQueueBatch); // Snatch the next batch of items. NSInteger maxCountToProcess = MIN(internalQueueCount, self.batchSize); @@ -281,16 +389,17 @@ static void runLoopSourceCallback(void *info) { } // itemsToProcess will be empty if _queueConsumer == nil so no need to check again. - if (itemsToProcess.empty() == false) { -#if ASRunloopQueueLoggingEnabled - NSLog(@"<%@> - Starting processing of: %ld", self, itemsToProcess.size()); -#endif + auto count = itemsToProcess.size(); + if (count > 0) { + as_activity_scope_verbose(as_activity_create("Process run loop queue batch", _rootActivity, OS_ACTIVITY_FLAG_DEFAULT)); 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 + __unsafe_unretained id value = *iterator; + _queueConsumer(value, isQueueDrained && iterator == itemsEnd - 1); + as_log_verbose(ASDisplayLog(), "processed %@", value); + } + if (count > 1) { + as_log_verbose(ASDisplayLog(), "processed %lu items", (unsigned long)count); } } @@ -300,7 +409,7 @@ static void runLoopSourceCallback(void *info) { CFRunLoopWakeUp(_runLoop); } - ASProfilingSignpostEnd(0, self); + ASSignpostEnd(ASSignpostRunLoopQueueBatch); } - (void)enqueue:(id)object diff --git a/Source/ASScrollNode.h b/Source/ASScrollNode.h index 5adbedf62d..1a3fad57ab 100644 --- a/Source/ASScrollNode.h +++ b/Source/ASScrollNode.h @@ -1,11 +1,18 @@ // // ASScrollNode.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/ASScrollNode.mm b/Source/ASScrollNode.mm index 7d257dd99b..7d1beaa3d2 100644 --- a/Source/ASScrollNode.mm +++ b/Source/ASScrollNode.mm @@ -1,11 +1,18 @@ // -// ASScrollNode.m -// AsyncDisplayKit +// ASScrollNode.mm +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/ASSectionController.h b/Source/ASSectionController.h index 5c74968432..82558a42c5 100644 --- a/Source/ASSectionController.h +++ b/Source/ASSectionController.h @@ -1,9 +1,18 @@ // // ASSectionController.h -// AsyncDisplayKit +// Texture // -// Created by Adlai Holler on 1/19/17. -// Copyright © 2017 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 /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK @@ -17,7 +26,7 @@ NS_ASSUME_NONNULL_BEGIN /** * A protocol that your section controllers should conform to, - * in addition to IGListSectionType, in order to be used with AsyncDisplayKit. + * in order to be used with AsyncDisplayKit. * * @note Your supplementary view source should conform to @c ASSupplementaryNodeSource. */ diff --git a/Source/ASSupplementaryNodeSource.h b/Source/ASSupplementaryNodeSource.h index d5c207fedb..5cacdf6158 100644 --- a/Source/ASSupplementaryNodeSource.h +++ b/Source/ASSupplementaryNodeSource.h @@ -1,9 +1,18 @@ // // ASSupplementaryNodeSource.h -// AsyncDisplayKit +// Texture // -// Created by Adlai Holler on 1/19/17. -// Copyright © 2017 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 /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK diff --git a/Source/ASTabBarController.h b/Source/ASTabBarController.h index cd772b872a..dbc05a8215 100644 --- a/Source/ASTabBarController.h +++ b/Source/ASTabBarController.h @@ -1,13 +1,18 @@ // // ASTabBarController.h -// AsyncDisplayKit -// -// Created by Garrett Moon on 5/10/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK diff --git a/Source/ASTabBarController.m b/Source/ASTabBarController.m index 3b6012c5b9..424beba5be 100644 --- a/Source/ASTabBarController.m +++ b/Source/ASTabBarController.m @@ -1,17 +1,23 @@ // // ASTabBarController.m -// AsyncDisplayKit -// -// Created by Garrett Moon on 5/10/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK #import +#import @implementation ASTabBarController { @@ -68,12 +74,18 @@ ASVisibilityDepthImplementation; - (void)setSelectedIndex:(NSUInteger)selectedIndex { + as_activity_create_for_scope("Set selected index of ASTabBarController"); + as_log_info(ASNodeLog(), "Selected tab %tu of %@", selectedIndex, self); + [super setSelectedIndex:selectedIndex]; [self visibilityDepthDidChange]; } - (void)setSelectedViewController:(__kindof UIViewController *)selectedViewController { + as_activity_create_for_scope("Set selected view controller of ASTabBarController"); + as_log_info(ASNodeLog(), "Selected view controller %@ of %@", selectedViewController, self); + [super setSelectedViewController:selectedViewController]; [self visibilityDepthDidChange]; } diff --git a/Source/ASTableNode+Beta.h b/Source/ASTableNode+Beta.h new file mode 100644 index 0000000000..c2b27fa971 --- /dev/null +++ b/Source/ASTableNode+Beta.h @@ -0,0 +1,25 @@ +// +// ASTableNode+Beta.h +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +@protocol ASBatchFetchingDelegate; + +NS_ASSUME_NONNULL_BEGIN + +@interface ASTableNode (Beta) + +@property (nonatomic, weak) id batchFetchingDelegate; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Source/ASTableNode.h b/Source/ASTableNode.h index 4e9ba2ca87..dec0be7eb8 100644 --- a/Source/ASTableNode.h +++ b/Source/ASTableNode.h @@ -1,20 +1,25 @@ // // ASTableNode.h -// AsyncDisplayKit -// -// Created by Steven Ramkumar on 11/4/15. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK #import #import #import #import - +#import NS_ASSUME_NONNULL_BEGIN @@ -26,7 +31,7 @@ NS_ASSUME_NONNULL_BEGIN * ASTableNode is a node based class that wraps an ASTableView. It can be used * as a subnode of another node, and provide room for many (great) features and improvements later on. */ -@interface ASTableNode : ASDisplayNode +@interface ASTableNode : ASDisplayNode - (instancetype)init; // UITableViewStylePlain - (instancetype)initWithStyle:(UITableViewStyle)style NS_DESIGNATED_INITIALIZER; @@ -37,11 +42,30 @@ NS_ASSUME_NONNULL_BEGIN @property (weak, nonatomic) id delegate; @property (weak, nonatomic) id dataSource; +/** + * The number of screens left to scroll before the delegate -tableNode:beginBatchFetchingWithContext: is called. + * + * Defaults to two screenfuls. + */ +@property (nonatomic, assign) CGFloat leadingScreensForBatching; + /* * A Boolean value that determines whether the table will be flipped. * If the value of this property is YES, the first cell node will be at the bottom of the table (as opposed to the top by default). This is useful for chat/messaging apps. The default value is NO. */ @property (nonatomic, assign) BOOL inverted; + +/** + * 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. + */ +@property (nonatomic, assign) BOOL automaticallyAdjustsContentOffset; + /* * A Boolean value that determines whether users can select a row. * If the value of this property is YES (the default), users can select rows. If you set it to NO, they cannot select rows. Setting this property affects cell selection only when the table view is not in editing mode. If you want to restrict selection of cells in editing mode, use `allowsSelectionDuringEditing`. @@ -158,7 +182,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)performBatchAnimated:(BOOL)animated updates:(nullable AS_NOESCAPE void (^)())updates completion:(nullable void (^)(BOOL finished))completion; /** - * Perform a batch of updates asynchronously, optionally disabling all animations in the batch. This method must be called from the main thread. + * Perform a batch of updates asynchronously with animations in the batch. This method must be called from the main thread. * The data source must be updated to reflect the changes before the update block completes. * * @param updates The block that performs the relevant insert, delete, reload, or move operations. diff --git a/Source/ASTableNode.mm b/Source/ASTableNode.mm index 181b59bf49..11ed40ac39 100644 --- a/Source/ASTableNode.mm +++ b/Source/ASTableNode.mm @@ -1,17 +1,24 @@ // // ASTableNode.mm -// AsyncDisplayKit -// -// Created by Steven Ramkumar on 11/4/15. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK #import +#import + #import #import #import @@ -36,6 +43,8 @@ @property (nonatomic, assign) BOOL allowsMultipleSelection; @property (nonatomic, assign) BOOL allowsMultipleSelectionDuringEditing; @property (nonatomic, assign) BOOL inverted; +@property (nonatomic, assign) CGFloat leadingScreensForBatching; +@property (nonatomic, assign) BOOL automaticallyAdjustsContentOffset; @end @implementation _ASTablePendingState @@ -49,6 +58,8 @@ _allowsMultipleSelection = NO; _allowsMultipleSelectionDuringEditing = NO; _inverted = NO; + _leadingScreensForBatching = 2; + _automaticallyAdjustsContentOffset = NO; } return self; } @@ -60,15 +71,12 @@ @interface ASTableNode () { ASDN::RecursiveMutex _environmentStateLock; + id _batchFetchingDelegate; } @property (nonatomic, strong) _ASTablePendingState *pendingState; @end -@interface ASTableView () -- (instancetype)_initWithFrame:(CGRect)frame style:(UITableViewStyle)style dataControllerClass:(Class)dataControllerClass; -@end - @implementation ASTableNode #pragma mark Lifecycle @@ -80,7 +88,7 @@ [self setViewBlock:^{ // Variable will be unused if event logging is off. __unused __typeof__(self) strongSelf = weakSelf; - return [[ASTableView alloc] _initWithFrame:CGRectZero style:style dataControllerClass:nil eventLog:ASDisplayNodeGetEventLog(strongSelf)]; + return [[ASTableView alloc] _initWithFrame:CGRectZero style:style dataControllerClass:nil owningNode:strongSelf eventLog:ASDisplayNodeGetEventLog(strongSelf)]; }]; } return self; @@ -127,18 +135,20 @@ [self.rangeController clearContents]; } -- (void)didExitPreloadState -{ - [super didExitPreloadState]; - [self.rangeController clearPreloadedData]; -} - - (void)interfaceStateDidChange:(ASInterfaceState)newState fromState:(ASInterfaceState)oldState { [super interfaceStateDidChange:newState fromState:oldState]; [ASRangeController layoutDebugOverlayIfNeeded]; } +- (void)didEnterPreloadState +{ + [super didEnterPreloadState]; + // Intentionally allocate the view here and trigger a layout pass on it, which in turn will trigger the intial data load. + // We can get rid of this call later when ASDataController, ASRangeController and ASCollectionLayout can operate without the view. + [[self view] layoutIfNeeded]; +} + #if ASRangeControllerLoggingEnabled - (void)didEnterVisibleState { @@ -153,6 +163,12 @@ } #endif +- (void)didExitPreloadState +{ + [super didExitPreloadState]; + [self.rangeController clearPreloadedData]; +} + #pragma mark Setter / Getter // TODO: Implement this without the view. Then revisit ASLayoutElementCollectionTableSetTraitCollection @@ -196,6 +212,48 @@ } } +- (void)setLeadingScreensForBatching:(CGFloat)leadingScreensForBatching +{ + _ASTablePendingState *pendingState = self.pendingState; + if (pendingState) { + pendingState.leadingScreensForBatching = leadingScreensForBatching; + } else { + ASDisplayNodeAssert(self.nodeLoaded, @"ASTableNode should be loaded if pendingState doesn't exist"); + self.view.leadingScreensForBatching = leadingScreensForBatching; + } +} + +- (CGFloat)leadingScreensForBatching +{ + _ASTablePendingState *pendingState = self.pendingState; + if (pendingState) { + return pendingState.leadingScreensForBatching; + } else { + return self.view.leadingScreensForBatching; + } +} + +- (void)setAutomaticallyAdjustsContentOffset:(BOOL)automaticallyAdjustsContentOffset +{ + _ASTablePendingState *pendingState = self.pendingState; + if (pendingState) { + pendingState.automaticallyAdjustsContentOffset = automaticallyAdjustsContentOffset; + } else { + ASDisplayNodeAssert(self.nodeLoaded, @"ASTableNode should be loaded if pendingState doesn't exist"); + self.view.automaticallyAdjustsContentOffset = automaticallyAdjustsContentOffset; + } +} + +- (BOOL)automaticallyAdjustsContentOffset +{ + _ASTablePendingState *pendingState = self.pendingState; + if (pendingState) { + return pendingState.automaticallyAdjustsContentOffset; + } else { + return self.view.automaticallyAdjustsContentOffset; + } +} + - (void)setDelegate:(id )delegate { if ([self pendingState]) { @@ -327,6 +385,16 @@ } } +- (void)setBatchFetchingDelegate:(id)batchFetchingDelegate +{ + _batchFetchingDelegate = batchFetchingDelegate; +} + +- (id)batchFetchingDelegate +{ + return _batchFetchingDelegate; +} + #pragma mark ASRangeControllerUpdateRangeProtocol - (void)updateCurrentRangeWithMode:(ASLayoutRangeMode)rangeMode diff --git a/Source/ASTableView.h b/Source/ASTableView.h index 3ebb667ffe..14d183ccf1 100644 --- a/Source/ASTableView.h +++ b/Source/ASTableView.h @@ -1,11 +1,18 @@ // // ASTableView.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK @@ -26,13 +33,6 @@ NS_ASSUME_NONNULL_BEGIN /** * Asynchronous UITableView with Intelligent Preloading capabilities. * - * @discussion ASTableView is a true subclass of UITableView, meaning it is pointer-compatible with code that - * currently uses UITableView - * - * The main difference is that asyncDataSource expects -nodeForRowAtIndexPath, an ASCellNode, and - * the heightForRowAtIndexPath: method is eliminated (as are the performance problems caused by it). - * This is made possible because ASCellNodes can calculate their own size, and preload ahead of time. - * * @note ASTableNode is strongly recommended over ASTableView. This class is provided for adoption convenience. */ @interface ASTableView : UITableView @@ -45,29 +45,6 @@ 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 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. - */ -@property (nonatomic) BOOL automaticallyAdjustsContentOffset; - -/** - * The number of screens left to scroll before the delegate -tableView:beginBatchFetchingWithContext: is called. - * - * Defaults to two screenfuls. - */ -@property (nonatomic, assign) CGFloat leadingScreensForBatching; - -/* - * A Boolean value that determines whether the nodes that the data source renders will be flipped. - */ -@property (nonatomic, assign) BOOL inverted; - @end @interface ASTableView (Deprecated) @@ -85,6 +62,30 @@ NS_ASSUME_NONNULL_BEGIN */ - (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style ASDISPLAYNODE_DEPRECATED_MSG("Please use ASTableNode instead of ASTableView."); +/** + * The number of screens left to scroll before the delegate -tableView:beginBatchFetchingWithContext: is called. + * + * Defaults to two screenfuls. + */ +@property (nonatomic, assign) CGFloat leadingScreensForBatching ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode property instead."); + + +/** + * 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. + */ +@property (nonatomic) BOOL automaticallyAdjustsContentOffset ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode property instead."); + +/* + * A Boolean value that determines whether the nodes that the data source renders will be flipped. + */ +@property (nonatomic, assign) BOOL inverted ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode property instead."); + /** * Tuning parameters for a range type in full mode. * diff --git a/Source/ASTableView.mm b/Source/ASTableView.mm index 5f46b6cc56..2763696f6a 100644 --- a/Source/ASTableView.mm +++ b/Source/ASTableView.mm @@ -1,11 +1,18 @@ // // ASTableView.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK @@ -15,7 +22,6 @@ #import #import #import -#import #import #import #import @@ -25,7 +31,7 @@ #import #import #import -#import +#import #import #import #import @@ -46,6 +52,26 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; return __val; \ } +#define UITABLEVIEW_RESPONDS_TO_SELECTOR() \ + ({ \ + static BOOL superResponds; \ + static dispatch_once_t onceToken; \ + dispatch_once(&onceToken, ^{ \ + superResponds = [UITableView instancesRespondToSelector:_cmd]; \ + }); \ + superResponds; \ + }) + +@interface UITableView (ScrollViewDelegate) + +- (void)scrollViewDidScroll:(UIScrollView *)scrollView; +- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView; +- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView; +- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset; +- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate; + +@end + #pragma mark - #pragma mark ASCellNode<->UITableViewCell bridging. @@ -57,7 +83,8 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; @interface _ASTableViewCell : UITableViewCell @property (nonatomic, weak) id<_ASTableViewCellDelegate> delegate; -@property (nonatomic, weak) ASCellNode *node; +@property (nonatomic, strong, readonly) ASCellNode *node; +@property (nonatomic, strong) ASCollectionElement *element; @end @implementation _ASTableViewCell @@ -76,9 +103,15 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; [super didTransitionToState:state]; } -- (void)setNode:(ASCellNode *)node +- (ASCellNode *)node { - _node = node; + return self.element.node; +} + +- (void)setElement:(ASCollectionElement *)element +{ + _element = element; + ASCellNode *node = element.node; if (node) { self.backgroundColor = node.backgroundColor; @@ -101,19 +134,19 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; - (void)setSelected:(BOOL)selected animated:(BOOL)animated { [super setSelected:selected animated:animated]; - [_node __setSelectedFromUIKit:selected]; + [self.node __setSelectedFromUIKit:selected]; } - (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated { [super setHighlighted:highlighted animated:animated]; - [_node __setHighlightedFromUIKit:highlighted]; + [self.node __setHighlightedFromUIKit:highlighted]; } - (void)prepareForReuse { - // Need to clear node pointer before UIKit calls setSelected:NO / setHighlighted:NO on its cells - self.node = nil; + // Need to clear element before UIKit calls setSelected:NO / setHighlighted:NO on its cells + self.element = nil; [super prepareForReuse]; } @@ -122,7 +155,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; #pragma mark - #pragma mark ASTableView -@interface ASTableView () +@interface ASTableView () { ASTableViewProxy *_proxyDataSource; ASTableViewProxy *_proxyDelegate; @@ -136,11 +169,14 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; // 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; + BOOL _inverted; // 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; + CGFloat _leadingScreensForBatching; + BOOL _automaticallyAdjustsContentOffset; CGPoint _deceleratingVelocity; @@ -156,10 +192,13 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; CGFloat _nodesConstrainedWidth; BOOL _queuedNodeHeightUpdate; BOOL _isDeallocating; - NSMutableSet *_cellsForVisibilityUpdates; + NSHashTable<_ASTableViewCell *> *_cellsForVisibilityUpdates; + + // CountedSet because UIKit may display the same element in multiple cells e.g. during animations. + NSCountedSet *_visibleElements; BOOL _remeasuringCellNodes; - NSMutableSet *_cellsForLayoutUpdates; + NSHashTable *_cellsForLayoutUpdates; // See documentation on same property in ASCollectionView BOOL _hasEverCheckedForBatchFetchingDueToUpdate; @@ -183,6 +222,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; unsigned int scrollViewWillBeginDragging:1; unsigned int scrollViewDidEndDragging:1; unsigned int scrollViewWillEndDragging:1; + unsigned int scrollViewDidEndDecelerating:1; unsigned int tableNodeWillDisplayNodeForRow:1; unsigned int tableViewWillDisplayNodeForRow:1; unsigned int tableViewWillDisplayNodeForRowDeprecated:1; @@ -260,8 +300,22 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; #pragma mark - #pragma mark Lifecycle -- (void)configureWithDataControllerClass:(Class)dataControllerClass eventLog:(ASEventLog *)eventLog +- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style { + return [self _initWithFrame:frame style:style dataControllerClass:nil owningNode:nil eventLog:nil]; +} + +- (instancetype)_initWithFrame:(CGRect)frame style:(UITableViewStyle)style dataControllerClass:(Class)dataControllerClass owningNode:(ASTableNode *)tableNode eventLog:(ASEventLog *)eventLog +{ + if (!(self = [super initWithFrame:frame style:style])) { + return nil; + } + _cellsForVisibilityUpdates = [NSHashTable hashTableWithOptions:NSHashTableObjectPointerPersonality]; + _cellsForLayoutUpdates = [NSHashTable hashTableWithOptions:NSHashTableObjectPointerPersonality]; + if (!dataControllerClass) { + dataControllerClass = [[self class] dataControllerClass]; + } + _layoutController = [[ASTableLayoutController alloc] initWithTableView:self]; _rangeController = [[ASRangeController alloc] init]; @@ -269,13 +323,13 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; _rangeController.dataSource = self; _rangeController.delegate = self; - _dataController = [[dataControllerClass alloc] initWithDataSource:self eventLog:eventLog]; + _dataController = [[dataControllerClass alloc] initWithDataSource:self node:tableNode eventLog:eventLog]; _dataController.delegate = _rangeController; - _dataController.environmentDelegate = self; - + _leadingScreensForBatching = 2.0; _batchContext = [[ASBatchContext alloc] init]; - + _visibleElements = [[NSCountedSet alloc] init]; + _automaticallyAdjustsContentOffset = NO; _nodesConstrainedWidth = self.bounds.size.width; @@ -285,27 +339,8 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; _proxyDataSource = [[ASTableViewProxy alloc] initWithTarget:nil interceptor:self]; super.dataSource = (id)_proxyDataSource; - - [self registerClass:_ASTableViewCell.class forCellReuseIdentifier:kCellReuseIdentifier]; -} - -- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style -{ - return [self _initWithFrame:frame style:style dataControllerClass:nil eventLog:nil]; -} - -- (instancetype)_initWithFrame:(CGRect)frame style:(UITableViewStyle)style dataControllerClass:(Class)dataControllerClass eventLog:(ASEventLog *)eventLog -{ - if (!(self = [super initWithFrame:frame style:style])) { - return nil; - } - _cellsForVisibilityUpdates = [NSMutableSet set]; - _cellsForLayoutUpdates = [NSMutableSet set]; - if (!dataControllerClass) { - dataControllerClass = [[self class] dataControllerClass]; - } - [self configureWithDataControllerClass:dataControllerClass eventLog:eventLog]; + [self registerClass:_ASTableViewCell.class forCellReuseIdentifier:kCellReuseIdentifier]; if (!AS_AT_LEAST_IOS9) { _retainedLayer = self.layer; @@ -433,6 +468,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; _asyncDelegateFlags.tableViewDidEndDisplayingNodeForRow = [_asyncDelegate respondsToSelector:@selector(tableView:didEndDisplayingNode:forRowAtIndexPath:)]; _asyncDelegateFlags.tableNodeDidEndDisplayingNodeForRow = [_asyncDelegate respondsToSelector:@selector(tableNode:didEndDisplayingRowWithNode:)]; _asyncDelegateFlags.scrollViewWillEndDragging = [_asyncDelegate respondsToSelector:@selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:)]; + _asyncDelegateFlags.scrollViewDidEndDecelerating = [_asyncDelegate respondsToSelector:@selector(scrollViewDidEndDecelerating:)]; _asyncDelegateFlags.tableViewWillBeginBatchFetch = [_asyncDelegate respondsToSelector:@selector(tableView:willBeginBatchFetchWithContext:)]; _asyncDelegateFlags.tableNodeWillBeginBatchFetch = [_asyncDelegate respondsToSelector:@selector(tableNode:willBeginBatchFetchWithContext:)]; _asyncDelegateFlags.shouldBatchFetchForTableView = [_asyncDelegate respondsToSelector:@selector(shouldBatchFetchForTableView:)]; @@ -541,11 +577,6 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; return [_rangeController tuningParametersForRangeMode:rangeMode rangeType:rangeType]; } -- (ASTableNode *)tableNode -{ - return (ASTableNode *)ASViewToDisplayNode(self); -} - - (ASElementMap *)elementMapForRangeController:(ASRangeController *)rangeController { return _dataController.visibleMap; @@ -651,7 +682,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; - (NSArray *)visibleNodes { - NSArray *elements = [self visibleElementsForRangeController:_rangeController]; + auto elements = [self visibleElementsForRangeController:_rangeController]; return ASArrayByFlatMapping(elements, ASCollectionElement *e, e.node); } @@ -874,11 +905,11 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; _ASTableViewCell *cell = [self dequeueReusableCellWithIdentifier:kCellReuseIdentifier forIndexPath:indexPath]; cell.delegate = self; - ASCellNode *node = [_dataController.visibleMap elementForItemAtIndexPath:indexPath].node; + ASCollectionElement *element = [_dataController.visibleMap elementForItemAtIndexPath:indexPath]; + cell.element = element; + ASCellNode *node = element.node; if (node) { [_rangeController configureContentView:cell.contentView forCellNode:node]; - - cell.node = node; } return cell; @@ -941,7 +972,9 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; - (void)tableView:(UITableView *)tableView willDisplayCell:(_ASTableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath { - ASCellNode *cellNode = [cell node]; + ASCollectionElement *element = cell.element; + [_visibleElements addObject:element]; + ASCellNode *cellNode = element.node; cellNode.scrollView = tableView; ASDisplayNodeAssertNotNil(cellNode, @"Expected node associated with cell that will be displayed not to be nil. indexPath: %@", indexPath); @@ -967,7 +1000,9 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; - (void)tableView:(UITableView *)tableView didEndDisplayingCell:(_ASTableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath { - ASCellNode *cellNode = [cell node]; + ASCollectionElement *element = cell.element; + [_visibleElements removeObject:element]; + ASCellNode *cellNode = element.node; [_rangeController setNeedsUpdate]; @@ -1166,6 +1201,10 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; - (void)scrollViewDidScroll:(UIScrollView *)scrollView { + if (scrollView != self && UITABLEVIEW_RESPONDS_TO_SELECTOR()) { + [super scrollViewDidScroll:scrollView]; + return; + } // If a scroll happenes the current range mode needs to go to full ASInterfaceState interfaceState = [self interfaceStateForRangeController:_rangeController]; if (ASInterfaceStateIncludesVisible(interfaceState)) { @@ -1185,6 +1224,10 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset { + if (scrollView != self && UITABLEVIEW_RESPONDS_TO_SELECTOR()) { + [super scrollViewWillEndDragging:scrollView withVelocity:velocity targetContentOffset:targetContentOffset]; + return; + } CGPoint contentOffset = scrollView.contentOffset; _deceleratingVelocity = CGPointMake( contentOffset.x - ((targetContentOffset != NULL) ? targetContentOffset->x : 0), @@ -1193,7 +1236,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; if (targetContentOffset != NULL) { ASDisplayNodeAssert(_batchContext != nil, @"Batch context should exist"); - [self _beginBatchFetchingIfNeededWithContentOffset:*targetContentOffset]; + [self _beginBatchFetchingIfNeededWithContentOffset:*targetContentOffset velocity:velocity]; } if (_asyncDelegateFlags.scrollViewWillEndDragging) { @@ -1201,8 +1244,25 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; } } +- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView +{ + if (scrollView != self && UITABLEVIEW_RESPONDS_TO_SELECTOR()) { + [super scrollViewDidEndDecelerating:scrollView]; + return; + } + _deceleratingVelocity = CGPointZero; + + if (_asyncDelegateFlags.scrollViewDidEndDecelerating) { + [_asyncDelegate scrollViewDidEndDecelerating:scrollView]; + } +} + - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { + if (scrollView != self && UITABLEVIEW_RESPONDS_TO_SELECTOR()) { + [super scrollViewWillBeginDragging:scrollView]; + return; + } for (_ASTableViewCell *tableViewCell in _cellsForVisibilityUpdates) { [[tableViewCell node] cellNodeVisibilityEvent:ASCellNodeVisibilityEventWillBeginDragging inScrollView:scrollView @@ -1215,6 +1275,10 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate { + if (scrollView != self && UITABLEVIEW_RESPONDS_TO_SELECTOR()) { + [super scrollViewDidEndDragging:scrollView willDecelerate:decelerate]; + return; + } for (_ASTableViewCell *tableViewCell in _cellsForVisibilityUpdates) { [[tableViewCell node] cellNodeVisibilityEvent:ASCellNodeVisibilityEventDidEndDragging inScrollView:scrollView @@ -1225,6 +1289,38 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; } } +#pragma mark - Misc + +- (BOOL)inverted +{ + return _inverted; +} + +- (void)setInverted:(BOOL)inverted +{ + _inverted = inverted; +} + +- (CGFloat)leadingScreensForBatching +{ + return _leadingScreensForBatching; +} + +- (void)setLeadingScreensForBatching:(CGFloat)leadingScreensForBatching +{ + _leadingScreensForBatching = leadingScreensForBatching; +} + +- (BOOL)automaticallyAdjustsContentOffset +{ + return _automaticallyAdjustsContentOffset; +} + +- (void)setAutomaticallyAdjustsContentOffset:(BOOL)automaticallyAdjustsContentOffset +{ + _automaticallyAdjustsContentOffset = automaticallyAdjustsContentOffset; +} + #pragma mark - Scroll Direction - (ASScrollDirection)scrollDirection @@ -1296,6 +1392,11 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; } } +- (id)batchFetchingDelegate +{ + return self.tableNode.batchFetchingDelegate; +} + - (void)_scheduleCheckForBatchFetchingForNumberOfChanges:(NSUInteger)changes { // Prevent fetching will continually trigger in a loop after reaching end of content and no new content was provided @@ -1317,12 +1418,12 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; return; } - [self _beginBatchFetchingIfNeededWithContentOffset:self.contentOffset]; + [self _beginBatchFetchingIfNeededWithContentOffset:self.contentOffset velocity:CGPointZero]; } -- (void)_beginBatchFetchingIfNeededWithContentOffset:(CGPoint)contentOffset +- (void)_beginBatchFetchingIfNeededWithContentOffset:(CGPoint)contentOffset velocity:(CGPoint)velocity { - if (ASDisplayShouldFetchBatchForScrollView(self, self.scrollDirection, ASScrollDirectionVerticalDirections, contentOffset)) { + if (ASDisplayShouldFetchBatchForScrollView(self, self.scrollDirection, ASScrollDirectionVerticalDirections, contentOffset, velocity)) { [self _beginBatchFetching]; } } @@ -1349,36 +1450,12 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; - (ASRangeController *)rangeController { - return _rangeController; + return _rangeController; } -- (NSArray *)visibleElementsForRangeController:(ASRangeController *)rangeController +- (NSHashTable *)visibleElementsForRangeController:(ASRangeController *)rangeController { - ASDisplayNodeAssertMainThread(); - - CGRect bounds = self.bounds; - // Calling indexPathsForVisibleRows will trigger UIKit to call reloadData if it never has, which can result - // in incorrect layout if performed at zero size. We can use the fact that nothing can be visible at zero size to return fast. - if (CGRectIsEmpty(bounds)) { - return @[]; - } - - NSArray *visibleIndexPaths = self.indexPathsForVisibleRows; - - // In some cases (grouped-style tables with particular geometry) indexPathsForVisibleRows will return extra index paths. - // This is a very serious issue because we rely on the fact that any node that is marked Visible is hosted inside of a cell, - // or else we may not mark it invisible before the node is released. See testIssue2252. - // Calling indexPathForCell: and cellForRowAtIndexPath: are both pretty expensive – this is the quickest approach we have. - // It would be possible to cache this NSPredicate as an ivar, but that would require unsafeifying self and calling @c bounds - // for each item. Since the performance cost is pretty small, prefer simplicity. - if (self.style == UITableViewStyleGrouped && visibleIndexPaths.count != self.visibleCells.count) { - visibleIndexPaths = [visibleIndexPaths filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(NSIndexPath *indexPath, NSDictionary * _Nullable bindings) { - return CGRectIntersectsRect(bounds, [self rectForRowAtIndexPath:indexPath]); - }]]; - } - - ASElementMap *map = _dataController.visibleMap; - return ASArrayByFlatMapping(visibleIndexPaths, NSIndexPath *indexPath, [map elementForItemAtIndexPath:indexPath]); + return ASPointerTableByFlatMapping(_visibleElements, id element, element); } - (ASScrollDirection)scrollDirectionForRangeController:(ASRangeController *)rangeController @@ -1410,10 +1487,11 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; } } -- (void)rangeController:(ASRangeController *)rangeController didUpdateWithChangeSet:(_ASHierarchyChangeSet *)changeSet +- (void)rangeController:(ASRangeController *)rangeController didUpdateWithChangeSet:(_ASHierarchyChangeSet *)changeSet updates:(dispatch_block_t)updates { ASDisplayNodeAssertMainThread(); if (!self.asyncDataSource || _updatingInResponseToInteractiveMove) { + updates(); [changeSet executeCompletionHandlerWithFinished:NO]; return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes } @@ -1424,6 +1502,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; if (self.test_enableSuperUpdateCallLogging) { NSLog(@"-[super reloadData]"); } + updates(); [super reloadData]; // Flush any range changes that happened as part of submitting the reload. [_rangeController updateIfNeeded]; @@ -1437,6 +1516,8 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; LOG(@"--- UITableView beginUpdates"); [super beginUpdates]; + + updates(); for (_ASHierarchyItemChange *change in [changeSet itemChangesOfType:_ASHierarchyChangeTypeReload]) { NSArray *indexPaths = change.indexPaths; @@ -1546,7 +1627,13 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; [changeSet executeCompletionHandlerWithFinished:YES]; } -#pragma mark - ASDataControllerDelegate +#pragma mark - ASDataControllerSource + +- (id)dataController:(ASDataController *)dataController viewModelForItemAtIndexPath:(NSIndexPath *)indexPath +{ + // Not currently supported for tables. Will be added when the collection API stabilizes. + return nil; +} - (ASCellNodeBlock)dataController:(ASDataController *)dataController nodeBlockAtIndexPath:(NSIndexPath *)indexPath { ASCellNodeBlock block = nil; @@ -1665,6 +1752,10 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; - (BOOL)dataController:(ASDataController *)dataController presentedSizeForElement:(ASCollectionElement *)element matchesSize:(CGSize)size { NSIndexPath *indexPath = [self indexPathForNode:element.node]; + if (indexPath == nil) { + ASDisplayNodeFailAssert(@"Data controller should not ask for presented size for element that is not presented."); + return YES; + } CGRect rect = [self rectForRowAtIndexPath:indexPath]; /** @@ -1678,13 +1769,6 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; return (fabs(rect.size.height - size.height) < FLT_EPSILON); } -#pragma mark - ASDataControllerEnvironmentDelegate - -- (id)dataControllerEnvironment -{ - return self.tableNode; -} - #pragma mark - _ASTableViewCellDelegate - (void)didLayoutSubviewsOfTableViewCell:(_ASTableViewCell *)tableViewCell @@ -1751,21 +1835,6 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; [self setNeedsLayout]; } -- (void)nodeDidRelayout:(ASCellNode *)node sizeChanged:(BOOL)sizeChanged -{ - ASDisplayNodeAssertMainThread(); - - if (!sizeChanged || _queuedNodeHeightUpdate || _remeasuringCellNodes) { - return; - } - - _queuedNodeHeightUpdate = YES; - [self performSelector:@selector(requeryNodeHeights) - withObject:nil - afterDelay:0 - inModes:@[ NSRunLoopCommonModes ]]; -} - // Cause UITableView to requery for the new height of this node - (void)requeryNodeHeights { diff --git a/Source/ASTableViewInternal.h b/Source/ASTableViewInternal.h index fc07a36ed5..b25bef538e 100644 --- a/Source/ASTableViewInternal.h +++ b/Source/ASTableViewInternal.h @@ -1,13 +1,18 @@ // // ASTableViewInternal.h -// AsyncDisplayKit -// -// Created by Huy Nguyen on 26/10/15. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK @@ -37,7 +42,7 @@ * * @param eventLog An event log passed through to the data controller. */ -- (instancetype)_initWithFrame:(CGRect)frame style:(UITableViewStyle)style dataControllerClass:(Class)dataControllerClass eventLog:(ASEventLog *)eventLog; +- (instancetype)_initWithFrame:(CGRect)frame style:(UITableViewStyle)style dataControllerClass:(Class)dataControllerClass owningNode:(ASTableNode *)tableNode eventLog:(ASEventLog *)eventLog; /// Set YES and we'll log every time we call [super insertRows…] etc @property (nonatomic) BOOL test_enableSuperUpdateCallLogging; diff --git a/Source/ASTableViewProtocols.h b/Source/ASTableViewProtocols.h index a662d2b517..0c0e679719 100644 --- a/Source/ASTableViewProtocols.h +++ b/Source/ASTableViewProtocols.h @@ -1,11 +1,18 @@ // // ASTableViewProtocols.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK diff --git a/Source/ASTextNode+Beta.h b/Source/ASTextNode+Beta.h index a20bac8625..8c75568111 100644 --- a/Source/ASTextNode+Beta.h +++ b/Source/ASTextNode+Beta.h @@ -1,13 +1,18 @@ // // ASTextNode+Beta.h -// AsyncDisplayKit -// -// Created by Luke on 1/25/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -16,6 +21,20 @@ NS_ASSUME_NONNULL_BEGIN +// When enabled, use ASTextNode2 for subclasses, random instances, or all instances of ASTextNode. +// See ASAvailability.h declaration of ASTEXTNODE_EXPERIMENT_GLOBAL_ENABLE for a compile-time option. +typedef NS_OPTIONS(NSUInteger, ASTextNodeExperimentOptions) { + // All subclass instances use the experimental implementation. + ASTextNodeExperimentSubclasses = 1 << 0, + // Random instances of ASTextNode (50% chance) (not subclasses) use experimental impl. + // Useful for profiling with apps that have no custom text node subclasses. + ASTextNodeExperimentRandomInstances = 1 << 1, + // All instances of ASTextNode itself use experimental implementation. Supersedes `.randomInstances`. + ASTextNodeExperimentAllInstances = 1 << 2, + // Add highlighting etc. for debugging. + ASTextNodeExperimentDebugging = 1 << 3 +}; + @interface ASTextNode () /** @@ -33,6 +52,19 @@ NS_ASSUME_NONNULL_BEGIN */ @property (nonatomic, assign) UIEdgeInsets textContainerInset; +/** + * Opt in to an experimental implementation of text node. The implementation may improve performance and correctness, + * but may not support all features and has not been thoroughly tested in production. + * + * @precondition You may not call this after allocating any text nodes. You may only call this once. + */ ++ (void)setExperimentOptions:(ASTextNodeExperimentOptions)options; + +/** + * Returns YES if this node is using the experimental implementation. NO otherwise. Will not change. + */ +@property (atomic, readonly) BOOL usingExperiment; + @end NS_ASSUME_NONNULL_END diff --git a/Source/ASTextNode.h b/Source/ASTextNode.h index 78bdd12005..f3670bb0fb 100644 --- a/Source/ASTextNode.h +++ b/Source/ASTextNode.h @@ -1,19 +1,32 @@ // // ASTextNode.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // +#import #import +#if ASTEXTNODE_EXPERIMENT_GLOBAL_ENABLE + #import +#endif NS_ASSUME_NONNULL_BEGIN @protocol ASTextNodeDelegate; +#if !ASTEXTNODE_EXPERIMENT_GLOBAL_ENABLE + /** * Highlight styles. */ @@ -225,6 +238,13 @@ typedef NS_ENUM(NSUInteger, ASTextNodeHighlightStyle) { @end +#else + +@interface ASTextNode : ASTextNode2 +@end + +#endif + /** * @abstract Text node delegate. */ @@ -280,6 +300,8 @@ typedef NS_ENUM(NSUInteger, ASTextNodeHighlightStyle) { @end +#if !ASTEXTNODE_EXPERIMENT_GLOBAL_ENABLE + @interface ASTextNode (Unavailable) - (instancetype)initWithLayerBlock:(ASDisplayNodeLayerBlock)viewBlock didLoadBlock:(nullable ASDisplayNodeDidLoadBlock)didLoadBlock __unavailable; @@ -312,6 +334,6 @@ typedef NS_ENUM(NSUInteger, ASTextNodeHighlightStyle) { @end +#endif + NS_ASSUME_NONNULL_END - - diff --git a/Source/ASTextNode.mm b/Source/ASTextNode.mm index 35e4209b72..18f0198efc 100644 --- a/Source/ASTextNode.mm +++ b/Source/ASTextNode.mm @@ -1,14 +1,24 @@ // // ASTextNode.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import +#import + +#if !ASTEXTNODE_EXPERIMENT_GLOBAL_ENABLE #import #include @@ -27,6 +37,7 @@ #import #import +#import #import /** @@ -44,24 +55,8 @@ static const CGFloat ASTextNodeHighlightLightOpacity = 0.11; static const CGFloat ASTextNodeHighlightDarkOpacity = 0.22; static NSString *ASTextNodeTruncationTokenAttributeName = @"ASTextNodeTruncationAttribute"; -struct ASTextNodeDrawParameter { - CGRect bounds; - UIColor *backgroundColor; -}; - #pragma mark - ASTextKitRenderer -// Not used at the moment but handy to have -/*ASDISPLAYNODE_INLINE NSUInteger ASHashFromCGRect(CGRect rect) -{ - return ((*(NSUInteger *)&rect.origin.x << 10 ^ *(NSUInteger *)&rect.origin.y) + (*(NSUInteger *)&rect.size.width << 10 ^ *(NSUInteger *)&rect.size.height)); -}*/ - -ASDISPLAYNODE_INLINE NSUInteger ASHashFromCGSize(CGSize size) -{ - return ((*(NSUInteger *)&size.width << 10 ^ *(NSUInteger *)&size.height)); -} - @interface ASTextNodeRendererKey : NSObject @property (assign, nonatomic) ASTextKitAttributes attributes; @property (assign, nonatomic) CGSize constrainedSize; @@ -71,7 +66,17 @@ ASDISPLAYNODE_INLINE NSUInteger ASHashFromCGSize(CGSize size) - (NSUInteger)hash { - return _attributes.hash() ^ ASHashFromCGSize(_constrainedSize); +#pragma clang diagnostic push +#pragma clang diagnostic warning "-Wpadded" + struct { + size_t attributesHash; + CGSize constrainedSize; +#pragma clang diagnostic pop + } data = { + _attributes.hash(), + _constrainedSize + }; + return ASHashBytes(&data, sizeof(data)); } - (BOOL)isEqual:(ASTextNodeRendererKey *)object @@ -119,6 +124,42 @@ static ASTextKitRenderer *rendererForAttributes(ASTextKitAttributes attributes, return renderer; } +#pragma mark - ASTextNodeDrawParameter + +@interface ASTextNodeDrawParameter : NSObject { +@package + ASTextKitAttributes _rendererAttributes; + UIColor *_backgroundColor; + UIEdgeInsets _textContainerInsets; +} +@end + +@implementation ASTextNodeDrawParameter + +- (instancetype)initWithRendererAttributes:(ASTextKitAttributes)rendererAttributes + backgroundColor:(/*nullable*/ UIColor *)backgroundColor + textContainerInsets:(UIEdgeInsets)textContainerInsets +{ + self = [super init]; + if (self != nil) { + _rendererAttributes = rendererAttributes; + _backgroundColor = backgroundColor; + _textContainerInsets = textContainerInsets; + } + return self; +} + +- (ASTextKitRenderer *)rendererForBounds:(CGRect)bounds +{ + CGRect rect = UIEdgeInsetsInsetRect(bounds, _textContainerInsets); + return rendererForAttributes(_rendererAttributes, rect.size); +} + +@end + + +#pragma mark - ASTextNode + @interface ASTextNode () @end @@ -143,24 +184,10 @@ static ASTextKitRenderer *rendererForAttributes(ASTextKitAttributes attributes, NSRange _highlightRange; ASHighlightOverlayLayer *_activeHighlightLayer; - ASTextNodeDrawParameter _drawParameter; - UILongPressGestureRecognizer *_longPressGestureRecognizer; } @dynamic placeholderEnabled; -#pragma mark - NSObject - -+ (void)initialize -{ - [super initialize]; - - if (self != [ASTextNode class]) { - // Prevent custom drawing in subclasses - ASDisplayNodeAssert(!ASSubclassOverridesClassSelector([ASTextNode class], self, @selector(drawRect:withParameters:isCancelled:isRasterizing:)), @"Subclass %@ must not override drawRect:withParameters:isCancelled:isRasterizing: method. Custom drawing in %@ subclass is not supported.", NSStringFromClass(self), NSStringFromClass([ASTextNode class])); - } -} - static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; - (instancetype)init @@ -225,7 +252,7 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; NSMutableArray *result = [super propertiesForDescription]; NSString *plainString = [self _plainStringForDescription]; if (plainString.length > 0) { - [result insertObject:@{ @"text" : ASStringWithQuotesIfMultiword(plainString) } atIndex:0]; + [result addObject:@{ (id)kCFNull : ASStringWithQuotesIfMultiword(plainString) }]; } return result; } @@ -242,14 +269,6 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; #pragma mark - ASDisplayNode -- (void)clearContents -{ - // We discard the backing store and renderer to prevent the very large - // memory overhead of maintaining these for all text nodes. They can be - // regenerated when layout is necessary. - [super clearContents]; // ASDisplayNode will set layer.contents = nil -} - - (void)didLoad { [super didLoad]; @@ -264,27 +283,55 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; } } +- (BOOL)supportsLayerBacking +{ + if (!super.supportsLayerBacking) { + return NO; + } + + // If the text contains any links, return NO. + NSAttributedString *attributedText = self.attributedText; + NSRange range = NSMakeRange(0, attributedText.length); + for (NSString *linkAttributeName in _linkAttributeNames) { + __block BOOL hasLink = NO; + [attributedText enumerateAttribute:linkAttributeName inRange:range options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired usingBlock:^(id _Nullable value, NSRange range, BOOL * _Nonnull stop) { + hasLink = (value != nil); + *stop = YES; + }]; + if (hasLink) { + return NO; + } + } + return YES; +} + #pragma mark - Renderer Management - (ASTextKitRenderer *)_renderer { - CGSize constrainedSize = self.threadSafeBounds.size; - return [self _rendererWithBoundsSlow:{.size = constrainedSize}]; + ASDN::MutexLocker l(__instanceLock__); + return [self _locked_renderer]; } -- (ASTextKitRenderer *)_rendererWithBoundsSlow:(CGRect)bounds +- (ASTextKitRenderer *)_rendererWithBounds:(CGRect)bounds { ASDN::MutexLocker l(__instanceLock__); - bounds.size.width -= (_textContainerInset.left + _textContainerInset.right); - bounds.size.height -= (_textContainerInset.top + _textContainerInset.bottom); - return rendererForAttributes([self _rendererAttributes], bounds.size); + return [self _locked_rendererWithBounds:bounds]; } - -- (ASTextKitAttributes)_rendererAttributes +- (ASTextKitRenderer *)_locked_renderer +{ + return [self _locked_rendererWithBounds:[self _locked_threadSafeBounds]]; +} + +- (ASTextKitRenderer *)_locked_rendererWithBounds:(CGRect)bounds +{ + bounds = UIEdgeInsetsInsetRect(bounds, _textContainerInset); + return rendererForAttributes([self _locked_rendererAttributes], bounds.size); +} + +- (ASTextKitAttributes)_locked_rendererAttributes { - ASDN::MutexLocker l(__instanceLock__); - return { .attributedString = _attributedText, .truncationAttributedString = [self _locked_composedTruncationText], @@ -334,7 +381,7 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; [self setNeedsDisplay]; - ASTextKitRenderer *renderer = [self _rendererWithBoundsSlow:{.size = constrainedSize}]; + ASTextKitRenderer *renderer = [self _locked_rendererWithBounds:{.size = constrainedSize}]; CGSize size = renderer.size; if (_attributedText.length > 0) { self.style.ascender = [[self class] ascenderWithAttributedString:_attributedText]; @@ -449,30 +496,23 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; { ASDN::MutexLocker l(__instanceLock__); - _drawParameter = { - .backgroundColor = self.backgroundColor, - .bounds = self.bounds - }; - return nil; + return [[ASTextNodeDrawParameter alloc] initWithRendererAttributes:[self _locked_rendererAttributes] + backgroundColor:self.backgroundColor + textContainerInsets:_textContainerInset]; } - -- (void)drawRect:(CGRect)bounds withParameters:(id )p isCancelled:(asdisplaynode_iscancelled_block_t)isCancelledBlock isRasterizing:(BOOL)isRasterizing; ++ (void)drawRect:(CGRect)bounds withParameters:(id)parameters isCancelled:(asdisplaynode_iscancelled_block_t)isCancelledBlock isRasterizing:(BOOL)isRasterizing { - ASDN::MutexLocker l(__instanceLock__); - - ASTextNodeDrawParameter drawParameter = _drawParameter; - CGRect drawParameterBounds = drawParameter.bounds; - UIColor *backgroundColor = isRasterizing ? nil : drawParameter.backgroundColor; + ASTextNodeDrawParameter *drawParameter = (ASTextNodeDrawParameter *)parameters; + UIColor *backgroundColor = (isRasterizing || drawParameter == nil) ? nil : drawParameter->_backgroundColor; + UIEdgeInsets textContainerInsets = drawParameter ? drawParameter->_textContainerInsets : UIEdgeInsetsZero; + ASTextKitRenderer *renderer = [drawParameter rendererForBounds:bounds]; CGContextRef context = UIGraphicsGetCurrentContext(); ASDisplayNodeAssert(context, @"This is no good without a context."); - + CGContextSaveGState(context); - - CGContextTranslateCTM(context, _textContainerInset.left, _textContainerInset.top); - - ASTextKitRenderer *renderer = [self _rendererWithBoundsSlow:drawParameterBounds]; + CGContextTranslateCTM(context, textContainerInsets.left, textContainerInsets.top); // Fill background if (backgroundColor != nil) { @@ -481,8 +521,7 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; } // Draw text - [renderer drawInContext:context bounds:drawParameterBounds]; - + [renderer drawInContext:context bounds:bounds]; CGContextRestoreGState(context); } @@ -509,7 +548,7 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; ASDN::MutexLocker l(__instanceLock__); - ASTextKitRenderer *renderer = [self _renderer]; + ASTextKitRenderer *renderer = [self _locked_renderer]; NSRange visibleRange = renderer.firstVisibleRange; NSAttributedString *attributedString = _attributedText; NSRange clampedRange = NSIntersectionRange(visibleRange, NSMakeRange(0, attributedString.length)); @@ -815,7 +854,7 @@ static CGRect ASTextNodeAdjustRenderRectForShadowPadding(CGRect rendererRect, UI { ASDN::MutexLocker l(__instanceLock__); - NSArray *rects = [[self _renderer] rectsForTextRange:textRange measureOption:measureOption]; + NSArray *rects = [[self _locked_renderer] rectsForTextRange:textRange measureOption:measureOption]; NSMutableArray *adjustedRects = [NSMutableArray array]; for (NSValue *rectValue in rects) { @@ -833,7 +872,7 @@ static CGRect ASTextNodeAdjustRenderRectForShadowPadding(CGRect rendererRect, UI { ASDN::MutexLocker l(__instanceLock__); - CGRect rect = [[self _renderer] trailingRect]; + CGRect rect = [[self _locked_renderer] trailingRect]; return ASTextNodeAdjustRenderRectForShadowPadding(rect, self.shadowPadding); } @@ -841,7 +880,7 @@ static CGRect ASTextNodeAdjustRenderRectForShadowPadding(CGRect rendererRect, UI { ASDN::MutexLocker l(__instanceLock__); - CGRect frame = [[self _renderer] frameForTextRange:textRange]; + CGRect frame = [[self _locked_renderer] frameForTextRange:textRange]; return ASTextNodeAdjustRenderRectForShadowPadding(frame, self.shadowPadding); } @@ -871,7 +910,7 @@ static CGRect ASTextNodeAdjustRenderRectForShadowPadding(CGRect rendererRect, UI UIGraphicsBeginImageContext(size); [self.placeholderColor setFill]; - ASTextKitRenderer *renderer = [self _renderer]; + ASTextKitRenderer *renderer = [self _locked_renderer]; NSRange visibleRange = renderer.firstVisibleRange; // cap height is both faster and creates less subpixel blending @@ -949,7 +988,7 @@ static CGRect ASTextNodeAdjustRenderRectForShadowPadding(CGRect rendererRect, UI NSRange visibleRange = NSMakeRange(0, 0); { ASDN::MutexLocker l(__instanceLock__); - visibleRange = [self _renderer].firstVisibleRange; + visibleRange = [self _locked_renderer].firstVisibleRange; } NSRange truncationMessageRange = [self _additionalTruncationMessageRangeWithVisibleRange:visibleRange]; [self _setHighlightRange:truncationMessageRange forAttributeName:ASTextNodeTruncationTokenAttributeName value:nil animated:YES]; @@ -1131,15 +1170,9 @@ static CGRect ASTextNodeAdjustRenderRectForShadowPadding(CGRect rendererRect, UI } - (UIEdgeInsets)shadowPadding -{ - return [self shadowPaddingWithRenderer:[self _renderer]]; -} - -- (UIEdgeInsets)shadowPaddingWithRenderer:(ASTextKitRenderer *)renderer { ASDN::MutexLocker l(__instanceLock__); - - return renderer.shadower.shadowPadding; + return [self _locked_renderer].shadower.shadowPadding; } #pragma mark - Truncation Message @@ -1203,8 +1236,7 @@ static NSAttributedString *DefaultTruncationAttributedString() { ASDN::MutexLocker l(__instanceLock__); - ASTextKitRenderer *renderer = [self _renderer]; - return renderer.isTruncated; + return [[self _locked_renderer] isTruncated]; } - (void)setPointSizeScaleFactors:(NSArray *)pointSizeScaleFactors @@ -1240,7 +1272,7 @@ static NSAttributedString *DefaultTruncationAttributedString() { ASDN::MutexLocker l(__instanceLock__); - return [[self _renderer] lineCount]; + return [[self _locked_renderer] lineCount]; } #pragma mark - Truncation Message @@ -1352,6 +1384,70 @@ static NSAttributedString *DefaultTruncationAttributedString() } #endif +static ASDN::Mutex _experimentLock; +static ASTextNodeExperimentOptions _experimentOptions; +static BOOL _hasAllocatedNode; + ++ (void)setExperimentOptions:(ASTextNodeExperimentOptions)options +{ + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + ASDN::MutexLocker lock(_experimentLock); + + // They must call this before allocating any text nodes. + ASDisplayNodeAssertFalse(_hasAllocatedNode); + + _experimentOptions = options; + + // Set superclass of all subclasses to ASTextNode2 + if (options & ASTextNodeExperimentSubclasses) { + unsigned int classCount; + Class originalClass = [ASTextNode class]; + Class newClass = [ASTextNode2 class]; + Class *classes = objc_copyClassList(&classCount); + for (int i = 0; i < classCount; i++) { + Class c = classes[i]; + if (class_getSuperclass(c) == originalClass) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + class_setSuperclass(c, newClass); +#pragma clang diagnostic pop + } + } + free(classes); + } + + if (options & ASTextNodeExperimentDebugging) { + [ASTextNode2 enableDebugging]; + } + }); +} + ++ (id)allocWithZone:(struct _NSZone *)zone +{ + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + ASDN::MutexLocker lock(_experimentLock); + _hasAllocatedNode = YES; + }); + + // All instances || (random instances && rand() != 0) + BOOL useExperiment = (_experimentOptions & ASTextNodeExperimentAllInstances) + || ((_experimentOptions & ASTextNodeExperimentRandomInstances) + && (arc4random_uniform(2) != 0)); + + if (useExperiment) { + return (ASTextNode *)[ASTextNode2 allocWithZone:zone]; + } else { + return [super allocWithZone:zone]; + } +} + +- (BOOL)usingExperiment +{ + return NO; +} + @end @implementation ASTextNode (Deprecated) @@ -1377,3 +1473,10 @@ static NSAttributedString *DefaultTruncationAttributedString() } @end + +#else + +@implementation ASTextNode +@end + +#endif diff --git a/Source/ASTextNode2.h b/Source/ASTextNode2.h new file mode 100644 index 0000000000..aef3ac468d --- /dev/null +++ b/Source/ASTextNode2.h @@ -0,0 +1,247 @@ +// +// ASTextNode2.h +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#if !ASTEXTNODE_EXPERIMENT_GLOBAL_ENABLE +// Import this to get ASTextNodeHighlightStyle +#import +#else +@protocol ASTextNodeDelegate; + +/** + * Highlight styles. + */ +typedef NS_ENUM(NSUInteger, ASTextNodeHighlightStyle) { + /** + * Highlight style for text on a light background. + */ + ASTextNodeHighlightStyleLight, + + /** + * Highlight style for text on a dark background. + */ + ASTextNodeHighlightStyleDark +}; +#endif + +NS_ASSUME_NONNULL_BEGIN + +/** + @abstract Draws interactive rich text. + @discussion Backed by the code in TextExperiment folder, on top of CoreText. + */ +@interface ASTextNode2 : ASControlNode + +/** + @abstract The styled text displayed by the node. + @discussion Defaults to nil, no text is shown. + For inline image attachments, add an attribute of key NSAttachmentAttributeName, with a value of an NSTextAttachment. + */ +@property (nullable, nonatomic, copy) NSAttributedString *attributedText; + +#pragma mark - Truncation + +/** + @abstract The attributedText to use when the text must be truncated. + @discussion Defaults to a localized ellipsis character. + */ +@property (nullable, nonatomic, copy) NSAttributedString *truncationAttributedText; + +/** + @summary The second attributed string appended for truncation. + @discussion This string will be highlighted on touches. + @default nil + */ +@property (nullable, nonatomic, copy) NSAttributedString *additionalTruncationMessage; + +/** + @abstract Determines how the text is truncated to fit within the receiver's maximum size. + @discussion Defaults to NSLineBreakByWordWrapping. + @note Setting a truncationMode in attributedString will override the truncation mode set here. + */ +@property (nonatomic, assign) NSLineBreakMode truncationMode; + +/** + @abstract If the text node is truncated. Text must have been sized first. + */ +@property (nonatomic, readonly, assign, getter=isTruncated) BOOL truncated; + +/** + @abstract The maximum number of lines to render of the text before truncation. + @default 0 (No limit) + */ +@property (nonatomic, assign) NSUInteger maximumNumberOfLines; + +/** + @abstract The number of lines in the text. Text must have been sized first. + */ +@property (nonatomic, readonly, assign) NSUInteger lineCount; + +/** + * An array of path objects representing the regions where text should not be displayed. + * + * @discussion The default value of this property is an empty array. You can + * assign an array of UIBezierPath objects to exclude text from one or more regions in + * the text node's bounds. You can use this property to have text wrap around images, + * shapes or other text like a fancy magazine. + */ +@property (nullable, nonatomic, strong) NSArray *exclusionPaths; + +#pragma mark - Placeholders + +/** + * @abstract ASTextNode has a special placeholder behavior when placeholderEnabled is YES. + * + * @discussion Defaults to NO. When YES, it draws rectangles for each line of text, + * following the true shape of the text's wrapping. This visually mirrors the overall + * shape and weight of paragraphs, making the appearance of the finished text less jarring. + */ +@property (nonatomic, assign) BOOL placeholderEnabled; + +/** + @abstract The placeholder color. + */ +@property (nullable, nonatomic, strong) UIColor *placeholderColor; + +/** + @abstract Inset each line of the placeholder. + */ +@property (nonatomic, assign) UIEdgeInsets placeholderInsets; + +#pragma mark - Shadow + +/** + @abstract When you set these ASDisplayNode properties, they are composited into the bitmap instead of being applied by CA. + + @property (nonatomic, assign) CGColorRef shadowColor; + @property (nonatomic, assign) CGFloat shadowOpacity; + @property (nonatomic, assign) CGSize shadowOffset; + @property (nonatomic, assign) CGFloat shadowRadius; + */ + +/** + @abstract The number of pixels used for shadow padding on each side of the receiver. + @discussion Each inset will be less than or equal to zero, so that applying + UIEdgeInsetsRect(boundingRectForText, shadowPadding) + will return a CGRect large enough to fit both the text and the appropriate shadow padding. + */ +@property (nonatomic, readonly, assign) UIEdgeInsets shadowPadding; + +#pragma mark - Positioning + +/** + @abstract Returns an array of rects bounding the characters in a given text range. + @param textRange A range of text. Must be valid for the receiver's string. + @discussion Use this method to detect all the different rectangles a given range of text occupies. + The rects returned are not guaranteed to be contiguous (for example, if the given text range spans + a line break, the rects returned will be on opposite sides and different lines). The rects returned + are in the coordinate system of the receiver. + */ +- (NSArray *)rectsForTextRange:(NSRange)textRange AS_WARN_UNUSED_RESULT; + +/** + @abstract Returns an array of rects used for highlighting the characters in a given text range. + @param textRange A range of text. Must be valid for the receiver's string. + @discussion Use this method to detect all the different rectangles the highlights of a given range of text occupies. + The rects returned are not guaranteed to be contiguous (for example, if the given text range spans + a line break, the rects returned will be on opposite sides and different lines). The rects returned + are in the coordinate system of the receiver. This method is useful for visual coordination with a + highlighted range of text. + */ +- (NSArray *)highlightRectsForTextRange:(NSRange)textRange AS_WARN_UNUSED_RESULT; + +/** + @abstract Returns a bounding rect for the given text range. + @param textRange A range of text. Must be valid for the receiver's string. + @discussion The height of the frame returned is that of the receiver's line-height; adjustment for + cap-height and descenders is not performed. This method raises an exception if textRange is not + a valid substring range of the receiver's string. + */ +- (CGRect)frameForTextRange:(NSRange)textRange AS_WARN_UNUSED_RESULT; + +/** + @abstract Returns the trailing rectangle of space in the receiver, after the final character. + @discussion Use this method to detect which portion of the receiver is not occupied by characters. + The rect returned is in the coordinate system of the receiver. + */ +- (CGRect)trailingRect AS_WARN_UNUSED_RESULT; + + +#pragma mark - Actions + +/** + @abstract The set of attribute names to consider links. Defaults to NSLinkAttributeName. + */ +@property (nonatomic, copy) NSArray *linkAttributeNames; + +/** + @abstract Indicates whether the receiver has an entity at a given point. + @param point The point, in the receiver's coordinate system. + @param attributeNameOut The name of the attribute at the point. Can be NULL. + @param rangeOut The ultimate range of the found text. Can be NULL. + @result YES if an entity exists at `point`; NO otherwise. + */ +- (nullable id)linkAttributeValueAtPoint:(CGPoint)point attributeName:(out NSString * _Nullable * _Nullable)attributeNameOut range:(out NSRange * _Nullable)rangeOut AS_WARN_UNUSED_RESULT; + +/** + @abstract The style to use when highlighting text. + */ +@property (nonatomic, assign) ASTextNodeHighlightStyle highlightStyle; + +/** + @abstract The range of text highlighted by the receiver. Changes to this property are not animated by default. + */ +@property (nonatomic, assign) NSRange highlightRange; + +/** + @abstract Set the range of text to highlight, with optional animation. + + @param highlightRange The range of text to highlight. + + @param animated Whether the text should be highlighted with an animation. + */ +- (void)setHighlightRange:(NSRange)highlightRange animated:(BOOL)animated; + +/** + @abstract Responds to actions from links in the text node. + @discussion The delegate must be set before the node is loaded, and implement + textNode:longPressedLinkAttribute:value:atPoint:textRange: in order for + the long press gesture recognizer to be installed. + */ +@property (nonatomic, weak) id delegate; + +/** + @abstract If YES and a long press is recognized, touches are cancelled. Default is NO + */ +@property (nonatomic, assign) BOOL longPressCancelsTouches; + +/** + @abstract if YES will not intercept touches for non-link areas of the text. Default is NO. + */ +@property (nonatomic, assign) BOOL passthroughNonlinkTouches; + ++ (void)enableDebugging; + +@end + +@interface ASTextNode2 (Unavailable) + +- (instancetype)initWithLayerBlock:(ASDisplayNodeLayerBlock)viewBlock didLoadBlock:(nullable ASDisplayNodeDidLoadBlock)didLoadBlock __unavailable; + +- (instancetype)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock didLoadBlock:(nullable ASDisplayNodeDidLoadBlock)didLoadBlock __unavailable; + +@end + +NS_ASSUME_NONNULL_END + + diff --git a/Source/ASTextNode2.mm b/Source/ASTextNode2.mm new file mode 100644 index 0000000000..48ff3c4bb9 --- /dev/null +++ b/Source/ASTextNode2.mm @@ -0,0 +1,1171 @@ +// +// ASTextNode2.mm +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import // Definition of ASTextNodeDelegate + +#import +#import + +#import +#import +#import +#import + +#import +#import +#import + +#import + +#import +#import +#import + +@interface ASTextCacheValue : NSObject { + @package + ASDN::Mutex _m; + std::deque> _layouts; +} +@end +@implementation ASTextCacheValue +@end + +/** + * If set, we will record all values set to attributedText into an array + * and once we get 2000, we'll write them all out into a plist file. + * + * This is useful for gathering realistic text data sets from apps for performance + * testing. + */ +#define AS_TEXTNODE2_RECORD_ATTRIBUTED_STRINGS 0 + +#define AS_TEXT_ALERT_UNIMPLEMENTED_FEATURE() { \ + static dispatch_once_t onceToken; \ + dispatch_once(&onceToken, ^{ \ + NSLog(@"[Texture] Warning: Feature %@ is unimplemented in the experimental text node.", NSStringFromSelector(_cmd)); \ + });\ +} + +static const CGFloat ASTextNodeHighlightLightOpacity = 0.11; +static const CGFloat ASTextNodeHighlightDarkOpacity = 0.22; +static NSString *ASTextNodeTruncationTokenAttributeName = @"ASTextNodeTruncationAttribute"; + +@interface ASTextNode2 () + +@end + +@implementation ASTextNode2 { + ASTextContainer *_textContainer; + + CGSize _shadowOffset; + CGColorRef _shadowColor; + CGFloat _shadowOpacity; + CGFloat _shadowRadius; + + NSAttributedString *_attributedText; + NSAttributedString *_composedTruncationText; + NSArray *_pointSizeScaleFactors; + + NSString *_highlightedLinkAttributeName; + id _highlightedLinkAttributeValue; + ASTextNodeHighlightStyle _highlightStyle; + NSRange _highlightRange; + ASHighlightOverlayLayer *_activeHighlightLayer; + + UILongPressGestureRecognizer *_longPressGestureRecognizer; +} +@dynamic placeholderEnabled; + +static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; + +- (instancetype)init +{ + if (self = [super init]) { + _textContainer = [[ASTextContainer alloc] init]; + // Load default values from superclass. + _shadowOffset = [super shadowOffset]; + _shadowColor = CGColorRetain([super shadowColor]); + _shadowOpacity = [super shadowOpacity]; + _shadowRadius = [super shadowRadius]; + + // Disable user interaction for text node by default. + self.userInteractionEnabled = NO; + self.needsDisplayOnBoundsChange = YES; + + _textContainer.truncationType = ASTextTruncationTypeEnd; + + // The common case is for a text node to be non-opaque and blended over some background. + self.opaque = NO; + self.backgroundColor = [UIColor clearColor]; + + self.linkAttributeNames = DefaultLinkAttributeNames; + + // Accessibility + self.isAccessibilityElement = YES; + self.accessibilityTraits = UIAccessibilityTraitStaticText; + + // Placeholders + // Disabled by default in ASDisplayNode, but add a few options for those who toggle + // on the special placeholder behavior of ASTextNode. + _placeholderColor = ASDisplayNodeDefaultPlaceholderColor(); + _placeholderInsets = UIEdgeInsetsMake(1.0, 0.0, 1.0, 0.0); + } + + return self; +} + +- (void)dealloc +{ + CGColorRelease(_shadowColor); + + if (_longPressGestureRecognizer) { + _longPressGestureRecognizer.delegate = nil; + [_longPressGestureRecognizer removeTarget:nil action:NULL]; + [self.view removeGestureRecognizer:_longPressGestureRecognizer]; + } +} + +#pragma mark - Description + +- (NSString *)_plainStringForDescription +{ + NSString *plainString = [[self.attributedText string] stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]]; + if (plainString.length > 50) { + plainString = [[plainString substringToIndex:50] stringByAppendingString:@"…"]; + } + return plainString; +} + +- (NSMutableArray *)propertiesForDescription +{ + NSMutableArray *result = [super propertiesForDescription]; + NSString *plainString = [self _plainStringForDescription]; + if (plainString.length > 0) { + [result insertObject:@{ @"text" : ASStringWithQuotesIfMultiword(plainString) } atIndex:0]; + } + return result; +} + +- (NSMutableArray *)propertiesForDebugDescription +{ + NSMutableArray *result = [super propertiesForDebugDescription]; + NSString *plainString = [self _plainStringForDescription]; + if (plainString.length > 0) { + [result insertObject:@{ @"text" : ASStringWithQuotesIfMultiword(plainString) } atIndex:0]; + } + return result; +} + +#pragma mark - ASDisplayNode + +- (void)didLoad +{ + [super didLoad]; + + // If we are view-backed and the delegate cares, support the long-press callback. + SEL longPressCallback = @selector(textNode:longPressedLinkAttribute:value:atPoint:textRange:); + if (!self.isLayerBacked && [_delegate respondsToSelector:longPressCallback]) { + _longPressGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(_handleLongPress:)]; + _longPressGestureRecognizer.cancelsTouchesInView = self.longPressCancelsTouches; + _longPressGestureRecognizer.delegate = self; + [self.view addGestureRecognizer:_longPressGestureRecognizer]; + } +} + +- (BOOL)supportsLayerBacking +{ + if (!super.supportsLayerBacking) { + return NO; + } + + // If the text contains any links, return NO. + NSAttributedString *attributedText = self.attributedText; + NSRange range = NSMakeRange(0, attributedText.length); + for (NSString *linkAttributeName in _linkAttributeNames) { + __block BOOL hasLink = NO; + [attributedText enumerateAttribute:linkAttributeName inRange:range options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired usingBlock:^(id _Nullable value, NSRange range, BOOL * _Nonnull stop) { + hasLink = (value != nil); + *stop = YES; + }]; + if (hasLink) { + return NO; + } + } + return YES; +} + +#pragma mark - Layout and Sizing + +- (void)setTextContainerInset:(UIEdgeInsets)textContainerInset +{ + BOOL needsUpdate = !UIEdgeInsetsEqualToEdgeInsets(_textContainer.insets, textContainerInset); + _textContainer.insets = textContainerInset; + + if (needsUpdate) { + [self setNeedsLayout]; + } +} + +- (UIEdgeInsets)textContainerInset +{ + return _textContainer.insets; +} + +- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize +{ + ASDisplayNodeAssert(constrainedSize.width >= 0, @"Constrained width for text (%f) is too narrow", constrainedSize.width); + ASDisplayNodeAssert(constrainedSize.height >= 0, @"Constrained height for text (%f) is too short", constrainedSize.height); + + ASTextContainer *container = [_textContainer copy]; + NSAttributedString *attributedText = self.attributedText; + container.size = constrainedSize; + [self _ensureTruncationText]; + + NSMutableAttributedString *mutableText = [attributedText mutableCopy]; + [self prepareAttributedStringForDrawing:mutableText]; + ASTextLayout *layout = [ASTextNode2 compatibleLayoutWithContainer:container text:mutableText]; + + [self setNeedsDisplay]; + + return layout.textBoundingSize; +} + +#pragma mark - Modifying User Text + +// Returns the ascender of the first character in attributedString by also including the line height if specified in paragraph style. ++ (CGFloat)ascenderWithAttributedString:(NSAttributedString *)attributedString +{ + UIFont *font = [attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL]; + NSParagraphStyle *paragraphStyle = [attributedString attribute:NSParagraphStyleAttributeName atIndex:0 effectiveRange:NULL]; + if (!paragraphStyle) { + return font.ascender; + } + CGFloat lineHeight = MAX(font.lineHeight, paragraphStyle.minimumLineHeight); + if (paragraphStyle.maximumLineHeight > 0) { + lineHeight = MIN(lineHeight, paragraphStyle.maximumLineHeight); + } + return lineHeight + font.descender; +} + +- (NSAttributedString *)attributedText +{ + ASDN::MutexLocker l(__instanceLock__); + return _attributedText; +} + +- (void)setAttributedText:(NSAttributedString *)attributedText +{ + + if (attributedText == nil) { + attributedText = [[NSAttributedString alloc] initWithString:@"" attributes:nil]; + } + + // Don't hold textLock for too long. + { + ASDN::MutexLocker l(__instanceLock__); + if (ASObjectIsEqual(attributedText, _attributedText)) { + return; + } + + _attributedText = attributedText; +#if AS_TEXTNODE2_RECORD_ATTRIBUTED_STRINGS + [ASTextNode _registerAttributedText:_attributedText]; +#endif + } + + // Since truncation text matches style of attributedText, invalidate it now. + [self _invalidateTruncationText]; + + NSUInteger length = attributedText.length; + if (length > 0) { + self.style.ascender = [[self class] ascenderWithAttributedString:attributedText]; + self.style.descender = [[attributedText attribute:NSFontAttributeName atIndex:attributedText.length - 1 effectiveRange:NULL] descender]; + } + + // Tell the display node superclasses that the cached layout is incorrect now + [self setNeedsLayout]; + + // Force display to create renderer with new size and redisplay with new string + [self setNeedsDisplay]; + + + // Accessiblity + self.accessibilityLabel = attributedText.string; + self.isAccessibilityElement = (length != 0); // We're an accessibility element by default if there is a string. +} + +#pragma mark - Text Layout + +- (void)setExclusionPaths:(NSArray *)exclusionPaths +{ + _textContainer.exclusionPaths = exclusionPaths; + + [self setNeedsLayout]; + [self setNeedsDisplay]; +} + +- (NSArray *)exclusionPaths +{ + return _textContainer.exclusionPaths; +} + +- (void)prepareAttributedStringForDrawing:(NSMutableAttributedString *)attributedString +{ + ASDN::MutexLocker lock(__instanceLock__); + + // Apply paragraph style if needed + [attributedString enumerateAttribute:NSParagraphStyleAttributeName inRange:NSMakeRange(0, attributedString.length) options:kNilOptions usingBlock:^(NSParagraphStyle *style, NSRange range, BOOL * _Nonnull stop) { + if (style == nil || style.lineBreakMode == _truncationMode) { + return; + } + + NSMutableParagraphStyle *paragraphStyle = [style mutableCopy] ?: [[NSMutableParagraphStyle alloc] init]; + paragraphStyle.lineBreakMode = _truncationMode; + [attributedString addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:range]; + }]; + + // Apply background color if needed + UIColor *backgroundColor = self.backgroundColor; + if (CGColorGetAlpha(backgroundColor.CGColor) > 0) { + [attributedString addAttribute:NSBackgroundColorAttributeName value:backgroundColor range:NSMakeRange(0, attributedString.length)]; + } + + // Apply shadow if needed + if (_shadowOpacity > 0 && (_shadowRadius != 0 || !CGSizeEqualToSize(_shadowOffset, CGSizeZero)) && CGColorGetAlpha(_shadowColor) > 0) { + NSShadow *shadow = [[NSShadow alloc] init]; + if (_shadowOpacity != 1) { + shadow.shadowColor = [UIColor colorWithCGColor:CGColorCreateCopyWithAlpha(_shadowColor, _shadowOpacity * CGColorGetAlpha(_shadowColor))]; + } else { + shadow.shadowColor = [UIColor colorWithCGColor:_shadowColor]; + } + shadow.shadowOffset = _shadowOffset; + shadow.shadowBlurRadius = _shadowRadius; + [attributedString addAttribute:NSShadowAttributeName value:shadow range:NSMakeRange(0, attributedString.length)]; + } +} + +#pragma mark - Drawing + +- (NSObject *)drawParametersForAsyncLayer:(_ASDisplayLayer *)layer +{ + [self _ensureTruncationText]; + ASTextContainer *copiedContainer = [_textContainer copy]; + copiedContainer.size = self.bounds.size; + NSMutableAttributedString *mutableText = [self.attributedText mutableCopy] ?: [[NSMutableAttributedString alloc] init]; + [self prepareAttributedStringForDrawing:mutableText]; + return @{ + @"container": copiedContainer, + @"text": mutableText + }; +} + +/** + * If it can't find a compatible layout, this method creates one. + */ ++ (ASTextLayout *)compatibleLayoutWithContainer:(ASTextContainer *)container + text:(NSAttributedString *)text + +{ + static ASDN::Mutex layoutCacheLock; + static NSCache *textLayoutCache; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + textLayoutCache = [[NSCache alloc] init]; + }); + + ASTextCacheValue *cacheValue = ({ + ASDN::MutexLocker lock(layoutCacheLock); + cacheValue = [textLayoutCache objectForKey:text]; + if (cacheValue == nil) { + cacheValue = [[ASTextCacheValue alloc] init]; + [textLayoutCache setObject:cacheValue forKey:text]; + } + cacheValue; + }); + + CGRect containerBounds = (CGRect){ .size = container.size }; + { + ASDN::MutexLocker lock(cacheValue->_m); + for (auto &t : cacheValue->_layouts) { + CGSize constrainedSize = std::get<0>(t); + ASTextLayout *layout = std::get<1>(t); + + CGSize layoutSize = layout.textBoundingSize; + // 1. CoreText can return frames that are narrower than the constrained width, for obvious reasons. + // 2. CoreText can return frames that are slightly wider than the constrained width, for some reason. + // We have to trust that somehow it's OK to try and draw within our size constraint, despite the return value. + // 3. Thus, those two values (constrained width & returned width) form a range, where + // intermediate values in that range will be snapped. Thus, we can use a given layout as long as our + // width is in that range, between the min and max of those two values. + CGRect minRect = CGRectMake(0, 0, MIN(layoutSize.width, constrainedSize.width), MIN(layoutSize.height, constrainedSize.height)); + if (!CGRectContainsRect(containerBounds, minRect)) { + continue; + } + CGRect maxRect = CGRectMake(0, 0, MAX(layoutSize.width, constrainedSize.width), MAX(layoutSize.height, constrainedSize.height)); + if (!CGRectContainsRect(maxRect, containerBounds)) { + continue; + } + + // Now check container params. + ASTextContainer *otherContainer = layout.container; + if (!UIEdgeInsetsEqualToEdgeInsets(container.insets, otherContainer.insets)) { + continue; + } + if (!ASObjectIsEqual(container.exclusionPaths, otherContainer.exclusionPaths)) { + continue; + } + if (container.maximumNumberOfRows != otherContainer.maximumNumberOfRows) { + continue; + } + if (container.truncationType != otherContainer.truncationType) { + continue; + } + if (!ASObjectIsEqual(container.truncationToken, otherContainer.truncationToken)) { + continue; + } + // TODO: When we get a cache hit, move this entry to the front (LRU). + return layout; + } + } + + // Cache Miss. + + // Compute the text layout. + ASTextLayout *layout = [ASTextLayout layoutWithContainer:container text:text]; + + // Store the result in the cache. + { + ASDN::MutexLocker lock(cacheValue->_m); + cacheValue->_layouts.push_front(std::make_tuple(container.size, layout)); + if (cacheValue->_layouts.size() > 3) { + cacheValue->_layouts.pop_back(); + } + } + + return layout; +} + ++ (void)drawRect:(CGRect)bounds withParameters:(NSDictionary *)layoutDict isCancelled:(asdisplaynode_iscancelled_block_t)isCancelledBlock isRasterizing:(BOOL)isRasterizing; +{ + ASTextContainer *container = layoutDict[@"container"]; + NSAttributedString *text = layoutDict[@"text"]; + ASTextLayout *layout = [self compatibleLayoutWithContainer:container text:text]; + + if (isCancelledBlock()) { + return; + } + CGContextRef context = UIGraphicsGetCurrentContext(); + ASDisplayNodeAssert(context, @"This is no good without a context."); + + [layout drawInContext:context size:bounds.size point:bounds.origin view:nil layer:nil debug:[ASTextDebugOption sharedDebugOption] cancel:isCancelledBlock]; +} + +#pragma mark - Attributes + +- (id)linkAttributeValueAtPoint:(CGPoint)point + attributeName:(out NSString **)attributeNameOut + range:(out NSRange *)rangeOut +{ + return [self _linkAttributeValueAtPoint:point + attributeName:attributeNameOut + range:rangeOut + inAdditionalTruncationMessage:NULL + forHighlighting:NO]; +} + +- (id)_linkAttributeValueAtPoint:(CGPoint)point + attributeName:(out NSString **)attributeNameOut + range:(out NSRange *)rangeOut + inAdditionalTruncationMessage:(out BOOL *)inAdditionalTruncationMessageOut + forHighlighting:(BOOL)highlighting +{ + ASDN::MutexLocker l(__instanceLock__); + + // TODO: The copy and application of size shouldn't be required, but it is currently. + // See discussion in https://github.com/TextureGroup/Texture/pull/396 + ASTextContainer *containerCopy = [_textContainer copy]; + containerCopy.size = self.calculatedSize; + ASTextLayout *layout = [ASTextNode2 compatibleLayoutWithContainer:containerCopy text:_attributedText]; + NSRange visibleRange = layout.visibleRange; + NSRange clampedRange = NSIntersectionRange(visibleRange, NSMakeRange(0, _attributedText.length)); + + ASTextRange *range = [layout closestTextRangeAtPoint:point]; + + // For now, assume that a tap inside this text, but outside the text range is a tap on the + // truncation token. + if (![layout textRangeAtPoint:point]) { + *inAdditionalTruncationMessageOut = YES; + return nil; + } + + NSRange effectiveRange = NSMakeRange(0, 0); + for (__strong NSString *attributeName in self.linkAttributeNames) { + id value = [self.attributedText attribute:attributeName atIndex:range.start.offset longestEffectiveRange:&effectiveRange inRange:clampedRange]; + if (value == nil) { + // Didn't find any links specified with this attribute. + continue; + } + + // If highlighting, check with delegate first. If not implemented, assume YES. + if (highlighting + && [_delegate respondsToSelector:@selector(textNode:shouldHighlightLinkAttribute:value:atPoint:)] + && ![_delegate textNode:(ASTextNode *)self shouldHighlightLinkAttribute:attributeName value:value atPoint:point]) { + value = nil; + attributeName = nil; + } + + if (value != nil || attributeName != nil) { + *rangeOut = NSIntersectionRange(visibleRange, effectiveRange); + + if (attributeNameOut != NULL) { + *attributeNameOut = attributeName; + } + + return value; + } + } + + return nil; +} + +#pragma mark - UIGestureRecognizerDelegate + +- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer +{ + ASDisplayNodeAssertMainThread(); + + if (gestureRecognizer == _longPressGestureRecognizer) { + // Don't allow long press on truncation message + if ([self _pendingTruncationTap]) { + return NO; + } + + // Ask our delegate if a long-press on an attribute is relevant + if ([_delegate respondsToSelector:@selector(textNode:shouldLongPressLinkAttribute:value:atPoint:)]) { + return [_delegate textNode:(ASTextNode *)self + shouldLongPressLinkAttribute:_highlightedLinkAttributeName + value:_highlightedLinkAttributeValue + atPoint:[gestureRecognizer locationInView:self.view]]; + } + + // Otherwise we are good to go. + return YES; + } + + if (([self _pendingLinkTap] || [self _pendingTruncationTap]) + && [gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]] + && CGRectContainsPoint(self.threadSafeBounds, [gestureRecognizer locationInView:self.view])) { + return NO; + } + + return [super gestureRecognizerShouldBegin:gestureRecognizer]; +} + +#pragma mark - Highlighting + +- (ASTextNodeHighlightStyle)highlightStyle +{ + ASDN::MutexLocker l(__instanceLock__); + + return _highlightStyle; +} + +- (void)setHighlightStyle:(ASTextNodeHighlightStyle)highlightStyle +{ + ASDN::MutexLocker l(__instanceLock__); + + _highlightStyle = highlightStyle; +} + +- (NSRange)highlightRange +{ + ASDisplayNodeAssertMainThread(); + + return _highlightRange; +} + +- (void)setHighlightRange:(NSRange)highlightRange +{ + [self setHighlightRange:highlightRange animated:NO]; +} + +- (void)setHighlightRange:(NSRange)highlightRange animated:(BOOL)animated +{ + [self _setHighlightRange:highlightRange forAttributeName:nil value:nil animated:animated]; +} + +- (void)_setHighlightRange:(NSRange)highlightRange forAttributeName:(NSString *)highlightedAttributeName value:(id)highlightedAttributeValue animated:(BOOL)animated +{ + // Set these so that link tapping works. + _highlightedLinkAttributeName = highlightedAttributeName; + _highlightedLinkAttributeValue = highlightedAttributeValue; + + AS_TEXT_ALERT_UNIMPLEMENTED_FEATURE(); + // Much of the code from original ASTextNode is probably usable here. + + return; +} + +- (void)_clearHighlightIfNecessary +{ + ASDisplayNodeAssertMainThread(); + + if ([self _pendingLinkTap] || [self _pendingTruncationTap]) { + [self setHighlightRange:NSMakeRange(0, 0) animated:YES]; + } +} + ++ (CGColorRef)_highlightColorForStyle:(ASTextNodeHighlightStyle)style +{ + return [UIColor colorWithWhite:(style == ASTextNodeHighlightStyleLight ? 0.0 : 1.0) alpha:1.0].CGColor; +} + ++ (CGFloat)_highlightOpacityForStyle:(ASTextNodeHighlightStyle)style +{ + return (style == ASTextNodeHighlightStyleLight) ? ASTextNodeHighlightLightOpacity : ASTextNodeHighlightDarkOpacity; +} + +#pragma mark - Text rects + +- (NSArray *)rectsForTextRange:(NSRange)textRange +{ + AS_TEXT_ALERT_UNIMPLEMENTED_FEATURE(); + return @[]; +} + +- (NSArray *)highlightRectsForTextRange:(NSRange)textRange +{ + AS_TEXT_ALERT_UNIMPLEMENTED_FEATURE(); + return @[]; +} + +- (CGRect)trailingRect +{ + AS_TEXT_ALERT_UNIMPLEMENTED_FEATURE(); + return CGRectZero; +} + +- (CGRect)frameForTextRange:(NSRange)textRange +{ + AS_TEXT_ALERT_UNIMPLEMENTED_FEATURE(); + return CGRectZero; +} + +#pragma mark - Placeholders + +- (void)setPlaceholderColor:(UIColor *)placeholderColor +{ + ASDN::MutexLocker l(__instanceLock__); + + _placeholderColor = placeholderColor; + + // prevent placeholders if we don't have a color + self.placeholderEnabled = placeholderColor != nil; +} + +- (UIImage *)placeholderImage +{ + AS_TEXT_ALERT_UNIMPLEMENTED_FEATURE(); + return nil; +} + +#pragma mark - Touch Handling + +- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event +{ + ASDisplayNodeAssertMainThread(); + + if (!_passthroughNonlinkTouches) { + return [super pointInside:point withEvent:event]; + } + + NSRange range = NSMakeRange(0, 0); + NSString *linkAttributeName = nil; + BOOL inAdditionalTruncationMessage = NO; + + id linkAttributeValue = [self _linkAttributeValueAtPoint:point + attributeName:&linkAttributeName + range:&range + inAdditionalTruncationMessage:&inAdditionalTruncationMessage + forHighlighting:YES]; + + NSUInteger lastCharIndex = NSIntegerMax; + BOOL linkCrossesVisibleRange = (lastCharIndex > range.location) && (lastCharIndex < NSMaxRange(range) - 1); + + if (inAdditionalTruncationMessage) { + return YES; + } else if (range.length && !linkCrossesVisibleRange && linkAttributeValue != nil && linkAttributeName != nil) { + return YES; + } else { + return NO; + } +} + +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event +{ + ASDisplayNodeAssertMainThread(); + + [super touchesBegan:touches withEvent:event]; + + CGPoint point = [[touches anyObject] locationInView:self.view]; + + NSRange range = NSMakeRange(0, 0); + NSString *linkAttributeName = nil; + BOOL inAdditionalTruncationMessage = NO; + + id linkAttributeValue = [self _linkAttributeValueAtPoint:point + attributeName:&linkAttributeName + range:&range + inAdditionalTruncationMessage:&inAdditionalTruncationMessage + forHighlighting:YES]; + + NSUInteger lastCharIndex = NSIntegerMax; + BOOL linkCrossesVisibleRange = (lastCharIndex > range.location) && (lastCharIndex < NSMaxRange(range) - 1); + + if (inAdditionalTruncationMessage) { + NSRange visibleRange = NSMakeRange(0, 0); + { + ASDN::MutexLocker l(__instanceLock__); + // TODO: The copy and application of size shouldn't be required, but it is currently. + // See discussion in https://github.com/TextureGroup/Texture/pull/396 + ASTextContainer *containerCopy = [_textContainer copy]; + containerCopy.size = self.calculatedSize; + ASTextLayout *layout = [ASTextNode2 compatibleLayoutWithContainer:containerCopy text:_attributedText]; + visibleRange = layout.visibleRange; + } + NSRange truncationMessageRange = [self _additionalTruncationMessageRangeWithVisibleRange:visibleRange]; + [self _setHighlightRange:truncationMessageRange forAttributeName:ASTextNodeTruncationTokenAttributeName value:nil animated:YES]; + } else if (range.length && !linkCrossesVisibleRange && linkAttributeValue != nil && linkAttributeName != nil) { + [self _setHighlightRange:range forAttributeName:linkAttributeName value:linkAttributeValue animated:YES]; + } + + return; +} + + +- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event +{ + ASDisplayNodeAssertMainThread(); + [super touchesCancelled:touches withEvent:event]; + + [self _clearHighlightIfNecessary]; +} + +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event +{ + ASDisplayNodeAssertMainThread(); + [super touchesEnded:touches withEvent:event]; + + if ([self _pendingLinkTap] && [_delegate respondsToSelector:@selector(textNode:tappedLinkAttribute:value:atPoint:textRange:)]) { + CGPoint point = [[touches anyObject] locationInView:self.view]; + [_delegate textNode:(ASTextNode *)self tappedLinkAttribute:_highlightedLinkAttributeName value:_highlightedLinkAttributeValue atPoint:point textRange:_highlightRange]; + } + + if ([self _pendingTruncationTap]) { + if ([_delegate respondsToSelector:@selector(textNodeTappedTruncationToken:)]) { + [_delegate textNodeTappedTruncationToken:(ASTextNode *)self]; + } + } + + [self _clearHighlightIfNecessary]; +} + +- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event +{ + ASDisplayNodeAssertMainThread(); + [super touchesMoved:touches withEvent:event]; + + UITouch *touch = [touches anyObject]; + CGPoint locationInView = [touch locationInView:self.view]; + // on 3D Touch enabled phones, this gets fired with changes in force, and usually will get fired immediately after touchesBegan:withEvent: + if (CGPointEqualToPoint([touch previousLocationInView:self.view], locationInView)) + return; + + // If touch has moved out of the current highlight range, clear the highlight. + if (_highlightRange.length > 0) { + NSRange range = NSMakeRange(0, 0); + [self _linkAttributeValueAtPoint:locationInView + attributeName:NULL + range:&range + inAdditionalTruncationMessage:NULL + forHighlighting:YES]; + + if (!NSEqualRanges(_highlightRange, range)) { + [self _clearHighlightIfNecessary]; + } + } +} + +- (void)_handleLongPress:(UILongPressGestureRecognizer *)longPressRecognizer +{ + ASDisplayNodeAssertMainThread(); + + // Respond to long-press when it begins, not when it ends. + if (longPressRecognizer.state == UIGestureRecognizerStateBegan) { + if ([_delegate respondsToSelector:@selector(textNode:longPressedLinkAttribute:value:atPoint:textRange:)]) { + CGPoint touchPoint = [_longPressGestureRecognizer locationInView:self.view]; + [_delegate textNode:(ASTextNode *)self longPressedLinkAttribute:_highlightedLinkAttributeName value:_highlightedLinkAttributeValue atPoint:touchPoint textRange:_highlightRange]; + } + } +} + +- (BOOL)_pendingLinkTap +{ + ASDN::MutexLocker l(__instanceLock__); + + return (_highlightedLinkAttributeValue != nil && ![self _pendingTruncationTap]) && _delegate != nil; +} + +- (BOOL)_pendingTruncationTap +{ + ASDN::MutexLocker l(__instanceLock__); + + return [_highlightedLinkAttributeName isEqualToString:ASTextNodeTruncationTokenAttributeName]; +} + +#pragma mark - Shadow Properties + +/** + * Note about shadowed text: + * + * Shadowed text is pretty rare, and we are a framework that targets serious developers. + * We should probably ignore these properties and tell developers to set the shadow into their attributed text instead. + */ +- (CGColorRef)shadowColor +{ + ASDN::MutexLocker l(__instanceLock__); + + return _shadowColor; +} + +- (void)setShadowColor:(CGColorRef)shadowColor +{ + __instanceLock__.lock(); + + if (_shadowColor != shadowColor && CGColorEqualToColor(shadowColor, _shadowColor) == NO) { + CGColorRelease(_shadowColor); + _shadowColor = CGColorRetain(shadowColor); + __instanceLock__.unlock(); + + [self setNeedsDisplay]; + return; + } + + __instanceLock__.unlock(); +} + +- (CGSize)shadowOffset +{ + ASDN::MutexLocker l(__instanceLock__); + + return _shadowOffset; +} + +- (void)setShadowOffset:(CGSize)shadowOffset +{ + { + ASDN::MutexLocker l(__instanceLock__); + + if (CGSizeEqualToSize(_shadowOffset, shadowOffset)) { + return; + } + _shadowOffset = shadowOffset; + } + + [self setNeedsDisplay]; +} + +- (CGFloat)shadowOpacity +{ + ASDN::MutexLocker l(__instanceLock__); + + return _shadowOpacity; +} + +- (void)setShadowOpacity:(CGFloat)shadowOpacity +{ + { + ASDN::MutexLocker l(__instanceLock__); + + if (_shadowOpacity == shadowOpacity) { + return; + } + + _shadowOpacity = shadowOpacity; + } + + [self setNeedsDisplay]; +} + +- (CGFloat)shadowRadius +{ + ASDN::MutexLocker l(__instanceLock__); + + return _shadowRadius; +} + +- (void)setShadowRadius:(CGFloat)shadowRadius +{ + { + ASDN::MutexLocker l(__instanceLock__); + + if (_shadowRadius == shadowRadius) { + return; + } + + _shadowRadius = shadowRadius; + } + + [self setNeedsDisplay]; +} + +- (UIEdgeInsets)shadowPadding +{ + AS_TEXT_ALERT_UNIMPLEMENTED_FEATURE(); + return UIEdgeInsetsZero; +} + +- (void)setPointSizeScaleFactors:(NSArray *)scaleFactors +{ + AS_TEXT_ALERT_UNIMPLEMENTED_FEATURE(); + _pointSizeScaleFactors = [scaleFactors copy]; +} + +- (NSArray *)pointSizeScaleFactors +{ + return _pointSizeScaleFactors; +} + +#pragma mark - Truncation Message + +static NSAttributedString *DefaultTruncationAttributedString() +{ + static NSAttributedString *defaultTruncationAttributedString; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + defaultTruncationAttributedString = [[NSAttributedString alloc] initWithString:NSLocalizedString(@"\u2026", @"Default truncation string")]; + }); + return defaultTruncationAttributedString; +} + +- (void)_ensureTruncationText +{ + if (_textContainer.truncationToken == nil) { + ASDN::MutexLocker l(__instanceLock__); + _textContainer.truncationToken = [self _locked_composedTruncationText]; + } +} + +- (void)setTruncationAttributedText:(NSAttributedString *)truncationAttributedText +{ + { + ASDN::MutexLocker l(__instanceLock__); + + if (ASObjectIsEqual(_truncationAttributedText, truncationAttributedText)) { + return; + } + + _truncationAttributedText = [truncationAttributedText copy]; + } + + [self _invalidateTruncationText]; +} + +- (void)setAdditionalTruncationMessage:(NSAttributedString *)additionalTruncationMessage +{ + { + ASDN::MutexLocker l(__instanceLock__); + + if (ASObjectIsEqual(_additionalTruncationMessage, additionalTruncationMessage)) { + return; + } + + _additionalTruncationMessage = [additionalTruncationMessage copy]; + } + + [self _invalidateTruncationText]; +} + +- (void)setTruncationMode:(NSLineBreakMode)truncationMode +{ + ASDN::MutexLocker lock(__instanceLock__); + if (_truncationMode == truncationMode) { + return; + } + _truncationMode = truncationMode; + + ASTextTruncationType truncationType; + switch (truncationMode) { + case NSLineBreakByTruncatingHead: + truncationType = ASTextTruncationTypeStart; + break; + case NSLineBreakByTruncatingTail: + truncationType = ASTextTruncationTypeEnd; + break; + case NSLineBreakByTruncatingMiddle: + truncationType = ASTextTruncationTypeMiddle; + break; + default: + truncationType = ASTextTruncationTypeNone; + } + + _textContainer.truncationType = truncationType; + + [self setNeedsDisplay]; +} + +- (BOOL)isTruncated +{ + AS_TEXT_ALERT_UNIMPLEMENTED_FEATURE(); + return NO; +} + +- (NSUInteger)maximumNumberOfLines +{ + return _textContainer.maximumNumberOfRows; +} + +- (void)setMaximumNumberOfLines:(NSUInteger)maximumNumberOfLines +{ + if (_textContainer.maximumNumberOfRows == maximumNumberOfLines) { + return; + } + _textContainer.maximumNumberOfRows = maximumNumberOfLines; + + [self setNeedsDisplay]; +} + +- (NSUInteger)lineCount +{ + ASDN::MutexLocker l(__instanceLock__); + AS_TEXT_ALERT_UNIMPLEMENTED_FEATURE(); + return 0; +} + +#pragma mark - Truncation Message + +- (void)_invalidateTruncationText +{ + _textContainer.truncationToken = nil; + [self setNeedsDisplay]; +} + +/** + * @return the additional truncation message range within the as-rendered text. + * Must be called from main thread + */ +- (NSRange)_additionalTruncationMessageRangeWithVisibleRange:(NSRange)visibleRange +{ + ASDN::MutexLocker l(__instanceLock__); + + // Check if we even have an additional truncation message. + if (!_additionalTruncationMessage) { + return NSMakeRange(NSNotFound, 0); + } + + // Character location of the unicode ellipsis (the first index after the visible range) + NSInteger truncationTokenIndex = NSMaxRange(visibleRange); + + NSUInteger additionalTruncationMessageLength = _additionalTruncationMessage.length; + // We get the location of the truncation token, then add the length of the + // truncation attributed string +1 for the space between. + return NSMakeRange(truncationTokenIndex + _truncationAttributedText.length + 1, additionalTruncationMessageLength); +} + +/** + * @return the truncation message for the string. If there are both an + * additional truncation message and a truncation attributed string, they will + * be properly composed. + */ +- (NSAttributedString *)_locked_composedTruncationText +{ + if (_composedTruncationText == nil) { + if (_truncationAttributedText != nil && _additionalTruncationMessage != nil) { + NSMutableAttributedString *newComposedTruncationString = [[NSMutableAttributedString alloc] initWithAttributedString:_truncationAttributedText]; + [newComposedTruncationString.mutableString appendString:@" "]; + [newComposedTruncationString appendAttributedString:_additionalTruncationMessage]; + _composedTruncationText = newComposedTruncationString; + } else if (_truncationAttributedText != nil) { + _composedTruncationText = _truncationAttributedText; + } else if (_additionalTruncationMessage != nil) { + _composedTruncationText = _additionalTruncationMessage; + } else { + _composedTruncationText = DefaultTruncationAttributedString(); + } + _composedTruncationText = [self _locked_prepareTruncationStringForDrawing:_composedTruncationText]; + } + return _composedTruncationText; +} + +/** + * - cleanses it of core text attributes so TextKit doesn't crash + * - Adds whole-string attributes so the truncation message matches the styling + * of the body text + */ +- (NSAttributedString *)_locked_prepareTruncationStringForDrawing:(NSAttributedString *)truncationString +{ + NSMutableAttributedString *truncationMutableString = [truncationString mutableCopy]; + // Grab the attributes from the full string + if (_attributedText.length > 0) { + NSAttributedString *originalString = _attributedText; + NSInteger originalStringLength = _attributedText.length; + // Add any of the original string's attributes to the truncation string, + // but don't overwrite any of the truncation string's attributes + NSDictionary *originalStringAttributes = [originalString attributesAtIndex:originalStringLength-1 effectiveRange:NULL]; + [truncationString enumerateAttributesInRange:NSMakeRange(0, truncationString.length) options:0 usingBlock: + ^(NSDictionary *attributes, NSRange range, BOOL *stop) { + NSMutableDictionary *futureTruncationAttributes = [NSMutableDictionary dictionaryWithDictionary:originalStringAttributes]; + [futureTruncationAttributes addEntriesFromDictionary:attributes]; + [truncationMutableString setAttributes:futureTruncationAttributes range:range]; + }]; + } + return truncationMutableString; +} + +#if AS_TEXTNODE2_RECORD_ATTRIBUTED_STRINGS ++ (void)_registerAttributedText:(NSAttributedString *)str +{ + static NSMutableArray *array; + static NSLock *lock; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + lock = [NSLock new]; + array = [NSMutableArray new]; + }); + [lock lock]; + [array addObject:str]; + if (array.count % 20 == 0) { + NSLog(@"Got %d strings", (int)array.count); + } + if (array.count == 2000) { + NSString *path = [NSTemporaryDirectory() stringByAppendingPathComponent:@"AttributedStrings.plist"]; + NSAssert([NSKeyedArchiver archiveRootObject:array toFile:path], nil); + NSLog(@"Saved to %@", path); + } + [lock unlock]; +} +#endif + ++ (void)enableDebugging +{ + ASTextDebugOption *debugOption = [[ASTextDebugOption alloc] init]; + debugOption.CTLineFillColor = [UIColor colorWithRed:0 green:0.3 blue:1 alpha:0.1]; + [ASTextDebugOption setSharedDebugOption:debugOption]; +} + +- (BOOL)usingExperiment +{ + return YES; +} + +@end diff --git a/Source/ASVideoNode.h b/Source/ASVideoNode.h index 0f3734babe..160b7b73b9 100644 --- a/Source/ASVideoNode.h +++ b/Source/ASVideoNode.h @@ -1,11 +1,18 @@ // // ASVideoNode.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK @@ -104,7 +111,7 @@ NS_ASSUME_NONNULL_BEGIN * @param videoNode The video node. * @param state player state that is going to be set. * @discussion Delegate method invoked when player changes it's state to - * ASVideoNodePlayerStatePlaying or ASVideoNodePlayerStatePaused + * ASVideoNodePlayerStatePlaying or ASVideoNodePlayerStatePaused * and asks delegate if state change is valid */ - (BOOL)videoNode:(ASVideoNode*)videoNode shouldChangePlayerStateTo:(ASVideoNodePlayerState)state; @@ -141,6 +148,14 @@ NS_ASSUME_NONNULL_BEGIN * @param videoNode The videoNode */ - (void)videoNodeDidRecoverFromStall:(ASVideoNode *)videoNode; +/** + * @abstract Delegate method invoked when an error occurs while trying trying to load an asset + * @param videoNode The videoNode. + * @param key The key of value that failed to load. + * @param asset The asset. + * @param error The error that occurs. + */ +- (void)videoNode:(ASVideoNode *)videoNode didFailToLoadValueForKey:(NSString *)key asset:(AVAsset *)asset error:(NSError *)error; @end diff --git a/Source/ASVideoNode.mm b/Source/ASVideoNode.mm index d95b61f3bf..291d733419 100644 --- a/Source/ASVideoNode.mm +++ b/Source/ASVideoNode.mm @@ -1,11 +1,18 @@ // // ASVideoNode.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK #import @@ -51,6 +58,7 @@ static NSString * const kRate = @"rate"; unsigned int delegateVideoNodeDidSetCurrentItem:1; unsigned int delegateVideoNodeDidStallAtTimeInterval:1; unsigned int delegateVideoNodeDidRecoverFromStall:1; + unsigned int delegateVideoNodeDidFailToLoadValueForKey:1; } _delegateFlags; BOOL _shouldBePlaying; @@ -101,6 +109,9 @@ static NSString * const kRate = @"rate"; [self addTarget:self action:@selector(tapped) forControlEvents:ASControlNodeEventTouchUpInside]; _lastPlaybackTime = kCMTimeZero; + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + [notificationCenter addObserver:self selector:@selector(applicationDidBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:nil]; + return self; } @@ -143,6 +154,9 @@ static NSString * const kRate = @"rate"; AVKeyValueStatus keyStatus = [asset statusOfValueForKey:key error:&error]; if (keyStatus == AVKeyValueStatusFailed) { NSLog(@"Asset loading failed with error: %@", error); + if (_delegateFlags.delegateVideoNodeDidFailToLoadValueForKey) { + [self.delegate videoNode:self didFailToLoadValueForKey:key asset:asset error:error]; + } } } @@ -593,6 +607,7 @@ static NSString * const kRate = @"rate"; _delegateFlags.delegateVideoNodeDidSetCurrentItem = [delegate respondsToSelector:@selector(videoNode:didSetCurrentItem:)]; _delegateFlags.delegateVideoNodeDidStallAtTimeInterval = [delegate respondsToSelector:@selector(videoNode:didStallAtTimeInterval:)]; _delegateFlags.delegateVideoNodeDidRecoverFromStall = [delegate respondsToSelector:@selector(videoNodeDidRecoverFromStall:)]; + _delegateFlags.delegateVideoNodeDidFailToLoadValueForKey = [delegate respondsToSelector:@selector(videoNode:didFailToLoadValueForKey:asset:error:)]; } } @@ -707,6 +722,13 @@ static NSString * const kRate = @"rate"; #pragma mark - Playback observers +- (void)applicationDidBecomeActive:(NSNotification *)notification +{ + if (self.shouldBePlaying && self.isVisible) { + [self play]; + } +} + - (void)didPlayToEnd:(NSNotification *)notification { self.playerState = ASVideoNodePlayerStateFinished; @@ -818,6 +840,9 @@ static NSString * const kRate = @"rate"; _timeObserver = nil; [self removePlayerItemObservers:_currentPlayerItem]; [self removePlayerObservers:_player]; + + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + [notificationCenter removeObserver:self name:UIApplicationDidBecomeActiveNotification object:nil]; } @end diff --git a/Source/ASVideoPlayerNode.h b/Source/ASVideoPlayerNode.h index 41c5c9aa82..4b475f920f 100644 --- a/Source/ASVideoPlayerNode.h +++ b/Source/ASVideoPlayerNode.h @@ -1,13 +1,18 @@ // // ASVideoPlayerNode.h -// AsyncDisplayKit -// -// Created by Erekle on 5/6/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK diff --git a/Source/ASVideoPlayerNode.mm b/Source/ASVideoPlayerNode.mm index 21a412a6e0..753960db05 100644 --- a/Source/ASVideoPlayerNode.mm +++ b/Source/ASVideoPlayerNode.mm @@ -1,14 +1,21 @@ // // ASVideoPlayerNode.mm -// AsyncDisplayKit -// -// Created by Erekle on 5/6/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. // +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import #ifndef MINIMAL_ASDK #if TARGET_OS_IOS @@ -250,6 +257,11 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext; } } +- (BOOL)supportsLayerBacking +{ + return NO; +} + #pragma mark - UI - (void)createControls @@ -670,7 +682,9 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext; - (void)didTapFullScreenButton:(ASButtonNode*)node { - [_delegate didTapFullScreenButtonNode:node]; + if (_delegateFlags.delegateDidTapFullScreenButtonNode) { + [_delegate didTapFullScreenButtonNode:node]; + } } - (void)beginSeek diff --git a/Source/ASViewController.h b/Source/ASViewController.h index 07ee2a997f..f96bd0194d 100644 --- a/Source/ASViewController.h +++ b/Source/ASViewController.h @@ -1,13 +1,18 @@ // // ASViewController.h -// AsyncDisplayKit -// -// Created by Huy Nguyen on 16/09/15. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK diff --git a/Source/ASViewController.mm b/Source/ASViewController.mm index 9cd1a50c7c..2531867d7c 100644 --- a/Source/ASViewController.mm +++ b/Source/ASViewController.mm @@ -1,27 +1,30 @@ // // ASViewController.mm -// AsyncDisplayKit -// -// Created by Huy Nguyen on 16/09/15. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK #import #import -#import #import #import +#import #import #import #import -#define AS_LOG_VISIBILITY_CHANGES 0 - @implementation ASViewController { BOOL _ensureDisplayed; @@ -166,7 +169,11 @@ ASVisibilityDidMoveToParentViewController; - (void)viewWillAppear:(BOOL)animated { + as_activity_create_for_scope("ASViewController will appear"); + as_log_debug(ASNodeLog(), "View controller %@ will appear", self); + [super viewWillAppear:animated]; + _ensureDisplayed = YES; // A layout pass is forced this early to get nodes like ASCollectionNode, ASTableNode etc. @@ -188,7 +195,7 @@ ASVisibilityDepthImplementation; - (void)visibilityDepthDidChange { ASLayoutRangeMode rangeMode = ASLayoutRangeModeForVisibilityDepth(self.visibilityDepth); -#if AS_LOG_VISIBILITY_CHANGES +#if ASEnableVerboseLogging NSString *rangeModeString; switch (rangeMode) { case ASLayoutRangeModeMinimum: @@ -210,7 +217,7 @@ ASVisibilityDepthImplementation; default: break; } - NSLog(@"Updating visibility of:%@ to: %@ (visibility depth: %d)", self, rangeModeString, self.visibilityDepth); + as_log_verbose(ASNodeLog(), "Updating visibility of %@ to: %@ (visibility depth: %zd)", self, rangeModeString, self.visibilityDepth); #endif [self updateCurrentRangeModeWithModeIfPossible:rangeMode]; } @@ -281,6 +288,8 @@ ASVisibilityDepthImplementation; ASPrimitiveTraitCollection oldTraitCollection = self.node.primitiveTraitCollection; if (ASPrimitiveTraitCollectionIsEqualToASPrimitiveTraitCollection(traitCollection, oldTraitCollection) == NO) { + as_activity_scope_verbose(as_activity_create("Propagate ASViewController trait collection", AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT)); + as_log_debug(ASNodeLog(), "Propagating new traits for %@: %@", self, NSStringFromASPrimitiveTraitCollection(traitCollection)); self.node.primitiveTraitCollection = traitCollection; NSArray> *children = [self.node sublayoutElements]; @@ -292,6 +301,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. + as_activity_scope_verbose(as_activity_create("Layout ASViewController node with new traits", AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT)); [_node layoutThatFits:[self nodeConstrainedSize]]; #pragma clang diagnostic pop } diff --git a/Source/ASVisibilityProtocols.h b/Source/ASVisibilityProtocols.h index 836b8bafa6..67ee0ab0c4 100644 --- a/Source/ASVisibilityProtocols.h +++ b/Source/ASVisibilityProtocols.h @@ -1,13 +1,18 @@ // // ASVisibilityProtocols.h -// AsyncDisplayKit -// -// Created by Garrett Moon on 4/27/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/ASVisibilityProtocols.m b/Source/ASVisibilityProtocols.m index 4203bc8060..4078793292 100644 --- a/Source/ASVisibilityProtocols.m +++ b/Source/ASVisibilityProtocols.m @@ -1,13 +1,18 @@ // // ASVisibilityProtocols.m -// AsyncDisplayKit -// -// Created by Garrett Moon on 4/28/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/AsyncDisplayKit+IGListKitMethods.h b/Source/AsyncDisplayKit+IGListKitMethods.h index 5bc08f613b..8c7002dca8 100644 --- a/Source/AsyncDisplayKit+IGListKitMethods.h +++ b/Source/AsyncDisplayKit+IGListKitMethods.h @@ -1,12 +1,23 @@ // // AsyncDisplayKit+IGListKitMethods.h -// AsyncDisplayKit +// Texture // -// Created by Adlai Holler on 2/27/17. -// Copyright © 2017 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 /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // -#if IG_LIST_KIT +#import + +#if AS_IG_LIST_KIT #import #import @@ -30,7 +41,7 @@ AS_SUBCLASSING_RESTRICTED /** * Call this for your section controller's @c cellForItemAtIndex: method. */ -+ (__kindof UICollectionViewCell *)cellForItemAtIndex:(NSInteger)index sectionController:(IGListSectionController *)sectionController; ++ (__kindof UICollectionViewCell *)cellForItemAtIndex:(NSInteger)index sectionController:(IGListSectionController *)sectionController; /** * Call this for your section controller's @c sizeForItemAtIndex: method. @@ -47,7 +58,7 @@ AS_SUBCLASSING_RESTRICTED */ + (__kindof UICollectionReusableView *)viewForSupplementaryElementOfKind:(NSString *)elementKind atIndex:(NSInteger)index - sectionController:(IGListSectionController *)sectionController; + sectionController:(IGListSectionController *)sectionController; /** * Call this for your supplementary source's @c sizeForSupplementaryViewOfKind:atIndex: method. diff --git a/Source/AsyncDisplayKit+IGListKitMethods.m b/Source/AsyncDisplayKit+IGListKitMethods.m index a4ac764c53..186909729c 100644 --- a/Source/AsyncDisplayKit+IGListKitMethods.m +++ b/Source/AsyncDisplayKit+IGListKitMethods.m @@ -1,22 +1,36 @@ // // AsyncDisplayKit+IGListKitMethods.m -// AsyncDisplayKit +// Texture // -// Created by Adlai Holler on 2/27/17. -// Copyright © 2017 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 /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // -#if IG_LIST_KIT +#import + +#if AS_IG_LIST_KIT #import "AsyncDisplayKit+IGListKitMethods.h" #import #import +#import + @implementation ASIGListSectionControllerMethods -+ (__kindof UICollectionViewCell *)cellForItemAtIndex:(NSInteger)index sectionController:(IGListSectionController *)sectionController ++ (__kindof UICollectionViewCell *)cellForItemAtIndex:(NSInteger)index sectionController:(IGListSectionController *)sectionController { - return [sectionController.collectionContext dequeueReusableCellOfClass:[_ASCollectionViewCell class] forSectionController:sectionController atIndex:index]; + // Cast to id for backwards-compatibility until 3.0.0 is officially released – IGListSectionType was removed. This is safe. + return [sectionController.collectionContext dequeueReusableCellOfClass:[_ASCollectionViewCell class] forSectionController:(id)sectionController atIndex:index]; } + (CGSize)sizeForItemAtIndex:(NSInteger)index @@ -31,9 +45,9 @@ + (__kindof UICollectionReusableView *)viewForSupplementaryElementOfKind:(NSString *)elementKind atIndex:(NSInteger)index - sectionController:(IGListSectionController *)sectionController + sectionController:(IGListSectionController *)sectionController { - return [sectionController.collectionContext dequeueReusableSupplementaryViewOfKind:elementKind forSectionController:sectionController class:[UICollectionReusableView class] atIndex:index]; + return [sectionController.collectionContext dequeueReusableSupplementaryViewOfKind:elementKind forSectionController:(id)sectionController class:[_ASCollectionReusableView class] atIndex:index]; } + (CGSize)sizeForSupplementaryViewOfKind:(NSString *)elementKind atIndex:(NSInteger)index @@ -44,4 +58,4 @@ @end -#endif // IG_LIST_KIT +#endif // AS_IG_LIST_KIT diff --git a/Source/AsyncDisplayKit-Prefix.pch b/Source/AsyncDisplayKit-Prefix.pch deleted file mode 100644 index 72d11cee9c..0000000000 --- a/Source/AsyncDisplayKit-Prefix.pch +++ /dev/null @@ -1,30 +0,0 @@ -// -// Prefix header -// -// The contents of this file are implicitly included at the beginning of every source file. -// - -#ifdef __OBJC__ - #import -#endif - -// Some build systems (Cocoapods, Buck, Bazel, etc) will define these flags manually if the functionality -// is needed by the app. Carthage in particular, or if a user forgets to set the build flag, benefit from -// checking if each flag is not defined and then setting it to whether or not the header is accessible. - -#ifndef PIN_REMOTE_IMAGE -// For Carthage or manual builds, this will define PIN_REMOTE_IMAGE if the header is available in the -// search path e.g. they've dragged in the framework (technically this will not be able to detect if -// a user does not include the framework in the link binary with build step). - #define PIN_REMOTE_IMAGE __has_include() -#endif - -#ifndef IG_LIST_KIT - #define IG_LIST_KIT __has_include() - - /** - * For IGListKit versions < 3.0, you have to use IGListCollectionView. - * For 3.0 and later, that class is removed and you use UICollectionView. - */ - #define IG_LIST_COLLECTION_VIEW __has_include() -#endif diff --git a/Source/AsyncDisplayKit.h b/Source/AsyncDisplayKit.h index 5e84654d17..ed61ea4c5c 100644 --- a/Source/AsyncDisplayKit.h +++ b/Source/AsyncDisplayKit.h @@ -1,20 +1,30 @@ // // AsyncDisplayKit.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // +#import #import +#import #import #import #import #import #import +#import #import #import #import @@ -35,14 +45,17 @@ #import #import #import +#import #import +#import +#import +#import +#import +#import + #import #import -#if IG_LIST_KIT -#import -#import -#endif #import @@ -81,23 +94,23 @@ #import #import #import -#import #import #import #import #import #import +#import +#import #import #import #import #import -#import #import #import +#import #import #import #import -#import #import #import @@ -109,6 +122,10 @@ #import #import +#import #import #import + +#import +#import diff --git a/Source/Base/ASAssert.h b/Source/Base/ASAssert.h index 23cff76acc..ddf87d882a 100644 --- a/Source/Base/ASAssert.h +++ b/Source/Base/ASAssert.h @@ -1,17 +1,25 @@ // // ASAssert.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #pragma once #import #import +#import #define ASDISPLAYNODE_ASSERTIONS_ENABLED (!defined(NS_BLOCK_ASSERTIONS)) @@ -35,8 +43,8 @@ #define ASDisplayNodeAssertNotInstantiable() ASDisplayNodeAssert(NO, nil, @"This class is not instantiable."); #define ASDisplayNodeAssertNotSupported() ASDisplayNodeAssert(NO, nil, @"This method is not supported by class %@", [self class]); -#define ASDisplayNodeAssertMainThread() ASDisplayNodeAssert(0 != pthread_main_np(), @"This method must be called on the main thread") -#define ASDisplayNodeCAssertMainThread() ASDisplayNodeCAssert(0 != pthread_main_np(), @"This function must be called on the main thread") +#define ASDisplayNodeAssertMainThread() ASDisplayNodeAssert(ASMainThreadAssertionsAreDisabled() || 0 != pthread_main_np(), @"This method must be called on the main thread") +#define ASDisplayNodeCAssertMainThread() ASDisplayNodeCAssert(ASMainThreadAssertionsAreDisabled() || 0 != pthread_main_np(), @"This function must be called on the main thread") #define ASDisplayNodeAssertNotMainThread() ASDisplayNodeAssert(0 == pthread_main_np(), @"This method must be called off the main thread") #define ASDisplayNodeCAssertNotMainThread() ASDisplayNodeCAssert(0 == pthread_main_np(), @"This function must be called off the main thread") @@ -62,9 +70,28 @@ #define ASDisplayNodeErrorDomain @"ASDisplayNodeErrorDomain" #define ASDisplayNodeNonFatalErrorCode 1 -#define ASDisplayNodeAssertNonFatal(condition, desc, ...) \ - ASDisplayNodeAssert(condition, desc, ##__VA_ARGS__); \ - if (condition == NO) { \ +/** + * In debug methods, it can be useful to disable main thread assertions to get valuable information, + * even if it means violating threading requirements. These functions are used in -debugDescription and let + * threads decide to suppress/re-enable main thread assertions. + */ +#pragma mark - Main Thread Assertions Disabling + +ASDISPLAYNODE_EXTERN_C_BEGIN +BOOL ASMainThreadAssertionsAreDisabled(); + +void ASPushMainThreadAssertionsDisabled(); + +void ASPopMainThreadAssertionsDisabled(); +ASDISPLAYNODE_EXTERN_C_END + +#pragma mark - Non-Fatal Assertions + +/// Returns YES if assertion passed, NO otherwise. +#define ASDisplayNodeAssertNonFatal(condition, desc, ...) ({ \ + BOOL __evaluated = condition; \ + if (__evaluated == NO) { \ + ASDisplayNodeFailAssert(desc, ##__VA_ARGS__); \ ASDisplayNodeNonFatalErrorBlock block = [ASDisplayNode nonFatalErrorBlock]; \ if (block != nil) { \ NSDictionary *userInfo = nil; \ @@ -74,4 +101,6 @@ NSError *error = [NSError errorWithDomain:ASDisplayNodeErrorDomain code:ASDisplayNodeNonFatalErrorCode userInfo:userInfo]; \ block(error); \ } \ - } + } \ + __evaluated; \ +}) \ diff --git a/Source/Base/ASAssert.m b/Source/Base/ASAssert.m new file mode 100644 index 0000000000..7759ff53f0 --- /dev/null +++ b/Source/Base/ASAssert.m @@ -0,0 +1,39 @@ +// +// ASAssert.m +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +static pthread_key_t ASMainThreadAssertionsDisabledKey() +{ + return ASPthreadStaticKey(NULL); +} + +BOOL ASMainThreadAssertionsAreDisabled() { + return (size_t)pthread_getspecific(ASMainThreadAssertionsDisabledKey()) > 0; +} + +void ASPushMainThreadAssertionsDisabled() { + pthread_key_t key = ASMainThreadAssertionsDisabledKey(); + size_t oldValue = (size_t)pthread_getspecific(key); + pthread_setspecific(key, (void *)(oldValue + 1)); +} + +void ASPopMainThreadAssertionsDisabled() { + pthread_key_t key = ASMainThreadAssertionsDisabledKey(); + size_t oldValue = (size_t)pthread_getspecific(key); + if (oldValue > 0) { + pthread_setspecific(key, (void *)(oldValue - 1)); + } else { + ASDisplayNodeCFailAssert(@"Attempt to pop thread assertion-disabling without corresponding push."); + } +} diff --git a/Source/Base/ASAvailability.h b/Source/Base/ASAvailability.h index 48b65ac088..ff71816027 100644 --- a/Source/Base/ASAvailability.h +++ b/Source/Base/ASAvailability.h @@ -1,11 +1,18 @@ // // ASAvailability.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -23,58 +30,29 @@ #define AS_AT_LEAST_IOS9 (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_9_0) #define AS_AT_LEAST_IOS10 (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_10_0) -#define AS_TARGET_OS_OSX (!(TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH)) -#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. +// NOTE: Yoga integration is experimental and not fully tested. Use with caution and test layouts carefully. #ifndef YOGA_HEADER_PATH - #define YOGA_HEADER_PATH + #define YOGA_HEADER_PATH #endif #ifndef YOGA #define YOGA __has_include(YOGA_HEADER_PATH) #endif -#if AS_TARGET_OS_OSX - -#define UIEdgeInsets NSEdgeInsets -#define NSStringFromCGSize NSStringFromSize -#define NSStringFromCGPoint NSStringFromPoint - -#import - -@interface NSValue (ASAvailability) -+ (NSValue *)valueWithCGPoint:(CGPoint)point; -+ (NSValue *)valueWithCGSize:(CGSize)size; -- (CGRect)CGRectValue; -- (CGPoint)CGPointValue; -- (CGSize)CGSizeValue; -@end - -@implementation NSValue(ASAvailability) -+ (NSValue *)valueWithCGPoint:(CGPoint)point -{ - return [self valueWithPoint:point]; -} -+ (NSValue *)valueWithCGSize:(CGSize)size -{ - return [self valueWithSize:size]; -} -- (CGRect)CGRectValue -{ - return self.rectValue; -} - -- (CGPoint)CGPointValue -{ - return self.pointValue; -} - -- (CGSize)CGSizeValue -{ - return self.sizeValue; -} -@end - +// When enabled, use ASTextNode2 for ALL instances of ASTextNode. +// This includes what ASButtonNode uses internally, as well as all app references to ASTextNode. +// See ASTextNode+Beta.h declaration of ASTextNodeExperimentOptions for more details. +#ifndef ASTEXTNODE_EXPERIMENT_GLOBAL_ENABLE + #define ASTEXTNODE_EXPERIMENT_GLOBAL_ENABLE 0 #endif + +#define AS_PIN_REMOTE_IMAGE __has_include() +#define AS_IG_LIST_KIT __has_include() + +/** + * For IGListKit versions < 3.0, you have to use IGListCollectionView. + * For 3.0 and later, that class is removed and you use UICollectionView. + */ +#define IG_LIST_COLLECTION_VIEW __has_include() diff --git a/Source/Base/ASBaseDefines.h b/Source/Base/ASBaseDefines.h index c854206535..9eb1ec0b0f 100755 --- a/Source/Base/ASBaseDefines.h +++ b/Source/Base/ASBaseDefines.h @@ -1,24 +1,19 @@ // // ASBaseDefines.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // - -#pragma once - -#import - -#ifndef PIN_REMOTE_IMAGE -#if __has_include() -#define PIN_REMOTE_IMAGE 1 -#else -#define PIN_REMOTE_IMAGE 0 -#endif -#endif // The C++ compiler mangles C function names. extern "C" { /* your C functions */ } prevents this. // You should wrap all C function prototypes declared in headers with ASDISPLAYNODE_EXTERN_C_BEGIN/END, even if @@ -112,6 +107,10 @@ # define ASDISPLAYNODE_NOTHROW #endif +#ifndef AS_ENABLE_TIPS +#define AS_ENABLE_TIPS 0 +#endif + /** * The event backtraces take a static 2KB of memory * and retain all objects present in all the registers @@ -212,12 +211,36 @@ #define AS_SUBCLASSING_RESTRICTED #endif +#define ASPthreadStaticKey(dtor) ({ \ + static dispatch_once_t onceToken; \ + static pthread_key_t key; \ + dispatch_once(&onceToken, ^{ \ + pthread_key_create(&key, dtor); \ + }); \ + key; \ +}) + +#define ASCreateOnce(expr) ({ \ + static dispatch_once_t onceToken; \ + static __typeof__(expr) staticVar; \ + dispatch_once(&onceToken, ^{ \ + staticVar = expr; \ + }); \ + staticVar; \ +}) + /// Ensure that class is of certain kind #define ASDynamicCast(x, c) ({ \ id __val = x;\ ((c *) ([__val isKindOfClass:[c class]] ? __val : nil));\ }) +/// Ensure that class is of certain kind, assuming it is subclass restricted +#define ASDynamicCastStrict(x, c) ({ \ + id __val = x;\ + ((c *) ([__val class] == [c class] ? __val : nil));\ +}) + /** * Create a new set by mapping `collection` over `work`, ignoring nil. */ @@ -232,6 +255,20 @@ s; \ }) +/** + * Create a new ObjectPointerPersonality NSHashTable by mapping `collection` over `work`, ignoring nil. + */ +#define ASPointerTableByFlatMapping(collection, decl, work) ({ \ + NSHashTable *t = [NSHashTable hashTableWithOptions:NSHashTableObjectPointerPersonality]; \ + for (decl in collection) {\ + id result = work; \ + if (result != nil) { \ + [t addObject:result]; \ + } \ + } \ + t; \ +}) + /** * Create a new array by mapping `collection` over `work`, ignoring nil. */ diff --git a/Source/Base/ASDisplayNode+Ancestry.h b/Source/Base/ASDisplayNode+Ancestry.h new file mode 100644 index 0000000000..6680659c35 --- /dev/null +++ b/Source/Base/ASDisplayNode+Ancestry.h @@ -0,0 +1,63 @@ +// +// ASDisplayNode+Ancestry.h +// Texture +// +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface ASDisplayNode (Ancestry) + +/** + * Returns an object to enumerate the supernode ancestry of this node, starting with its supernode. + * + * For instance, you could write: + * for (ASDisplayNode *node in self.supernodes) { + * if ([node.backgroundColor isEqual:[UIColor blueColor]]) { + * node.hidden = YES; + * } + * } + * + * Note: If this property is read on the main thread, the enumeration will attempt to go up + * the layer hierarchy if it finds a break in the display node hierarchy. + */ +@property (atomic, readonly) id supernodes; + +/** + * Same as `supernodes` but begins the enumeration with self. + */ +@property (atomic, readonly) id supernodesIncludingSelf; + +/** + * Searches the supernodes of this node for one matching the given class. + * + * @param supernodeClass The class of node you're looking for. + * @param includeSelf Whether to include self in the search. + * @return A node of the given class that is an ancestor of this node, or nil. + * + * @note See the documentation on `supernodes` for details about the upward traversal. + */ +- (nullable __kindof ASDisplayNode *)supernodeOfClass:(Class)supernodeClass includingSelf:(BOOL)includeSelf; + +/** + * e.g. "(, , )" + */ +@property (atomic, copy, readonly) NSString *ancestryDescription; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Source/Base/ASDisplayNode+Ancestry.m b/Source/Base/ASDisplayNode+Ancestry.m new file mode 100644 index 0000000000..566cc8bd33 --- /dev/null +++ b/Source/Base/ASDisplayNode+Ancestry.m @@ -0,0 +1,98 @@ +// +// ASDisplayNode+Ancestry.m +// Texture +// +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import "ASDisplayNode+Ancestry.h" +#import +#import + +AS_SUBCLASSING_RESTRICTED +@interface ASNodeAncestryEnumerator : NSEnumerator +@end + +@implementation ASNodeAncestryEnumerator { + ASDisplayNode *_lastNode; // This needs to be strong because enumeration will not retain the current batch of objects + BOOL _initialState; +} + +- (instancetype)initWithNode:(ASDisplayNode *)node +{ + if (self = [super init]) { + _initialState = YES; + _lastNode = node; + } + return self; +} + +- (id)nextObject +{ + if (_initialState) { + _initialState = NO; + return _lastNode; + } + + ASDisplayNode *nextNode = _lastNode.supernode; + if (nextNode == nil && ASDisplayNodeThreadIsMain()) { + CALayer *layer = _lastNode.nodeLoaded ? _lastNode.layer.superlayer : nil; + while (layer != nil) { + nextNode = ASLayerToDisplayNode(layer); + if (nextNode != nil) { + break; + } + layer = layer.superlayer; + } + } + _lastNode = nextNode; + return nextNode; +} + +@end + +@implementation ASDisplayNode (Ancestry) + +- (id)supernodes +{ + NSEnumerator *result = [[ASNodeAncestryEnumerator alloc] initWithNode:self]; + [result nextObject]; // discard first object (self) + return result; +} + +- (id)supernodesIncludingSelf +{ + return [[ASNodeAncestryEnumerator alloc] initWithNode:self]; +} + +- (nullable __kindof ASDisplayNode *)supernodeOfClass:(Class)supernodeClass includingSelf:(BOOL)includeSelf +{ + id chain = includeSelf ? self.supernodesIncludingSelf : self.supernodes; + for (ASDisplayNode *ancestor in chain) { + if ([ancestor isKindOfClass:supernodeClass]) { + return ancestor; + } + } + return nil; +} + +- (NSString *)ancestryDescription +{ + NSMutableArray *strings = [NSMutableArray array]; + for (ASDisplayNode *node in self.supernodes) { + [strings addObject:ASObjectDescriptionMakeTiny(node)]; + } + return strings.description; +} + +@end diff --git a/Source/Base/ASEqualityHelpers.h b/Source/Base/ASEqualityHelpers.h index 0652c197e1..9bdfb2edef 100644 --- a/Source/Base/ASEqualityHelpers.h +++ b/Source/Base/ASEqualityHelpers.h @@ -1,11 +1,18 @@ // // ASEqualityHelpers.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Base/ASLog.h b/Source/Base/ASLog.h index fbc12d17c9..412b9d905c 100644 --- a/Source/Base/ASLog.h +++ b/Source/Base/ASLog.h @@ -1,56 +1,121 @@ // // ASLog.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // -#pragma once - #import +#import +#import +#import +#import -#define ASMultiplexImageNodeLogDebug(...) -#define ASMultiplexImageNodeCLogDebug(...) - -#define ASMultiplexImageNodeLogError(...) -#define ASMultiplexImageNodeCLogError(...) - -// Note: `` only exists in Xcode 8 and later. -#if PROFILE && __has_include("") - -#import - -// These definitions are required to build the backward-compatible kdebug trace -// on the iOS 10 SDK. The kdebug_trace function crashes if run on iOS 9 and earlier. -// It's valuable to support trace signposts on iOS 9, because A5 devices don't support iOS 10. -#ifndef DBG_MACH_CHUD -#define DBG_MACH_CHUD 0x0A -#define DBG_FUNC_START 1 -#define DBG_FUNC_END 2 -#define DBG_APPS 33 -#define SYS_kdebug_trace 180 -#define KDBG_CODE(Class, SubClass, code) (((Class & 0xff) << 24) | ((SubClass & 0xff) << 16) | ((code & 0x3fff) << 2)) -#define APPSDBG_CODE(SubClass,code) KDBG_CODE(DBG_APPS, SubClass, code) +#ifndef ASEnableLogs + #define ASEnableLogs 1 #endif -#define ASProfilingSignpost(x) \ - AS_AT_LEAST_IOS10 ? kdebug_signpost(x, 0, 0, 0, (uint32_t)(x % 4)) \ - : syscall(SYS_kdebug_trace, APPSDBG_CODE(DBG_MACH_CHUD, x) | DBG_FUNC_NONE, 0, 0, 0, (uint32_t)(x % 4)); +#ifndef ASEnableVerboseLogging + #define ASEnableVerboseLogging 0 +#endif -#define ASProfilingSignpostStart(x, y) \ - AS_AT_LEAST_IOS10 ? kdebug_signpost_start((uint32_t)x, (uintptr_t)y, 0, 0, (uint32_t)(x % 4)) \ - : syscall(SYS_kdebug_trace, APPSDBG_CODE(DBG_MACH_CHUD, x) | DBG_FUNC_START, (uintptr_t)y, 0, 0, (uint32_t)(x % 4)); +ASDISPLAYNODE_EXTERN_C_BEGIN + +/// Log for general node events e.g. interfaceState, didLoad. +#define ASNodeLogEnabled 1 +os_log_t ASNodeLog(); + +/// Log for layout-specific events e.g. calculateLayout. +#define ASLayoutLogEnabled 1 +os_log_t ASLayoutLog(); + +/// Log for display-specific events e.g. display queue batches. +#define ASDisplayLogEnabled 1 +os_log_t ASDisplayLog(); + +/// Log for collection events e.g. reloadData, performBatchUpdates. +#define ASCollectionLogEnabled 1 +os_log_t ASCollectionLog(); + +/// Log for ASNetworkImageNode and ASMultiplexImageNode events. +#define ASImageLoadingLogEnabled 1 +os_log_t ASImageLoadingLog(); + +/// Specialized log for our main thread deallocation trampoline. +#define ASMainThreadDeallocationLogEnabled 0 +os_log_t ASMainThreadDeallocationLog(); + +ASDISPLAYNODE_EXTERN_C_END + +/** + * The activity tracing system changed a lot between iOS 9 and 10. + * In iOS 10, the system was merged with logging and became much more powerful + * and adopted a new API. + * + * The legacy API is visible, but its functionality is extremely limited and the API is so different + * that we don't bother with it. For example, activities described by os_activity_start/end are not + * reflected in the log whereas activities described by the newer + * os_activity_scope are. So unfortunately we must use these iOS 10 + * APIs to get meaningful logging data. + */ +#if OS_LOG_TARGET_HAS_10_12_FEATURES + +#define OS_ACTIVITY_NULLABLE nullable +#define AS_ACTIVITY_CURRENT OS_ACTIVITY_CURRENT +#define as_activity_scope(activity) os_activity_scope(activity) +#define as_activity_apply(activity, block) os_activity_apply(activity, block) +#define as_activity_create(description, parent_activity, flags) os_activity_create(description, parent_activity, flags) +#define as_activity_scope_enter(activity, statePtr) os_activity_scope_enter(activity, statePtr) +#define as_activity_scope_leave(statePtr) os_activity_scope_leave(statePtr) +#define as_activity_get_identifier(activity, outParentID) os_activity_get_identifier(activity, outParentID) -#define ASProfilingSignpostEnd(x, y) \ - AS_AT_LEAST_IOS10 ? kdebug_signpost_end((uint32_t)x, (uintptr_t)y, 0, 0, (uint32_t)(x % 4)) \ - : syscall(SYS_kdebug_trace, APPSDBG_CODE(DBG_MACH_CHUD, x) | DBG_FUNC_END, (uintptr_t)y, 0, 0, (uint32_t)(x % 4)); #else -#define ASProfilingSignpost(x) -#define ASProfilingSignpostStart(x, y) -#define ASProfilingSignpostEnd(x, y) +#define OS_ACTIVITY_NULLABLE +#define AS_ACTIVITY_CURRENT OS_ACTIVITY_NULL +#define as_activity_scope(activity) +#define as_activity_apply(activity, block) +#define as_activity_create(description, parent_activity, flags) OS_ACTIVITY_NULL +#define as_activity_scope_enter(activity, statePtr) +#define as_activity_scope_leave(statePtr) +#define as_activity_get_identifier(activity, outParentID) (os_activity_id_t)0 +#endif // OS_LOG_TARGET_HAS_10_12_FEATURES + +// Create activities only when verbose enabled. Doesn't materially impact performance, but good if we're cluttering up +// activity scopes and reducing readability. +#if ASEnableVerboseLogging + #define as_activity_scope_verbose(activity) as_activity_scope(activity) +#else + #define as_activity_scope_verbose(activity) +#endif + +// Convenience for: as_activity_scope(as_activity_create(description, AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT)) +#define as_activity_create_for_scope(description) \ + as_activity_scope(as_activity_create(description, AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT)) + +/** + * The logging macros are not guarded by deployment-target checks like the activity macros are, but they are + * only available on iOS >= 9 at runtime, so just make them conditional. + */ +#define as_log_create(subsystem, category) (AS_AT_LEAST_IOS9 ? os_log_create(subsystem, category) : (os_log_t)0) +#define as_log_debug(log, format, ...) (AS_AT_LEAST_IOS9 ? os_log_debug(log, format, ##__VA_ARGS__) : (void)0) +#define as_log_info(log, format, ...) (AS_AT_LEAST_IOS9 ? os_log_info(log, format, ##__VA_ARGS__) : (void)0) +#define as_log_error(log, format, ...) (AS_AT_LEAST_IOS9 ? os_log_error(log, format, ##__VA_ARGS__) : (void)0) +#define as_log_fault(log, format, ...) (AS_AT_LEAST_IOS9 ? os_log_fault(log, format, ##__VA_ARGS__) : (void)0) + +#if ASEnableVerboseLogging + #define as_log_verbose(log, format, ...) as_log_debug(log, format, ##__VA_ARGS__) +#else + #define as_log_verbose(log, format, ...) #endif diff --git a/Source/Base/ASLog.m b/Source/Base/ASLog.m new file mode 100644 index 0000000000..42148de9f3 --- /dev/null +++ b/Source/Base/ASLog.m @@ -0,0 +1,37 @@ +// +// ASLog.m +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +os_log_t ASNodeLog() { + return ASCreateOnce((ASEnableLogs && ASNodeLogEnabled) ? as_log_create("org.TextureGroup.Texture", "Node") : OS_LOG_DISABLED); +} + +os_log_t ASLayoutLog() { + return ASCreateOnce((ASEnableLogs && ASLayoutLogEnabled) ? as_log_create("org.TextureGroup.Texture", "Layout") : OS_LOG_DISABLED); +} + +os_log_t ASCollectionLog() { + return ASCreateOnce((ASEnableLogs && ASCollectionLogEnabled) ? as_log_create("org.TextureGroup.Texture", "Collection") : OS_LOG_DISABLED); +} + +os_log_t ASDisplayLog() { + return ASCreateOnce((ASEnableLogs && ASDisplayLogEnabled) ? as_log_create("org.TextureGroup.Texture", "Display") : OS_LOG_DISABLED); +} + +os_log_t ASImageLoadingLog() { + return ASCreateOnce((ASEnableLogs && ASImageLoadingLogEnabled) ? as_log_create("org.TextureGroup.Texture", "ImageLoading") : OS_LOG_DISABLED); +} + +os_log_t ASMainThreadDeallocationLog() { + return ASCreateOnce((ASEnableLogs && ASMainThreadDeallocationLogEnabled) ? as_log_create("org.TextureGroup.Texture", "MainDealloc") : OS_LOG_DISABLED); +} diff --git a/Source/Base/ASSignpost.h b/Source/Base/ASSignpost.h new file mode 100644 index 0000000000..c11a71878f --- /dev/null +++ b/Source/Base/ASSignpost.h @@ -0,0 +1,94 @@ +// +// ASSignpost.h +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +/// The signposts we use. Signposts are grouped by color. The SystemTrace.tracetemplate file +/// should be kept up-to-date with these values. +typedef NS_ENUM(uint32_t, ASSignpostName) { + // Collection/Table (Blue) + ASSignpostDataControllerBatch = 300, // Alloc/layout nodes before collection update. + ASSignpostRangeControllerUpdate, // Ranges update pass. + ASSignpostCollectionUpdate, // Entire update process, from -endUpdates to [super perform…] + + // Rendering (Green) + ASSignpostLayerDisplay = 325, // Client display callout. + ASSignpostRunLoopQueueBatch, // One batch of ASRunLoopQueue. + + // Layout (Purple) + ASSignpostCalculateLayout = 350, // Start of calculateLayoutThatFits to end. Max 1 per thread. + + // Misc (Orange) + ASSignpostDeallocQueueDrain = 375, // One chunk of dealloc queue work. arg0 is count. + ASSignpostCATransactionLayout, // The CA transaction commit layout phase. + ASSignpostCATransactionCommit // The CA transaction commit post-layout phase. +}; + +typedef NS_ENUM(uintptr_t, ASSignpostColor) { + ASSignpostColorBlue, + ASSignpostColorGreen, + ASSignpostColorPurple, + ASSignpostColorOrange, + ASSignpostColorRed, + ASSignpostColorDefault +}; + +static inline ASSignpostColor ASSignpostGetColor(ASSignpostName name, ASSignpostColor colorPref) { + if (colorPref == ASSignpostColorDefault) { + return (ASSignpostColor)((name / 25) % 4); + } else { + return colorPref; + } +} + +#define AS_KDEBUG_ENABLE defined(PROFILE) && __has_include() + +#if AS_KDEBUG_ENABLE + +#import + +// These definitions are required to build the backward-compatible kdebug trace +// on the iOS 10 SDK. The kdebug_trace function crashes if run on iOS 9 and earlier. +// It's valuable to support trace signposts on iOS 9, because A5 devices don't support iOS 10. +#ifndef DBG_MACH_CHUD +#define DBG_MACH_CHUD 0x0A +#define DBG_FUNC_NONE 0 +#define DBG_FUNC_START 1 +#define DBG_FUNC_END 2 +#define DBG_APPS 33 +#define SYS_kdebug_trace 180 +#define KDBG_CODE(Class, SubClass, code) (((Class & 0xff) << 24) | ((SubClass & 0xff) << 16) | ((code & 0x3fff) << 2)) +#define APPSDBG_CODE(SubClass,code) KDBG_CODE(DBG_APPS, SubClass, code) +#endif + +// Currently we'll reserve arg3. +#define ASSignpost(name, identifier, arg2, color) \ +AS_AT_LEAST_IOS10 ? kdebug_signpost(name, (uintptr_t)identifier, (uintptr_t)arg2, 0, ASSignpostGetColor(name, color)) \ +: syscall(SYS_kdebug_trace, APPSDBG_CODE(DBG_MACH_CHUD, name) | DBG_FUNC_NONE, (uintptr_t)identifier, (uintptr_t)arg2, 0, ASSignpostGetColor(name, color)); + +#define ASSignpostStartCustom(name, identifier, arg2) \ +AS_AT_LEAST_IOS10 ? kdebug_signpost_start(name, (uintptr_t)identifier, (uintptr_t)arg2, 0, 0) \ +: syscall(SYS_kdebug_trace, APPSDBG_CODE(DBG_MACH_CHUD, name) | DBG_FUNC_START, (uintptr_t)identifier, (uintptr_t)arg2, 0, 0); +#define ASSignpostStart(name) ASSignpostStartCustom(name, self, 0) + +#define ASSignpostEndCustom(name, identifier, arg2, color) \ +AS_AT_LEAST_IOS10 ? kdebug_signpost_end(name, (uintptr_t)identifier, (uintptr_t)arg2, 0, ASSignpostGetColor(name, color)) \ +: syscall(SYS_kdebug_trace, APPSDBG_CODE(DBG_MACH_CHUD, name) | DBG_FUNC_END, (uintptr_t)identifier, (uintptr_t)arg2, 0, ASSignpostGetColor(name, color)); +#define ASSignpostEnd(name) ASSignpostEndCustom(name, self, 0, ASSignpostColorDefault) + +#else + +#define ASSignpost(name, identifier, arg2, color) +#define ASSignpostStartCustom(name, identifier, arg2) +#define ASSignpostStart(name) +#define ASSignpostEndCustom(name, identifier, arg2, color) +#define ASSignpostEnd(name) + +#endif diff --git a/Source/Debug/AsyncDisplayKit+Debug.h b/Source/Debug/AsyncDisplayKit+Debug.h index c24d45e208..235c2d0bd1 100644 --- a/Source/Debug/AsyncDisplayKit+Debug.h +++ b/Source/Debug/AsyncDisplayKit+Debug.h @@ -1,13 +1,18 @@ // // AsyncDisplayKit+Debug.h -// AsyncDisplayKit -// -// Created by Hannah Troisi on 3/7/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Debug/AsyncDisplayKit+Debug.m b/Source/Debug/AsyncDisplayKit+Debug.m index 14731c66ff..59f0c4f830 100644 --- a/Source/Debug/AsyncDisplayKit+Debug.m +++ b/Source/Debug/AsyncDisplayKit+Debug.m @@ -1,13 +1,18 @@ // // AsyncDisplayKit+Debug.m -// AsyncDisplayKit -// -// Created by Hannah Troisi on 3/7/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Debug/AsyncDisplayKit+Tips.h b/Source/Debug/AsyncDisplayKit+Tips.h new file mode 100644 index 0000000000..9a412c591f --- /dev/null +++ b/Source/Debug/AsyncDisplayKit+Tips.h @@ -0,0 +1,51 @@ +// +// AsyncDisplayKit+Tips.h +// Texture +// +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +typedef void(^ASTipDisplayBlock)(ASDisplayNode *node, NSString *message); + +/** + * The methods added to ASDisplayNode to control the tips system. + * + * To enable tips, define AS_ENABLE_TIPS=1 (e.g. modify ASBaseDefines.h). + */ +@interface ASDisplayNode (Tips) + +/** + * Whether this class should have tips active. Default YES. + * + * NOTE: This property is for _disabling_ tips on a per-class basis, + * if they become annoying or have false-positives. The tips system + * is completely disabled unless you define AS_ENABLE_TIPS=1. + */ +@property (class) BOOL enableTips; + +/** + * A block to be run on the main thread to show text when a tip is tapped. + * + * If nil, the default, the message is just logged to the console with the + * ancestry of the node. + */ +@property (class, nonatomic, copy, null_resettable) ASTipDisplayBlock tipDisplayBlock; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Source/Debug/AsyncDisplayKit+Tips.m b/Source/Debug/AsyncDisplayKit+Tips.m new file mode 100644 index 0000000000..3b1f461a22 --- /dev/null +++ b/Source/Debug/AsyncDisplayKit+Tips.m @@ -0,0 +1,56 @@ +// +// AsyncDisplayKit+Tips.m +// Texture +// +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import "AsyncDisplayKit+Tips.h" +#import + +@implementation ASDisplayNode (Tips) + +static char ASDisplayNodeEnableTipsKey; +static ASTipDisplayBlock _Nullable __tipDisplayBlock; + +/** + * Use associated objects with NSNumbers. This is a debug property - simplicity is king. + */ ++ (void)setEnableTips:(BOOL)enableTips +{ + objc_setAssociatedObject(self, &ASDisplayNodeEnableTipsKey, @(enableTips), OBJC_ASSOCIATION_COPY); +} + ++ (BOOL)enableTips +{ + NSNumber *result = objc_getAssociatedObject(self, &ASDisplayNodeEnableTipsKey); + if (result == nil) { + return YES; + } + return result.boolValue; +} + + ++ (void)setTipDisplayBlock:(ASTipDisplayBlock)tipDisplayBlock +{ + __tipDisplayBlock = tipDisplayBlock; +} + ++ (ASTipDisplayBlock)tipDisplayBlock +{ + return __tipDisplayBlock ?: ^(ASDisplayNode *node, NSString *string) { + NSLog(@"%@. Node ancestry: %@", string, node.ancestryDescription); + }; +} + +@end diff --git a/Source/Details/ASAbstractLayoutController.h b/Source/Details/ASAbstractLayoutController.h index c79f13e8b3..c59e1a4e98 100644 --- a/Source/Details/ASAbstractLayoutController.h +++ b/Source/Details/ASAbstractLayoutController.h @@ -1,11 +1,18 @@ // // ASAbstractLayoutController.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -29,9 +36,9 @@ ASDISPLAYNODE_EXTERN_C_END @interface ASAbstractLayoutController (Unavailable) -- (NSSet *)indexPathsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType __unavailable; +- (NSHashTable *)indexPathsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType __unavailable; -- (void)allIndexPathsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode displaySet:(NSSet * _Nullable * _Nullable)displaySet preloadSet:(NSSet * _Nullable * _Nullable)preloadSet __unavailable; +- (void)allIndexPathsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode displaySet:(NSHashTable * _Nullable * _Nullable)displaySet preloadSet:(NSHashTable * _Nullable * _Nullable)preloadSet __unavailable; @end diff --git a/Source/Details/ASAbstractLayoutController.mm b/Source/Details/ASAbstractLayoutController.mm index 0bf6bd750f..4681206d3d 100644 --- a/Source/Details/ASAbstractLayoutController.mm +++ b/Source/Details/ASAbstractLayoutController.mm @@ -1,11 +1,18 @@ // // ASAbstractLayoutController.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -167,13 +174,13 @@ extern CGRect CGRectExpandToRangeWithScrollableDirections(CGRect rect, ASRangeTu #pragma mark - Abstract Index Path Range Support -- (NSSet *)elementsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType map:(ASElementMap *)map +- (NSHashTable *)elementsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType map:(ASElementMap *)map { ASDisplayNodeAssertNotSupported(); return nil; } -- (void)allElementsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode displaySet:(NSSet *__autoreleasing _Nullable *)displaySet preloadSet:(NSSet *__autoreleasing _Nullable *)preloadSet map:(ASElementMap *)map +- (void)allElementsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode displaySet:(NSHashTable *__autoreleasing _Nullable *)displaySet preloadSet:(NSHashTable *__autoreleasing _Nullable *)preloadSet map:(ASElementMap *)map { ASDisplayNodeAssertNotSupported(); } diff --git a/Source/Details/ASBasicImageDownloader.h b/Source/Details/ASBasicImageDownloader.h index 87343cf759..867e50832a 100644 --- a/Source/Details/ASBasicImageDownloader.h +++ b/Source/Details/ASBasicImageDownloader.h @@ -1,11 +1,18 @@ // // ASBasicImageDownloader.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK diff --git a/Source/Details/ASBasicImageDownloader.mm b/Source/Details/ASBasicImageDownloader.mm index 0dd8cc3226..45961ced6e 100644 --- a/Source/Details/ASBasicImageDownloader.mm +++ b/Source/Details/ASBasicImageDownloader.mm @@ -1,11 +1,18 @@ // // ASBasicImageDownloader.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK #import diff --git a/Source/Details/ASBatchContext.h b/Source/Details/ASBatchContext.h index 2b90d1e15b..c4ba99911b 100644 --- a/Source/Details/ASBatchContext.h +++ b/Source/Details/ASBatchContext.h @@ -1,11 +1,18 @@ // // ASBatchContext.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK diff --git a/Source/Details/ASBatchContext.mm b/Source/Details/ASBatchContext.mm index 3e9367f2a5..66d5e1f05c 100644 --- a/Source/Details/ASBatchContext.mm +++ b/Source/Details/ASBatchContext.mm @@ -1,16 +1,24 @@ // // ASBatchContext.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK #import +#import #import typedef NS_ENUM(NSInteger, ASBatchContextState) { @@ -57,6 +65,7 @@ typedef NS_ENUM(NSInteger, ASBatchContextState) { - (void)completeBatchFetching:(BOOL)didComplete { if (didComplete) { + as_log_debug(ASCollectionLog(), "Completed batch fetch with context %@", self); ASDN::MutexLocker l(__instanceLock__); _state = ASBatchContextStateCompleted; } diff --git a/Source/Details/ASBatchFetchingDelegate.h b/Source/Details/ASBatchFetchingDelegate.h new file mode 100644 index 0000000000..972a03b182 --- /dev/null +++ b/Source/Details/ASBatchFetchingDelegate.h @@ -0,0 +1,27 @@ +// +// ASBatchFetchingDelegate.h +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +@protocol ASBatchFetchingDelegate + +/** + * @abstract Determine if batch fetching should begin based on the remaining time. + * If the delegate doesn't have enough information to confidently decide, it can take the given hint. + * + * @param remainingTime The amount of time left for user to reach the end of the scroll view's content. + * + * @param hint A hint for the delegate to fallback to. + */ +- (BOOL)shouldFetchBatchWithRemainingTime:(NSTimeInterval)remainingTime hint:(BOOL)hint; + +@end diff --git a/Source/Details/ASCollectionElement.h b/Source/Details/ASCollectionElement.h index 0e9f527900..54e2bc50bc 100644 --- a/Source/Details/ASCollectionElement.h +++ b/Source/Details/ASCollectionElement.h @@ -1,13 +1,18 @@ // // ASCollectionElement.h -// AsyncDisplayKit -// -// Created by Huy Nguyen on 2/28/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK @@ -16,6 +21,7 @@ #import @class ASDisplayNode; +@protocol ASRangeManagingNode; NS_ASSUME_NONNULL_BEGIN @@ -25,13 +31,15 @@ AS_SUBCLASSING_RESTRICTED //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, assign) ASSizeRange constrainedSize; -@property (nonatomic, weak) ASDisplayNode *owningNode; +@property (nonatomic, readonly, weak) id owningNode; @property (nonatomic, assign) ASPrimitiveTraitCollection traitCollection; +@property (nonatomic, readonly, nullable) id viewModel; -- (instancetype)initWithNodeBlock:(ASCellNodeBlock)nodeBlock +- (instancetype)initWithViewModel:(nullable id)viewModel + nodeBlock:(ASCellNodeBlock)nodeBlock supplementaryElementKind:(nullable NSString *)supplementaryElementKind constrainedSize:(ASSizeRange)constrainedSize - owningNode:(ASDisplayNode *)owningNode + owningNode:(id)owningNode traitCollection:(ASPrimitiveTraitCollection)traitCollection; /** diff --git a/Source/Details/ASCollectionElement.mm b/Source/Details/ASCollectionElement.mm index cd29245862..6b4f197cbd 100644 --- a/Source/Details/ASCollectionElement.mm +++ b/Source/Details/ASCollectionElement.mm @@ -1,13 +1,18 @@ // // ASCollectionElement.mm -// AsyncDisplayKit -// -// Created by Huy Nguyen on 2/28/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK @@ -27,15 +32,17 @@ ASCellNode *_node; } -- (instancetype)initWithNodeBlock:(ASCellNodeBlock)nodeBlock +- (instancetype)initWithViewModel:(id)viewModel + nodeBlock:(ASCellNodeBlock)nodeBlock supplementaryElementKind:(NSString *)supplementaryElementKind constrainedSize:(ASSizeRange)constrainedSize - owningNode:(ASDisplayNode *)owningNode + owningNode:(id)owningNode traitCollection:(ASPrimitiveTraitCollection)traitCollection { NSAssert(nodeBlock != nil, @"Node block must not be nil"); self = [super init]; if (self) { + _viewModel = viewModel; _nodeBlock = nodeBlock; _supplementaryElementKind = [supplementaryElementKind copy]; _constrainedSize = constrainedSize; @@ -58,6 +65,7 @@ node.owningNode = _owningNode; node.collectionElement = self; ASTraitCollectionPropagateDown(node, _traitCollection); + node.viewModel = _viewModel; _node = node; } return _node; diff --git a/Source/Details/ASCollectionFlowLayoutDelegate.h b/Source/Details/ASCollectionFlowLayoutDelegate.h new file mode 100644 index 0000000000..82c4babd90 --- /dev/null +++ b/Source/Details/ASCollectionFlowLayoutDelegate.h @@ -0,0 +1,40 @@ +// +// ASCollectionFlowLayoutDelegate.h +// Texture +// +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#ifndef MINIMAL_ASDK + +#import + +NS_ASSUME_NONNULL_BEGIN + +AS_SUBCLASSING_RESTRICTED + +/** + * A thread-safe, high performant layout delegate that arranges items into a flow layout. + * It uses a concurrent and multi-line ASStackLayoutSpec under the hood. Thus, per-child flex properties (i.e alignSelf, + * flexShrink, flexGrow, etc - see @ASStackLayoutElement) can be set directly on cell nodes to be used + * to calculate the final collection layout. + */ +@interface ASCollectionFlowLayoutDelegate : NSObject + +- (instancetype)initWithScrollableDirections:(ASScrollDirection)scrollableDirections NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/Source/Details/ASCollectionFlowLayoutDelegate.m b/Source/Details/ASCollectionFlowLayoutDelegate.m new file mode 100644 index 0000000000..2f36436290 --- /dev/null +++ b/Source/Details/ASCollectionFlowLayoutDelegate.m @@ -0,0 +1,88 @@ +// +// ASCollectionFlowLayoutDelegate.m +// Texture +// +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#ifndef MINIMAL_ASDK + +#import + +#import +#import +#import +#import +#import +#import +#import +#import + +@implementation ASCollectionFlowLayoutDelegate { + ASScrollDirection _scrollableDirections; +} + +- (instancetype)init +{ + return [self initWithScrollableDirections:ASScrollDirectionVerticalDirections]; +} + +- (instancetype)initWithScrollableDirections:(ASScrollDirection)scrollableDirections +{ + self = [super init]; + if (self) { + _scrollableDirections = scrollableDirections; + } + return self; +} + +- (ASScrollDirection)scrollableDirections +{ + return _scrollableDirections; +} + +- (id)additionalInfoForLayoutWithElements:(ASElementMap *)elements +{ + return nil; +} + ++ (ASCollectionLayoutState *)calculateLayoutWithContext:(ASCollectionLayoutContext *)context +{ + ASElementMap *elements = context.elements; + NSMutableArray *children = ASArrayByFlatMapping(elements.itemElements, ASCollectionElement *element, element.node); + if (children.count == 0) { + return [[ASCollectionLayoutState alloc] initWithContext:context + contentSize:CGSizeZero + elementToLayoutAttributesTable:[NSMapTable elementToLayoutAttributesTable]]; + } + + ASStackLayoutSpec *stackSpec = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal + spacing:0 + justifyContent:ASStackLayoutJustifyContentStart + alignItems:ASStackLayoutAlignItemsStart + flexWrap:ASStackLayoutFlexWrapWrap + alignContent:ASStackLayoutAlignContentStart + children:children]; + stackSpec.concurrent = YES; + + ASSizeRange sizeRange = ASSizeRangeForCollectionLayoutThatFitsViewportSize(context.viewportSize, context.scrollableDirections); + ASLayout *layout = [stackSpec layoutThatFits:sizeRange]; + + return [[ASCollectionLayoutState alloc] initWithContext:context layout:layout getElementBlock:^ASCollectionElement * _Nonnull(ASLayout * _Nonnull sublayout) { + return ((ASCellNode *)sublayout.layoutElement).collectionElement; + }]; +} + +@end + +#endif diff --git a/Source/Details/ASCollectionGalleryLayoutDelegate.h b/Source/Details/ASCollectionGalleryLayoutDelegate.h new file mode 100644 index 0000000000..d414f35075 --- /dev/null +++ b/Source/Details/ASCollectionGalleryLayoutDelegate.h @@ -0,0 +1,36 @@ +// +// ASCollectionGalleryLayoutDelegate.h +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#ifndef MINIMAL_ASDK + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * A thread-safe layout delegate that arranges items with the same size into a flow layout. + * + * @note Supplemenraty elements are not supported. + */ +AS_SUBCLASSING_RESTRICTED +@interface ASCollectionGalleryLayoutDelegate : NSObject + +- (instancetype)initWithScrollableDirections:(ASScrollDirection)scrollableDirections itemSize:(CGSize)itemSize NS_DESIGNATED_INITIALIZER; + +- (instancetype)init __unavailable; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/Source/Details/ASCollectionGalleryLayoutDelegate.m b/Source/Details/ASCollectionGalleryLayoutDelegate.m new file mode 100644 index 0000000000..0d68971e51 --- /dev/null +++ b/Source/Details/ASCollectionGalleryLayoutDelegate.m @@ -0,0 +1,90 @@ +// +// ASCollectionGalleryLayoutDelegate.m +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#ifndef MINIMAL_ASDK + +#import + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +#pragma mark - ASCollectionGalleryLayoutDelegate + +@implementation ASCollectionGalleryLayoutDelegate { + ASScrollDirection _scrollableDirections; + CGSize _itemSize; +} + +- (instancetype)initWithScrollableDirections:(ASScrollDirection)scrollableDirections itemSize:(CGSize)itemSize +{ + self = [super init]; + if (self) { + ASDisplayNodeAssertFalse(CGSizeEqualToSize(CGSizeZero, itemSize)); + _scrollableDirections = scrollableDirections; + _itemSize = itemSize; + } + return self; +} + +- (ASScrollDirection)scrollableDirections +{ + return _scrollableDirections; +} + +- (id)additionalInfoForLayoutWithElements:(ASElementMap *)elements +{ + return [NSValue valueWithCGSize:_itemSize]; +} + ++ (ASCollectionLayoutState *)calculateLayoutWithContext:(ASCollectionLayoutContext *)context +{ + ASElementMap *elements = context.elements; + CGSize pageSize = context.viewportSize; + CGSize itemSize = ((NSValue *)context.additionalInfo).CGSizeValue; + ASScrollDirection scrollableDirections = context.scrollableDirections; + NSMutableArray<_ASGalleryLayoutItem *> *children = ASArrayByFlatMapping(elements.itemElements, + ASCollectionElement *element, + [[_ASGalleryLayoutItem alloc] initWithItemSize:itemSize collectionElement:element]); + if (children.count == 0) { + return [[ASCollectionLayoutState alloc] initWithContext:context + contentSize:CGSizeZero + elementToLayoutAttributesTable:[NSMapTable weakToStrongObjectsMapTable]]; + } + + // Use a stack spec to calculate layout content size and frames of all elements without actually measuring each element + ASStackLayoutSpec *stackSpec = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal + spacing:0 + justifyContent:ASStackLayoutJustifyContentStart + alignItems:ASStackLayoutAlignItemsStart + flexWrap:ASStackLayoutFlexWrapWrap + alignContent:ASStackLayoutAlignContentStart + children:children]; + stackSpec.concurrent = YES; + ASLayout *layout = [stackSpec layoutThatFits:ASSizeRangeForCollectionLayoutThatFitsViewportSize(pageSize, scrollableDirections)]; + + return [[ASCollectionLayoutState alloc] initWithContext:context layout:layout getElementBlock:^ASCollectionElement *(ASLayout *sublayout) { + return ((_ASGalleryLayoutItem *)sublayout.layoutElement).collectionElement; + }]; +} + +@end + +#endif diff --git a/Source/Details/ASCollectionInternal.h b/Source/Details/ASCollectionInternal.h index de3863370a..3d8b2f280a 100644 --- a/Source/Details/ASCollectionInternal.h +++ b/Source/Details/ASCollectionInternal.h @@ -1,13 +1,18 @@ // // ASCollectionInternal.h -// AsyncDisplayKit -// -// Created by Scott Goodson on 1/1/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK @@ -22,12 +27,22 @@ NS_ASSUME_NONNULL_BEGIN @class ASRangeController; @interface ASCollectionView () -- (instancetype)_initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout layoutFacilitator:(nullable id)layoutFacilitator eventLog:(nullable ASEventLog *)eventLog; +- (instancetype)_initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout layoutFacilitator:(nullable id)layoutFacilitator owningNode:(nullable ASCollectionNode *)owningNode eventLog:(nullable ASEventLog *)eventLog; @property (nonatomic, weak, readwrite) ASCollectionNode *collectionNode; @property (nonatomic, strong, readonly) ASDataController *dataController; @property (nonatomic, strong, readonly) ASRangeController *rangeController; +/** + * The change set that we're currently building, if any. + */ +@property (nonatomic, strong, nullable, readonly) _ASHierarchyChangeSet *changeSet; + +/** + * @see ASCollectionNode+Beta.h for full documentation. + */ +@property (nonatomic, assign) BOOL usesSynchronousDataLoading; + /** * Attempt to get the view-layer index path for the item with the given index path. * diff --git a/Source/Details/ASCollectionInternal.m b/Source/Details/ASCollectionInternal.m index fd39342a48..5408f81d11 100644 --- a/Source/Details/ASCollectionInternal.m +++ b/Source/Details/ASCollectionInternal.m @@ -1,12 +1,17 @@ // // ASCollectionInternal.m -// AsyncDisplayKit -// -// Created by Scott Goodson on 1/1/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // diff --git a/Source/Details/ASCollectionLayoutContext.h b/Source/Details/ASCollectionLayoutContext.h new file mode 100644 index 0000000000..0868a68235 --- /dev/null +++ b/Source/Details/ASCollectionLayoutContext.h @@ -0,0 +1,42 @@ +// +// ASCollectionLayoutContext.h +// Texture +// +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#ifndef MINIMAL_ASDK + +#import +#import +#import + +@class ASElementMap; + +NS_ASSUME_NONNULL_BEGIN + +AS_SUBCLASSING_RESTRICTED +@interface ASCollectionLayoutContext : NSObject + +@property (nonatomic, assign, readonly) CGSize viewportSize; +@property (nonatomic, assign, readonly) ASScrollDirection scrollableDirections; +@property (nonatomic, weak, readonly) ASElementMap *elements; +@property (nonatomic, strong, readonly, nullable) id additionalInfo; + +- (instancetype)init __unavailable; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/Source/Details/ASCollectionLayoutContext.m b/Source/Details/ASCollectionLayoutContext.m new file mode 100644 index 0000000000..8d651d3974 --- /dev/null +++ b/Source/Details/ASCollectionLayoutContext.m @@ -0,0 +1,111 @@ +// +// ASCollectionLayoutContext.m +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#ifndef MINIMAL_ASDK + +#import +#import + +#import +#import +#import +#import +#import +#import + +@implementation ASCollectionLayoutContext { + Class _layoutDelegateClass; + + // This ivar doesn't directly involve in the layout calculation process, i.e contexts can be equal regardless of the layout caches. + // As a result, this ivar is ignored in -isEqualToContext: and -hash. + __weak ASCollectionLayoutCache *_layoutCache; +} + +- (instancetype)initWithViewportSize:(CGSize)viewportSize + scrollableDirections:(ASScrollDirection)scrollableDirections + elements:(ASElementMap *)elements + layoutDelegateClass:(Class)layoutDelegateClass + layoutCache:(ASCollectionLayoutCache *)layoutCache + additionalInfo:(id)additionalInfo +{ + self = [super init]; + if (self) { + ASDisplayNodeAssertTrue([layoutDelegateClass conformsToProtocol:@protocol(ASCollectionLayoutDelegate)]); + _viewportSize = viewportSize; + _scrollableDirections = scrollableDirections; + _elements = elements; + _layoutDelegateClass = layoutDelegateClass; + _layoutCache = layoutCache; + _additionalInfo = additionalInfo; + } + return self; +} + +- (Class)layoutDelegateClass +{ + return _layoutDelegateClass; +} + +- (ASCollectionLayoutCache *)layoutCache +{ + return _layoutCache; +} + +- (BOOL)isEqualToContext:(ASCollectionLayoutContext *)context +{ + if (context == nil) { + return NO; + } + + // NOTE: ASObjectIsEqual returns YES when both objects are nil. + // So don't use ASObjectIsEqual on _elements. + // It is a weak property and 2 layouts generated from different sets of elements + // should never be considered the same even if they are nil now. + return CGSizeEqualToSize(_viewportSize, context.viewportSize) + && _scrollableDirections == context.scrollableDirections + && [_elements isEqual:context.elements] + && _layoutDelegateClass == context.layoutDelegateClass + && ASObjectIsEqual(_additionalInfo, context.additionalInfo); +} + +- (BOOL)isEqual:(id)other +{ + if (self == other) { + return YES; + } + if (! [other isKindOfClass:[ASCollectionLayoutContext class]]) { + return NO; + } + return [self isEqualToContext:other]; +} + +- (NSUInteger)hash +{ + struct { + CGSize viewportSize; + ASScrollDirection scrollableDirections; + NSUInteger elementsHash; + NSUInteger layoutDelegateClassHash; + NSUInteger additionalInfoHash; + } data = { + _viewportSize, + _scrollableDirections, + _elements.hash, + _layoutDelegateClass.hash, + [_additionalInfo hash] + }; + return ASHashBytes(&data, sizeof(data)); +} + +@end + +#endif diff --git a/Source/Details/ASCollectionLayoutDelegate.h b/Source/Details/ASCollectionLayoutDelegate.h new file mode 100644 index 0000000000..1c31aa9fc1 --- /dev/null +++ b/Source/Details/ASCollectionLayoutDelegate.h @@ -0,0 +1,69 @@ +// +// ASCollectionLayoutDelegate.h +// Texture +// +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#ifndef MINIMAL_ASDK + +#import +#import +#import + +@class ASElementMap, ASCollectionLayoutContext, ASCollectionLayoutState; + +NS_ASSUME_NONNULL_BEGIN + +@protocol ASCollectionLayoutDelegate + +/** + * @abstract Returns the scrollable directions of the coming layout (@see @c -calculateLayoutWithContext:). + * It will be available in the context parameter in +calculateLayoutWithContext: + * + * @return The scrollable directions. + */ +- (ASScrollDirection)scrollableDirections; + +/** + * @abstract Returns any additional information needed for a coming layout pass (@see @c -calculateLayoutWithContext:) with the given elements. + * + * @param elements The elements to be laid out later. + * + * @discussion The returned object must support equality and hashing (i.e `-isEqual:` and `-hash` must be properly implemented). + * It should contain all the information needed for the layout pass to perform. It will be available in the context parameter in +calculateLayoutWithContext: + * + * This method will be called on main thread. + */ +- (nullable id)additionalInfoForLayoutWithElements:(ASElementMap *)elements; + +/** + * @abstract Prepares and returns a new layout for given context. + * + * @param context A context that contains all elements to be laid out and any additional information needed. + * + * @return The new layout calculated for the given context. + * + * @discussion This method is called ahead of time, i.e before the underlying collection/table view is aware of the provided elements. + * As a result, clients must solely rely on the given context and should not reach out to other objects for information not available in the context. + * + * This method can be called on background theads. It must be thread-safe and should not change any internal state of this delegate. + * It must block the calling thread but can dispatch to other theads to reduce total blocking time. + */ ++ (ASCollectionLayoutState *)calculateLayoutWithContext:(ASCollectionLayoutContext *)context; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/Source/Details/ASCollectionLayoutState.h b/Source/Details/ASCollectionLayoutState.h new file mode 100644 index 0000000000..eb91944611 --- /dev/null +++ b/Source/Details/ASCollectionLayoutState.h @@ -0,0 +1,114 @@ +// +// ASCollectionLayoutState.h +// Texture +// +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#ifndef MINIMAL_ASDK + +#import +#import +#import + +@class ASCollectionLayoutContext, ASLayout, ASCollectionElement; + +NS_ASSUME_NONNULL_BEGIN + +@interface NSMapTable (ASCollectionLayoutConvenience) + ++ (NSMapTable *)elementToLayoutAttributesTable; + +@end + +AS_SUBCLASSING_RESTRICTED + +/// An immutable state of the collection layout +@interface ASCollectionLayoutState : NSObject + +/// The context used to calculate this object +@property (nonatomic, strong, readonly) ASCollectionLayoutContext *context; + +/// The final content size of the collection's layout +@property (nonatomic, assign, readonly) CGSize contentSize; + +- (instancetype)init __unavailable; + +/** + * Designated initializer. + * + * @param context The context used to calculate this object + * + * @param contentSize The content size of the collection's layout + * + * @param table A map between elements to their layout attributes. It must contain all elements. + * It should have NSMapTableObjectPointerPersonality and NSMapTableWeakMemory as key options. + */ +- (instancetype)initWithContext:(ASCollectionLayoutContext *)context + contentSize:(CGSize)contentSize + elementToLayoutAttributesTable:(NSMapTable *)table NS_DESIGNATED_INITIALIZER; + +/** + * Convenience initializer. + * + * @param context The context used to calculate this object + * + * @param layout The layout describes size and position of all elements. + * + * @param getElementBlock A block that can retrieve the collection element from a direct sublayout of the root layout. + */ +- (instancetype)initWithContext:(ASCollectionLayoutContext *)context + layout:(ASLayout *)layout + getElementBlock:(ASCollectionElement *(^)(ASLayout *))getElementBlock; + +/** + * Returns all layout attributes present in this object. + */ +- (NSArray *)allLayoutAttributes; + +/** + * Returns layout attributes of elements in the specified rect. + * + * @param rect The rect containing the target elements. + */ +- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect; + +/** + * Returns layout attributes of the element at the specified index path. + * + * @param indexPath The index path of the item. + */ +- (nullable UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath; + +/** + * Returns layout attributes of the specified supplementary element. + * + * @param kind A string that identifies the type of the supplementary element. + * + * @param indexPath The index path of the element. + */ +- (nullable UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryElementOfKind:(NSString *)kind + atIndexPath:(NSIndexPath *)indexPath; + +/** + * Returns layout attributes of the specified element. + * + * @element The element. + */ +- (nullable UICollectionViewLayoutAttributes *)layoutAttributesForElement:(ASCollectionElement *)element; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/Source/Details/ASCollectionLayoutState.mm b/Source/Details/ASCollectionLayoutState.mm new file mode 100644 index 0000000000..9432805d94 --- /dev/null +++ b/Source/Details/ASCollectionLayoutState.mm @@ -0,0 +1,221 @@ +// +// ASCollectionLayoutState.mm +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#ifndef MINIMAL_ASDK + +#import +#import + +#import +#import +#import +#import +#import +#import +#import +#import +#import + +@implementation NSMapTable (ASCollectionLayoutConvenience) + ++ (NSMapTable *)elementToLayoutAttributesTable +{ + return [NSMapTable mapTableWithKeyOptions:(NSMapTableWeakMemory | NSMapTableObjectPointerPersonality) valueOptions:NSMapTableStrongMemory]; +} + +@end + +@implementation ASCollectionLayoutState { + ASDN::Mutex __instanceLock__; + NSMapTable *_elementToLayoutAttributesTable; + ASPageToLayoutAttributesTable *_pageToLayoutAttributesTable; + ASPageToLayoutAttributesTable *_unmeasuredPageToLayoutAttributesTable; +} + +- (instancetype)initWithContext:(ASCollectionLayoutContext *)context + layout:(ASLayout *)layout + getElementBlock:(ASCollectionElement *(^)(ASLayout *))getElementBlock +{ + ASElementMap *elements = context.elements; + NSMapTable *table = [NSMapTable elementToLayoutAttributesTable]; + + for (ASLayout *sublayout in layout.sublayouts) { + ASCollectionElement *element = getElementBlock(sublayout); + if (element == nil) { + ASDisplayNodeFailAssert(@"Element not found!"); + continue; + } + + NSIndexPath *indexPath = [elements indexPathForElement:element]; + NSString *supplementaryElementKind = element.supplementaryElementKind; + + UICollectionViewLayoutAttributes *attrs; + if (supplementaryElementKind == nil) { + attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath]; + } else { + attrs = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:supplementaryElementKind withIndexPath:indexPath]; + } + + attrs.frame = sublayout.frame; + [table setObject:attrs forKey:element]; + } + + return [self initWithContext:context contentSize:layout.size elementToLayoutAttributesTable:table]; +} + +- (instancetype)initWithContext:(ASCollectionLayoutContext *)context + contentSize:(CGSize)contentSize + elementToLayoutAttributesTable:(NSMapTable *)table +{ + self = [super init]; + if (self) { + _context = context; + _contentSize = contentSize; + _elementToLayoutAttributesTable = [table copy]; // Copy the given table to make sure clients can't mutate it after this point. + CGSize pageSize = context.viewportSize; + _pageToLayoutAttributesTable = [ASPageTable pageTableWithLayoutAttributes:table.objectEnumerator contentSize:contentSize pageSize:pageSize]; + _unmeasuredPageToLayoutAttributesTable = [ASCollectionLayoutState _unmeasuredLayoutAttributesTableFromTable:table contentSize:contentSize pageSize:pageSize]; + } + return self; +} + +- (NSArray *)allLayoutAttributes +{ + return [_elementToLayoutAttributesTable.objectEnumerator allObjects]; +} + +- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath +{ + ASCollectionElement *element = [_context.elements elementForItemAtIndexPath:indexPath]; + return [_elementToLayoutAttributesTable objectForKey:element]; +} + +- (UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryElementOfKind:(NSString *)elementKind + atIndexPath:(NSIndexPath *)indexPath +{ + ASCollectionElement *element = [_context.elements supplementaryElementOfKind:elementKind atIndexPath:indexPath]; + return [_elementToLayoutAttributesTable objectForKey:element]; +} + +- (UICollectionViewLayoutAttributes *)layoutAttributesForElement:(ASCollectionElement *)element +{ + return [_elementToLayoutAttributesTable objectForKey:element]; +} + +- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect +{ + CGSize pageSize = _context.viewportSize; + NSPointerArray *pages = ASPageCoordinatesForPagesThatIntersectRect(rect, _contentSize, pageSize); + if (pages.count == 0) { + return @[]; + } + + // Use a set here because some items may span multiple pages + NSMutableSet *result = [NSMutableSet set]; + for (id pagePtr in pages) { + ASPageCoordinate page = (ASPageCoordinate)pagePtr; + NSArray *allAttrs = [_pageToLayoutAttributesTable objectForPage:page]; + if (allAttrs.count > 0) { + CGRect pageRect = ASPageCoordinateGetPageRect(page, pageSize); + + if (CGRectContainsRect(rect, pageRect)) { + [result addObjectsFromArray:allAttrs]; + } else { + for (UICollectionViewLayoutAttributes *attrs in allAttrs) { + if (CGRectIntersectsRect(rect, attrs.frame)) { + [result addObject:attrs]; + } + } + } + } + } + + return [result allObjects]; +} + +- (ASPageToLayoutAttributesTable *)getAndRemoveUnmeasuredLayoutAttributesPageTableInRect:(CGRect)rect + contentSize:(CGSize)contentSize + pageSize:(CGSize)pageSize +{ + ASDN::MutexLocker l(__instanceLock__); + if (_unmeasuredPageToLayoutAttributesTable.count == 0 || CGRectIsNull(rect) || CGRectIsEmpty(rect) || CGSizeEqualToSize(CGSizeZero, contentSize) || CGSizeEqualToSize(CGSizeZero, pageSize)) { + return nil; + } + + // Step 1: Determine all the pages that intersect the specified rect + NSPointerArray *pagesInRect = ASPageCoordinatesForPagesThatIntersectRect(rect, contentSize, pageSize); + if (pagesInRect.count == 0) { + return nil; + } + + // Step 2: Filter out attributes in these pages that intersect the specified rect. + ASPageToLayoutAttributesTable *result = nil; + for (id pagePtr in pagesInRect) { + ASPageCoordinate page = (ASPageCoordinate)pagePtr; + NSMutableArray *attrsInPage = [_unmeasuredPageToLayoutAttributesTable objectForPage:page]; + if (attrsInPage.count == 0) { + continue; + } + + NSMutableArray *intersectingAttrsInPage = nil; + CGRect pageRect = ASPageCoordinateGetPageRect(page, pageSize); + if (CGRectContainsRect(rect, pageRect)) { + // This page fits well within the specified rect. Simply return all of its attributes. + intersectingAttrsInPage = attrsInPage; + } else { + // The page intersects the specified rect. Some attributes in this page are returned, some are not. + for (UICollectionViewLayoutAttributes *attrs in attrsInPage) { + if (CGRectIntersectsRect(rect, attrs.frame)) { + if (intersectingAttrsInPage == nil) { + intersectingAttrsInPage = [NSMutableArray array]; + } + [intersectingAttrsInPage addObject:attrs]; + } + } + } + + if (intersectingAttrsInPage.count > 0) { + if (attrsInPage.count == intersectingAttrsInPage.count) { + [_unmeasuredPageToLayoutAttributesTable removeObjectForPage:page]; + } else { + [attrsInPage removeObjectsInArray:intersectingAttrsInPage]; + } + if (result == nil) { + result = [ASPageTable pageTableForStrongObjectPointers]; + } + [result setObject:intersectingAttrsInPage forPage:page]; + } + } + + return result; +} + +#pragma mark - Private methods + ++ (ASPageToLayoutAttributesTable *)_unmeasuredLayoutAttributesTableFromTable:(NSMapTable *)table + contentSize:(CGSize)contentSize + pageSize:(CGSize)pageSize +{ + NSMutableArray *unmeasuredAttrs = [NSMutableArray array]; + for (ASCollectionElement *element in table) { + UICollectionViewLayoutAttributes *attrs = [table objectForKey:element]; + if (element.nodeIfAllocated == nil || CGSizeEqualToSize(element.nodeIfAllocated.calculatedSize, attrs.frame.size) == NO) { + [unmeasuredAttrs addObject:attrs]; + } + } + + return [ASPageTable pageTableWithLayoutAttributes:unmeasuredAttrs contentSize:contentSize pageSize:pageSize]; +} + +@end + +#endif diff --git a/Source/Details/ASCollectionViewLayoutController.h b/Source/Details/ASCollectionViewLayoutController.h index ccd58d9442..178bfdba34 100644 --- a/Source/Details/ASCollectionViewLayoutController.h +++ b/Source/Details/ASCollectionViewLayoutController.h @@ -1,11 +1,18 @@ // // ASCollectionViewLayoutController.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK diff --git a/Source/Details/ASCollectionViewLayoutController.m b/Source/Details/ASCollectionViewLayoutController.m index 2e459dcb22..d132313d5c 100644 --- a/Source/Details/ASCollectionViewLayoutController.m +++ b/Source/Details/ASCollectionViewLayoutController.m @@ -1,18 +1,25 @@ // -// ASCollectionViewLayoutController.mm -// AsyncDisplayKit +// ASCollectionViewLayoutController.m +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK #import #import -#import +#import #import #import #import @@ -48,14 +55,14 @@ typedef struct ASRangeGeometry ASRangeGeometry; return self; } -- (NSSet *)elementsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType map:(ASElementMap *)map +- (NSHashTable *)elementsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType map:(ASElementMap *)map { ASRangeTuningParameters tuningParameters = [self tuningParametersForRangeMode:rangeMode rangeType:rangeType]; CGRect rangeBounds = [self rangeBoundsWithScrollDirection:scrollDirection rangeTuningParameters:tuningParameters]; return [self elementsWithinRangeBounds:rangeBounds map:map]; } -- (void)allElementsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode displaySet:(NSSet *__autoreleasing _Nullable *)displaySet preloadSet:(NSSet *__autoreleasing _Nullable *)preloadSet map:(ASElementMap *)map +- (void)allElementsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode displaySet:(NSHashTable *__autoreleasing _Nullable *)displaySet preloadSet:(NSHashTable *__autoreleasing _Nullable *)preloadSet map:(ASElementMap *)map { if (displaySet == NULL || preloadSet == NULL) { return; @@ -68,9 +75,10 @@ typedef struct ASRangeGeometry ASRangeGeometry; CGRect unionBounds = CGRectUnion(displayBounds, preloadBounds); NSArray *layoutAttributes = [_collectionViewLayout layoutAttributesForElementsInRect:unionBounds]; + NSInteger count = layoutAttributes.count; - NSMutableSet *display = [NSMutableSet setWithCapacity:layoutAttributes.count]; - NSMutableSet *preload = [NSMutableSet setWithCapacity:layoutAttributes.count]; + __auto_type display = [[NSHashTable alloc] initWithOptions:NSHashTableObjectPointerPersonality capacity:count]; + __auto_type preload = [[NSHashTable alloc] initWithOptions:NSHashTableObjectPointerPersonality capacity:count]; for (UICollectionViewLayoutAttributes *la in layoutAttributes) { // Manually filter out elements that don't intersect the range bounds. @@ -99,10 +107,10 @@ typedef struct ASRangeGeometry ASRangeGeometry; return; } -- (NSSet *)elementsWithinRangeBounds:(CGRect)rangeBounds map:(ASElementMap *)map +- (NSHashTable *)elementsWithinRangeBounds:(CGRect)rangeBounds map:(ASElementMap *)map { NSArray *layoutAttributes = [_collectionViewLayout layoutAttributesForElementsInRect:rangeBounds]; - NSMutableSet *elementSet = [NSMutableSet setWithCapacity:layoutAttributes.count]; + NSHashTable *elementSet = [[NSHashTable alloc] initWithOptions:NSHashTableObjectPointerPersonality capacity:layoutAttributes.count]; for (UICollectionViewLayoutAttributes *la in layoutAttributes) { // Manually filter out elements that don't intersect the range bounds. diff --git a/Source/Details/ASCollectionViewLayoutInspector.h b/Source/Details/ASCollectionViewLayoutInspector.h index 8e19d21aaf..668526c5b8 100644 --- a/Source/Details/ASCollectionViewLayoutInspector.h +++ b/Source/Details/ASCollectionViewLayoutInspector.h @@ -1,9 +1,18 @@ // // ASCollectionViewLayoutInspector.h -// AsyncDisplayKit +// Texture // -// Created by Garrett Moon on 11/19/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 /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK diff --git a/Source/Details/ASCollectionViewLayoutInspector.m b/Source/Details/ASCollectionViewLayoutInspector.m index aec0b7e5a7..04809fe63f 100644 --- a/Source/Details/ASCollectionViewLayoutInspector.m +++ b/Source/Details/ASCollectionViewLayoutInspector.m @@ -1,9 +1,18 @@ // // ASCollectionViewLayoutInspector.m -// AsyncDisplayKit +// Texture // -// Created by Garrett Moon on 11/19/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 /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK #import diff --git a/Source/Details/ASDataController.h b/Source/Details/ASDataController.h index c0e606791a..635820e02a 100644 --- a/Source/Details/ASDataController.h +++ b/Source/Details/ASDataController.h @@ -1,11 +1,18 @@ // // ASDataController.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK @@ -28,10 +35,14 @@ NS_ASSUME_NONNULL_BEGIN #endif @class ASCellNode; +@class ASCollectionElement; +@class ASCollectionLayoutContext; +@class ASCollectionLayoutState; @class ASDataController; @class ASElementMap; -@class ASCollectionElement; +@class ASLayout; @class _ASHierarchyChangeSet; +@protocol ASRangeManagingNode; @protocol ASTraitEnvironment; @protocol ASSectionContext; @@ -52,11 +63,6 @@ extern NSString * const ASCollectionInvalidUpdateException; */ - (ASCellNodeBlock)dataController:(ASDataController *)dataController nodeBlockAtIndexPath:(NSIndexPath *)indexPath; -/** - The constrained size range for layout. - */ -- (ASSizeRange)dataController:(ASDataController *)dataController constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath; - /** Fetch the number of rows in specific section. */ @@ -68,30 +74,35 @@ extern NSString * const ASCollectionInvalidUpdateException; - (NSUInteger)numberOfSectionsInDataController:(ASDataController *)dataController; /** - Returns if the collection element size matches a given size + Returns if the collection element size matches a given size. + @precondition The element is present in the data controller's visible map. */ - (BOOL)dataController:(ASDataController *)dataController presentedSizeForElement:(ASCollectionElement *)element matchesSize:(CGSize)size; +- (nullable id)dataController:(ASDataController *)dataController viewModelForItemAtIndexPath:(NSIndexPath *)indexPath; + @optional +/** + The constrained size range for layout. Called only if collection layout delegate is not provided. + */ +- (ASSizeRange)dataController:(ASDataController *)dataController constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath; + - (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; +/** + The constrained size range for layout. Called only if no data controller layout delegate is provided. + */ - (ASSizeRange)dataController:(ASDataController *)dataController constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; - (nullable id)dataController:(ASDataController *)dataController contextForSection:(NSInteger)section; @end -@protocol ASDataControllerEnvironmentDelegate - -- (nullable id)dataControllerEnvironment; - -@end - /** Delegate for notify the data updating of data controller. These methods will be invoked from main thread right now, but it may be moved to background thread in the future. @@ -109,8 +120,41 @@ extern NSString * const ASCollectionInvalidUpdateException; * Called for change set updates. * * @param changeSet The change set that includes all updates + * + * @param updates The block that performs relevant data updates. + * + * @discussion The updates block must always be executed or the data controller will get into a bad state. + * It should be called at the time the backing view is ready to process the updates, + * i.e inside the updates block of `-[UICollectionView performBatchUpdates:completion:] or after calling `-[UITableView beginUpdates]`. */ -- (void)dataController:(ASDataController *)dataController didUpdateWithChangeSet:(_ASHierarchyChangeSet *)changeSet; +- (void)dataController:(ASDataController *)dataController didUpdateWithChangeSet:(_ASHierarchyChangeSet *)changeSet updates:(dispatch_block_t)updates; + +@end + +@protocol ASDataControllerLayoutDelegate + +/** + * @abstract Returns a layout context needed for a coming layout pass with the given elements. + * The context should contain the elements and any additional information needed. + * + * @discussion This method will be called on main thread. + */ +- (ASCollectionLayoutContext *)layoutContextWithElements:(ASElementMap *)elements; + +/** + * @abstract Prepares and returns a new layout for given context. + * + * @param context A context that was previously returned by `-layoutContextWithElements:`. + * + * @return The new layout calculated for the given context. + * + * @discussion This method is called ahead of time, i.e before the underlying collection/table view is aware of the provided elements. + * As a result, clients must solely rely on the given context and should not reach out to other objects for information not available in the context. + * + * This method will be called on background theads. It must be thread-safe and should not change any internal state of the conforming object. + * It must block the calling thread but can dispatch to other theads to reduce total blocking time. + */ ++ (ASCollectionLayoutState *)calculateLayoutWithContext:(ASCollectionLayoutContext *)context; @end @@ -123,17 +167,30 @@ extern NSString * const ASCollectionInvalidUpdateException; */ @interface ASDataController : NSObject -- (instancetype)initWithDataSource:(id)dataSource eventLog:(nullable ASEventLog *)eventLog NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithDataSource:(id)dataSource node:(nullable id)node eventLog:(nullable ASEventLog *)eventLog NS_DESIGNATED_INITIALIZER; + +- (instancetype)init NS_UNAVAILABLE; + +/** + * The node that owns this data controller, if any. + * + * NOTE: Soon we will drop support for using ASTableView/ASCollectionView without the node, so this will be non-null. + */ +@property (nonatomic, nullable, weak, readonly) id node; /** * The map that is currently displayed. The "UIKit index space." + * + * This property will only be changed on the main thread. */ -@property (nonatomic, strong, readonly) ASElementMap *visibleMap; +@property (atomic, copy, readonly) ASElementMap *visibleMap; /** * The latest map fetched from the data source. May be more recent than @c visibleMap. + * + * This property will only be changed on the main thread. */ -@property (nonatomic, strong, readonly) ASElementMap *pendingMap; +@property (atomic, copy, readonly) ASElementMap *pendingMap; /** Data source for fetching data info. @@ -151,9 +208,9 @@ extern NSString * const ASCollectionInvalidUpdateException; @property (nonatomic, weak) id delegate; /** - * + * Delegate for preparing layouts. Main thead only. */ -@property (nonatomic, weak) id environmentDelegate; +@property (nonatomic, weak) id layoutDelegate; #ifdef __cplusplus /** @@ -181,6 +238,11 @@ extern NSString * const ASCollectionInvalidUpdateException; @property (nonatomic, strong, readonly) ASEventLog *eventLog; #endif +/** + * @see ASCollectionNode+Beta.h for full documentation. + */ +@property (nonatomic, assign) BOOL usesSynchronousDataLoading; + /** @name Data Updating */ - (void)updateWithChangeSet:(_ASHierarchyChangeSet *)changeSet; @@ -194,7 +256,7 @@ extern NSString * const ASCollectionInvalidUpdateException; - (void)relayoutAllNodes; /** - * Re-measures given noades in the backing store. + * Re-measures given nodes in the backing store. * * @discussion Used to respond to setNeedsLayout calls in ASCellNode */ diff --git a/Source/Details/ASDataController.mm b/Source/Details/ASDataController.mm index aac7e36dc3..f00377a537 100644 --- a/Source/Details/ASDataController.mm +++ b/Source/Details/ASDataController.mm @@ -1,11 +1,18 @@ // // ASDataController.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK @@ -13,17 +20,24 @@ #import #import +#import #import #import +#import +#import +#import +#import +#import #import +#import +#import #import +#import +#import +#import #import #import -#import -#import -#import -#import -#import + #import #import #import @@ -32,8 +46,6 @@ //#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)) @@ -44,16 +56,10 @@ const static char * kASDataControllerEditingQueueContext = "kASDataControllerEdi 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 +typedef dispatch_block_t ASDataControllerCompletionBlock; @interface ASDataController () { + id _layoutDelegate; NSInteger _nextSectionID; @@ -71,28 +77,33 @@ typedef void (^ASDataControllerCompletionBlock)(NSArray * unsigned int supplementaryNodeKindsInSections:1; unsigned int supplementaryNodesOfKindInSection:1; unsigned int supplementaryNodeBlockOfKindAtIndexPath:1; + unsigned int constrainedSizeForNodeAtIndexPath:1; unsigned int constrainedSizeForSupplementaryNodeOfKindAtIndexPath:1; unsigned int contextForSection:1; } _dataSourceFlags; } +@property (atomic, copy, readwrite) ASElementMap *pendingMap; +@property (atomic, copy, readwrite) ASElementMap *visibleMap; @end @implementation ASDataController #pragma mark - Lifecycle -- (instancetype)initWithDataSource:(id)dataSource eventLog:(ASEventLog *)eventLog +- (instancetype)initWithDataSource:(id)dataSource node:(nullable id)node eventLog:(ASEventLog *)eventLog { if (!(self = [super init])) { return nil; } + _node = node; _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.constrainedSizeForNodeAtIndexPath = [_dataSource respondsToSelector:@selector(dataController:constrainedSizeForNodeAtIndexPath:)]; _dataSourceFlags.constrainedSizeForSupplementaryNodeOfKindAtIndexPath = [_dataSource respondsToSelector:@selector(dataController:constrainedSizeForSupplementaryNodeOfKind:atIndexPath:)]; _dataSourceFlags.contextForSection = [_dataSource respondsToSelector:@selector(dataController:contextForSection:)]; @@ -100,7 +111,7 @@ typedef void (^ASDataControllerCompletionBlock)(NSArray * _eventLog = eventLog; #endif - _visibleMap = _pendingMap = [[ASElementMap alloc] init]; + self.visibleMap = self.pendingMap = [[ASElementMap alloc] init]; _nextSectionID = 0; @@ -114,14 +125,6 @@ typedef void (^ASDataControllerCompletionBlock)(NSArray * 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; @@ -134,22 +137,33 @@ typedef void (^ASDataControllerCompletionBlock)(NSArray * return parallelProcessorCount; } +- (id)layoutDelegate +{ + ASDisplayNodeAssertMainThread(); + return _layoutDelegate; +} + +- (void)setLayoutDelegate:(id)layoutDelegate +{ + ASDisplayNodeAssertMainThread(); + if (layoutDelegate != _layoutDelegate) { + _layoutDelegate = layoutDelegate; + } +} + #pragma mark - Cell Layout -- (void)batchLayoutNodesFromContexts:(NSArray *)elements batchSize:(NSInteger)batchSize batchCompletion:(ASDataControllerCompletionBlock)batchCompletionHandler +- (void)batchAllocateNodesFromElements:(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(@[], @[]); + batchCompletionHandler(); return; } - ASProfilingSignpostStart(2, _dataSource); - + ASSignpostStart(ASSignpostDataControllerBatch); + if (batchSize == 0) { batchSize = [[ASDataController class] parallelProcessorCount] * kASDataControllerSizingCountPerProcessor; } @@ -158,12 +172,15 @@ typedef void (^ASDataControllerCompletionBlock)(NSArray * // 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); + NSArray *batchedElements = [elements subarrayWithRange:batchedRange]; + { + as_activity_create_for_scope("Data controller batch"); + [self _allocateNodesFromElements:batchedElements]; + } + batchCompletionHandler(); } - - ASProfilingSignpostEnd(2, _dataSource); + + ASSignpostEndCustom(ASSignpostDataControllerBatch, self, 0, (_dataSource != nil ? ASSignpostColorDefault : ASSignpostColorRed)); } /** @@ -178,17 +195,16 @@ typedef void (^ASDataControllerCompletionBlock)(NSArray * node.frame = frame; } -- (NSArray *)_layoutNodesFromContexts:(NSArray *)elements +// TODO Is returned array still needed? Can it be removed? +- (void)_allocateNodesFromElements:(NSArray *)elements { ASSERT_ON_EDITING_QUEUE; NSUInteger nodeCount = elements.count; if (!nodeCount || _dataSource == nil) { - return @[]; + 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(); @@ -200,31 +216,13 @@ typedef void (^ASDataControllerCompletionBlock)(NSArray * 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) @@ -249,9 +247,10 @@ typedef void (^ASDataControllerCompletionBlock)(NSArray * } }]; } else if (_dataSourceFlags.supplementaryNodesOfKindInSection) { + id dataSource = _dataSource; [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]; + NSUInteger itemCount = [dataSource dataController:self supplementaryNodesOfKind:kind inSection:sectionIndex]; for (NSUInteger i = 0; i < itemCount; i++) { [indexPaths addObject:[NSIndexPath indexPathForItem:i inSection:sectionIndex]]; } @@ -268,16 +267,17 @@ typedef void (^ASDataControllerCompletionBlock)(NSArray * * @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 owningNode The node that owns the new elements. * @param traitCollection The trait collection needed to initialize elements * @param indexPathsAreNew YES if index paths are "after the update," NO otherwise. + * @param shouldFetchSizeRanges Whether constrained sizes should be fetched from data source */ - (void)_repopulateSupplementaryNodesIntoMap:(ASMutableElementMap *)map forSectionsContainingIndexPaths:(NSArray *)indexPaths changeSet:(_ASHierarchyChangeSet *)changeSet - owningNode:(ASDisplayNode *)owningNode traitCollection:(ASPrimitiveTraitCollection)traitCollection indexPathsAreNew:(BOOL)indexPathsAreNew + shouldFetchSizeRanges:(BOOL)shouldFetchSizeRanges + previousMap:(ASElementMap *)previousMap { ASDisplayNodeAssertMainThread(); @@ -287,7 +287,6 @@ typedef void (^ASDataControllerCompletionBlock)(NSArray * // 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; @@ -300,7 +299,7 @@ typedef void (^ASDataControllerCompletionBlock)(NSArray * } for (NSString *kind in [self supplementaryKindsInSections:newSections]) { - [self _insertElementsIntoMap:map kind:kind forSections:newSections owningNode:owningNode traitCollection:traitCollection]; + [self _insertElementsIntoMap:map kind:kind forSections:newSections traitCollection:traitCollection shouldFetchSizeRanges:shouldFetchSizeRanges changeSet:changeSet previousMap:previousMap]; } } @@ -309,14 +308,16 @@ typedef void (^ASDataControllerCompletionBlock)(NSArray * * * @param kind The kind of the elements, e.g ASDataControllerRowNodeKind * @param sections The sections that should be populated by new elements - * @param owningNode The node that owns the new elements. * @param traitCollection The trait collection needed to initialize elements + * @param shouldFetchSizeRanges Whether constrained sizes should be fetched from data source */ - (void)_insertElementsIntoMap:(ASMutableElementMap *)map kind:(NSString *)kind forSections:(NSIndexSet *)sections - owningNode:(ASDisplayNode *)owningNode traitCollection:(ASPrimitiveTraitCollection)traitCollection + shouldFetchSizeRanges:(BOOL)shouldFetchSizeRanges + changeSet:(_ASHierarchyChangeSet *)changeSet + previousMap:(ASElementMap *)previousMap { ASDisplayNodeAssertMainThread(); @@ -325,7 +326,7 @@ typedef void (^ASDataControllerCompletionBlock)(NSArray * } NSArray *indexPaths = [self _allIndexPathsForItemsOfKind:kind inSections:sections]; - [self _insertElementsIntoMap:map kind:kind atIndexPaths:indexPaths owningNode:owningNode traitCollection:traitCollection]; + [self _insertElementsIntoMap:map kind:kind atIndexPaths:indexPaths traitCollection:traitCollection shouldFetchSizeRanges:shouldFetchSizeRanges changeSet:changeSet previousMap:previousMap]; } /** @@ -334,14 +335,16 @@ typedef void (^ASDataControllerCompletionBlock)(NSArray * * @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 owningNode The node that owns the new elements. * @param traitCollection The trait collection needed to initialize elements + * @param shouldFetchSizeRanges Whether constrained sizes should be fetched from data source */ - (void)_insertElementsIntoMap:(ASMutableElementMap *)map kind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths - owningNode:(ASDisplayNode *)owningNode traitCollection:(ASPrimitiveTraitCollection)traitCollection + shouldFetchSizeRanges:(BOOL)shouldFetchSizeRanges + changeSet:(_ASHierarchyChangeSet *)changeSet + previousMap:(ASElementMap *)previousMap { ASDisplayNodeAssertMainThread(); @@ -356,19 +359,45 @@ typedef void (^ASDataControllerCompletionBlock)(NSArray * } LOG(@"Populating elements of kind: %@, for index paths: %@", kind, indexPaths); + id dataSource = self.dataSource; + id node = self.node; for (NSIndexPath *indexPath in indexPaths) { ASCellNodeBlock nodeBlock; + id viewModel; if (isRowKind) { - nodeBlock = [_dataSource dataController:self nodeBlockAtIndexPath:indexPath]; + viewModel = [dataSource dataController:self viewModelForItemAtIndexPath:indexPath]; + + // Get the prior element and attempt to update the existing cell node. + if (viewModel != nil && !changeSet.includesReloadData) { + NSIndexPath *oldIndexPath = [changeSet oldIndexPathForNewIndexPath:indexPath]; + if (oldIndexPath != nil) { + ASCollectionElement *oldElement = [previousMap elementForItemAtIndexPath:oldIndexPath]; + ASCellNode *oldNode = oldElement.node; + if ([oldNode canUpdateToViewModel:viewModel]) { + // Just wrap the node in a block. The collection element will -setViewModel: + nodeBlock = ^{ + return oldNode; + }; + } + } + } + if (nodeBlock == nil) { + nodeBlock = [dataSource dataController:self nodeBlockAtIndexPath:indexPath]; + } } else { - nodeBlock = [_dataSource dataController:self supplementaryNodeBlockOfKind:kind atIndexPath:indexPath]; + nodeBlock = [dataSource dataController:self supplementaryNodeBlockOfKind:kind atIndexPath:indexPath]; } - ASSizeRange constrainedSize = [self constrainedSizeForNodeOfKind:kind atIndexPath:indexPath]; - ASCollectionElement *element = [[ASCollectionElement alloc] initWithNodeBlock:nodeBlock + ASSizeRange constrainedSize = ASSizeRangeUnconstrained; + if (shouldFetchSizeRanges) { + constrainedSize = [self constrainedSizeForNodeOfKind:kind atIndexPath:indexPath]; + } + + ASCollectionElement *element = [[ASCollectionElement alloc] initWithViewModel:viewModel + nodeBlock:nodeBlock supplementaryElementKind:isRowKind ? nil : kind constrainedSize:constrainedSize - owningNode:owningNode + owningNode:node traitCollection:traitCollection]; [map insertElement:element atIndexPath:indexPath]; } @@ -406,18 +435,34 @@ typedef void (^ASDataControllerCompletionBlock)(NSArray * return @[]; } +- (ASSizeRange)constrainedSizeForElement:(ASCollectionElement *)element inElementMap:(ASElementMap *)map +{ + ASDisplayNodeAssertMainThread(); + NSString *kind = element.supplementaryElementKind ?: ASDataControllerRowNodeKind; + NSIndexPath *indexPath = [map indexPathForElement:element]; + return [self constrainedSizeForNodeOfKind:kind atIndexPath:indexPath]; +} + + - (ASSizeRange)constrainedSizeForNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { ASDisplayNodeAssertMainThread(); + + id dataSource = _dataSource; + if (dataSource == nil) { + return ASSizeRangeZero; + } + if ([kind isEqualToString:ASDataControllerRowNodeKind]) { - return [_dataSource dataController:self constrainedSizeForNodeAtIndexPath:indexPath]; + ASDisplayNodeAssert(_dataSourceFlags.constrainedSizeForNodeAtIndexPath, @"-dataController:constrainedSizeForNodeAtIndexPath: must also be implemented"); + return [dataSource dataController:self constrainedSizeForNodeAtIndexPath:indexPath]; } if (_dataSourceFlags.constrainedSizeForSupplementaryNodeOfKindAtIndexPath){ - return [_dataSource dataController:self constrainedSizeForSupplementaryNodeOfKind:kind atIndexPath:indexPath]; + return [dataSource dataController:self constrainedSizeForSupplementaryNodeOfKind:kind atIndexPath:indexPath]; } - ASDisplayNodeAssert(NO, @"Unknown constrained size for node of kind %@ by data source %@", kind, _dataSource); + ASDisplayNodeAssert(NO, @"Unknown constrained size for node of kind %@ by data source %@", kind, dataSource); return ASSizeRangeZero; } @@ -435,15 +480,27 @@ typedef void (^ASDataControllerCompletionBlock)(NSArray * ASDisplayNodeAssertMainThread(); if (changeSet.includesReloadData) { - _initialReloadDataHasBeenCalled = YES; + if (_initialReloadDataHasBeenCalled) { + as_log_debug(ASCollectionLog(), "reloadData %@", ASViewToDisplayNode(ASDynamicCast(self.dataSource, UIView))); + } else { + as_log_debug(ASCollectionLog(), "Initial reloadData %@", ASViewToDisplayNode(ASDynamicCast(self.dataSource, UIView))); + _initialReloadDataHasBeenCalled = YES; + } + } else { + as_log_debug(ASCollectionLog(), "performBatchUpdates %@ %@", ASViewToDisplayNode(ASDynamicCast(self.dataSource, UIView)), changeSet); } - dispatch_group_wait(_editingTransactionGroup, DISPATCH_TIME_FOREVER); + NSTimeInterval transactionQueueFlushDuration = 0.0f; + { + ASDN::ScopeTimer t(transactionQueueFlushDuration); + 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) { + as_log_debug(ASCollectionLog(), "%@ Skipped update because load hasn't happened.", ASObjectDescriptionMakeTiny(_dataSource)); [changeSet executeCompletionHandlerWithFinished:YES]; return; } @@ -451,11 +508,14 @@ typedef void (^ASDataControllerCompletionBlock)(NSArray * [self invalidateDataSourceItemCounts]; // Log events - ASDataControllerLogEvent(self, @"triggeredUpdate: %@", changeSet); #if ASEVENTLOG_ENABLE + ASDataControllerLogEvent(self, @"updateWithChangeSet waited on previous update for %fms. changeSet: %@", + transactionQueueFlushDuration * 1000.0f, changeSet); + NSTimeInterval changeSetStartTime = CACurrentMediaTime(); NSString *changeSetDescription = ASObjectDescriptionMakeTiny(changeSet); [changeSet addCompletionHandler:^(BOOL finished) { - ASDataControllerLogEvent(self, @"finishedUpdate: %@", changeSetDescription); + ASDataControllerLogEvent(self, @"finishedUpdate in %fms: %@", + (CACurrentMediaTime() - changeSetStartTime) * 1000.0f, changeSetDescription); }]; #endif @@ -472,47 +532,80 @@ typedef void (^ASDataControllerCompletionBlock)(NSArray * @throw e; } } - - // Since we waited for _editingTransactionGroup at the beginning of this method, at this point we can guarantee that _pendingMap equals to _visibleMap. - // So if the change set is empty, we don't need to modify data and can safely schedule to notify the delegate. - if (changeSet.isEmpty) { - [_mainSerialQueue performBlockOnMainThread:^{ - [_delegate dataController:self willUpdateWithChangeSet:changeSet]; - [_delegate dataController:self didUpdateWithChangeSet:changeSet]; - }]; - return; - } - - // 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]; - __weak id environment = [self.environmentDelegate dataControllerEnvironment]; - __weak ASDisplayNode *owningNode = (ASDisplayNode *)environment; // This is gross! - ASPrimitiveTraitCollection existingTraitCollection = [environment primitiveTraitCollection]; - [self _updateElementsInMap:mutableMap changeSet:changeSet owningNode:owningNode traitCollection:existingTraitCollection]; - - // Step 2: Clone the new data - ASElementMap *newMap = [mutableMap copy]; - _pendingMap = newMap; - + BOOL canDelegate = (self.layoutDelegate != nil); + ASElementMap *newMap; + id layoutContext; + { + as_activity_scope(as_activity_create("Latch new data for collection update", changeSet.rootActivity, OS_ACTIVITY_FLAG_DEFAULT)); + + // Step 1: Populate a new map that reflects the data source's state and use it as pendingMap + ASElementMap *previousMap = self.pendingMap; + if (changeSet.isEmpty) { + // If the change set is empty, nothing has changed so we can just reuse the previous map + newMap = previousMap; + } else { + // Mutable copy of current data. + ASMutableElementMap *mutableMap = [previousMap mutableCopy]; + + // Step 1.1: Update the mutable copies to match the data source's state + [self _updateSectionContextsInMap:mutableMap changeSet:changeSet]; + ASPrimitiveTraitCollection existingTraitCollection = [self.node primitiveTraitCollection]; + [self _updateElementsInMap:mutableMap changeSet:changeSet traitCollection:existingTraitCollection shouldFetchSizeRanges:(! canDelegate) previousMap:previousMap]; + + // Step 1.2: Clone the new data + newMap = [mutableMap copy]; + } + self.pendingMap = newMap; + + // Step 2: Ask layout delegate for contexts + if (canDelegate) { + layoutContext = [self.layoutDelegate layoutContextWithElements:newMap]; + } + } + + as_log_debug(ASCollectionLog(), "New content: %@", newMap.smallDescription); + + Class layoutDelegateClass = [self.layoutDelegate class]; dispatch_group_async(_editingTransactionGroup, _editingTransactionQueue, ^{ - // Step 3: Layout **all** new elements without batching in background. - NSArray *unmeasuredElements = [ASDataController unmeasuredElementsFromMap:newMap]; - [self batchLayoutNodesFromContexts:unmeasuredElements batchSize:unmeasuredElements.count batchCompletion:^(id, id) { - ASSERT_ON_EDITING_QUEUE; + __block __unused os_activity_scope_state_s preparationScope = {}; // unused if deployment target < iOS10 + as_activity_scope_enter(as_activity_create("Prepare nodes for collection update", AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT), &preparationScope); + + dispatch_block_t completion = ^() { [_mainSerialQueue performBlockOnMainThread:^{ + as_activity_scope_leave(&preparationScope); + // TODO Merge the two delegate methods below [_delegate dataController:self willUpdateWithChangeSet:changeSet]; - // Step 4: Deploy the new data as "completed" and inform delegate - _visibleMap = newMap; - - [_delegate dataController:self didUpdateWithChangeSet:changeSet]; + // Step 4: Inform the delegate + [_delegate dataController:self didUpdateWithChangeSet:changeSet updates:^{ + // Step 5: Deploy the new data as "completed" + // + // Note that since the backing collection view might be busy responding to user events (e.g scrolling), + // it will not consume the batch update blocks immediately. + // As a result, in a short intermidate time, the view will still be relying on the old data source state. + // Thus, we can't just swap the new map immediately before step 4, but until this update block is executed. + // (https://github.com/TextureGroup/Texture/issues/378) + self.visibleMap = newMap; + }]; }]; - }]; + }; + + // Step 3: Call the layout delegate if possible. Otherwise, allocate and layout all elements + if (canDelegate) { + [layoutDelegateClass calculateLayoutWithContext:layoutContext]; + completion(); + } else { + NSArray *elementsToProcess = ASArrayByFlatMapping(newMap, + ASCollectionElement *element, + (element.nodeIfAllocated.calculatedLayout == nil ? element : nil)); + [self batchAllocateNodesFromElements:elementsToProcess batchSize:elementsToProcess.count batchCompletion:completion]; + } }); + + if (_usesSynchronousDataLoading) { + [self waitUntilAllUpdatesAreCommitted]; + } } /** @@ -566,58 +659,65 @@ typedef void (^ASDataControllerCompletionBlock)(NSArray * */ - (void)_updateElementsInMap:(ASMutableElementMap *)map changeSet:(_ASHierarchyChangeSet *)changeSet - owningNode:(ASDisplayNode *)owningNode traitCollection:(ASPrimitiveTraitCollection)traitCollection + shouldFetchSizeRanges:(BOOL)shouldFetchSizeRanges + previousMap:(ASElementMap *)previousMap { ASDisplayNodeAssertMainThread(); - + 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 owningNode:owningNode traitCollection:traitCollection]; + [self _insertElementsIntoMap:map sections:sectionIndexes traitCollection:traitCollection shouldFetchSizeRanges:shouldFetchSizeRanges changeSet:changeSet previousMap:previousMap]; } // Return immediately because reloadData can't be used in conjuntion with other updates. return; } + // Migrate old supplementary nodes to their new index paths. + [map migrateSupplementaryElementsWithSectionMapping:changeSet.sectionMapping]; + 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 - owningNode:owningNode traitCollection:traitCollection - indexPathsAreNew:NO]; + indexPathsAreNew:NO + shouldFetchSizeRanges:shouldFetchSizeRanges + previousMap:previousMap]; } 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 owningNode:owningNode traitCollection:traitCollection]; + [self _insertElementsIntoMap:map sections:change.indexSet traitCollection:traitCollection shouldFetchSizeRanges:shouldFetchSizeRanges changeSet:changeSet previousMap:previousMap]; } for (_ASHierarchyItemChange *change in [changeSet itemChangesOfType:_ASHierarchyChangeTypeInsert]) { - [self _insertElementsIntoMap:map kind:ASDataControllerRowNodeKind atIndexPaths:change.indexPaths owningNode:owningNode traitCollection:traitCollection]; + [self _insertElementsIntoMap:map kind:ASDataControllerRowNodeKind atIndexPaths:change.indexPaths traitCollection:traitCollection shouldFetchSizeRanges:shouldFetchSizeRanges changeSet:changeSet previousMap:previousMap]; // Aggressively reload supplementary nodes (#1773 & #1629) [self _repopulateSupplementaryNodesIntoMap:map forSectionsContainingIndexPaths:change.indexPaths changeSet:changeSet - owningNode:owningNode traitCollection:traitCollection - indexPathsAreNew:YES]; + indexPathsAreNew:YES + shouldFetchSizeRanges:shouldFetchSizeRanges + previousMap:previousMap]; } } - (void)_insertElementsIntoMap:(ASMutableElementMap *)map sections:(NSIndexSet *)sectionIndexes - owningNode:(ASDisplayNode *)owningNode traitCollection:(ASPrimitiveTraitCollection)traitCollection + shouldFetchSizeRanges:(BOOL)shouldFetchSizeRanges + changeSet:(_ASHierarchyChangeSet *)changeSet + previousMap:(ASElementMap *)previousMap { ASDisplayNodeAssertMainThread(); @@ -627,12 +727,12 @@ typedef void (^ASDataControllerCompletionBlock)(NSArray * // Items [map insertEmptySectionsOfItemsAtIndexes:sectionIndexes]; - [self _insertElementsIntoMap:map kind:ASDataControllerRowNodeKind forSections:sectionIndexes owningNode:owningNode traitCollection:traitCollection]; + [self _insertElementsIntoMap:map kind:ASDataControllerRowNodeKind forSections:sectionIndexes traitCollection:traitCollection shouldFetchSizeRanges:shouldFetchSizeRanges changeSet:changeSet previousMap:previousMap]; // Supplementaries for (NSString *kind in [self supplementaryKindsInSections:sectionIndexes]) { // Step 2: Populate new elements for all sections - [self _insertElementsIntoMap:map kind:kind forSections:sectionIndexes owningNode:owningNode traitCollection:traitCollection]; + [self _insertElementsIntoMap:map kind:kind forSections:sectionIndexes traitCollection:traitCollection shouldFetchSizeRanges:shouldFetchSizeRanges changeSet:changeSet previousMap:previousMap]; } } @@ -647,12 +747,21 @@ typedef void (^ASDataControllerCompletionBlock)(NSArray * return; } + id dataSource = self.dataSource; + auto visibleMap = self.visibleMap; + auto pendingMap = self.pendingMap; for (ASCellNode *node in nodes) { - NSString *kind = node.collectionElement.supplementaryElementKind ?: ASDataControllerRowNodeKind; - NSIndexPath *indexPath = [_pendingMap indexPathForElement:node.collectionElement]; - ASSizeRange constrainedSize = [self constrainedSizeForNodeOfKind:kind atIndexPath:indexPath]; + auto element = node.collectionElement; + // Ensure the element is present in both maps or skip it. If it's not in the visible map, + // then we can't check the presented size. If it's not in the pending map, we can't get the constrained size. + // This will only happen if the element has been deleted, so the specifics of this behavior aren't important. + if ([visibleMap indexPathForElement:element] == nil || [pendingMap indexPathForElement:element] == nil) { + continue; + } + + ASSizeRange constrainedSize = [self constrainedSizeForElement:element inElementMap:pendingMap]; [self _layoutNode:node withConstrainedSize:constrainedSize]; - BOOL matchesSize = [_dataSource dataController:self presentedSizeForElement:node.collectionElement matchesSize:node.frame.size]; + BOOL matchesSize = [dataSource dataController:self presentedSizeForElement:element matchesSize:node.frame.size]; if (! matchesSize) { [nodesSizesChanged addObject:node]; } @@ -678,9 +787,7 @@ typedef void (^ASDataControllerCompletionBlock)(NSArray * { ASDisplayNodeAssertMainThread(); for (ASCollectionElement *element in _visibleMap) { - NSIndexPath *indexPath = [_visibleMap indexPathForElement:element]; - NSString *kind = element.supplementaryElementKind ?: ASDataControllerRowNodeKind; - ASSizeRange constrainedSize = [self constrainedSizeForNodeOfKind:kind atIndexPath:indexPath]; + ASSizeRange constrainedSize = [self constrainedSizeForElement:element inElementMap:_visibleMap]; if (ASSizeRangeHasSignificantArea(constrainedSize)) { element.constrainedSize = constrainedSize; @@ -706,7 +813,7 @@ typedef void (^ASDataControllerCompletionBlock)(NSArray * // Can't update the trait collection right away because _visibleMap may not be up-to-date, // i.e there might be some elements that were allocated using the old trait collection but haven't been added to _visibleMap [self _scheduleBlockOnMainSerialQueue:^{ - ASPrimitiveTraitCollection newTraitCollection = [[_environmentDelegate dataControllerEnvironment] primitiveTraitCollection]; + ASPrimitiveTraitCollection newTraitCollection = [self.node primitiveTraitCollection]; for (ASCollectionElement *element in _visibleMap) { element.traitCollection = newTraitCollection; } @@ -723,41 +830,6 @@ typedef void (^ASDataControllerCompletionBlock)(NSArray * [_mainSerialQueue performBlockOnMainThread:block]; } -+ (NSArray *)unmeasuredElementsFromMap:(ASElementMap *)map -{ - NSMutableArray *unloadedContexts = [NSMutableArray array]; - for (ASCollectionElement *element in map) { - 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 - -#endif +#endif \ No newline at end of file diff --git a/Source/Details/ASDelegateProxy.h b/Source/Details/ASDelegateProxy.h index 1f448aa44f..862598a647 100644 --- a/Source/Details/ASDelegateProxy.h +++ b/Source/Details/ASDelegateProxy.h @@ -1,11 +1,18 @@ // // ASDelegateProxy.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Details/ASDelegateProxy.m b/Source/Details/ASDelegateProxy.m index acd0c5349a..f9cf09ee8e 100644 --- a/Source/Details/ASDelegateProxy.m +++ b/Source/Details/ASDelegateProxy.m @@ -1,11 +1,18 @@ // // ASDelegateProxy.m -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -54,7 +61,8 @@ selector == @selector(tableView:didEndDisplayingCell:forRowAtIndexPath:) || // used for batch fetching API - selector == @selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:) + selector == @selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:) || + selector == @selector(scrollViewDidEndDecelerating:) ); } @@ -96,6 +104,7 @@ // used for batch fetching API selector == @selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:) || + selector == @selector(scrollViewDidEndDecelerating:) || // used for ASCellNode visibility selector == @selector(scrollViewDidScroll:) || diff --git a/Source/Private/ASElementMap.h b/Source/Details/ASElementMap.h similarity index 79% rename from Source/Private/ASElementMap.h rename to Source/Details/ASElementMap.h index afc524f5bb..ab67c4e338 100644 --- a/Source/Private/ASElementMap.h +++ b/Source/Details/ASElementMap.h @@ -1,9 +1,18 @@ // // ASElementMap.h -// AsyncDisplayKit +// Texture // -// Created by Adlai Holler on 2/22/17. -// Copyright © 2017 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 /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK @@ -51,6 +60,11 @@ AS_SUBCLASSING_RESTRICTED */ @property (copy, readonly) NSArray *itemIndexPaths; +/** + * All the item elements in this map, in ascending order. O(N) + */ +@property (copy, readonly) NSArray *itemElements; + /** * Returns the index path that corresponds to the same element in @c map at the given @c indexPath. O(1) */ @@ -84,6 +98,11 @@ AS_SUBCLASSING_RESTRICTED */ - (nullable ASCollectionElement *)elementForLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes; +/** + * A very terse description e.g. { itemCounts = [ ] } + */ +@property (atomic, readonly) NSString *smallDescription; + #pragma mark - Initialization -- Only Useful to ASDataController diff --git a/Source/Private/ASElementMap.m b/Source/Details/ASElementMap.m similarity index 85% rename from Source/Private/ASElementMap.m rename to Source/Details/ASElementMap.m index 8c093a8f8c..b2b67518e2 100644 --- a/Source/Private/ASElementMap.m +++ b/Source/Details/ASElementMap.m @@ -1,9 +1,18 @@ // // ASElementMap.m -// AsyncDisplayKit +// Texture // -// Created by Adlai Holler on 2/22/17. -// Copyright © 2017 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 /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK @@ -71,6 +80,11 @@ return ASIndexPathsForTwoDimensionalArray(_sectionsOfItems); } +- (NSArray *)itemElements +{ + return ASElementsInTwoDimensionalArray(_sectionsOfItems); +} + - (NSInteger)numberOfSections { return _sectionsOfItems.count; @@ -171,6 +185,18 @@ return [_elementToIndexPathMap countByEnumeratingWithState:state objects:buffer count:len]; } +- (NSString *)smallDescription +{ + NSMutableArray *sectionDescriptions = [NSMutableArray array]; + + NSUInteger i = 0; + for (NSArray *section in _sectionsOfItems) { + [sectionDescriptions addObject:[NSString stringWithFormat:@"", i, section.count]]; + i++; + } + return ASObjectDescriptionMakeWithoutObject(@[ @{ @"itemCounts": sectionDescriptions }]); +} + #pragma mark - ASDescriptionProvider - (NSString *)description diff --git a/Source/Details/ASEventLog.h b/Source/Details/ASEventLog.h index 38d122dd06..379ae53a72 100644 --- a/Source/Details/ASEventLog.h +++ b/Source/Details/ASEventLog.h @@ -1,13 +1,18 @@ // // ASEventLog.h -// AsyncDisplayKit -// -// Created by Huy Nguyen on 4/11/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Details/ASEventLog.mm b/Source/Details/ASEventLog.mm index dc9f4da824..1389f93577 100644 --- a/Source/Details/ASEventLog.mm +++ b/Source/Details/ASEventLog.mm @@ -1,13 +1,18 @@ // -// ASEventLog.m -// AsyncDisplayKit -// -// Created by Huy Nguyen on 4/11/16. +// ASEventLog.mm +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Details/ASHashing.h b/Source/Details/ASHashing.h new file mode 100644 index 0000000000..0fc8d72ff8 --- /dev/null +++ b/Source/Details/ASHashing.h @@ -0,0 +1,46 @@ +// +// ASHashing.h +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN +ASDISPLAYNODE_EXTERN_C_BEGIN + +/** + * When std::hash is unavailable, this function will hash a bucket o' bits real fast. + * The hashing algorithm is copied from CoreFoundation's private function CFHashBytes. + * https://opensource.apple.com/source/CF/CF-1153.18/CFUtilities.c.auto.html + * + * Simple example: + * CGRect myRect = { ... }; + * ASHashBytes(&myRect, sizeof(myRect)); + * + * Example: + * struct { + * NSUInteger imageHash; + * CGSize size; + * } data = { + * _image.hash, + * _bounds.size + * }; + * return ASHashBytes(&data, sizeof(data)); + * + * @warning: If a struct has padding, any fields that are intiailized in {} + * will have garbage data for their padding, which will break this hash! Either + * use `pragma clang diagnostic warning "-Wpadded"` around your struct definition + * or manually initialize the fields of your struct (`myStruct.x = 7;` etc). + */ +NSUInteger ASHashBytes(void *bytes, size_t length); + +ASDISPLAYNODE_EXTERN_C_END +NS_ASSUME_NONNULL_END diff --git a/Source/Details/ASHashing.m b/Source/Details/ASHashing.m new file mode 100644 index 0000000000..9f6d0f2349 --- /dev/null +++ b/Source/Details/ASHashing.m @@ -0,0 +1,42 @@ +// +// ASHashing.m +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#define ELF_STEP(B) T1 = (H << 4) + B; T2 = T1 & 0xF0000000; if (T2) T1 ^= (T2 >> 24); T1 &= (~T2); H = T1; + +/** + * The hashing algorithm copied from CoreFoundation CFHashBytes function. + * https://opensource.apple.com/source/CF/CF-1153.18/CFUtilities.c.auto.html + */ +NSUInteger ASHashBytes(void *bytesarg, size_t length) { + /* The ELF hash algorithm, used in the ELF object file format */ + uint8_t *bytes = (uint8_t *)bytesarg; + UInt32 H = 0, T1, T2; + SInt32 rem = (SInt32)length; + while (3 < rem) { + ELF_STEP(bytes[length - rem]); + ELF_STEP(bytes[length - rem + 1]); + ELF_STEP(bytes[length - rem + 2]); + ELF_STEP(bytes[length - rem + 3]); + rem -= 4; + } + switch (rem) { + case 3: ELF_STEP(bytes[length - 3]); + case 2: ELF_STEP(bytes[length - 2]); + case 1: ELF_STEP(bytes[length - 1]); + case 0: ; + } + return H; +} + +#undef ELF_STEP diff --git a/Source/Details/ASHighlightOverlayLayer.h b/Source/Details/ASHighlightOverlayLayer.h index 20d0b01e11..f000b0252e 100644 --- a/Source/Details/ASHighlightOverlayLayer.h +++ b/Source/Details/ASHighlightOverlayLayer.h @@ -1,11 +1,18 @@ // // ASHighlightOverlayLayer.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Details/ASHighlightOverlayLayer.mm b/Source/Details/ASHighlightOverlayLayer.mm index 669d6dd144..ac298308d2 100644 --- a/Source/Details/ASHighlightOverlayLayer.mm +++ b/Source/Details/ASHighlightOverlayLayer.mm @@ -1,11 +1,18 @@ // // ASHighlightOverlayLayer.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Details/ASImageContainerProtocolCategories.h b/Source/Details/ASImageContainerProtocolCategories.h index 7c5813dc85..529056238c 100644 --- a/Source/Details/ASImageContainerProtocolCategories.h +++ b/Source/Details/ASImageContainerProtocolCategories.h @@ -1,13 +1,18 @@ // // ASImageContainerProtocolCategories.h -// AsyncDisplayKit -// -// Created by Garrett Moon on 3/18/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Details/ASImageContainerProtocolCategories.m b/Source/Details/ASImageContainerProtocolCategories.m index 903164ee6f..546dc3fda5 100644 --- a/Source/Details/ASImageContainerProtocolCategories.m +++ b/Source/Details/ASImageContainerProtocolCategories.m @@ -1,13 +1,18 @@ // // ASImageContainerProtocolCategories.m -// AsyncDisplayKit -// -// Created by Garrett Moon on 3/18/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Details/ASImageProtocols.h b/Source/Details/ASImageProtocols.h index dfd1f82c42..3fdf321e45 100644 --- a/Source/Details/ASImageProtocols.h +++ b/Source/Details/ASImageProtocols.h @@ -1,11 +1,18 @@ // // ASImageProtocols.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -109,6 +116,17 @@ typedef NS_ENUM(NSUInteger, ASImageDownloaderPriority) { @optional +/** + @abstract Cancels an image download, however indicating resume data should be stored in case of redownload. + @param downloadIdentifier The opaque download identifier object returned from + `downloadImageWithURL:callbackQueue:downloadProgressBlock:completion:`. + @discussion This method has no effect if `downloadIdentifier` is nil. If implemented, this method + may be called instead of `cancelImageDownloadForIdentifier:` in cases where ASDK believes there's a chance + the image download will be resumed (currently when an image exits preload range). You can use this to store + any data that has already been downloaded for use in resuming the download later. + */ +- (void)cancelImageDownloadWithResumePossibilityForIdentifier:(id)downloadIdentifier; + /** @abstract Return an object that conforms to ASAnimatedImageProtocol @param animatedImageData Data that represents an animated image. @@ -145,7 +163,7 @@ withDownloadIdentifier:(id)downloadIdentifier; /** @abstract A block which receives the cover image. Should be called when the objects cover image is ready. */ -@property (nonatomic, strong, readwrite) void (^coverImageReadyCallback)(UIImage *coverImage); +@property (nonatomic, readwrite) void (^coverImageReadyCallback)(UIImage *coverImage); /** @abstract Returns whether the supplied data contains a supported animated image format. @@ -191,7 +209,7 @@ withDownloadIdentifier:(id)downloadIdentifier; /** @abstract Should be called when playback is ready. */ -@property (nonatomic, strong, readwrite) dispatch_block_t playbackReadyCallback; +@property (nonatomic, readwrite) dispatch_block_t playbackReadyCallback; /** @abstract Return the image at a given index. diff --git a/Source/Details/ASIntegerMap.h b/Source/Details/ASIntegerMap.h new file mode 100644 index 0000000000..2b94a8fced --- /dev/null +++ b/Source/Details/ASIntegerMap.h @@ -0,0 +1,70 @@ +// +// ASIntegerMap.h +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * An objective-C wrapper for unordered_map. + */ +AS_SUBCLASSING_RESTRICTED +@interface ASIntegerMap : NSObject + +/** + * Creates a map based on the specified update to an array. + * + * If oldCount is 0, returns the empty map. + * If deleted and inserted are empty, returns the identity map. + */ ++ (ASIntegerMap *)mapForUpdateWithOldCount:(NSInteger)oldCount + deleted:(nullable NSIndexSet *)deleted + inserted:(nullable NSIndexSet *)inserted; + +/** + * A singleton that maps each integer to itself. Its inverse is itself. + * + * Note: You cannot mutate this. + */ +@property (class, atomic, readonly) ASIntegerMap *identityMap; + +/** + * A singleton that returns NSNotFound for all keys. Its inverse is itself. + * + * Note: You cannot mutate this. + */ +@property (class, atomic, readonly) ASIntegerMap *emptyMap; + +/** + * Retrieves the integer for a given key, or NSNotFound if the key is not found. + * + * @param key A key to lookup the value for. + */ +- (NSInteger)integerForKey:(NSInteger)key; + +/** + * Sets the value for a given key. + * + * @param value The new value. + * @param key The key to store the value for. + */ +- (void)setInteger:(NSInteger)value forKey:(NSInteger)key; + +/** + * Create and return a map with the inverse mapping. + */ +- (ASIntegerMap *)inverseMap; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Source/Details/ASIntegerMap.mm b/Source/Details/ASIntegerMap.mm new file mode 100644 index 0000000000..4208b71a6b --- /dev/null +++ b/Source/Details/ASIntegerMap.mm @@ -0,0 +1,188 @@ +// +// ASIntegerMap.mm +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import "ASIntegerMap.h" +#import +#import +#import +#import + +/** + * This is just a friendly Objective-C interface to unordered_map + */ +@interface ASIntegerMap () +@end + +@implementation ASIntegerMap { + std::unordered_map _map; + BOOL _isIdentity; + BOOL _isEmpty; + BOOL _immutable; // identity map and empty mape are immutable. +} + +#pragma mark - Singleton + ++ (ASIntegerMap *)identityMap +{ + static ASIntegerMap *identityMap; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + identityMap = [[ASIntegerMap alloc] init]; + identityMap->_isIdentity = YES; + identityMap->_immutable = YES; + }); + return identityMap; +} + ++ (ASIntegerMap *)emptyMap +{ + static ASIntegerMap *emptyMap; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + emptyMap = [[ASIntegerMap alloc] init]; + emptyMap->_isEmpty = YES; + emptyMap->_immutable = YES; + }); + return emptyMap; +} + ++ (ASIntegerMap *)mapForUpdateWithOldCount:(NSInteger)oldCount deleted:(NSIndexSet *)deletions inserted:(NSIndexSet *)insertions +{ + if (oldCount == 0) { + return ASIntegerMap.emptyMap; + } + + if (deletions.count == 0 && insertions.count == 0) { + return ASIntegerMap.identityMap; + } + + ASIntegerMap *result = [[ASIntegerMap alloc] init]; + // Start with the old indexes + NSMutableIndexSet *indexes = [NSMutableIndexSet indexSetWithIndexesInRange:NSMakeRange(0, oldCount)]; + + // Descending order, shift deleted ranges left + [deletions enumerateRangesWithOptions:NSEnumerationReverse usingBlock:^(NSRange range, BOOL * _Nonnull stop) { + [indexes shiftIndexesStartingAtIndex:NSMaxRange(range) by:-range.length]; + }]; + + // Ascending order, shift inserted ranges right + [insertions enumerateRangesUsingBlock:^(NSRange range, BOOL * _Nonnull stop) { + [indexes shiftIndexesStartingAtIndex:range.location by:range.length]; + }]; + + __block NSInteger oldIndex = 0; + [indexes enumerateRangesUsingBlock:^(NSRange range, BOOL * _Nonnull stop) { + // Note we advance oldIndex unconditionally, not newIndex + for (NSInteger newIndex = range.location; newIndex < NSMaxRange(range); oldIndex++) { + if ([deletions containsIndex:oldIndex]) { + // index was deleted, do nothing, just let oldIndex advance. + } else { + // assign the next index for this item. + result->_map[oldIndex] = newIndex++; + } + } + }]; + return result; +} + +- (NSInteger)integerForKey:(NSInteger)key +{ + if (_isIdentity) { + return key; + } else if (_isEmpty) { + return NSNotFound; + } + + auto result = _map.find(key); + return result != _map.end() ? result->second : NSNotFound; +} + +- (void)setInteger:(NSInteger)value forKey:(NSInteger)key +{ + if (_immutable) { + ASDisplayNodeFailAssert(@"Cannot mutate special integer map: %@", self); + return; + } + + _map[key] = value; +} + +- (ASIntegerMap *)inverseMap +{ + if (_isIdentity || _isEmpty) { + return self; + } + + auto result = [[ASIntegerMap alloc] init]; + for (auto it = _map.begin(); it != _map.end(); it++) { + result->_map[it->second] = it->first; + } + return result; +} + +#pragma mark - NSCopying + +- (id)copyWithZone:(NSZone *)zone +{ + if (_immutable) { + return self; + } + + auto newMap = [[ASIntegerMap allocWithZone:zone] init]; + newMap->_map = _map; + return newMap; +} + +#pragma mark - Description + +- (NSMutableArray *)propertiesForDescription +{ + NSMutableArray *result = [NSMutableArray array]; + + if (_isIdentity) { + [result addObject:@{ @"map": @"" }]; + } else if (_isEmpty) { + [result addObject:@{ @"map": @"" }]; + } else { + // { 1->2 3->4 5->6 } + NSMutableString *str = [NSMutableString string]; + for (auto it = _map.begin(); it != _map.end(); it++) { + [str appendFormat:@" %zd->%zd", it->first, it->second]; + } + // Remove leading space + if (str.length > 0) { + [str deleteCharactersInRange:NSMakeRange(0, 1)]; + } + [result addObject:@{ @"map": str }]; + } + + return result; +} + +- (NSString *)description +{ + return ASObjectDescriptionMakeWithoutObject([self propertiesForDescription]); +} + +- (BOOL)isEqual:(id)object +{ + if ([super isEqual:object]) { + return YES; + } + + if (auto otherMap = ASDynamicCast(object, ASIntegerMap)) { + return otherMap->_map == _map; + } + return NO; +} + +@end diff --git a/Source/Details/ASLayoutController.h b/Source/Details/ASLayoutController.h index 36017b6233..dacaf09c6a 100644 --- a/Source/Details/ASLayoutController.h +++ b/Source/Details/ASLayoutController.h @@ -1,11 +1,18 @@ // // ASLayoutController.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -34,9 +41,9 @@ ASDISPLAYNODE_EXTERN_C_END - (ASRangeTuningParameters)tuningParametersForRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType; -- (NSSet *)elementsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType map:(ASElementMap *)map; +- (NSHashTable *)elementsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType map:(ASElementMap *)map; -- (void)allElementsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode displaySet:(NSSet * _Nullable * _Nullable)displaySet preloadSet:(NSSet * _Nullable * _Nullable)preloadSet map:(ASElementMap *)map; +- (void)allElementsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode displaySet:(NSHashTable * _Nullable * _Nullable)displaySet preloadSet:(NSHashTable * _Nullable * _Nullable)preloadSet map:(ASElementMap *)map; @optional diff --git a/Source/Details/ASLayoutRangeType.h b/Source/Details/ASLayoutRangeType.h index 3e5556437d..45e1635441 100644 --- a/Source/Details/ASLayoutRangeType.h +++ b/Source/Details/ASLayoutRangeType.h @@ -1,11 +1,18 @@ // // ASLayoutRangeType.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Details/ASMainSerialQueue.h b/Source/Details/ASMainSerialQueue.h index 0dc96d52d1..ef939effc3 100644 --- a/Source/Details/ASMainSerialQueue.h +++ b/Source/Details/ASMainSerialQueue.h @@ -1,13 +1,18 @@ // // ASMainSerialQueue.h -// AsyncDisplayKit -// -// Created by Garrett Moon on 12/11/15. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Details/ASMainSerialQueue.mm b/Source/Details/ASMainSerialQueue.mm index ec6522bede..e79481fa42 100644 --- a/Source/Details/ASMainSerialQueue.mm +++ b/Source/Details/ASMainSerialQueue.mm @@ -1,13 +1,18 @@ // // ASMainSerialQueue.mm -// AsyncDisplayKit -// -// Created by Garrett Moon on 12/11/15. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Details/ASMutableAttributedStringBuilder.h b/Source/Details/ASMutableAttributedStringBuilder.h index 3ee9d88215..c6ecbea75c 100644 --- a/Source/Details/ASMutableAttributedStringBuilder.h +++ b/Source/Details/ASMutableAttributedStringBuilder.h @@ -1,11 +1,18 @@ // // ASMutableAttributedStringBuilder.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Details/ASMutableAttributedStringBuilder.m b/Source/Details/ASMutableAttributedStringBuilder.m index 5b0d40c4a1..4137a92def 100644 --- a/Source/Details/ASMutableAttributedStringBuilder.m +++ b/Source/Details/ASMutableAttributedStringBuilder.m @@ -1,11 +1,18 @@ // // ASMutableAttributedStringBuilder.m -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Details/ASObjectDescriptionHelpers.h b/Source/Details/ASObjectDescriptionHelpers.h index e829f79af3..c4ae0ec35d 100644 --- a/Source/Details/ASObjectDescriptionHelpers.h +++ b/Source/Details/ASObjectDescriptionHelpers.h @@ -1,9 +1,18 @@ // // ASObjectDescriptionHelpers.h -// AsyncDisplayKit +// Texture // -// Created by Adlai Holler on 9/7/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 /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Details/ASObjectDescriptionHelpers.m b/Source/Details/ASObjectDescriptionHelpers.m index 327b96b29e..b34872868c 100644 --- a/Source/Details/ASObjectDescriptionHelpers.m +++ b/Source/Details/ASObjectDescriptionHelpers.m @@ -1,19 +1,23 @@ // // ASObjectDescriptionHelpers.m -// AsyncDisplayKit +// Texture // -// Created by Adlai Holler on 9/7/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 /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // -#import #import -#if AS_TARGET_OS_IOS #import -#else -#import -#endif #import @@ -43,6 +47,9 @@ NSString *ASGetDescriptionValueString(id object) [strings addObject:[NSString stringWithFormat:@"%lu", (unsigned long)[indexPath indexAtPosition:i]]]; } return [NSString stringWithFormat:@"(%@)", [strings componentsJoinedByString:@", "]]; + } else if ([object respondsToSelector:@selector(componentsJoinedByString:)]) { + // e.g. "[ ]" + return [NSString stringWithFormat:@"[ %@ ]", [object componentsJoinedByString:@" "]]; } return [object description]; } @@ -52,7 +59,13 @@ NSString *_ASObjectDescriptionMakePropertyList(NSArray * _Nullab NSMutableArray *components = [NSMutableArray array]; for (NSDictionary *properties in propertyGroups) { [properties enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) { - [components addObject:[NSString stringWithFormat:@"%@ = %@", key, ASGetDescriptionValueString(obj)]]; + NSString *str; + if (key == (id)kCFNull) { + str = ASGetDescriptionValueString(obj); + } else { + str = [NSString stringWithFormat:@"%@ = %@", key, ASGetDescriptionValueString(obj)]; + } + [components addObject:str]; }]; } return [components componentsJoinedByString:@"; "]; @@ -69,7 +82,7 @@ NSString *ASObjectDescriptionMake(__autoreleasing id object, NSArray 0) { diff --git a/Source/Details/ASPINRemoteImageDownloader.h b/Source/Details/ASPINRemoteImageDownloader.h index 8fa6a52c60..2cdee5ed9c 100644 --- a/Source/Details/ASPINRemoteImageDownloader.h +++ b/Source/Details/ASPINRemoteImageDownloader.h @@ -1,18 +1,25 @@ // // ASPINRemoteImageDownloader.h -// AsyncDisplayKit -// -// Created by Garrett Moon on 2/5/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK -#if PIN_REMOTE_IMAGE +#import + +#if AS_PIN_REMOTE_IMAGE #import diff --git a/Source/Details/ASPINRemoteImageDownloader.m b/Source/Details/ASPINRemoteImageDownloader.m index 3479199e97..6427c23af4 100644 --- a/Source/Details/ASPINRemoteImageDownloader.m +++ b/Source/Details/ASPINRemoteImageDownloader.m @@ -1,18 +1,25 @@ // // ASPINRemoteImageDownloader.m -// AsyncDisplayKit -// -// Created by Garrett Moon on 2/5/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK -#if PIN_REMOTE_IMAGE +#import + +#if AS_PIN_REMOTE_IMAGE #import #import @@ -247,7 +254,13 @@ static ASPINRemoteImageDownloader *sharedDownloader = nil; - (void)cancelImageDownloadForIdentifier:(id)downloadIdentifier { ASDisplayNodeAssert([downloadIdentifier isKindOfClass:[NSUUID class]], @"downloadIdentifier must be NSUUID"); - [[self sharedPINRemoteImageManager] cancelTaskWithUUID:downloadIdentifier]; + [[self sharedPINRemoteImageManager] cancelTaskWithUUID:downloadIdentifier storeResumeData:NO]; +} + +- (void)cancelImageDownloadWithResumePossibilityForIdentifier:(id)downloadIdentifier +{ + ASDisplayNodeAssert([downloadIdentifier isKindOfClass:[NSUUID class]], @"downloadIdentifier must be NSUUID"); + [[self sharedPINRemoteImageManager] cancelTaskWithUUID:downloadIdentifier storeResumeData:YES]; } - (void)setProgressImageBlock:(ASImageDownloaderProgressImage)progressBlock callbackQueue:(dispatch_queue_t)callbackQueue withDownloadIdentifier:(id)downloadIdentifier @@ -269,18 +282,18 @@ static ASPINRemoteImageDownloader *sharedDownloader = nil; { ASDisplayNodeAssert([downloadIdentifier isKindOfClass:[NSUUID class]], @"downloadIdentifier must be NSUUID"); - PINRemoteImageManagerPriority pi_priority = PINRemoteImageManagerPriorityMedium; + PINRemoteImageManagerPriority pi_priority = PINRemoteImageManagerPriorityDefault; switch (priority) { case ASImageDownloaderPriorityPreload: - pi_priority = PINRemoteImageManagerPriorityMedium; + pi_priority = PINRemoteImageManagerPriorityLow; break; case ASImageDownloaderPriorityImminent: - pi_priority = PINRemoteImageManagerPriorityHigh; + pi_priority = PINRemoteImageManagerPriorityDefault; break; case ASImageDownloaderPriorityVisible: - pi_priority = PINRemoteImageManagerPriorityVeryHigh; + pi_priority = PINRemoteImageManagerPriorityHigh; break; } [[self sharedPINRemoteImageManager] setPriority:pi_priority ofTaskWithUUID:downloadIdentifier]; diff --git a/Source/Details/ASPageTable.h b/Source/Details/ASPageTable.h new file mode 100644 index 0000000000..da4607f5ff --- /dev/null +++ b/Source/Details/ASPageTable.h @@ -0,0 +1,132 @@ +// +// ASPageTable.h +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#ifndef MINIMAL_ASDK + +#import +#import +#import +#import + +@class ASCollectionElement; + +NS_ASSUME_NONNULL_BEGIN + +ASDISPLAYNODE_EXTERN_C_BEGIN + +/** + * Represents x and y coordinates of a page. + */ +typedef uintptr_t ASPageCoordinate; + +/** + * Returns a page coordinate with the given x and y values. Both of them must be less than 65,535. + */ +extern ASPageCoordinate ASPageCoordinateMake(uint16_t x, uint16_t y) AS_WARN_UNUSED_RESULT; + +/** + * Returns coordinate of the page that contains the specified point. + * Similar to CGRectContainsPoint, a point is considered inside a page if its lie inside the page or on the minimum X or minimum Y edge. + * + * @param point The point that the page at the returned should contain. Any negative of the point will be corrected to 0.0 + * + * @param pageSize The size of each page. + */ +extern ASPageCoordinate ASPageCoordinateForPageThatContainsPoint(CGPoint point, CGSize pageSize) AS_WARN_UNUSED_RESULT; + +extern uint16_t ASPageCoordinateGetX(ASPageCoordinate pageCoordinate) AS_WARN_UNUSED_RESULT; + +extern uint16_t ASPageCoordinateGetY(ASPageCoordinate pageCoordinate) AS_WARN_UNUSED_RESULT; + +extern CGRect ASPageCoordinateGetPageRect(ASPageCoordinate pageCoordinate, CGSize pageSize) AS_WARN_UNUSED_RESULT; + +/** + * Returns coordinate pointers for pages that intersect the specified rect. For each pointer, use ASPageCoordinateFromPointer() to get the original coordinate. + * The specified rect is restricted to the bounds of a content rect that has an origin of {0, 0} and a size of the given contentSize. + * + * @param rect The rect intersecting the target pages. + * + * @param contentSize The combined size of all pages. + * + * @param pageSize The size of each page. + */ +extern NSPointerArray * _Nullable ASPageCoordinatesForPagesThatIntersectRect(CGRect rect, CGSize contentSize, CGSize pageSize) AS_WARN_UNUSED_RESULT; + +ASDISPLAYNODE_EXTERN_C_END + +/** + * An alias for an NSMapTable created to store objects using ASPageCoordinates as keys. + * + * You should not call -objectForKey:, -setObject:forKey:, or -removeObjectForKey: + * on these objects. + */ +typedef NSMapTable ASPageTable; + +/** + * A page to array of layout attributes table. + */ +typedef ASPageTable *> ASPageToLayoutAttributesTable; + +/** + * A category for creating & using map tables meant for storing objects using ASPage as keys. + */ +@interface NSMapTable (ASPageTableMethods) + +/** + * Creates a new page table with (NSMapTableStrongMemory | NSMapTableObjectPointerPersonality) for values. + */ ++ (ASPageTable *)pageTableForStrongObjectPointers; + +/** + * Creates a new page table with (NSMapTableWeakMemory | NSMapTableObjectPointerPersonality) for values. + */ ++ (ASPageTable *)pageTableForWeakObjectPointers; + +/** + * Builds a new page to layout attributes from the given layout attributes. + * + * @param layoutAttributesEnumerator The layout attributes to build from + * + * @param contentSize The combined size of all pages. + * + * @param pageSize The size of each page. + */ ++ (ASPageToLayoutAttributesTable *)pageTableWithLayoutAttributes:(id)layoutAttributesEnumerator contentSize:(CGSize)contentSize pageSize:(CGSize)pageSize; + +/** + * Retrieves the object for a given page, or nil if the page is not found. + * + * @param page A page to lookup the object for. + */ +- (nullable ObjectType)objectForPage:(ASPageCoordinate)page; + +/** + * Sets the given object for the associated page. + * + * @param object The object to store as value. + * + * @param page The page to use for the rect. + */ +- (void)setObject:(ObjectType)object forPage:(ASPageCoordinate)page; + +/** + * Removes the object for the given page, if one exists. + * + * @param page The page to remove. + */ +- (void)removeObjectForPage:(ASPageCoordinate)page; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/Source/Details/ASPageTable.m b/Source/Details/ASPageTable.m new file mode 100644 index 0000000000..df4251523e --- /dev/null +++ b/Source/Details/ASPageTable.m @@ -0,0 +1,155 @@ +// +// ASPageTable.m +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#ifndef MINIMAL_ASDK + +#import + +extern ASPageCoordinate ASPageCoordinateMake(uint16_t x, uint16_t y) +{ + // Add 1 to the end result because 0 is not accepted by NSArray and NSMapTable. + // To avoid overflow after adding, x and y can't be UINT16_MAX (0xFFFF) **at the same time**. + // But for API simplification, we enforce the same restriction to both values. + ASDisplayNodeCAssert(x < UINT16_MAX, @"x coordinate must be less than 65,535"); + ASDisplayNodeCAssert(y < UINT16_MAX, @"y coordinate must be less than 65,535"); + return (x << 16) + y + 1; +} + +extern ASPageCoordinate ASPageCoordinateForPageThatContainsPoint(CGPoint point, CGSize pageSize) +{ + return ASPageCoordinateMake((MAX(0.0, point.x) / pageSize.width), (MAX(0.0, point.y) / pageSize.height)); +} + +extern uint16_t ASPageCoordinateGetX(ASPageCoordinate pageCoordinate) +{ + return (pageCoordinate - 1) >> 16; +} + +extern uint16_t ASPageCoordinateGetY(ASPageCoordinate pageCoordinate) +{ + return (pageCoordinate - 1) & ~(0xFFFF<<16); +} + +extern CGRect ASPageCoordinateGetPageRect(ASPageCoordinate pageCoordinate, CGSize pageSize) +{ + CGFloat pageWidth = pageSize.width; + CGFloat pageHeight = pageSize.height; + return CGRectMake(ASPageCoordinateGetX(pageCoordinate) * pageWidth, ASPageCoordinateGetY(pageCoordinate) * pageHeight, pageWidth, pageHeight); +} + +extern NSPointerArray *ASPageCoordinatesForPagesThatIntersectRect(CGRect rect, CGSize contentSize, CGSize pageSize) +{ + CGRect contentRect = CGRectMake(0.0, 0.0, contentSize.width, contentSize.height); + // Make sure the specified rect is within contentRect + rect = CGRectIntersection(rect, contentRect); + if (CGRectIsNull(rect) || CGRectIsEmpty(rect)) { + return nil; + } + + NSPointerArray *result = [NSPointerArray pointerArrayWithOptions:(NSPointerFunctionsIntegerPersonality | NSPointerFunctionsOpaqueMemory)]; + + ASPageCoordinate minPage = ASPageCoordinateForPageThatContainsPoint(CGPointMake(CGRectGetMinX(rect), CGRectGetMinY(rect)), pageSize); + ASPageCoordinate maxPage = ASPageCoordinateForPageThatContainsPoint(CGPointMake(CGRectGetMaxX(rect), CGRectGetMaxY(rect)), pageSize); + if (minPage == maxPage) { + [result addPointer:(void *)minPage]; + return result; + } + + NSUInteger minX = ASPageCoordinateGetX(minPage); + NSUInteger minY = ASPageCoordinateGetY(minPage); + NSUInteger maxX = ASPageCoordinateGetX(maxPage); + NSUInteger maxY = ASPageCoordinateGetY(maxPage); + + for (NSUInteger x = minX; x <= maxX; x++) { + for (NSUInteger y = minY; y <= maxY; y++) { + ASPageCoordinate page = ASPageCoordinateMake(x, y); + [result addPointer:(void *)page]; + } + } + + return result; +} + +@implementation NSMapTable (ASPageTableMethods) + ++ (instancetype)pageTableWithValuePointerFunctions:(NSPointerFunctions *)valueFuncs +{ + static NSPointerFunctions *pageCoordinatesFuncs; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + pageCoordinatesFuncs = [NSPointerFunctions pointerFunctionsWithOptions:NSPointerFunctionsIntegerPersonality | NSPointerFunctionsOpaqueMemory]; + }); + + return [[NSMapTable alloc] initWithKeyPointerFunctions:pageCoordinatesFuncs valuePointerFunctions:valueFuncs capacity:0]; +} + ++ (ASPageTable *)pageTableForStrongObjectPointers +{ + static NSPointerFunctions *strongObjectPointerFuncs; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + strongObjectPointerFuncs = [NSPointerFunctions pointerFunctionsWithOptions:NSPointerFunctionsStrongMemory]; + }); + return [self pageTableWithValuePointerFunctions:strongObjectPointerFuncs]; +} + ++ (ASPageTable *)pageTableForWeakObjectPointers +{ + static NSPointerFunctions *weakObjectPointerFuncs; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + weakObjectPointerFuncs = [NSPointerFunctions pointerFunctionsWithOptions:NSPointerFunctionsWeakMemory]; + }); + return [self pageTableWithValuePointerFunctions:weakObjectPointerFuncs]; +} + ++ (ASPageToLayoutAttributesTable *)pageTableWithLayoutAttributes:(id)layoutAttributesEnumerator contentSize:(CGSize)contentSize pageSize:(CGSize)pageSize +{ + ASPageToLayoutAttributesTable *result = [ASPageTable pageTableForStrongObjectPointers]; + for (UICollectionViewLayoutAttributes *attrs in layoutAttributesEnumerator) { + // This attrs may span multiple pages. Make sure it's registered to all of them + NSPointerArray *pages = ASPageCoordinatesForPagesThatIntersectRect(attrs.frame, contentSize, pageSize); + + for (id pagePtr in pages) { + ASPageCoordinate page = (ASPageCoordinate)pagePtr; + NSMutableArray *attrsInPage = [result objectForPage:page]; + if (attrsInPage == nil) { + attrsInPage = [NSMutableArray array]; + [result setObject:attrsInPage forPage:page]; + } + [attrsInPage addObject:attrs]; + } + } + return result; +} + +- (id)objectForPage:(ASPageCoordinate)page +{ + __unsafe_unretained id key = (__bridge id)(void *)page; + return [self objectForKey:key]; +} + +- (void)setObject:(id)object forPage:(ASPageCoordinate)page +{ + __unsafe_unretained id key = (__bridge id)(void *)page; + [self setObject:object forKey:key]; +} + +- (void)removeObjectForPage:(ASPageCoordinate)page +{ + __unsafe_unretained id key = (__bridge id)(void *)page; + [self removeObjectForKey:key]; +} + +@end + +#endif diff --git a/Source/Details/ASPhotosFrameworkImageRequest.h b/Source/Details/ASPhotosFrameworkImageRequest.h index 020992d8f7..5f46b6d23b 100644 --- a/Source/Details/ASPhotosFrameworkImageRequest.h +++ b/Source/Details/ASPhotosFrameworkImageRequest.h @@ -1,13 +1,18 @@ // // ASPhotosFrameworkImageRequest.h -// AsyncDisplayKit -// -// Created by Adlai Holler on 9/25/15. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK diff --git a/Source/Details/ASPhotosFrameworkImageRequest.m b/Source/Details/ASPhotosFrameworkImageRequest.m index c4c21ead0c..33d5613947 100644 --- a/Source/Details/ASPhotosFrameworkImageRequest.m +++ b/Source/Details/ASPhotosFrameworkImageRequest.m @@ -1,18 +1,22 @@ // // ASPhotosFrameworkImageRequest.m -// AsyncDisplayKit -// -// Created by Adlai Holler on 9/25/15. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK #import #import -#import NSString *const ASPhotosURLScheme = @"ph"; diff --git a/Source/Details/ASRangeController.h b/Source/Details/ASRangeController.h index 6dbdfd3646..35c7a51251 100644 --- a/Source/Details/ASRangeController.h +++ b/Source/Details/ASRangeController.h @@ -1,11 +1,18 @@ // // ASRangeController.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK @@ -109,9 +116,9 @@ AS_SUBCLASSING_RESTRICTED /** * @param rangeController Sender. * - * @return an array of elements corresponding to the data currently visible onscreen (i.e., the visible range). + * @return an table of elements corresponding to the data currently visible onscreen (i.e., the visible range). */ -- (NSArray *)visibleElementsForRangeController:(ASRangeController *)rangeController; +- (nullable NSHashTable *)visibleElementsForRangeController:(ASRangeController *)rangeController; /** * @param rangeController Sender. @@ -151,8 +158,14 @@ AS_SUBCLASSING_RESTRICTED * Called after updating with given change set. * * @param changeSet The change set that includes all updates + * + * @param updates The block that performs relevant data updates. + * + * @discussion The updates block must always be executed or the data controller will get into a bad state. + * It should be called at the time the backing view is ready to process the updates, + * i.e inside the updates block of `-[UICollectionView performBatchUpdates:completion:] or after calling `-[UITableView beginUpdates]`. */ -- (void)rangeController:(ASRangeController *)rangeController didUpdateWithChangeSet:(_ASHierarchyChangeSet *)changeSet; +- (void)rangeController:(ASRangeController *)rangeController didUpdateWithChangeSet:(_ASHierarchyChangeSet *)changeSet updates:(dispatch_block_t)updates; @end diff --git a/Source/Details/ASRangeController.mm b/Source/Details/ASRangeController.mm index 458654970a..c25bdfe612 100644 --- a/Source/Details/ASRangeController.mm +++ b/Source/Details/ASRangeController.mm @@ -1,11 +1,18 @@ // // ASRangeController.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK @@ -19,6 +26,7 @@ #import // Required for interfaceState and hierarchyState setter methods. #import #import +#import #import #import @@ -36,7 +44,7 @@ BOOL _rangeIsValid; BOOL _needsRangeUpdate; NSSet *_allPreviousIndexPaths; - ASWeakSet *_visibleNodes; + NSHashTable *_visibleNodes; ASLayoutRangeMode _currentRangeMode; BOOL _preserveCurrentRangeMode; BOOL _didRegisterForNodeDisplayNotifications; @@ -185,7 +193,7 @@ static UIApplicationState __ApplicationState = UIApplicationStateActive; // NOTE: There is a minor risk here, if a node is transferred from one range controller // to another before the first rc updates and clears the node out of this set. It's a pretty // wild scenario that I doubt happens in practice. -- (void)_setVisibleNodes:(ASWeakSet *)newVisibleNodes +- (void)_setVisibleNodes:(NSHashTable *)newVisibleNodes { for (ASCellNode *node in _visibleNodes) { if (![newVisibleNodes containsObject:node] && node.isVisible) { @@ -197,6 +205,8 @@ static UIApplicationState __ApplicationState = UIApplicationStateActive; - (void)_updateVisibleNodeIndexPaths { + as_activity_scope_verbose(as_activity_create("Update range controller", AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT)); + as_log_verbose(ASCollectionLog(), "Updating ranges for %@", ASViewToDisplayNode(ASDynamicCast(self.delegate, UIView))); ASDisplayNodeAssert(_layoutController, @"An ASLayoutController is required by ASRangeController"); if (!_layoutController || !_dataSource) { return; @@ -210,14 +220,14 @@ static UIApplicationState __ApplicationState = UIApplicationStateActive; // 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]; - NSSet *visibleElements = [NSSet setWithArray:[_dataSource visibleElementsForRangeController:self]]; - ASWeakSet *newVisibleNodes = [[ASWeakSet alloc] init]; + auto visibleElements = [_dataSource visibleElementsForRangeController:self]; + NSHashTable *newVisibleNodes = [NSHashTable hashTableWithOptions:NSHashTableObjectPointerPersonality]; if (visibleElements.count == 0) { // if we don't have any visibleNodes currently (scrolled before or after content)... [self _setVisibleNodes:newVisibleNodes]; return; // don't do anything for this update, but leave _rangeIsValid == NO to make sure we update it later } - ASProfilingSignpostStart(1, self); + ASSignpostStart(ASSignpostRangeControllerUpdate); // Get the scroll direction. Default to using the previous one, if they're not scrolling. ASScrollDirection scrollDirection = [_dataSource scrollDirectionForRangeController:self]; @@ -249,14 +259,14 @@ static UIApplicationState __ApplicationState = UIApplicationStateActive; // Check if both Display and Preload are unique. If they are, we load them with a single fetch from the layout controller for performance. BOOL optimizedLoadingOfBothRanges = (equalDisplayPreload == NO && equalDisplayVisible == NO && emptyDisplayRange == NO); - NSSet *displayElements = nil; - NSSet *preloadElements = nil; + NSHashTable *displayElements = nil; + NSHashTable *preloadElements = nil; if (optimizedLoadingOfBothRanges) { [_layoutController allElementsForScrolling:scrollDirection rangeMode:rangeMode displaySet:&displayElements preloadSet:&preloadElements map:map]; } else { if (emptyDisplayRange == YES) { - displayElements = [NSSet set]; + displayElements = [NSHashTable hashTableWithOptions:NSHashTableObjectPointerPersonality]; } if (equalDisplayVisible == YES) { displayElements = visibleElements; } else { @@ -406,7 +416,7 @@ static UIApplicationState __ApplicationState = UIApplicationStateActive; NSLog(@"Range update complete; modifiedIndexPaths: %@", [self descriptionWithIndexPaths:modifiedIndexPaths]); #endif - ASProfilingSignpostEnd(1, self); + ASSignpostEnd(ASSignpostRangeControllerUpdate); } #pragma mark - Notification observers @@ -493,11 +503,11 @@ static UIApplicationState __ApplicationState = UIApplicationStateActive; [_delegate rangeController:self willUpdateWithChangeSet:changeSet]; } -- (void)dataController:(ASDataController *)dataController didUpdateWithChangeSet:(_ASHierarchyChangeSet *)changeSet +- (void)dataController:(ASDataController *)dataController didUpdateWithChangeSet:(_ASHierarchyChangeSet *)changeSet updates:(dispatch_block_t)updates { ASDisplayNodeAssertMainThread(); _rangeIsValid = NO; - [_delegate rangeController:self didUpdateWithChangeSet:changeSet]; + [_delegate rangeController:self didUpdateWithChangeSet:changeSet updates:updates]; } #pragma mark - Memory Management diff --git a/Source/Details/ASRangeControllerUpdateRangeProtocol+Beta.h b/Source/Details/ASRangeControllerUpdateRangeProtocol+Beta.h index 7f8c48abf8..4b15f8961f 100644 --- a/Source/Details/ASRangeControllerUpdateRangeProtocol+Beta.h +++ b/Source/Details/ASRangeControllerUpdateRangeProtocol+Beta.h @@ -1,11 +1,18 @@ // // ASRangeControllerUpdateRangeProtocol+Beta.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK diff --git a/Source/Details/ASScrollDirection.h b/Source/Details/ASScrollDirection.h index cd02831269..373e22f976 100644 --- a/Source/Details/ASScrollDirection.h +++ b/Source/Details/ASScrollDirection.h @@ -1,11 +1,18 @@ // // ASScrollDirection.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Details/ASScrollDirection.m b/Source/Details/ASScrollDirection.m index 27f22ea162..fa123c6a18 100644 --- a/Source/Details/ASScrollDirection.m +++ b/Source/Details/ASScrollDirection.m @@ -1,11 +1,18 @@ // // ASScrollDirection.m -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Details/ASSectionContext.h b/Source/Details/ASSectionContext.h index de6133c7e5..10b96e9b3c 100644 --- a/Source/Details/ASSectionContext.h +++ b/Source/Details/ASSectionContext.h @@ -1,13 +1,18 @@ // // ASSectionContext.h -// AsyncDisplayKit -// -// Created by Huy Nguyen on 28/08/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK diff --git a/Source/Details/ASTableLayoutController.h b/Source/Details/ASTableLayoutController.h index 560c6ac4c8..e57500f224 100644 --- a/Source/Details/ASTableLayoutController.h +++ b/Source/Details/ASTableLayoutController.h @@ -1,11 +1,18 @@ // // ASTableLayoutController.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK diff --git a/Source/Details/ASTableLayoutController.m b/Source/Details/ASTableLayoutController.m index 461309f169..479791b5cd 100644 --- a/Source/Details/ASTableLayoutController.m +++ b/Source/Details/ASTableLayoutController.m @@ -1,11 +1,18 @@ // // ASTableLayoutController.m -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK #import @@ -31,17 +38,17 @@ #pragma mark - ASLayoutController -- (NSSet *)elementsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType map:(ASElementMap *)map +- (NSHashTable *)elementsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType map:(ASElementMap *)map { CGRect bounds = _tableView.bounds; ASRangeTuningParameters tuningParameters = [self tuningParametersForRangeMode:rangeMode rangeType:rangeType]; CGRect rangeBounds = CGRectExpandToRangeWithScrollableDirections(bounds, tuningParameters, ASScrollDirectionVerticalDirections, scrollDirection); NSArray *array = [_tableView indexPathsForRowsInRect:rangeBounds]; - return ASSetByFlatMapping(array, NSIndexPath *indexPath, [map elementForItemAtIndexPath:indexPath]); + return ASPointerTableByFlatMapping(array, NSIndexPath *indexPath, [map elementForItemAtIndexPath:indexPath]); } -- (void)allElementsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode displaySet:(NSSet *__autoreleasing _Nullable *)displaySet preloadSet:(NSSet *__autoreleasing _Nullable *)preloadSet map:(ASElementMap *)map +- (void)allElementsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode displaySet:(NSHashTable *__autoreleasing _Nullable *)displaySet preloadSet:(NSHashTable *__autoreleasing _Nullable *)preloadSet map:(ASElementMap *)map { if (displaySet == NULL || preloadSet == NULL) { return; diff --git a/Source/Details/ASThread.h b/Source/Details/ASThread.h index 29071b2995..ca049d72a4 100644 --- a/Source/Details/ASThread.h +++ b/Source/Details/ASThread.h @@ -1,11 +1,18 @@ // // ASThread.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #pragma once @@ -15,8 +22,6 @@ #import #import -#import - #import #import @@ -64,6 +69,18 @@ static inline BOOL ASDisplayNodeThreadIsMain() _Pragma("clang diagnostic pop"); \ } while (0) +/** + * Assert if the current thread owns a mutex. + * This assertion is useful when you want to indicate and enforce the locking policy/expectation of methods. + * To determine when and which methods acquired a (recursive) mutex (to debug deadlocks, for example), + * put breakpoints at some of these assertions. When the breakpoints hit, walk through stack trace frames + * and check ownership count of the mutex. + */ +#if CHECK_LOCKING_SAFETY +#define ASDisplayNodeAssertLockUnownedByCurrentThread(lock) ASDisplayNodeAssertFalse(lock.ownedByCurrentThread()) +#else +#define ASDisplayNodeAssertLockUnownedByCurrentThread(lock) +#endif namespace ASDN { diff --git a/Source/Details/ASTraceEvent.h b/Source/Details/ASTraceEvent.h index db182ccfc4..a8a1eff962 100644 --- a/Source/Details/ASTraceEvent.h +++ b/Source/Details/ASTraceEvent.h @@ -1,9 +1,18 @@ // // ASTraceEvent.h -// AsyncDisplayKit +// Texture // -// Created by Adlai Holler on 9/13/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 /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Details/ASTraceEvent.m b/Source/Details/ASTraceEvent.m index 056129841f..8ef32915c7 100644 --- a/Source/Details/ASTraceEvent.m +++ b/Source/Details/ASTraceEvent.m @@ -1,9 +1,18 @@ // // ASTraceEvent.m -// AsyncDisplayKit +// Texture // -// Created by Adlai Holler on 9/13/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 /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Details/ASTraitCollection.h b/Source/Details/ASTraitCollection.h index a0737319c1..760919c0dd 100644 --- a/Source/Details/ASTraitCollection.h +++ b/Source/Details/ASTraitCollection.h @@ -1,22 +1,27 @@ // // ASTraitCollection.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // -#import - -#if AS_TARGET_OS_IOS #import #import @class ASTraitCollection; @protocol ASLayoutElement; +@protocol ASTraitEnvironment; NS_ASSUME_NONNULL_BEGIN @@ -59,7 +64,7 @@ extern NSString *NSStringFromASPrimitiveTraitCollection(ASPrimitiveTraitCollecti * This function will walk the layout element hierarchy and updates the layout element trait collection for every * layout element within the hierarchy. */ -extern void ASTraitCollectionPropagateDown(id root, ASPrimitiveTraitCollection traitCollection); +extern void ASTraitCollectionPropagateDown(id element, ASPrimitiveTraitCollection traitCollection); /// For backward compatibility reasons we redefine the old layout element trait collection struct name #define ASEnvironmentTraitCollection ASPrimitiveTraitCollection @@ -84,7 +89,6 @@ ASDISPLAYNODE_EXTERN_C_END - (void)setPrimitiveTraitCollection:(ASPrimitiveTraitCollection)traitCollection; /** - * Returns an NSObject-representation of the environment's ASEnvironmentDisplayTraits */ - (ASTraitCollection *)asyncTraitCollection; @@ -97,6 +101,16 @@ ASDISPLAYNODE_EXTERN_C_END @end +#define ASPrimitiveTraitCollectionDefaults \ +- (ASPrimitiveTraitCollection)primitiveTraitCollection\ +{\ + return _primitiveTraitCollection.load();\ +}\ +- (void)setPrimitiveTraitCollection:(ASPrimitiveTraitCollection)traitCollection\ +{\ + _primitiveTraitCollection = traitCollection;\ +}\ + #define ASPrimitiveTraitCollectionDeprecatedImplementation \ - (ASEnvironmentTraitCollection)environmentTraitCollection\ {\ @@ -158,9 +172,3 @@ AS_SUBCLASSING_RESTRICTED @end NS_ASSUME_NONNULL_END - -#else - -// Non iOS - -#endif diff --git a/Source/Details/ASTraitCollection.m b/Source/Details/ASTraitCollection.m index 1c716984df..0c845e9f63 100644 --- a/Source/Details/ASTraitCollection.m +++ b/Source/Details/ASTraitCollection.m @@ -1,28 +1,35 @@ // // ASTraitCollection.m -// AsyncDisplayKit -// -// Created by Ricky Cancro on 5/4/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // +#import #import - -#if AS_TARGET_OS_IOS - #import #import #pragma mark - ASPrimitiveTraitCollection -extern void ASTraitCollectionPropagateDown(id root, ASPrimitiveTraitCollection traitCollection) { - ASLayoutElementPerformBlockOnEveryElement(root, ^(id _Nonnull element) { +extern void ASTraitCollectionPropagateDown(id element, ASPrimitiveTraitCollection traitCollection) { + if (element) { element.primitiveTraitCollection = traitCollection; - }); + } + + for (id subelement in element.sublayoutElements) { + ASTraitCollectionPropagateDown(subelement, traitCollection); + } } ASPrimitiveTraitCollection ASPrimitiveTraitCollectionMakeDefault() @@ -196,9 +203,3 @@ NSString *NSStringFromASPrimitiveTraitCollection(ASPrimitiveTraitCollection trai } @end - -#else - -// Non iOS - -#endif diff --git a/Source/Details/ASWeakProxy.h b/Source/Details/ASWeakProxy.h index 7a7d2694d4..978204fbff 100644 --- a/Source/Details/ASWeakProxy.h +++ b/Source/Details/ASWeakProxy.h @@ -1,13 +1,18 @@ // // ASWeakProxy.h -// AsyncDisplayKit -// -// Created by Garrett Moon on 4/12/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Details/ASWeakProxy.m b/Source/Details/ASWeakProxy.m index de06b9fc62..9db9655419 100644 --- a/Source/Details/ASWeakProxy.m +++ b/Source/Details/ASWeakProxy.m @@ -1,13 +1,18 @@ // // ASWeakProxy.m -// AsyncDisplayKit -// -// Created by Garrett Moon on 4/12/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Details/ASWeakSet.h b/Source/Details/ASWeakSet.h index 781cbc0929..7d6b8d8d05 100644 --- a/Source/Details/ASWeakSet.h +++ b/Source/Details/ASWeakSet.h @@ -1,13 +1,18 @@ // // ASWeakSet.h -// AsyncDisplayKit -// -// Created by Adlai Holler on 1/7/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Details/ASWeakSet.m b/Source/Details/ASWeakSet.m index ee21f2b956..a469d56d30 100644 --- a/Source/Details/ASWeakSet.m +++ b/Source/Details/ASWeakSet.m @@ -1,13 +1,18 @@ // // ASWeakSet.m -// AsyncDisplayKit -// -// Created by Adlai Holler on 1/7/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -22,7 +27,7 @@ { self = [super init]; if (self) { - _hashTable = [NSHashTable hashTableWithOptions:NSPointerFunctionsWeakMemory | NSPointerFunctionsObjectPointerPersonality]; + _hashTable = [NSHashTable hashTableWithOptions:NSHashTableWeakMemory | NSHashTableObjectPointerPersonality]; } return self; } diff --git a/Source/Details/CoreGraphics+ASConvenience.h b/Source/Details/CoreGraphics+ASConvenience.h index 65535112bf..d4ec5ce02e 100644 --- a/Source/Details/CoreGraphics+ASConvenience.h +++ b/Source/Details/CoreGraphics+ASConvenience.h @@ -1,11 +1,18 @@ // -// CGRect+ASConvenience.h -// AsyncDisplayKit +// CoreGraphics+ASConvenience.h +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Details/CoreGraphics+ASConvenience.m b/Source/Details/CoreGraphics+ASConvenience.m index 1cca126371..92169ffe4e 100644 --- a/Source/Details/CoreGraphics+ASConvenience.m +++ b/Source/Details/CoreGraphics+ASConvenience.m @@ -1,11 +1,18 @@ // -// CGRect+ASConvenience.m -// AsyncDisplayKit +// CoreGraphics+ASConvenience.m +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Details/NSArray+Diffing.h b/Source/Details/NSArray+Diffing.h index 88658dba47..3a1729337f 100644 --- a/Source/Details/NSArray+Diffing.h +++ b/Source/Details/NSArray+Diffing.h @@ -1,13 +1,18 @@ // // NSArray+Diffing.h -// AsyncDisplayKit -// -// Created by Levi McCallum on 1/29/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Details/NSArray+Diffing.m b/Source/Details/NSArray+Diffing.m index b1b3b64e98..9e964e827b 100644 --- a/Source/Details/NSArray+Diffing.m +++ b/Source/Details/NSArray+Diffing.m @@ -1,13 +1,18 @@ // // NSArray+Diffing.m -// AsyncDisplayKit -// -// Created by Levi McCallum on 1/29/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Details/NSIndexSet+ASHelpers.h b/Source/Details/NSIndexSet+ASHelpers.h index a816b102f4..18bb2f2980 100644 --- a/Source/Details/NSIndexSet+ASHelpers.h +++ b/Source/Details/NSIndexSet+ASHelpers.h @@ -1,9 +1,18 @@ // // NSIndexSet+ASHelpers.h -// AsyncDisplayKit +// Texture // -// Created by Adlai Holler on 6/23/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 /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Details/NSIndexSet+ASHelpers.m b/Source/Details/NSIndexSet+ASHelpers.m index d0e3f34b83..06743e0ad3 100644 --- a/Source/Details/NSIndexSet+ASHelpers.m +++ b/Source/Details/NSIndexSet+ASHelpers.m @@ -1,18 +1,22 @@ // // NSIndexSet+ASHelpers.m -// AsyncDisplayKit +// Texture // -// Created by Adlai Holler on 6/23/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 /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // -#import - -#if AS_TARGET_OS_IOS - #import -#else - #import -#endif +// UIKit indexPath helpers +#import #import diff --git a/Source/Details/NSMutableAttributedString+TextKitAdditions.h b/Source/Details/NSMutableAttributedString+TextKitAdditions.h index fe1e9cf98d..49ff968194 100644 --- a/Source/Details/NSMutableAttributedString+TextKitAdditions.h +++ b/Source/Details/NSMutableAttributedString+TextKitAdditions.h @@ -1,11 +1,18 @@ // // NSMutableAttributedString+TextKitAdditions.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Details/NSMutableAttributedString+TextKitAdditions.m b/Source/Details/NSMutableAttributedString+TextKitAdditions.m index ab032903d1..aff849ae17 100644 --- a/Source/Details/NSMutableAttributedString+TextKitAdditions.m +++ b/Source/Details/NSMutableAttributedString+TextKitAdditions.m @@ -1,11 +1,18 @@ // // NSMutableAttributedString+TextKitAdditions.m -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Details/Transactions/_ASAsyncTransaction.h b/Source/Details/Transactions/_ASAsyncTransaction.h index eb1c2063c6..0f31992140 100644 --- a/Source/Details/Transactions/_ASAsyncTransaction.h +++ b/Source/Details/Transactions/_ASAsyncTransaction.h @@ -1,11 +1,18 @@ // // _ASAsyncTransaction.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -18,8 +25,8 @@ NS_ASSUME_NONNULL_BEGIN typedef void(^asyncdisplaykit_async_transaction_completion_block_t)(_ASAsyncTransaction *completedTransaction, BOOL canceled); typedef id _Nullable(^asyncdisplaykit_async_transaction_operation_block_t)(void); -typedef void(^asyncdisplaykit_async_transaction_operation_completion_block_t)(id _Nullable value, BOOL canceled); -typedef void(^asyncdisplaykit_async_transaction_complete_async_operation_block_t)(id _Nullable value); +typedef void(^asyncdisplaykit_async_transaction_operation_completion_block_t)(id _Nullable value, BOOL canceled); +typedef void(^asyncdisplaykit_async_transaction_complete_async_operation_block_t)(id _Nullable value); typedef void(^asyncdisplaykit_async_transaction_async_operation_block_t)(asyncdisplaykit_async_transaction_complete_async_operation_block_t completeOperationBlock); /** @@ -55,12 +62,9 @@ extern NSInteger const ASDefaultTransactionPriority; /** @summary Initialize a transaction that can start collecting async operations. - @see initWithCallbackQueue:commitBlock:completionBlock:executeConcurrently: - @param callbackQueue The dispatch queue that the completion blocks will be called on. Default is the main queue. @param completionBlock A block that is called when the transaction is completed. */ -- (instancetype)initWithCallbackQueue:(nullable dispatch_queue_t)callbackQueue - completionBlock:(nullable asyncdisplaykit_async_transaction_completion_block_t)completionBlock; +- (instancetype)initWithCompletionBlock:(nullable asyncdisplaykit_async_transaction_completion_block_t)completionBlock; /** @summary Block the main thread until the transaction is complete, including callbacks. @@ -69,11 +73,6 @@ extern NSInteger const ASDefaultTransactionPriority; */ - (void)waitUntilComplete; -/** - The dispatch queue that the completion blocks will be called on. - */ -@property (nonatomic, readonly, strong) dispatch_queue_t callbackQueue; - /** A block that is called when the transaction is completed. */ diff --git a/Source/Details/Transactions/_ASAsyncTransaction.mm b/Source/Details/Transactions/_ASAsyncTransaction.mm index f35e83db80..a7c25b9487 100644 --- a/Source/Details/Transactions/_ASAsyncTransaction.mm +++ b/Source/Details/Transactions/_ASAsyncTransaction.mm @@ -1,11 +1,18 @@ // // _ASAsyncTransaction.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // // We need this import for UITrackingRunLoopMode @@ -14,12 +21,10 @@ #import #import #import -#import #import #import #import #import -#import #define ASAsyncTransactionAssertMainThread() NSAssert(0 != pthread_main_np(), @"This method must be called on the main thread"); @@ -28,7 +33,7 @@ NSInteger const ASDefaultTransactionPriority = 0; @interface ASAsyncTransactionOperation : NSObject - (instancetype)initWithOperationCompletionBlock:(asyncdisplaykit_async_transaction_operation_completion_block_t)operationCompletionBlock; @property (nonatomic, copy) asyncdisplaykit_async_transaction_operation_completion_block_t operationCompletionBlock; -@property (nonatomic, strong) id value; // set on bg queue by the operation block +@property (atomic, strong) id value; // set on bg queue by the operation block @end @implementation ASAsyncTransactionOperation @@ -48,16 +53,17 @@ NSInteger const ASDefaultTransactionPriority = 0; - (void)callAndReleaseCompletionBlock:(BOOL)canceled; { + ASDisplayNodeAssertMainThread(); if (_operationCompletionBlock) { _operationCompletionBlock(self.value, canceled); - // Guarantee that _operationCompletionBlock is released on _callbackQueue: - self.operationCompletionBlock = nil; + // Guarantee that _operationCompletionBlock is released on main thread + _operationCompletionBlock = nil; } } - (NSString *)description { - return [NSString stringWithFormat:@"", self, self.value]; } @end @@ -246,9 +252,7 @@ void ASAsyncTransactionQueue::GroupImpl::schedule(NSInteger priority, dispatch_q Operation operation = entry.popNextOperation(respectPriority); lock.unlock(); if (operation._block) { - ASProfilingSignpostStart(3, operation._block); operation._block(); - ASProfilingSignpostEnd(3, operation._block); } operation._group->leave(); operation._block = nil; // the block must be freed while mutex is unlocked @@ -321,27 +325,25 @@ ASAsyncTransactionQueue & ASAsyncTransactionQueue::instance() return *instance; } +@interface _ASAsyncTransaction () +@property (atomic) ASAsyncTransactionState state; +@end + + @implementation _ASAsyncTransaction { ASAsyncTransactionQueue::Group *_group; NSMutableArray *_operations; - _Atomic(ASAsyncTransactionState) _state; } #pragma mark - #pragma mark Lifecycle -- (instancetype)initWithCallbackQueue:(dispatch_queue_t)callbackQueue - completionBlock:(void(^)(_ASAsyncTransaction *, BOOL))completionBlock +- (instancetype)initWithCompletionBlock:(void(^)(_ASAsyncTransaction *, BOOL))completionBlock { if ((self = [self init])) { - if (callbackQueue == NULL) { - callbackQueue = dispatch_get_main_queue(); - } - _callbackQueue = callbackQueue; _completionBlock = completionBlock; - - _state = ATOMIC_VAR_INIT(ASAsyncTransactionStateOpen); + self.state = ASAsyncTransactionStateOpen; } return self; } @@ -355,18 +357,6 @@ ASAsyncTransactionQueue & ASAsyncTransactionQueue::instance() } } -#pragma mark - Properties - -- (ASAsyncTransactionState)state -{ - return atomic_load(&_state); -} - -- (void)setState:(ASAsyncTransactionState)state -{ - atomic_store(&_state, state); -} - #pragma mark - Transaction Management - (void)addAsyncOperationWithBlock:(asyncdisplaykit_async_transaction_async_operation_block_t)block @@ -395,7 +385,7 @@ ASAsyncTransactionQueue & ASAsyncTransactionQueue::instance() @autoreleasepool { if (self.state != ASAsyncTransactionStateCanceled) { _group->enter(); - block(^(id value){ + block(^(id value){ operation.value = value; _group->leave(); }); @@ -438,7 +428,8 @@ ASAsyncTransactionQueue & ASAsyncTransactionQueue::instance() - (void)addCompletionBlock:(asyncdisplaykit_async_transaction_completion_block_t)completion { __weak __typeof__(self) weakSelf = self; - [self addOperationWithBlock:^(){return (id)nil;} queue:_callbackQueue completion:^(id value, BOOL canceled) { + dispatch_queue_t bsQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + [self addOperationWithBlock:^(){return (id)nil;} queue:bsQueue completion:^(id value, BOOL canceled) { __typeof__(self) strongSelf = weakSelf; completion(strongSelf, canceled); }]; @@ -465,10 +456,7 @@ ASAsyncTransactionQueue & ASAsyncTransactionQueue::instance() } else { NSAssert(_group != NULL, @"If there are operations, dispatch group should have been created"); - _group->notify(_callbackQueue, ^{ - // _callbackQueue is the main queue in current practice (also asserted in -waitUntilComplete). - // This code should be reviewed before taking on significantly different use cases. - ASAsyncTransactionAssertMainThread(); + _group->notify(dispatch_get_main_queue(), ^{ [self completeTransaction]; }); } @@ -476,6 +464,7 @@ ASAsyncTransactionQueue & ASAsyncTransactionQueue::instance() - (void)completeTransaction { + ASAsyncTransactionAssertMainThread(); ASAsyncTransactionState state = self.state; if (state != ASAsyncTransactionStateComplete) { BOOL isCanceled = (state == ASAsyncTransactionStateCanceled); @@ -499,7 +488,6 @@ ASAsyncTransactionQueue & ASAsyncTransactionQueue::instance() ASAsyncTransactionAssertMainThread(); if (self.state != ASAsyncTransactionStateComplete) { if (_group) { - NSAssert(_callbackQueue == dispatch_get_main_queue(), nil); _group->wait(); // At this point, the asynchronous operation may have completed, but the runloop diff --git a/Source/Details/Transactions/_ASAsyncTransactionContainer+Private.h b/Source/Details/Transactions/_ASAsyncTransactionContainer+Private.h index ca81f88d66..9ccf4eb67f 100644 --- a/Source/Details/Transactions/_ASAsyncTransactionContainer+Private.h +++ b/Source/Details/Transactions/_ASAsyncTransactionContainer+Private.h @@ -1,11 +1,18 @@ // // _ASAsyncTransactionContainer+Private.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Details/Transactions/_ASAsyncTransactionContainer.h b/Source/Details/Transactions/_ASAsyncTransactionContainer.h index 92037441ee..d6205c8950 100644 --- a/Source/Details/Transactions/_ASAsyncTransactionContainer.h +++ b/Source/Details/Transactions/_ASAsyncTransactionContainer.h @@ -1,11 +1,18 @@ // // _ASAsyncTransactionContainer.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #pragma once diff --git a/Source/Details/Transactions/_ASAsyncTransactionContainer.m b/Source/Details/Transactions/_ASAsyncTransactionContainer.m index c931b1f344..cedd268d33 100644 --- a/Source/Details/Transactions/_ASAsyncTransactionContainer.m +++ b/Source/Details/Transactions/_ASAsyncTransactionContainer.m @@ -1,11 +1,18 @@ // // _ASAsyncTransactionContainer.m -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -86,11 +93,11 @@ static const char *ASAsyncTransactionIsContainerKey = "ASTransactionIsContainer" if (transaction == nil) { NSHashTable *transactions = self.asyncdisplaykit_asyncLayerTransactions; if (transactions == nil) { - transactions = [NSHashTable hashTableWithOptions:NSPointerFunctionsObjectPointerPersonality]; + transactions = [NSHashTable hashTableWithOptions:NSHashTableObjectPointerPersonality]; self.asyncdisplaykit_asyncLayerTransactions = transactions; } __weak CALayer *weakSelf = self; - transaction = [[_ASAsyncTransaction alloc] initWithCallbackQueue:dispatch_get_main_queue() completionBlock:^(_ASAsyncTransaction *completedTransaction, BOOL cancelled) { + transaction = [[_ASAsyncTransaction alloc] initWithCompletionBlock:^(_ASAsyncTransaction *completedTransaction, BOOL cancelled) { __strong CALayer *self = weakSelf; if (self == nil) { return; diff --git a/Source/Details/Transactions/_ASAsyncTransactionGroup.h b/Source/Details/Transactions/_ASAsyncTransactionGroup.h index 44b155c36b..783d880654 100644 --- a/Source/Details/Transactions/_ASAsyncTransactionGroup.h +++ b/Source/Details/Transactions/_ASAsyncTransactionGroup.h @@ -1,11 +1,18 @@ // // _ASAsyncTransactionGroup.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Details/Transactions/_ASAsyncTransactionGroup.m b/Source/Details/Transactions/_ASAsyncTransactionGroup.m index c7c920f811..d8a9d569a2 100644 --- a/Source/Details/Transactions/_ASAsyncTransactionGroup.m +++ b/Source/Details/Transactions/_ASAsyncTransactionGroup.m @@ -1,11 +1,18 @@ // // _ASAsyncTransactionGroup.m -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -15,8 +22,6 @@ #import #import -static void _transactionGroupRunLoopObserverCallback(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info); - @interface _ASAsyncTransactionGroup () + (void)registerTransactionGroupAsMainRunloopObserver:(_ASAsyncTransactionGroup *)transactionGroup; - (void)commit; @@ -47,20 +52,15 @@ static void _transactionGroupRunLoopObserverCallback(CFRunLoopObserverRef observ CFRunLoopRef runLoop = CFRunLoopGetCurrent(); CFOptionFlags activities = (kCFRunLoopBeforeWaiting | // before the run loop starts sleeping kCFRunLoopExit); // before exiting a runloop run - CFRunLoopObserverContext context = { - 0, // version - (__bridge void *)transactionGroup, // info - &CFRetain, // retain - &CFRelease, // release - NULL // copyDescription - }; - observer = CFRunLoopObserverCreate(NULL, // allocator - activities, // activities - YES, // repeats - INT_MAX, // order after CA transaction commits - &_transactionGroupRunLoopObserverCallback, // callback - &context); // context + observer = CFRunLoopObserverCreateWithHandler(NULL, // allocator + activities, // activities + YES, // repeats + INT_MAX, // order after CA transaction commits + ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) { + ASDisplayNodeCAssertMainThread(); + [transactionGroup commit]; + }); CFRunLoopAddObserver(runLoop, observer, kCFRunLoopCommonModes); CFRelease(observer); } @@ -104,10 +104,3 @@ static void _transactionGroupRunLoopObserverCallback(CFRunLoopObserverRef observ } @end - -static void _transactionGroupRunLoopObserverCallback(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) -{ - ASDisplayNodeCAssertMainThread(); - _ASAsyncTransactionGroup *group = (__bridge _ASAsyncTransactionGroup *)info; - [group commit]; -} diff --git a/Source/Details/UICollectionViewLayout+ASConvenience.h b/Source/Details/UICollectionViewLayout+ASConvenience.h index 2b1c4b28f3..ae5d9e5247 100644 --- a/Source/Details/UICollectionViewLayout+ASConvenience.h +++ b/Source/Details/UICollectionViewLayout+ASConvenience.h @@ -1,11 +1,18 @@ // // UICollectionViewLayout+ASConvenience.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Details/UICollectionViewLayout+ASConvenience.m b/Source/Details/UICollectionViewLayout+ASConvenience.m index 6a015d1c3c..6f7633c71c 100644 --- a/Source/Details/UICollectionViewLayout+ASConvenience.m +++ b/Source/Details/UICollectionViewLayout+ASConvenience.m @@ -1,11 +1,18 @@ // // UICollectionViewLayout+ASConvenience.m -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK diff --git a/Source/Details/UIView+ASConvenience.h b/Source/Details/UIView+ASConvenience.h index 24a9021cfd..537a4ae9e7 100644 --- a/Source/Details/UIView+ASConvenience.h +++ b/Source/Details/UIView+ASConvenience.h @@ -1,11 +1,18 @@ // // UIView+ASConvenience.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -22,9 +29,13 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, assign) CGPoint position; @property (nonatomic, assign) CGFloat zPosition; @property (nonatomic, assign) CGPoint anchorPoint; -@property (nullable, nonatomic, strong) id contents; @property (nonatomic, assign) CGFloat cornerRadius; +@property (nullable, nonatomic, strong) id contents; +@property (nonatomic, copy) NSString *contentsGravity; +@property (nonatomic, assign) CGRect contentsRect; +@property (nonatomic, assign) CGRect contentsCenter; @property (nonatomic, assign) CGFloat contentsScale; +@property (nonatomic, assign) CGFloat rasterizationScale; @property (nonatomic, assign) CATransform3D transform; @property (nonatomic, assign) CATransform3D sublayerTransform; @property (nonatomic, assign) BOOL needsDisplayOnBoundsChange; @@ -42,6 +53,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)setNeedsDisplay; - (void)setNeedsLayout; +- (void)layoutIfNeeded; @end @@ -50,15 +62,16 @@ NS_ASSUME_NONNULL_BEGIN */ @protocol ASDisplayNodeViewProperties -@property (nonatomic, assign) BOOL clipsToBounds; -@property (nonatomic, getter=isHidden) BOOL hidden; -@property (nonatomic, assign) BOOL autoresizesSubviews; -@property (nonatomic, assign) UIViewAutoresizing autoresizingMask; -@property (nonatomic, strong, null_resettable) UIColor *tintColor; -@property (nonatomic, assign) CGFloat alpha; -@property (nonatomic, assign) CGRect bounds; -@property (nonatomic, assign) CGRect frame; // Only for use with nodes wrapping synchronous views -@property (nonatomic, assign) UIViewContentMode contentMode; +@property (nonatomic, assign) BOOL clipsToBounds; +@property (nonatomic, getter=isHidden) BOOL hidden; +@property (nonatomic, assign) BOOL autoresizesSubviews; +@property (nonatomic, assign) UIViewAutoresizing autoresizingMask; +@property (nonatomic, strong, null_resettable) UIColor *tintColor; +@property (nonatomic, assign) CGFloat alpha; +@property (nonatomic, assign) CGRect bounds; +@property (nonatomic, assign) CGRect frame; // Only for use with nodes wrapping synchronous views +@property (nonatomic, assign) UIViewContentMode contentMode; +@property (nonatomic, assign) UISemanticContentAttribute semanticContentAttribute; @property (nonatomic, assign, getter=isUserInteractionEnabled) BOOL userInteractionEnabled; @property (nonatomic, assign, getter=isExclusiveTouch) BOOL exclusiveTouch; @property (nonatomic, assign, getter=asyncdisplaykit_isAsyncTransactionContainer, setter = asyncdisplaykit_setAsyncTransactionContainer:) BOOL asyncdisplaykit_asyncTransactionContainer; diff --git a/Source/Details/_ASCollectionReusableView.h b/Source/Details/_ASCollectionReusableView.h new file mode 100644 index 0000000000..f464a8a41b --- /dev/null +++ b/Source/Details/_ASCollectionReusableView.h @@ -0,0 +1,38 @@ +// +// _ASCollectionReusableView.h +// Texture +// +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#ifndef MINIMAL_ASDK + +#import +#import + +@class ASCellNode, ASCollectionElement; + +NS_ASSUME_NONNULL_BEGIN + +AS_SUBCLASSING_RESTRICTED // Note: ASDynamicCastStrict is used on instances of this class based on this restriction. +@interface _ASCollectionReusableView : UICollectionReusableView + +@property (nonatomic, strong, readonly, nullable) ASCellNode *node; +@property (nonatomic, strong, nullable) ASCollectionElement *element; +@property (nonatomic, strong, nullable) UICollectionViewLayoutAttributes *layoutAttributes; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/Source/Details/_ASCollectionReusableView.m b/Source/Details/_ASCollectionReusableView.m new file mode 100644 index 0000000000..8f1ff1b03b --- /dev/null +++ b/Source/Details/_ASCollectionReusableView.m @@ -0,0 +1,100 @@ +// +// _ASCollectionReusableView.m +// Texture +// +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#ifndef MINIMAL_ASDK + +#import "_ASCollectionReusableView.h" +#import +#import + +@implementation _ASCollectionReusableView + +- (ASCellNode *)node +{ + return self.element.node; +} + +- (void)setElement:(ASCollectionElement *)element +{ + ASDisplayNodeAssertMainThread(); + element.node.layoutAttributes = _layoutAttributes; + _element = element; +} + +- (void)setLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes +{ + _layoutAttributes = layoutAttributes; + self.node.layoutAttributes = layoutAttributes; +} + +- (void)prepareForReuse +{ + self.layoutAttributes = nil; + + // Need to clear element before UIKit calls setSelected:NO / setHighlighted:NO on its cells + self.element = nil; + [super prepareForReuse]; +} + +/** + * In the initial case, this is called by UICollectionView during cell dequeueing, before + * we get a chance to assign a node to it, so we must be sure to set these layout attributes + * on our node when one is next assigned to us in @c setNode: . Since there may be cases when we _do_ already + * have our node assigned e.g. during a layout update for existing cells, we also attempt + * to update it now. + */ +- (void)applyLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes +{ + self.layoutAttributes = layoutAttributes; +} + +/** + * Keep our node filling our content view. + */ +- (void)layoutSubviews +{ + [super layoutSubviews]; + self.node.frame = self.bounds; +} + +@end + +/** + * A category that makes _ASCollectionReusableView conform to IGListBindable. + * + * We don't need to do anything to bind the view model – the cell node + * serves the same purpose. + */ +#if __has_include() + +#import + +@interface _ASCollectionReusableView (IGListBindable) +@end + +@implementation _ASCollectionReusableView (IGListBindable) + +- (void)bindViewModel:(id)viewModel +{ + // nop +} + +@end + +#endif + +#endif diff --git a/Source/Details/_ASCollectionViewCell.h b/Source/Details/_ASCollectionViewCell.h index 57057cb0df..add3c247b6 100644 --- a/Source/Details/_ASCollectionViewCell.h +++ b/Source/Details/_ASCollectionViewCell.h @@ -1,22 +1,47 @@ // // _ASCollectionViewCell.h -// AsyncDisplayKit +// Texture // -// Created by Adlai Holler on 1/30/17. -// Copyright © 2017 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 /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. // +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#ifndef MINIMAL_ASDK #import #import +#import -@class ASCellNode; +@class ASCollectionElement; NS_ASSUME_NONNULL_BEGIN -AS_SUBCLASSING_RESTRICTED +AS_SUBCLASSING_RESTRICTED // Note: ASDynamicCastStrict is used on instances of this class based on this restriction. @interface _ASCollectionViewCell : UICollectionViewCell -@property (nonatomic, weak) ASCellNode *node; + +@property (nonatomic, strong, nullable) ASCollectionElement *element; +@property (nonatomic, strong, readonly, nullable) ASCellNode *node; @property (nonatomic, strong, nullable) UICollectionViewLayoutAttributes *layoutAttributes; + +/** + * Whether or not this cell is interested in cell node visibility events. + * -cellNodeVisibilityEvent:inScrollView: should be called only if this property is YES. + */ +@property (nonatomic, readonly) BOOL consumesCellNodeVisibilityEvents; + +- (void)cellNodeVisibilityEvent:(ASCellNodeVisibilityEvent)event inScrollView:(UIScrollView *)scrollView; + @end NS_ASSUME_NONNULL_END + +#endif diff --git a/Source/Details/_ASCollectionViewCell.m b/Source/Details/_ASCollectionViewCell.m index fbcd1f3c42..41a92d1ae7 100644 --- a/Source/Details/_ASCollectionViewCell.m +++ b/Source/Details/_ASCollectionViewCell.m @@ -1,52 +1,82 @@ // // _ASCollectionViewCell.m -// AsyncDisplayKit +// Texture // -// Created by Adlai Holler on 1/30/17. -// Copyright © 2017 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 /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK #import "_ASCollectionViewCell.h" -#import "ASCellNode+Internal.h" -#import +#import +#import +#import @implementation _ASCollectionViewCell -- (void)setNode:(ASCellNode *)node +- (ASCellNode *)node +{ + return self.element.node; +} + +- (void)setElement:(ASCollectionElement *)element { ASDisplayNodeAssertMainThread(); + ASCellNode *node = element.node; node.layoutAttributes = _layoutAttributes; - _node = node; + _element = element; [node __setSelectedFromUIKit:self.selected]; [node __setHighlightedFromUIKit:self.highlighted]; } +- (BOOL)consumesCellNodeVisibilityEvents +{ + ASCellNode *node = self.node; + if (node == nil) { + return NO; + } + return ASSubclassOverridesSelector([ASCellNode class], [node class], @selector(cellNodeVisibilityEvent:inScrollView:withCellFrame:)); +} + +- (void)cellNodeVisibilityEvent:(ASCellNodeVisibilityEvent)event inScrollView:(UIScrollView *)scrollView +{ + [self.node cellNodeVisibilityEvent:event inScrollView:scrollView withCellFrame:self.frame]; +} + - (void)setSelected:(BOOL)selected { [super setSelected:selected]; - [_node __setSelectedFromUIKit:selected]; + [self.node __setSelectedFromUIKit:selected]; } - (void)setHighlighted:(BOOL)highlighted { [super setHighlighted:highlighted]; - [_node __setHighlightedFromUIKit:highlighted]; + [self.node __setHighlightedFromUIKit:highlighted]; } - (void)setLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes { _layoutAttributes = layoutAttributes; - _node.layoutAttributes = layoutAttributes; + self.node.layoutAttributes = layoutAttributes; } - (void)prepareForReuse { self.layoutAttributes = nil; - // Need to clear node pointer before UIKit calls setSelected:NO / setHighlighted:NO on its cells - self.node = nil; + // Need to clear element before UIKit calls setSelected:NO / setHighlighted:NO on its cells + self.element = nil; [super prepareForReuse]; } @@ -59,6 +89,7 @@ */ - (void)applyLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes { + [super applyLayoutAttributes:layoutAttributes]; self.layoutAttributes = layoutAttributes; } diff --git a/Source/Details/_ASDisplayLayer.h b/Source/Details/_ASDisplayLayer.h index cbc0fb38d4..11ed695781 100644 --- a/Source/Details/_ASDisplayLayer.h +++ b/Source/Details/_ASDisplayLayer.h @@ -1,11 +1,18 @@ // // _ASDisplayLayer.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -46,11 +53,11 @@ + (dispatch_queue_t)displayQueue; /** - @summary Delegate for asynchronous display of the layer. + @summary Delegate for asynchronous display of the layer. This should be the node (default) unless you REALLY know what you're doing. @desc The asyncDelegate will have the opportunity to override the methods related to async display. */ -@property (nonatomic, weak) id<_ASDisplayLayerDelegate> asyncDelegate; +@property (atomic, weak) id<_ASDisplayLayerDelegate> asyncDelegate; /** @summary Suspends both asynchronous and synchronous display of the receiver if YES. @@ -102,7 +109,7 @@ @param isCancelledBlock Execute this block to check whether the current drawing operation has been cancelled to avoid unnecessary work. A return value of YES means cancel drawing and return. @param isRasterizing YES if the layer is being rasterized into another layer, in which case drawRect: probably wants to avoid doing things like filling its bounds with a zero-alpha color to clear the backing store. */ -+ (void)drawRect:(CGRect)bounds withParameters:(id)parameters isCancelled:(AS_NOESCAPE asdisplaynode_iscancelled_block_t)isCancelledBlock isRasterizing:(BOOL)isRasterizing; ++ (void)drawRect:(CGRect)bounds withParameters:(id)parameters isCancelled:(AS_NOESCAPE asdisplaynode_iscancelled_block_t)isCancelledBlock isRasterizing:(BOOL)isRasterizing; /** @summary Delegate override to provide new layer contents as a UIImage. @@ -112,18 +119,6 @@ */ + (UIImage *)displayWithParameters:(id)parameters isCancelled:(AS_NOESCAPE asdisplaynode_iscancelled_block_t)isCancelledBlock; -/** - * @abstract instance version of drawRect class method - * @see drawRect:withParameters:isCancelled:isRasterizing class method - */ -- (void)drawRect:(CGRect)bounds withParameters:(id )parameters isCancelled:(AS_NOESCAPE asdisplaynode_iscancelled_block_t)isCancelledBlock isRasterizing:(BOOL)isRasterizing; - -/** - * @abstract instance version of display class method - * @see displayWithParameters:isCancelled class method - */ -- (UIImage *)displayWithParameters:(id )parameters isCancelled:(AS_NOESCAPE asdisplaynode_iscancelled_block_t)isCancelled; - // Called on the main thread only /** diff --git a/Source/Details/_ASDisplayLayer.mm b/Source/Details/_ASDisplayLayer.mm index 8425ff661d..e44265f195 100644 --- a/Source/Details/_ASDisplayLayer.mm +++ b/Source/Details/_ASDisplayLayer.mm @@ -1,11 +1,18 @@ // // _ASDisplayLayer.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -21,41 +28,24 @@ @implementation _ASDisplayLayer { - ASDN::Mutex _asyncDelegateLock; - // We can take this lock when we're setting displaySuspended and in setNeedsDisplay, so to not deadlock, this is recursive - ASDN::RecursiveMutex _displaySuspendedLock; - BOOL _displaySuspended; BOOL _attemptedDisplayWhileZeroSized; struct { BOOL delegateDidChangeBounds:1; } _delegateFlags; - - id<_ASDisplayLayerDelegate> __weak _asyncDelegate; } @dynamic displaysAsynchronously; -#pragma mark - -#pragma mark Lifecycle - -- (instancetype)init -{ - if ((self = [super init])) { - - self.opaque = YES; +#ifdef DEBUG +- (void)dealloc { + if (![NSThread isMainThread]) { + assert(true); } - return self; } +#endif -#pragma mark - -#pragma mark Properties - -- (id<_ASDisplayLayerDelegate>)asyncDelegate -{ - ASDN::MutexLocker l(_asyncDelegateLock); - return _asyncDelegate; -} +#pragma mark - Properties - (void)setDelegate:(id)delegate { @@ -63,22 +53,9 @@ _delegateFlags.delegateDidChangeBounds = [delegate respondsToSelector:@selector(layer:didChangeBoundsWithOldValue:newValue:)]; } -- (void)setAsyncDelegate:(id<_ASDisplayLayerDelegate>)asyncDelegate -{ - ASDisplayNodeAssert(!asyncDelegate || [asyncDelegate isKindOfClass:[ASDisplayNode class]], @"_ASDisplayLayer is inherently coupled to ASDisplayNode and cannot be used with another asyncDelegate. Please rethink what you are trying to do."); - ASDN::MutexLocker l(_asyncDelegateLock); - _asyncDelegate = asyncDelegate; -} - -- (BOOL)isDisplaySuspended -{ - ASDN::MutexLocker l(_displaySuspendedLock); - return _displaySuspended; -} - - (void)setDisplaySuspended:(BOOL)displaySuspended { - ASDN::MutexLocker l(_displaySuspendedLock); + ASDisplayNodeAssertMainThread(); if (_displaySuspended != displaySuspended) { _displaySuspended = displaySuspended; if (!displaySuspended) { @@ -93,6 +70,10 @@ - (void)setBounds:(CGRect)bounds { + BOOL valid = ASDisplayNodeAssertNonFatal(ASIsCGRectValidForLayout(bounds), @"Caught attempt to set invalid bounds %@ on %@.", NSStringFromCGRect(bounds), self); + if (!valid) { + return; + } if (_delegateFlags.delegateDidChangeBounds) { CGRect oldBounds = self.bounds; [super setBounds:bounds]; @@ -135,8 +116,6 @@ - (void)setNeedsDisplay { ASDisplayNodeAssertMainThread(); - - _displaySuspendedLock.lock(); // FIXME: Reconsider whether we should cancel a display in progress. // We should definitely cancel a display that is scheduled, but unstarted display. @@ -146,7 +125,6 @@ if (!_displaySuspended) { [super setNeedsDisplay]; } - _displaySuspendedLock.unlock(); } #pragma mark - @@ -168,13 +146,14 @@ { if ([key isEqualToString:@"displaysAsynchronously"]) { return @YES; + } else if ([key isEqualToString:@"opaque"]) { + return @YES; } else { return [super defaultValueForKey:key]; } } -#pragma mark - -#pragma mark Display +#pragma mark - Display - (void)displayImmediately { @@ -198,7 +177,7 @@ ASDisplayNodeAssertMainThread(); [self _hackResetNeedsDisplay]; - if (self.isDisplaySuspended) { + if (self.displaySuspended) { return; } @@ -210,29 +189,15 @@ if (CGRectIsEmpty(self.bounds)) { _attemptedDisplayWhileZeroSized = YES; } - - id<_ASDisplayLayerDelegate> NS_VALID_UNTIL_END_OF_SCOPE strongAsyncDelegate; - { - _asyncDelegateLock.lock(); - strongAsyncDelegate = _asyncDelegate; - _asyncDelegateLock.unlock(); - } - [strongAsyncDelegate displayAsyncLayer:self asynchronously:asynchronously]; + [self.asyncDelegate displayAsyncLayer:self asynchronously:asynchronously]; } - (void)cancelAsyncDisplay { ASDisplayNodeAssertMainThread(); - id<_ASDisplayLayerDelegate> NS_VALID_UNTIL_END_OF_SCOPE strongAsyncDelegate; - { - _asyncDelegateLock.lock(); - strongAsyncDelegate = _asyncDelegate; - _asyncDelegateLock.unlock(); - } - - [strongAsyncDelegate cancelDisplayAsyncLayer:self]; + [self.asyncDelegate cancelDisplayAsyncLayer:self]; } // e.g. > @@ -241,7 +206,7 @@ NSMutableString *description = [[super description] mutableCopy]; ASDisplayNode *node = self.asyncdisplaykit_node; if (node != nil) { - NSString *classString = [NSString stringWithFormat:@"%@-", [node class]]; + NSString *classString = [NSString stringWithFormat:@"%s-", object_getClassName(node)]; [description replaceOccurrencesOfString:@"_ASDisplay" withString:classString options:kNilOptions range:NSMakeRange(0, description.length)]; NSUInteger insertionIndex = [description rangeOfString:@">"].location; if (insertionIndex != NSNotFound) { diff --git a/Source/Details/_ASDisplayView.h b/Source/Details/_ASDisplayView.h index 5f1b5879fd..c4968d183c 100644 --- a/Source/Details/_ASDisplayView.h +++ b/Source/Details/_ASDisplayView.h @@ -1,11 +1,18 @@ // // _ASDisplayView.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Details/_ASDisplayView.mm b/Source/Details/_ASDisplayView.mm index a61b1ce571..93f3187f6a 100644 --- a/Source/Details/_ASDisplayView.mm +++ b/Source/Details/_ASDisplayView.mm @@ -1,11 +1,18 @@ // // _ASDisplayView.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -51,7 +58,7 @@ ASDisplayNode *node = _asyncdisplaykit_node; if (node != nil) { - NSString *classString = [NSString stringWithFormat:@"%@-", [node class]]; + NSString *classString = [NSString stringWithFormat:@"%s-", object_getClassName(node)]; [description replaceOccurrencesOfString:@"_ASDisplay" withString:classString options:kNilOptions range:NSMakeRange(0, description.length)]; NSUInteger semicolon = [description rangeOfString:@";"].location; if (semicolon != NSNotFound) { @@ -237,6 +244,12 @@ node.threadSafeBounds = bounds; } +- (void)addGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer +{ + [super addGestureRecognizer:gestureRecognizer]; + [_asyncdisplaykit_node nodeViewDidAddGestureRecognizer]; +} + #pragma mark - Event Handling + UIResponder Overrides - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { @@ -342,20 +355,20 @@ - (void)tintColorDidChange { - ASDisplayNode *node = _asyncdisplaykit_node; // Create strong reference to weak ivar. - [super tintColorDidChange]; - - [node tintColorDidChange]; + ASDisplayNode *node = _asyncdisplaykit_node; // Create strong reference to weak ivar. + [super tintColorDidChange]; + + [node tintColorDidChange]; } - (BOOL)canBecomeFirstResponder { - ASDisplayNode *node = _asyncdisplaykit_node; // Create strong reference to weak ivar. - return [node canBecomeFirstResponder]; + ASDisplayNode *node = _asyncdisplaykit_node; // Create strong reference to weak ivar. + return [node canBecomeFirstResponder]; } - (BOOL)canResignFirstResponder { - ASDisplayNode *node = _asyncdisplaykit_node; // Create strong reference to weak ivar. - return [node canResignFirstResponder]; + ASDisplayNode *node = _asyncdisplaykit_node; // Create strong reference to weak ivar. + return [node canResignFirstResponder]; } - (BOOL)canPerformAction:(SEL)action withSender:(id)sender diff --git a/Source/Details/_ASDisplayViewAccessiblity.h b/Source/Details/_ASDisplayViewAccessiblity.h index eb9d0f8bfc..5edd82e23f 100644 --- a/Source/Details/_ASDisplayViewAccessiblity.h +++ b/Source/Details/_ASDisplayViewAccessiblity.h @@ -1,11 +1,18 @@ // // _ASDisplayViewAccessiblity.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Details/_ASDisplayViewAccessiblity.mm b/Source/Details/_ASDisplayViewAccessiblity.mm index b6d174a5de..e2f937403e 100644 --- a/Source/Details/_ASDisplayViewAccessiblity.mm +++ b/Source/Details/_ASDisplayViewAccessiblity.mm @@ -1,11 +1,18 @@ // // _ASDisplayViewAccessiblity.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef ASDK_ACCESSIBILITY_DISABLE @@ -84,7 +91,7 @@ static void CollectUIAccessibilityElementsForNode(ASDisplayNode *node, ASDisplay ASDisplayNodeCAssertNotNil(elements, @"Should pass in a NSMutableArray"); ASDisplayNodePerformBlockOnEveryNodeBFS(node, ^(ASDisplayNode * _Nonnull currentNode) { - // For every subnode that is layer backed or it's supernode has shouldRasterizeDescendants enabled + // For every subnode that is layer backed or it's supernode has subtree rasterization enabled // we have to create a UIAccessibilityElement as no view for this node exists if (currentNode != containerNode && currentNode.isAccessibilityElement) { UIAccessibilityElement *accessibilityElement = [ASAccessibilityElement accessibilityElementWithContainer:container node:currentNode containerNode:containerNode]; @@ -101,7 +108,7 @@ static void CollectAccessibilityElementsForView(_ASDisplayView *view, NSMutableA ASDisplayNode *node = view.asyncdisplaykit_node; // Handle rasterize case - if (node.shouldRasterizeDescendants) { + if (node.rasterizesSubtree) { CollectUIAccessibilityElementsForNode(node, node, view, elements); return; } diff --git a/Source/IGListAdapter+AsyncDisplayKit.h b/Source/IGListAdapter+AsyncDisplayKit.h index ae19e74008..b5de9a7901 100644 --- a/Source/IGListAdapter+AsyncDisplayKit.h +++ b/Source/IGListAdapter+AsyncDisplayKit.h @@ -1,12 +1,23 @@ // // IGListAdapter+AsyncDisplayKit.h -// AsyncDisplayKit +// Texture // -// Created by Adlai Holler on 1/19/17. -// Copyright © 2017 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 /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // -#if IG_LIST_KIT +#import + +#if AS_IG_LIST_KIT #import @@ -31,4 +42,4 @@ NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_END -#endif // IG_LIST_KIT +#endif // AS_IG_LIST_KIT diff --git a/Source/IGListAdapter+AsyncDisplayKit.m b/Source/IGListAdapter+AsyncDisplayKit.m index 3c7606eaab..8f64ee0358 100644 --- a/Source/IGListAdapter+AsyncDisplayKit.m +++ b/Source/IGListAdapter+AsyncDisplayKit.m @@ -1,12 +1,23 @@ // // IGListAdapter+AsyncDisplayKit.m -// AsyncDisplayKit +// Texture // -// Created by Adlai Holler on 1/19/17. -// Copyright © 2017 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 /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // -#if IG_LIST_KIT +#import + +#if AS_IG_LIST_KIT #import "IGListAdapter+AsyncDisplayKit.h" #import "ASIGListAdapterBasedDataSource.h" @@ -47,4 +58,4 @@ @end -#endif // IG_LIST_KIT +#endif // AS_IG_LIST_KIT diff --git a/Source/Layout/ASAbsoluteLayoutElement.h b/Source/Layout/ASAbsoluteLayoutElement.h index 2c222ae1af..39ce04f1b1 100644 --- a/Source/Layout/ASAbsoluteLayoutElement.h +++ b/Source/Layout/ASAbsoluteLayoutElement.h @@ -1,11 +1,18 @@ // // ASAbsoluteLayoutElement.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Layout/ASAbsoluteLayoutSpec.h b/Source/Layout/ASAbsoluteLayoutSpec.h index 0f3cc89522..7d218dce0e 100644 --- a/Source/Layout/ASAbsoluteLayoutSpec.h +++ b/Source/Layout/ASAbsoluteLayoutSpec.h @@ -1,11 +1,18 @@ // // ASAbsoluteLayoutSpec.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Layout/ASAbsoluteLayoutSpec.mm b/Source/Layout/ASAbsoluteLayoutSpec.mm index 920a009376..c8d136a582 100644 --- a/Source/Layout/ASAbsoluteLayoutSpec.mm +++ b/Source/Layout/ASAbsoluteLayoutSpec.mm @@ -1,11 +1,18 @@ // // ASAbsoluteLayoutSpec.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Layout/ASAsciiArtBoxCreator.h b/Source/Layout/ASAsciiArtBoxCreator.h index d032751518..2eca66e52a 100644 --- a/Source/Layout/ASAsciiArtBoxCreator.h +++ b/Source/Layout/ASAsciiArtBoxCreator.h @@ -1,11 +1,18 @@ // // ASAsciiArtBoxCreator.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Layout/ASAsciiArtBoxCreator.m b/Source/Layout/ASAsciiArtBoxCreator.m index 03d49eb02c..293127a64f 100644 --- a/Source/Layout/ASAsciiArtBoxCreator.m +++ b/Source/Layout/ASAsciiArtBoxCreator.m @@ -1,11 +1,18 @@ // // ASAsciiArtBoxCreator.m -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Layout/ASBackgroundLayoutSpec.h b/Source/Layout/ASBackgroundLayoutSpec.h index dbdf79b9ca..882084d051 100644 --- a/Source/Layout/ASBackgroundLayoutSpec.h +++ b/Source/Layout/ASBackgroundLayoutSpec.h @@ -1,11 +1,18 @@ // // ASBackgroundLayoutSpec.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Layout/ASBackgroundLayoutSpec.mm b/Source/Layout/ASBackgroundLayoutSpec.mm index a3fb85e078..e8c50901d1 100644 --- a/Source/Layout/ASBackgroundLayoutSpec.mm +++ b/Source/Layout/ASBackgroundLayoutSpec.mm @@ -1,11 +1,18 @@ // // ASBackgroundLayoutSpec.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Layout/ASCenterLayoutSpec.h b/Source/Layout/ASCenterLayoutSpec.h index 4d995fcb8d..f51a44b3a3 100644 --- a/Source/Layout/ASCenterLayoutSpec.h +++ b/Source/Layout/ASCenterLayoutSpec.h @@ -1,11 +1,18 @@ // // ASCenterLayoutSpec.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Layout/ASCenterLayoutSpec.mm b/Source/Layout/ASCenterLayoutSpec.mm index f4715e422d..0017a2a8e8 100644 --- a/Source/Layout/ASCenterLayoutSpec.mm +++ b/Source/Layout/ASCenterLayoutSpec.mm @@ -1,11 +1,18 @@ // // ASCenterLayoutSpec.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Layout/ASDimension.h b/Source/Layout/ASDimension.h index cb14eef003..851f519e33 100644 --- a/Source/Layout/ASDimension.h +++ b/Source/Layout/ASDimension.h @@ -1,16 +1,23 @@ // // ASDimension.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #pragma once -#import -#import +#import +#import #import #import @@ -206,17 +213,6 @@ ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT NSString *NSStringFromASLayoutSize(AS NSStringFromASDimension(size.height)]; } -#pragma mark - ASEdgeInsets - -typedef struct { - ASDimension top; - ASDimension left; - ASDimension bottom; - ASDimension right; -} ASEdgeInsets; - -extern ASEdgeInsets const ASEdgeInsetsZero; - #pragma mark - ASSizeRange /** @@ -242,7 +238,7 @@ extern ASSizeRange const ASSizeRangeUnconstrained; */ ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT BOOL ASSizeRangeHasSignificantArea(ASSizeRange sizeRange) { - static CGFloat const limit = 0.1; + static CGFloat const limit = 0.1f; return (sizeRange.max.width > limit && sizeRange.max.height > limit); } @@ -301,5 +297,27 @@ ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT BOOL ASSizeRangeEqualToSizeRange(ASSi */ extern AS_WARN_UNUSED_RESULT NSString *NSStringFromASSizeRange(ASSizeRange sizeRange); +#if YOGA + +#pragma mark - ASEdgeInsets + +typedef struct { + ASDimension top; + ASDimension left; + ASDimension bottom; + ASDimension right; + ASDimension start; + ASDimension end; + ASDimension horizontal; + ASDimension vertical; + ASDimension all; +} ASEdgeInsets; + +extern ASEdgeInsets const ASEdgeInsetsZero; + +extern ASEdgeInsets ASEdgeInsetsMake(UIEdgeInsets edgeInsets); + +#endif + NS_ASSUME_NONNULL_END ASDISPLAYNODE_EXTERN_C_END diff --git a/Source/Layout/ASDimension.mm b/Source/Layout/ASDimension.mm index 6db911d11b..2a9ccbbb31 100644 --- a/Source/Layout/ASDimension.mm +++ b/Source/Layout/ASDimension.mm @@ -1,21 +1,22 @@ // // ASDimension.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import -#if AS_TARGET_OS_IOS -#import -#else -#import -#endif - #import #import @@ -44,7 +45,6 @@ ASOVERLOADABLE ASDimension ASDimensionMake(NSString *dimension) } } - ASDisplayNodeCAssert(NO, @"Parsing dimension failed for: %@", dimension); return ASDimensionAuto; } @@ -64,10 +64,6 @@ NSString *NSStringFromASDimension(ASDimension dimension) ASLayoutSize const ASLayoutSizeAuto = {ASDimensionAuto, ASDimensionAuto}; -#pragma mark - ASEdgeInsets - -ASEdgeInsets const ASEdgeInsetsZero = {}; - #pragma mark - ASSizeRange ASSizeRange const ASSizeRangeZero = {}; @@ -108,13 +104,30 @@ ASSizeRange ASSizeRangeIntersect(ASSizeRange sizeRange, ASSizeRange otherSizeRan NSString *NSStringFromASSizeRange(ASSizeRange sizeRange) { -#if AS_TARGET_OS_IOS - return [NSString stringWithFormat:@"", - NSStringFromCGSize(sizeRange.min), - NSStringFromCGSize(sizeRange.max)]; -#else - return [NSString stringWithFormat:@"", - NSStringFromRect(NSRectFromCGRect(sizeRange.min)), - NSStringFromRect(NSRectFromCGRect(sizeRange.max))]; -#endif + // 17 field length copied from iOS 10.3 impl of NSStringFromCGSize. + if (CGSizeEqualToSize(sizeRange.min, sizeRange.max)) { + return [NSString stringWithFormat:@"{{%.*g, %.*g}}", + 17, sizeRange.min.width, + 17, sizeRange.min.height]; + } + return [NSString stringWithFormat:@"{{%.*g, %.*g}, {%.*g, %.*g}}", + 17, sizeRange.min.width, + 17, sizeRange.min.height, + 17, sizeRange.max.width, + 17, sizeRange.max.height]; } + +#if YOGA +#pragma mark - Yoga - ASEdgeInsets +ASEdgeInsets const ASEdgeInsetsZero = {}; + +extern ASEdgeInsets ASEdgeInsetsMake(UIEdgeInsets edgeInsets) +{ + ASEdgeInsets asEdgeInsets = ASEdgeInsetsZero; + asEdgeInsets.top = ASDimensionMake(edgeInsets.top); + asEdgeInsets.left = ASDimensionMake(edgeInsets.left); + asEdgeInsets.bottom = ASDimensionMake(edgeInsets.bottom); + asEdgeInsets.right = ASDimensionMake(edgeInsets.right); + return asEdgeInsets; +} +#endif diff --git a/Source/Layout/ASDimensionDeprecated.h b/Source/Layout/ASDimensionDeprecated.h index 7ddd438dbd..94483edc2e 100644 --- a/Source/Layout/ASDimensionDeprecated.h +++ b/Source/Layout/ASDimensionDeprecated.h @@ -1,11 +1,18 @@ // // ASDimensionDeprecated.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #pragma once diff --git a/Source/Layout/ASDimensionDeprecated.mm b/Source/Layout/ASDimensionDeprecated.mm index 328b24110e..bca453d08a 100644 --- a/Source/Layout/ASDimensionDeprecated.mm +++ b/Source/Layout/ASDimensionDeprecated.mm @@ -1,11 +1,18 @@ // // ASDimensionDeprecated.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Layout/ASDimensionInternal.h b/Source/Layout/ASDimensionInternal.h index e4ef18ec5d..080500bc10 100644 --- a/Source/Layout/ASDimensionInternal.h +++ b/Source/Layout/ASDimensionInternal.h @@ -1,11 +1,18 @@ // // ASDimensionInternal.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #pragma once diff --git a/Source/Layout/ASDimensionInternal.mm b/Source/Layout/ASDimensionInternal.mm index b2c1013f2b..1774a99367 100644 --- a/Source/Layout/ASDimensionInternal.mm +++ b/Source/Layout/ASDimensionInternal.mm @@ -1,11 +1,18 @@ // // ASDimensionInternal.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Layout/ASInsetLayoutSpec.h b/Source/Layout/ASInsetLayoutSpec.h index 5515c9679e..f12e54ff11 100644 --- a/Source/Layout/ASInsetLayoutSpec.h +++ b/Source/Layout/ASInsetLayoutSpec.h @@ -1,14 +1,20 @@ // // ASInsetLayoutSpec.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // -#import #import NS_ASSUME_NONNULL_BEGIN diff --git a/Source/Layout/ASInsetLayoutSpec.mm b/Source/Layout/ASInsetLayoutSpec.mm index 7c4bc92f1a..5db1cf03e5 100644 --- a/Source/Layout/ASInsetLayoutSpec.mm +++ b/Source/Layout/ASInsetLayoutSpec.mm @@ -1,11 +1,18 @@ // // ASInsetLayoutSpec.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Layout/ASLayout.h b/Source/Layout/ASLayout.h index b592ddb13b..a0aff5b7b1 100644 --- a/Source/Layout/ASLayout.h +++ b/Source/Layout/ASLayout.h @@ -1,11 +1,18 @@ // // ASLayout.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #pragma once @@ -17,9 +24,9 @@ NS_ASSUME_NONNULL_BEGIN ASDISPLAYNODE_EXTERN_C_BEGIN -extern CGPoint const CGPointNull; +extern CGPoint const ASPointNull; // {NAN, NAN} -extern BOOL CGPointIsNull(CGPoint point); +extern BOOL ASPointIsNull(CGPoint point); /** * Safely calculates the layout of the given root layoutElement by guarding against nil nodes. @@ -59,9 +66,9 @@ ASDISPLAYNODE_EXTERN_C_END @property (nonatomic, assign, readonly) CGSize size; /** - * Position in parent. Default to CGPointNull. + * Position in parent. Default to ASPointNull. * - * @discussion When being used as a sublayout, this property must not equal CGPointNull. + * @discussion When being used as a sublayout, this property must not equal ASPointNull. */ @property (nonatomic, assign, readonly) CGPoint position; @@ -127,14 +134,6 @@ ASDISPLAYNODE_EXTERN_C_END */ + (instancetype)layoutWithLayoutElement:(id)layoutElement size:(CGSize)size AS_WARN_UNUSED_RESULT; -/** - * Convenience initializer that creates a layout based on the values of the given layout, with a new position - * - * @param layout The layout to use to create the new layout - * @param position The position of the new layout - */ -+ (instancetype)layoutWithLayout:(ASLayout *)layout position:(CGPoint)position AS_WARN_UNUSED_RESULT; - /** * Traverses the existing layout tree and generates a new tree that represents only ASDisplayNode layouts */ @@ -169,6 +168,18 @@ ASDISPLAYNODE_EXTERN_C_END @interface ASLayout (Debugging) +/** + * Set to YES to tell all ASLayout instances to retain their sublayout elements. Defaults to NO. + * Can be overridden at instance level. + */ ++ (void)setShouldRetainSublayoutLayoutElements:(BOOL)shouldRetain; + +/** + * Whether or not ASLayout instances should retain their sublayout elements. + * Can be overridden at instance level. + */ ++ (BOOL)shouldRetainSublayoutLayoutElements; + /** * Recrusively output the description of the layout tree. */ diff --git a/Source/Layout/ASLayout.mm b/Source/Layout/ASLayout.mm index aaa569ee4a..82eb62a87d 100644 --- a/Source/Layout/ASLayout.mm +++ b/Source/Layout/ASLayout.mm @@ -1,11 +1,18 @@ // // ASLayout.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -20,9 +27,9 @@ #import #import -CGPoint const CGPointNull = {NAN, NAN}; +CGPoint const ASPointNull = {NAN, NAN}; -extern BOOL CGPointIsNull(CGPoint point) +extern BOOL ASPointIsNull(CGPoint point) { return isnan(point.x) && isnan(point.y); } @@ -30,7 +37,7 @@ extern BOOL CGPointIsNull(CGPoint point) /** * Creates an defined number of " |" indent blocks for the recursive description. */ -static inline NSString * descriptionIndents(NSUInteger indents) +ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT NSString * descriptionIndents(NSUInteger indents) { NSMutableString *description = [NSMutableString string]; for (NSUInteger i = 0; i < indents; i++) { @@ -42,17 +49,34 @@ static inline NSString * descriptionIndents(NSUInteger indents) return description; } +ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT BOOL ASLayoutIsDisplayNodeType(ASLayout *layout) +{ + return layout.type == ASLayoutElementTypeDisplayNode; +} + +ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT BOOL ASLayoutIsFlattened(ASLayout *layout) +{ + // A layout is flattened if its position is null, and all of its sublayouts are of type displaynode with no sublayouts. + if (! ASPointIsNull(layout.position)) { + return NO; + } + + for (ASLayout *sublayout in layout.sublayouts) { + if (ASLayoutIsDisplayNodeType(sublayout) == NO || sublayout.sublayouts.count > 0) { + return NO; + } + } + + return YES; +} + @interface ASLayout () { ASLayoutElementType _layoutElementType; } -/** - * A boolean describing if the current layout has been flattened. - */ -@property (nonatomic, getter=isFlattened) BOOL flattened; /* - * Caches all sublayouts if set to YES or destroys the sublayout cache if set to NO. Defaults to YES + * Caches all sublayouts if set to YES or destroys the sublayout cache if set to NO. Defaults to NO */ @property (nonatomic, assign) BOOL retainSublayoutLayoutElements; @@ -69,6 +93,18 @@ static inline NSString * descriptionIndents(NSUInteger indents) @dynamic frame, type; +static std::atomic_bool static_retainsSublayoutLayoutElements = ATOMIC_VAR_INIT(NO); + ++ (void)setShouldRetainSublayoutLayoutElements:(BOOL)shouldRetain +{ + static_retainsSublayoutLayoutElements.store(shouldRetain); +} + ++ (BOOL)shouldRetainSublayoutLayoutElements +{ + return static_retainsSublayoutLayoutElements.load(); +} + - (instancetype)initWithLayoutElement:(id)layoutElement size:(CGSize)size position:(CGPoint)position @@ -80,7 +116,7 @@ static inline NSString * descriptionIndents(NSUInteger indents) if (self) { #if DEBUG for (ASLayout *sublayout in sublayouts) { - ASDisplayNodeAssert(CGPointIsNull(sublayout.position) == NO, @"Invalid position is not allowed in sublayout."); + ASDisplayNodeAssert(ASPointIsNull(sublayout.position) == NO, @"Invalid position is not allowed in sublayout."); } #endif @@ -97,21 +133,22 @@ static inline NSString * descriptionIndents(NSUInteger indents) } _size = size; - if (CGPointIsNull(position) == NO) { - _position = CGPointMake(ASCeilPixelValue(position.x), ASCeilPixelValue(position.y)); + if (ASPointIsNull(position) == NO) { + _position = ASCeilPointValues(position); } else { _position = position; } _sublayouts = sublayouts != nil ? [sublayouts copy] : @[]; - _elementToRectTable = [ASRectTable rectTableForWeakObjectPointers]; - for (ASLayout *layout in sublayouts) { - [_elementToRectTable setRect:layout.frame forKey:layout.layoutElement]; + if (_sublayouts.count > 0) { + _elementToRectTable = [ASRectTable rectTableForWeakObjectPointers]; + for (ASLayout *layout in sublayouts) { + [_elementToRectTable setRect:layout.frame forKey:layout.layoutElement]; + } } - _flattened = NO; - _retainSublayoutLayoutElements = NO; + self.retainSublayoutLayoutElements = [ASLayout shouldRetainSublayoutLayoutElements]; } return self; @@ -142,7 +179,7 @@ static inline NSString * descriptionIndents(NSUInteger indents) { return [self layoutWithLayoutElement:layoutElement size:size - position:CGPointNull + position:ASPointNull sublayouts:sublayouts]; } @@ -150,18 +187,10 @@ static inline NSString * descriptionIndents(NSUInteger indents) { return [self layoutWithLayoutElement:layoutElement size:size - position:CGPointNull + position:ASPointNull sublayouts:nil]; } -+ (instancetype)layoutWithLayout:(ASLayout *)layout position:(CGPoint)position -{ - return [self layoutWithLayoutElement:layout.layoutElement - size:layout.size - position:position - sublayouts:layout.sublayouts]; -} - #pragma mark - Sublayout Elements Caching - (void)setRetainSublayoutLayoutElements:(BOOL)retainSublayoutLayoutElements @@ -188,7 +217,12 @@ static inline NSString * descriptionIndents(NSUInteger indents) - (ASLayout *)filteredNodeLayoutTree { - NSMutableArray *flattenedSublayouts = [NSMutableArray array]; + if (ASLayoutIsFlattened(self)) { + // All flattened layouts must have this flag enabled + // to ensure sublayout elements are retained until the layouts are applied. + self.retainSublayoutLayoutElements = YES; + return self; + } struct Context { ASLayout *layout; @@ -197,28 +231,42 @@ static inline NSString * descriptionIndents(NSUInteger indents) // Queue used to keep track of sublayouts while traversing this layout in a DFS fashion. std::deque queue; - queue.push_front({self, CGPointZero}); - - while (!queue.empty()) { - Context context = queue.front(); - queue.pop_front(); - - if (self != context.layout && context.layout.type == ASLayoutElementTypeDisplayNode) { - ASLayout *layout = [ASLayout layoutWithLayout:context.layout position:context.absolutePosition]; - layout.flattened = YES; - [flattenedSublayouts addObject:layout]; - } - - std::vector sublayoutContexts; - for (ASLayout *sublayout in context.layout.sublayouts) { - if (sublayout.isFlattened == NO) { - sublayoutContexts.push_back({sublayout, context.absolutePosition + sublayout.position}); - } - } - queue.insert(queue.cbegin(), sublayoutContexts.begin(), sublayoutContexts.end()); + for (ASLayout *sublayout in self.sublayouts) { + queue.push_back({sublayout, sublayout.position}); } - ASLayout *layout = [ASLayout layoutWithLayoutElement:_layoutElement size:_size position:CGPointZero sublayouts:flattenedSublayouts]; + NSMutableArray *flattenedSublayouts = [NSMutableArray array]; + + while (!queue.empty()) { + const Context context = queue.front(); + queue.pop_front(); + + ASLayout *layout = context.layout; + const NSArray *sublayouts = layout.sublayouts; + const NSUInteger sublayoutsCount = sublayouts.count; + const CGPoint absolutePosition = context.absolutePosition; + + if (ASLayoutIsDisplayNodeType(layout)) { + if (sublayoutsCount > 0 || CGPointEqualToPoint(ASCeilPointValues(absolutePosition), layout.position) == NO) { + // Only create a new layout if the existing one can't be reused, which means it has either some sublayouts or an invalid absolute position. + layout = [ASLayout layoutWithLayoutElement:layout.layoutElement + size:layout.size + position:absolutePosition + sublayouts:@[]]; + } + [flattenedSublayouts addObject:layout]; + } else if (sublayoutsCount > 0){ + std::vector sublayoutContexts; + for (ASLayout *sublayout in sublayouts) { + sublayoutContexts.push_back({sublayout, context.absolutePosition + sublayout.position}); + } + queue.insert(queue.cbegin(), sublayoutContexts.begin(), sublayoutContexts.end()); + } + } + + ASLayout *layout = [ASLayout layoutWithLayoutElement:_layoutElement size:_size sublayouts:flattenedSublayouts]; + // All flattened layouts must have this flag enabled + // to ensure sublayout elements are retained until the layouts are applied. layout.retainSublayoutLayoutElements = YES; return layout; } @@ -232,7 +280,7 @@ static inline NSString * descriptionIndents(NSUInteger indents) - (CGRect)frameForElement:(id)layoutElement { - return [_elementToRectTable rectForKey:layoutElement]; + return _elementToRectTable ? [_elementToRectTable rectForKey:layoutElement] : CGRectNull; } - (CGRect)frame @@ -268,9 +316,16 @@ static inline NSString * descriptionIndents(NSUInteger indents) - (NSMutableArray *)propertiesForDescription { NSMutableArray *result = [NSMutableArray array]; - [result addObject:@{ @"layoutElement" : (self.layoutElement ?: (id)kCFNull) }]; - [result addObject:@{ @"position" : [NSValue valueWithCGPoint:self.position] }]; [result addObject:@{ @"size" : [NSValue valueWithCGSize:self.size] }]; + + if (auto layoutElement = self.layoutElement) { + [result addObject:@{ @"layoutElement" : layoutElement }]; + } + + auto pos = self.position; + if (!ASPointIsNull(pos)) { + [result addObject:@{ @"position" : [NSValue valueWithCGPoint:pos] }]; + } return result; } diff --git a/Source/Layout/ASLayoutElement.h b/Source/Layout/ASLayoutElement.h index 3af69103f8..2ebd55a46c 100644 --- a/Source/Layout/ASLayoutElement.h +++ b/Source/Layout/ASLayoutElement.h @@ -1,28 +1,33 @@ // // ASLayoutElement.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // -#import #import #import #import #import #import +#import +#import @class ASLayout; @class ASLayoutSpec; @protocol ASLayoutElementStylability; -#if AS_TARGET_OS_IOS -#import "ASTraitCollection.h" @protocol ASTraitEnvironment; -#endif NS_ASSUME_NONNULL_BEGIN @@ -38,16 +43,6 @@ typedef NS_ENUM(NSUInteger, ASLayoutElementType) { ASLayoutElementTypeDisplayNode }; -ASDISPLAYNODE_EXTERN_C_BEGIN - -/** - This function will walk the layout element hierarchy. It does run the block on the node provided - directly to the function call. - */ -extern void ASLayoutElementPerformBlockOnEveryElement(id root, void(^block)(id element)); - -ASDISPLAYNODE_EXTERN_C_END - #pragma mark - ASLayoutElement /** @@ -66,11 +61,7 @@ ASDISPLAYNODE_EXTERN_C_END * access to the options via convenience properties. If you are creating custom layout spec, then you can * extend the backing layout options class to accommodate any new layout options. */ -#if AS_TARGET_OS_IOS -@protocol ASLayoutElement -#else -@protocol ASLayoutElement -#endif +@protocol ASLayoutElement #pragma mark - Getter @@ -82,7 +73,7 @@ ASDISPLAYNODE_EXTERN_C_END /** * @abstract A size constraint that should apply to this ASLayoutElement. */ -@property (nonatomic, assign, readonly) ASLayoutElementStyle *style; +@property (nonatomic, strong, readonly) ASLayoutElementStyle *style; /** * @abstract Returns all children of an object which class conforms to the ASLayoutElement protocol @@ -154,6 +145,7 @@ ASDISPLAYNODE_EXTERN_C_END restrictedToSize:(ASLayoutElementSize)size relativeToParentSize:(CGSize)parentSize; +- (BOOL)implementsLayoutMethod; #pragma mark - Deprecated diff --git a/Source/Layout/ASLayoutElement.mm b/Source/Layout/ASLayoutElement.mm index de88730dd1..8d647c09aa 100644 --- a/Source/Layout/ASLayoutElement.mm +++ b/Source/Layout/ASLayoutElement.mm @@ -1,40 +1,48 @@ // // ASLayoutElement.mm -// AsyncDisplayKit -// -// Created by Huy Nguyen on 3/27/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // -#import "ASDisplayNode+FrameworkPrivate.h" +#import +#import #import #import #import #import +#import -#import #import #if YOGA #import YOGA_HEADER_PATH + #import #endif -extern void ASLayoutElementPerformBlockOnEveryElement(id element, void(^block)(id element)) -{ - if (element) { - block(element); - } +#pragma mark - ASLayoutElementContext - for (id subelement in element.sublayoutElements) { - ASLayoutElementPerformBlockOnEveryElement(subelement, block); +@implementation ASLayoutElementContext + +- (instancetype)init +{ + if (self = [super init]) { + _transitionID = ASLayoutElementContextDefaultTransitionID; } + return self; } -#pragma mark - ASLayoutElementContext +@end CGFloat const ASLayoutElementParentDimensionUndefined = NAN; CGSize const ASLayoutElementParentSizeUndefined = {ASLayoutElementParentDimensionUndefined, ASLayoutElementParentDimensionUndefined}; @@ -42,64 +50,38 @@ CGSize const ASLayoutElementParentSizeUndefined = {ASLayoutElementParentDimensio int32_t const ASLayoutElementContextInvalidTransitionID = 0; int32_t const ASLayoutElementContextDefaultTransitionID = ASLayoutElementContextInvalidTransitionID + 1; -static inline ASLayoutElementContext _ASLayoutElementContextMake(int32_t transitionID) -{ - struct ASLayoutElementContext context; - context.transitionID = transitionID; - return context; -} - -static inline BOOL _IsValidTransitionID(int32_t transitionID) -{ - return transitionID > ASLayoutElementContextInvalidTransitionID; -} - -struct ASLayoutElementContext const ASLayoutElementContextNull = _ASLayoutElementContextMake(ASLayoutElementContextInvalidTransitionID); - -BOOL ASLayoutElementContextIsNull(struct ASLayoutElementContext context) -{ - return !_IsValidTransitionID(context.transitionID); -} - -ASLayoutElementContext ASLayoutElementContextMake(int32_t transitionID) -{ - NSCAssert(_IsValidTransitionID(transitionID), @"Invalid transition ID"); - return _ASLayoutElementContextMake(transitionID); -} - -// Note: This is a non-recursive static lock. If it needs to be recursive, use ASDISPLAYNODE_MUTEX_RECURSIVE_INITIALIZER -static ASDN::StaticMutex _layoutElementContextLock = ASDISPLAYNODE_MUTEX_INITIALIZER; -static std::map layoutElementContextMap; - -static inline mach_port_t ASLayoutElementGetCurrentContextKey() -{ - return pthread_mach_thread_np(pthread_self()); -} - -void ASLayoutElementSetCurrentContext(struct ASLayoutElementContext context) -{ - const mach_port_t key = ASLayoutElementGetCurrentContextKey(); - ASDN::StaticMutexLocker l(_layoutElementContextLock); - layoutElementContextMap[key] = context; -} - -struct ASLayoutElementContext ASLayoutElementGetCurrentContext() -{ - const mach_port_t key = ASLayoutElementGetCurrentContextKey(); - ASDN::StaticMutexLocker l(_layoutElementContextLock); - const auto it = layoutElementContextMap.find(key); - if (it != layoutElementContextMap.end()) { - // Found an interator with above key. "it->first" is the key itself, "it->second" is the context value. - return it->second; +static void ASLayoutElementDestructor(void *p) { + if (p != NULL) { + ASDisplayNodeCFailAssert(@"Thread exited without clearing layout element context!"); + CFBridgingRelease(p); } - return ASLayoutElementContextNull; +}; + +pthread_key_t ASLayoutElementContextKey() +{ + return ASPthreadStaticKey(ASLayoutElementDestructor); } -void ASLayoutElementClearCurrentContext() +void ASLayoutElementPushContext(ASLayoutElementContext *context) { - const mach_port_t key = ASLayoutElementGetCurrentContextKey(); - ASDN::StaticMutexLocker l(_layoutElementContextLock); - layoutElementContextMap.erase(key); + // NOTE: It would be easy to support nested contexts – just use an NSMutableArray here. + ASDisplayNodeCAssertNil(ASLayoutElementGetCurrentContext(), @"Nested ASLayoutElementContexts aren't supported."); + pthread_setspecific(ASLayoutElementContextKey(), CFBridgingRetain(context)); +} + +ASLayoutElementContext *ASLayoutElementGetCurrentContext() +{ + // Don't retain here. Caller will retain if it wants to! + return (__bridge __unsafe_unretained ASLayoutElementContext *)pthread_getspecific(ASLayoutElementContextKey()); +} + +void ASLayoutElementPopContext() +{ + ASLayoutElementContextKey(); + ASDisplayNodeCAssertNotNil(ASLayoutElementGetCurrentContext(), @"Attempt to pop context when there wasn't a context!"); + auto key = ASLayoutElementContextKey(); + CFBridgingRelease(pthread_getspecific(key)); + pthread_setspecific(key, NULL); } #pragma mark - ASLayoutElementStyle @@ -123,16 +105,39 @@ NSString * const ASLayoutElementStyleDescenderProperty = @"ASLayoutElementStyleD NSString * const ASLayoutElementStyleLayoutPositionProperty = @"ASLayoutElementStyleLayoutPositionProperty"; +#if YOGA +NSString * const ASYogaFlexWrapProperty = @"ASLayoutElementStyleLayoutFlexWrapProperty"; +NSString * const ASYogaFlexDirectionProperty = @"ASYogaFlexDirectionProperty"; +NSString * const ASYogaDirectionProperty = @"ASYogaDirectionProperty"; +NSString * const ASYogaSpacingProperty = @"ASYogaSpacingProperty"; +NSString * const ASYogaJustifyContentProperty = @"ASYogaJustifyContentProperty"; +NSString * const ASYogaAlignItemsProperty = @"ASYogaAlignItemsProperty"; +NSString * const ASYogaPositionTypeProperty = @"ASYogaPositionTypeProperty"; +NSString * const ASYogaPositionProperty = @"ASYogaPositionProperty"; +NSString * const ASYogaMarginProperty = @"ASYogaMarginProperty"; +NSString * const ASYogaPaddingProperty = @"ASYogaPaddingProperty"; +NSString * const ASYogaBorderProperty = @"ASYogaBorderProperty"; +NSString * const ASYogaAspectRatioProperty = @"ASYogaAspectRatioProperty"; +#endif + +#define ASLayoutElementStyleSetSizeWithScope(x) \ + __instanceLock__.lock(); \ + ASLayoutElementSize newSize = _size.load(); \ + { x } \ + _size.store(newSize); \ + __instanceLock__.unlock(); + #define ASLayoutElementStyleCallDelegate(propertyName)\ do {\ + [self propertyDidChange:propertyName];\ [_delegate style:self propertyDidChange:propertyName];\ } while(0) @implementation ASLayoutElementStyle { ASDN::RecursiveMutex __instanceLock__; - ASLayoutElementSize _size; ASLayoutElementStyleExtensions _extensions; + std::atomic _size; std::atomic _spacingBefore; std::atomic _spacingAfter; std::atomic _flexGrow; @@ -144,8 +149,10 @@ do {\ std::atomic _layoutPosition; #if YOGA - std::atomic _direction; - std::atomic _spacing; + YGNodeRef _yogaNode; + std::atomic _flexWrap; + std::atomic _flexDirection; + std::atomic _direction; std::atomic _justifyContent; std::atomic _alignItems; std::atomic _positionType; @@ -154,7 +161,6 @@ do {\ std::atomic _padding; std::atomic _border; std::atomic _aspectRatio; - std::atomic _flexWrap; #endif } @@ -185,185 +191,190 @@ do {\ - (ASLayoutElementSize)size { - ASDN::MutexLocker l(__instanceLock__); - return _size; + return _size.load(); } - (void)setSize:(ASLayoutElementSize)size { - ASDN::MutexLocker l(__instanceLock__); - _size = size; + ASLayoutElementStyleSetSizeWithScope({ + newSize = size; + }); + // No CallDelegate method as ASLayoutElementSize is currently internal. } #pragma mark - ASLayoutElementStyleSizeForwarding - (ASDimension)width { - ASDN::MutexLocker l(__instanceLock__); - return _size.width; + return _size.load().width; } - (void)setWidth:(ASDimension)width { - ASDN::MutexLocker l(__instanceLock__); - _size.width = width; + ASLayoutElementStyleSetSizeWithScope({ + newSize.width = width; + }); ASLayoutElementStyleCallDelegate(ASLayoutElementStyleWidthProperty); } - (ASDimension)height { - ASDN::MutexLocker l(__instanceLock__); - return _size.height; + return _size.load().height; } - (void)setHeight:(ASDimension)height { - ASDN::MutexLocker l(__instanceLock__); - _size.height = height; + ASLayoutElementStyleSetSizeWithScope({ + newSize.height = height; + }); ASLayoutElementStyleCallDelegate(ASLayoutElementStyleHeightProperty); } - (ASDimension)minWidth { - ASDN::MutexLocker l(__instanceLock__); - return _size.minWidth; + return _size.load().minWidth; } - (void)setMinWidth:(ASDimension)minWidth { - ASDN::MutexLocker l(__instanceLock__); - _size.minWidth = minWidth; + ASLayoutElementStyleSetSizeWithScope({ + newSize.minWidth = minWidth; + }); ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMinWidthProperty); } - (ASDimension)maxWidth { - ASDN::MutexLocker l(__instanceLock__); - return _size.maxWidth; + return _size.load().maxWidth; } - (void)setMaxWidth:(ASDimension)maxWidth { - ASDN::MutexLocker l(__instanceLock__); - _size.maxWidth = maxWidth; + ASLayoutElementStyleSetSizeWithScope({ + newSize.maxWidth = maxWidth; + }); ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMaxWidthProperty); } - (ASDimension)minHeight { - ASDN::MutexLocker l(__instanceLock__); - return _size.minHeight; + return _size.load().minHeight; } - (void)setMinHeight:(ASDimension)minHeight { - ASDN::MutexLocker l(__instanceLock__); - _size.minHeight = minHeight; + ASLayoutElementStyleSetSizeWithScope({ + newSize.minHeight = minHeight; + }); ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMinHeightProperty); } - (ASDimension)maxHeight { - ASDN::MutexLocker l(__instanceLock__); - return _size.maxHeight; + return _size.load().maxHeight; } - (void)setMaxHeight:(ASDimension)maxHeight { - ASDN::MutexLocker l(__instanceLock__); - _size.maxHeight = maxHeight; + ASLayoutElementStyleSetSizeWithScope({ + newSize.maxHeight = maxHeight; + }); ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMaxHeightProperty); } #pragma mark - ASLayoutElementStyleSizeHelpers -// We explicitly not call the setter for (max/min) width and height to avoid locking overhead - - (void)setPreferredSize:(CGSize)preferredSize { - ASDN::MutexLocker l(__instanceLock__); - _size.width = ASDimensionMakeWithPoints(preferredSize.width); - _size.height = ASDimensionMakeWithPoints(preferredSize.height); + ASLayoutElementStyleSetSizeWithScope({ + newSize.width = ASDimensionMakeWithPoints(preferredSize.width); + newSize.height = ASDimensionMakeWithPoints(preferredSize.height); + }); ASLayoutElementStyleCallDelegate(ASLayoutElementStyleWidthProperty); ASLayoutElementStyleCallDelegate(ASLayoutElementStyleHeightProperty); } - (CGSize)preferredSize { - ASDN::MutexLocker l(__instanceLock__); - if (_size.width.unit == ASDimensionUnitFraction) { - NSCAssert(NO, @"Cannot get preferredSize of element with fractional width. Width: %@.", NSStringFromASDimension(_size.width)); + ASLayoutElementSize size = _size.load(); + if (size.width.unit == ASDimensionUnitFraction) { + NSCAssert(NO, @"Cannot get preferredSize of element with fractional width. Width: %@.", NSStringFromASDimension(size.width)); return CGSizeZero; } - if (_size.height.unit == ASDimensionUnitFraction) { - NSCAssert(NO, @"Cannot get preferredSize of element with fractional height. Height: %@.", NSStringFromASDimension(_size.height)); + if (size.height.unit == ASDimensionUnitFraction) { + NSCAssert(NO, @"Cannot get preferredSize of element with fractional height. Height: %@.", NSStringFromASDimension(size.height)); return CGSizeZero; } - return CGSizeMake(_size.width.value, _size.height.value); + return CGSizeMake(size.width.value, size.height.value); } - (void)setMinSize:(CGSize)minSize { - ASDN::MutexLocker l(__instanceLock__); - _size.minWidth = ASDimensionMakeWithPoints(minSize.width); - _size.minHeight = ASDimensionMakeWithPoints(minSize.height); + ASLayoutElementStyleSetSizeWithScope({ + newSize.minWidth = ASDimensionMakeWithPoints(minSize.width); + newSize.minHeight = ASDimensionMakeWithPoints(minSize.height); + }); ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMinWidthProperty); ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMinHeightProperty); } - (void)setMaxSize:(CGSize)maxSize { - ASDN::MutexLocker l(__instanceLock__); - _size.maxWidth = ASDimensionMakeWithPoints(maxSize.width); - _size.maxHeight = ASDimensionMakeWithPoints(maxSize.height); + ASLayoutElementStyleSetSizeWithScope({ + newSize.maxWidth = ASDimensionMakeWithPoints(maxSize.width); + newSize.maxHeight = ASDimensionMakeWithPoints(maxSize.height); + }); ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMaxWidthProperty); ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMaxHeightProperty); } - (ASLayoutSize)preferredLayoutSize { - ASDN::MutexLocker l(__instanceLock__); - return ASLayoutSizeMake(_size.width, _size.height); + ASLayoutElementSize size = _size.load(); + return ASLayoutSizeMake(size.width, size.height); } - (void)setPreferredLayoutSize:(ASLayoutSize)preferredLayoutSize { - ASDN::MutexLocker l(__instanceLock__); - _size.width = preferredLayoutSize.width; - _size.height = preferredLayoutSize.height; + ASLayoutElementStyleSetSizeWithScope({ + newSize.width = preferredLayoutSize.width; + newSize.height = preferredLayoutSize.height; + }); ASLayoutElementStyleCallDelegate(ASLayoutElementStyleWidthProperty); ASLayoutElementStyleCallDelegate(ASLayoutElementStyleHeightProperty); } - (ASLayoutSize)minLayoutSize { - ASDN::MutexLocker l(__instanceLock__); - return ASLayoutSizeMake(_size.minWidth, _size.minHeight); + ASLayoutElementSize size = _size.load(); + return ASLayoutSizeMake(size.minWidth, size.minHeight); } - (void)setMinLayoutSize:(ASLayoutSize)minLayoutSize { - ASDN::MutexLocker l(__instanceLock__); - _size.minWidth = minLayoutSize.width; - _size.minHeight = minLayoutSize.height; + ASLayoutElementStyleSetSizeWithScope({ + newSize.minWidth = minLayoutSize.width; + newSize.minHeight = minLayoutSize.height; + }); ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMinWidthProperty); ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMinHeightProperty); } - (ASLayoutSize)maxLayoutSize { - ASDN::MutexLocker l(__instanceLock__); - return ASLayoutSizeMake(_size.maxWidth, _size.maxHeight); + ASLayoutElementSize size = _size.load(); + return ASLayoutSizeMake(size.maxWidth, size.maxHeight); } - (void)setMaxLayoutSize:(ASLayoutSize)maxLayoutSize { - ASDN::MutexLocker l(__instanceLock__); - _size.maxWidth = maxLayoutSize.width; - _size.maxHeight = maxLayoutSize.height; + ASLayoutElementStyleSetSizeWithScope({ + newSize.maxWidth = maxLayoutSize.width; + newSize.maxHeight = maxLayoutSize.height; + }); ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMaxWidthProperty); ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMaxHeightProperty); } @@ -590,12 +601,153 @@ do {\ return result; } +- (void)propertyDidChange:(NSString *)propertyName +{ +#if YOGA + /* TODO(appleguy): STYLE SETTER METHODS LEFT TO IMPLEMENT + void YGNodeStyleSetOverflow(YGNodeRef node, YGOverflow overflow); + void YGNodeStyleSetFlex(YGNodeRef node, float flex); + */ + + if (_yogaNode == NULL) { + return; + } + // Because the NSStrings used to identify each property are const, use efficient pointer comparison. + if (propertyName == ASLayoutElementStyleWidthProperty) { + YGNODE_STYLE_SET_DIMENSION(_yogaNode, Width, self.width); + } + else if (propertyName == ASLayoutElementStyleMinWidthProperty) { + YGNODE_STYLE_SET_DIMENSION(_yogaNode, MinWidth, self.minWidth); + } + else if (propertyName == ASLayoutElementStyleMaxWidthProperty) { + YGNODE_STYLE_SET_DIMENSION(_yogaNode, MaxWidth, self.maxWidth); + } + else if (propertyName == ASLayoutElementStyleHeightProperty) { + YGNODE_STYLE_SET_DIMENSION(_yogaNode, Height, self.height); + } + else if (propertyName == ASLayoutElementStyleMinHeightProperty) { + YGNODE_STYLE_SET_DIMENSION(_yogaNode, MinHeight, self.minHeight); + } + else if (propertyName == ASLayoutElementStyleMaxHeightProperty) { + YGNODE_STYLE_SET_DIMENSION(_yogaNode, MaxHeight, self.maxHeight); + } + else if (propertyName == ASLayoutElementStyleFlexGrowProperty) { + YGNodeStyleSetFlexGrow(_yogaNode, self.flexGrow); + } + else if (propertyName == ASLayoutElementStyleFlexShrinkProperty) { + YGNodeStyleSetFlexShrink(_yogaNode, self.flexShrink); + } + else if (propertyName == ASLayoutElementStyleFlexBasisProperty) { + YGNODE_STYLE_SET_DIMENSION(_yogaNode, FlexBasis, self.flexBasis); + } + else if (propertyName == ASLayoutElementStyleAlignSelfProperty) { + YGNodeStyleSetAlignSelf(_yogaNode, yogaAlignSelf(self.alignSelf)); + } + else if (propertyName == ASYogaFlexWrapProperty) { + YGNodeStyleSetFlexWrap(_yogaNode, self.flexWrap); + } + else if (propertyName == ASYogaFlexDirectionProperty) { + YGNodeStyleSetFlexDirection(_yogaNode, yogaFlexDirection(self.flexDirection)); + } + else if (propertyName == ASYogaDirectionProperty) { + YGNodeStyleSetDirection(_yogaNode, self.direction); + } + else if (propertyName == ASYogaJustifyContentProperty) { + YGNodeStyleSetJustifyContent(_yogaNode, yogaJustifyContent(self.justifyContent)); + } + else if (propertyName == ASYogaAlignItemsProperty) { + ASStackLayoutAlignItems alignItems = self.alignItems; + if (alignItems != ASStackLayoutAlignItemsNotSet) { + YGNodeStyleSetAlignItems(_yogaNode, yogaAlignItems(alignItems)); + } + } + else if (propertyName == ASYogaPositionTypeProperty) { + YGNodeStyleSetPositionType(_yogaNode, self.positionType); + } + else if (propertyName == ASYogaPositionProperty) { + ASEdgeInsets position = self.position; + YGEdge edge = YGEdgeLeft; + for (int i = 0; i < YGEdgeAll + 1; ++i) { + YGNODE_STYLE_SET_DIMENSION_WITH_EDGE(_yogaNode, Position, dimensionForEdgeWithEdgeInsets(edge, position), edge); + edge = (YGEdge)(edge + 1); + } + } + else if (propertyName == ASYogaMarginProperty) { + ASEdgeInsets margin = self.margin; + YGEdge edge = YGEdgeLeft; + for (int i = 0; i < YGEdgeAll + 1; ++i) { + YGNODE_STYLE_SET_DIMENSION_WITH_EDGE(_yogaNode, Margin, dimensionForEdgeWithEdgeInsets(edge, margin), edge); + edge = (YGEdge)(edge + 1); + } + } + else if (propertyName == ASYogaPaddingProperty) { + ASEdgeInsets padding = self.padding; + YGEdge edge = YGEdgeLeft; + for (int i = 0; i < YGEdgeAll + 1; ++i) { + YGNODE_STYLE_SET_DIMENSION_WITH_EDGE(_yogaNode, Padding, dimensionForEdgeWithEdgeInsets(edge, padding), edge); + edge = (YGEdge)(edge + 1); + } + } + else if (propertyName == ASYogaBorderProperty) { + ASEdgeInsets border = self.border; + YGEdge edge = YGEdgeLeft; + for (int i = 0; i < YGEdgeAll + 1; ++i) { + YGNODE_STYLE_SET_FLOAT_WITH_EDGE(_yogaNode, Border, dimensionForEdgeWithEdgeInsets(edge, border), edge); + edge = (YGEdge)(edge + 1); + } + } + else if (propertyName == ASYogaAspectRatioProperty) { + CGFloat aspectRatio = self.aspectRatio; + if (aspectRatio > FLT_EPSILON && aspectRatio < CGFLOAT_MAX / 2.0) { + YGNodeStyleSetAspectRatio(_yogaNode, aspectRatio); + } + } +#endif +} + #pragma mark - Yoga Flexbox Properties #if YOGA -- (ASStackLayoutDirection)direction { return _direction.load(); } -- (CGFloat)spacing { return _spacing.load(); } ++ (void)initialize +{ + [super initialize]; + YGConfigSetPointScaleFactor(YGConfigGetDefault(), ASScreenScale()); + // Yoga recommends using Web Defaults for all new projects. This will be enabled for Texture very soon. + //YGConfigSetUseWebDefaults(YGConfigGetDefault(), true); +} + +- (YGNodeRef)yogaNode +{ + return _yogaNode; +} + +- (YGNodeRef)yogaNodeCreateIfNeeded +{ + if (_yogaNode == NULL) { + _yogaNode = YGNodeNew(); + } + return _yogaNode; +} + +- (void)destroyYogaNode +{ + if (_yogaNode != NULL) { + // Release the __bridge_retained Context object. + ASLayoutElementYogaUpdateMeasureFunc(_yogaNode, nil); + YGNodeFree(_yogaNode); + _yogaNode = NULL; + } +} + +- (void)dealloc +{ + [self destroyYogaNode]; +} + +- (YGWrap)flexWrap { return _flexWrap.load(); } +- (ASStackLayoutDirection)flexDirection { return _flexDirection.load(); } +- (YGDirection)direction { return _direction.load(); } - (ASStackLayoutJustifyContent)justifyContent { return _justifyContent.load(); } - (ASStackLayoutAlignItems)alignItems { return _alignItems.load(); } - (YGPositionType)positionType { return _positionType.load(); } @@ -604,21 +756,53 @@ do {\ - (ASEdgeInsets)padding { return _padding.load(); } - (ASEdgeInsets)border { return _border.load(); } - (CGFloat)aspectRatio { return _aspectRatio.load(); } -- (YGWrap)flexWrap { return _flexWrap.load(); } -- (void)setDirection:(ASStackLayoutDirection)direction { _direction.store(direction); } -- (void)setSpacing:(CGFloat)spacing { _spacing.store(spacing); } -- (void)setJustifyContent:(ASStackLayoutJustifyContent)justify { _justifyContent.store(justify); } -- (void)setAlignItems:(ASStackLayoutAlignItems)alignItems { _alignItems.store(alignItems); } -- (void)setPositionType:(YGPositionType)positionType { _positionType.store(positionType); } -- (void)setPosition:(ASEdgeInsets)position { _position.store(position); } -- (void)setMargin:(ASEdgeInsets)margin { _margin.store(margin); } -- (void)setPadding:(ASEdgeInsets)padding { _padding.store(padding); } -- (void)setBorder:(ASEdgeInsets)border { _border.store(border); } -- (void)setAspectRatio:(CGFloat)aspectRatio { _aspectRatio.store(aspectRatio); } -- (void)setFlexWrap:(YGWrap)flexWrap { _flexWrap.store(flexWrap); } +- (void)setFlexWrap:(YGWrap)flexWrap { + _flexWrap.store(flexWrap); + ASLayoutElementStyleCallDelegate(ASYogaFlexWrapProperty); +} +- (void)setFlexDirection:(ASStackLayoutDirection)flexDirection { + _flexDirection.store(flexDirection); + ASLayoutElementStyleCallDelegate(ASYogaFlexDirectionProperty); +} +- (void)setDirection:(YGDirection)direction { + _direction.store(direction); + ASLayoutElementStyleCallDelegate(ASYogaDirectionProperty); +} +- (void)setJustifyContent:(ASStackLayoutJustifyContent)justify { + _justifyContent.store(justify); + ASLayoutElementStyleCallDelegate(ASYogaJustifyContentProperty); +} +- (void)setAlignItems:(ASStackLayoutAlignItems)alignItems { + _alignItems.store(alignItems); + ASLayoutElementStyleCallDelegate(ASYogaAlignItemsProperty); +} +- (void)setPositionType:(YGPositionType)positionType { + _positionType.store(positionType); + ASLayoutElementStyleCallDelegate(ASYogaPositionTypeProperty); +} +- (void)setPosition:(ASEdgeInsets)position { + _position.store(position); + ASLayoutElementStyleCallDelegate(ASYogaPositionProperty); +} +- (void)setMargin:(ASEdgeInsets)margin { + _margin.store(margin); + ASLayoutElementStyleCallDelegate(ASYogaMarginProperty); +} +- (void)setPadding:(ASEdgeInsets)padding { + _padding.store(padding); + ASLayoutElementStyleCallDelegate(ASYogaPaddingProperty); +} +- (void)setBorder:(ASEdgeInsets)border { + _border.store(border); + ASLayoutElementStyleCallDelegate(ASYogaBorderProperty); +} +- (void)setAspectRatio:(CGFloat)aspectRatio { + _aspectRatio.store(aspectRatio); + ASLayoutElementStyleCallDelegate(ASYogaAspectRatioProperty); +} -#endif +#endif /* YOGA */ #pragma mark Deprecated diff --git a/Source/Layout/ASLayoutElementExtensibility.h b/Source/Layout/ASLayoutElementExtensibility.h index 9121530851..1afddd9c91 100644 --- a/Source/Layout/ASLayoutElementExtensibility.h +++ b/Source/Layout/ASLayoutElementExtensibility.h @@ -1,20 +1,21 @@ // // ASLayoutElementExtensibility.h -// AsyncDisplayKit -// -// Created by Michael Schneider on 3/29/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // -#if AS_TARGET_OS_IOS #import -#else -#import -#endif #import diff --git a/Source/Layout/ASLayoutElementPrivate.h b/Source/Layout/ASLayoutElementPrivate.h index cd227d412d..68c46de611 100644 --- a/Source/Layout/ASLayoutElementPrivate.h +++ b/Source/Layout/ASLayoutElementPrivate.h @@ -1,65 +1,66 @@ // // ASLayoutElementPrivate.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // -#import #import - -#if AS_TARGET_OS_IOS #import -#else -#import -#endif @protocol ASLayoutElement; @class ASLayoutElementStyle; #pragma mark - ASLayoutElementContext -struct ASLayoutElementContext { - int32_t transitionID; -}; +NS_ASSUME_NONNULL_BEGIN + +AS_SUBCLASSING_RESTRICTED +@interface ASLayoutElementContext : NSObject +@property (nonatomic) int32_t transitionID; +@end extern int32_t const ASLayoutElementContextInvalidTransitionID; extern int32_t const ASLayoutElementContextDefaultTransitionID; -extern struct ASLayoutElementContext const ASLayoutElementContextNull; +// Does not currently support nesting – there must be no current context. +extern void ASLayoutElementPushContext(ASLayoutElementContext * context); -extern BOOL ASLayoutElementContextIsNull(struct ASLayoutElementContext context); +extern ASLayoutElementContext * _Nullable ASLayoutElementGetCurrentContext(); -extern struct ASLayoutElementContext ASLayoutElementContextMake(int32_t transitionID); - -extern void ASLayoutElementSetCurrentContext(struct ASLayoutElementContext context); - -extern struct ASLayoutElementContext ASLayoutElementGetCurrentContext(); - -extern void ASLayoutElementClearCurrentContext(); +extern void ASLayoutElementPopContext(); +NS_ASSUME_NONNULL_END #pragma mark - ASLayoutElementLayoutDefaults #define ASLayoutElementLayoutCalculationDefaults \ - (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize\ {\ - _Pragma("clang diagnostic push")\ - _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"")\ - /* For now we just call the deprecated measureWithSizeRange: method to not break old API */ \ - return [self measureWithSizeRange:constrainedSize]; \ - _Pragma("clang diagnostic pop")\ -} \ + return [self layoutThatFits:constrainedSize parentSize:constrainedSize.max];\ +}\ \ - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize\ {\ return [self layoutThatFits:constrainedSize parentSize:constrainedSize.max];\ }\ \ +- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize parentSize:(CGSize)parentSize\ +{\ + return [self calculateLayoutThatFits:constrainedSize restrictedToSize:self.style.size relativeToParentSize:parentSize];\ +}\ +\ - (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize\ restrictedToSize:(ASLayoutElementSize)size\ relativeToParentSize:(CGSize)parentSize\ @@ -69,46 +70,6 @@ extern void ASLayoutElementClearCurrentContext(); }\ -#pragma mark - ASLayoutElementFinalLayoutElement -/** - * The base protocol for ASLayoutElementFinalLayoutElement. Generally the methods/properties in this class do not need to be - * called by the end user and are only called internally. However, there may be a case where the methods are useful. - */ -@protocol ASLayoutElementFinalLayoutElement - -/** - * @abstract This method can be used to give the user a chance to wrap an ASLayoutElement in an ASLayoutSpec - * just before it is added to a parent ASLayoutSpec. For example, if you wanted an ASTextNode that was always - * inside of an ASInsetLayoutSpec, you could subclass ASTextNode and implement finalLayoutElement so that it wraps - * itself in an inset spec. - * - * Note that any ASLayoutElement other than self that is returned MUST set isFinalLayoutElement to YES. Make sure - * to do this BEFORE adding a child to the ASLayoutElement. - * - * @return The layoutElement that will be added to the parent layout spec. Defaults to self. - */ -- (id)finalLayoutElement; - -/** - * A flag to indicate that this ASLayoutElement was created in finalLayoutElement. This MUST be set to YES - * before adding a child to this layoutElement. - */ -@property (nonatomic, assign) BOOL isFinalLayoutElement; - -@end - -// Default implementation for ASLayoutElementPrivate that can be used in classes that comply to ASLayoutElementPrivate - -#define ASLayoutElementFinalLayoutElementDefault \ -\ -@synthesize isFinalLayoutElement = _isFinalLayoutElement;\ -\ -- (id)finalLayoutElement\ -{\ - return self;\ -}\ - - #pragma mark - ASLayoutElementExtensibility // Provides extension points for elments that comply to ASLayoutElement like ASLayoutSpec to add additional diff --git a/Source/Layout/ASLayoutSpec+Subclasses.h b/Source/Layout/ASLayoutSpec+Subclasses.h index ae4549966c..21c97cb8b6 100644 --- a/Source/Layout/ASLayoutSpec+Subclasses.h +++ b/Source/Layout/ASLayoutSpec+Subclasses.h @@ -1,9 +1,18 @@ // // ASLayoutSpec+Subclasses.h -// AsyncDisplayKit +// Texture // -// Created by Michael Schneider on 9/15/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 /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -16,27 +25,6 @@ NS_ASSUME_NONNULL_BEGIN @interface ASLayoutSpec (Subclassing) -/** - * Helper method for finalLayoutElement support - * - * @warning If you are getting recursion crashes here after implementing finalLayoutElement, make sure - * that you are setting isFinalLayoutElement flag to YES. This must be one BEFORE adding a child - * to the new ASLayoutElement. - * - * For example: - * - (id)finalLayoutElement - * { - * ASInsetLayoutSpec *insetSpec = [[ASInsetLayoutSpec alloc] init]; - * insetSpec.insets = UIEdgeInsetsMake(10,10,10,10); - * insetSpec.isFinalLayoutElement = YES; - * [insetSpec setChild:self]; - * return insetSpec; - * } - * - * @see finalLayoutElement - */ -- (id)layoutElementToAddFromLayoutElement:(id)child; - /** * Adds a child with the given identifier to this layout spec. * diff --git a/Source/Layout/ASLayoutSpec+Subclasses.mm b/Source/Layout/ASLayoutSpec+Subclasses.mm index a743364ee5..561d94e844 100644 --- a/Source/Layout/ASLayoutSpec+Subclasses.mm +++ b/Source/Layout/ASLayoutSpec+Subclasses.mm @@ -1,9 +1,18 @@ // -// ASLayoutSpec+Subclasses.m -// AsyncDisplayKit +// ASLayoutSpec+Subclasses.mm +// Texture // -// Created by Michael Schneider on 9/15/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 /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -47,29 +56,13 @@ @implementation ASLayoutSpec (Subclassing) -#pragma mark - Final layoutElement - -- (id)layoutElementToAddFromLayoutElement:(id)child -{ - if (self.isFinalLayoutElement == NO) { - id finalLayoutElement = [child finalLayoutElement]; - if (finalLayoutElement != child) { -#if AS_TARGET_OS_IOS - finalLayoutElement.primitiveTraitCollection = child.primitiveTraitCollection; -#endif - return finalLayoutElement; - } - } - return child; -} - #pragma mark - Child with index - (void)setChild:(id)child atIndex:(NSUInteger)index { ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); - id layoutElement = child ? [self layoutElementToAddFromLayoutElement:child] : [ASNullLayoutSpec null]; + id layoutElement = child ?: [ASNullLayoutSpec null]; if (child) { if (_childrenArray.count < index) { diff --git a/Source/Layout/ASLayoutSpec.h b/Source/Layout/ASLayoutSpec.h index 4a28709d56..e4d9d42db1 100644 --- a/Source/Layout/ASLayoutSpec.h +++ b/Source/Layout/ASLayoutSpec.h @@ -1,11 +1,18 @@ // // ASLayoutSpec.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -17,7 +24,7 @@ NS_ASSUME_NONNULL_BEGIN /** * A layout spec is an immutable object that describes a layout, loosely inspired by React. */ -@interface ASLayoutSpec : NSObject +@interface ASLayoutSpec : NSObject /** * Creation of a layout spec should only happen by a user in layoutSpecThatFits:. During that method, a @@ -88,7 +95,7 @@ NS_ASSUME_NONNULL_BEGIN @end -@interface ASLayoutSpec (Debugging) +@interface ASLayoutSpec (Debugging) /** * Used by other layout specs to create ascii art debug strings */ diff --git a/Source/Layout/ASLayoutSpec.mm b/Source/Layout/ASLayoutSpec.mm index 8f81f288e4..6600f50867 100644 --- a/Source/Layout/ASLayoutSpec.mm +++ b/Source/Layout/ASLayoutSpec.mm @@ -1,11 +1,18 @@ // // ASLayoutSpec.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -16,7 +23,6 @@ #import #import #import -#import #import #import @@ -49,9 +55,7 @@ } _isMutable = YES; -#if AS_TARGET_OS_IOS _primitiveTraitCollection = ASPrimitiveTraitCollectionMakeDefault(); -#endif _childrenArray = [[NSMutableArray alloc] init]; return self; @@ -67,9 +71,10 @@ return YES; } -#pragma mark - Final LayoutElement - -ASLayoutElementFinalLayoutElementDefault +- (BOOL)implementsLayoutMethod +{ + return YES; +} #pragma mark - Style @@ -90,23 +95,7 @@ ASLayoutElementFinalLayoutElementDefault #pragma mark - Layout -- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize -{ - return [self layoutThatFits:constrainedSize parentSize:constrainedSize.max]; -} - -- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize parentSize:(CGSize)parentSize -{ - return [self calculateLayoutThatFits:constrainedSize restrictedToSize:self.style.size relativeToParentSize:parentSize]; -} - -- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize - restrictedToSize:(ASLayoutElementSize)size - relativeToParentSize:(CGSize)parentSize -{ - const ASSizeRange resolvedRange = ASSizeRangeIntersect(constrainedSize, ASLayoutElementSizeResolve(self.style.size, parentSize)); - return [self calculateLayoutThatFits:resolvedRange]; -} +ASLayoutElementLayoutCalculationDefaults - (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize { @@ -121,10 +110,7 @@ ASLayoutElementFinalLayoutElementDefault ASDisplayNodeAssert(_childrenArray.count < 2, @"This layout spec does not support more than one child. Use the setChildren: or the setChild:AtIndex: API"); if (child) { - id finalLayoutElement = [self layoutElementToAddFromLayoutElement:child]; - if (finalLayoutElement) { - _childrenArray[0] = finalLayoutElement; - } + _childrenArray[0] = child; } else { if (_childrenArray.count) { [_childrenArray removeObjectAtIndex:0]; @@ -150,7 +136,7 @@ ASLayoutElementFinalLayoutElementDefault NSUInteger i = 0; for (id child in children) { ASDisplayNodeAssert([child conformsToProtocol:NSProtocolFromString(@"ASLayoutElement")], @"Child %@ of spec %@ is not an ASLayoutElement!", child, self); - _childrenArray[i] = [self layoutElementToAddFromLayoutElement:child]; + _childrenArray[i] = child; i += 1; } } @@ -174,41 +160,45 @@ ASLayoutElementFinalLayoutElementDefault #pragma mark - ASTraitEnvironment -#if AS_TARGET_OS_IOS - -- (ASPrimitiveTraitCollection)primitiveTraitCollection -{ - return _primitiveTraitCollection; -} - -- (void)setPrimitiveTraitCollection:(ASPrimitiveTraitCollection)traitCollection -{ - _primitiveTraitCollection = traitCollection; -} - - (ASTraitCollection *)asyncTraitCollection { ASDN::MutexLocker l(__instanceLock__); return [ASTraitCollection traitCollectionWithASPrimitiveTraitCollection:self.primitiveTraitCollection]; } -#endif - -#if AS_TARGET_OS_IOS +ASPrimitiveTraitCollectionDefaults ASPrimitiveTraitCollectionDeprecatedImplementation -#endif #pragma mark - ASLayoutElementStyleExtensibility ASLayoutElementStyleExtensibilityForwarding +#pragma mark - ASDescriptionProvider + +- (NSMutableArray *)propertiesForDescription +{ + auto result = [NSMutableArray array]; + if (NSArray *children = self.children) { + // Use tiny descriptions because these trees can get nested very deep. + auto tinyDescriptions = ASArrayByFlatMapping(children, id object, ASObjectDescriptionMakeTiny(object)); + [result addObject:@{ @"children": tinyDescriptions }]; + } + return result; +} + +- (NSString *)description +{ + return ASObjectDescriptionMake(self, [self propertiesForDescription]); +} + #pragma mark - Framework Private -- (nullable NSSet> *)findDuplicatedElementsInSubtree +#if AS_DEDUPE_LAYOUT_SPEC_TREE +- (nullable NSHashTable> *)findDuplicatedElementsInSubtree { - NSMutableSet *result = nil; + NSHashTable *result = nil; NSUInteger count = 0; - [self _findDuplicatedElementsInSubtreeWithWorkingSet:[[NSMutableSet alloc] init] workingCount:&count result:&result]; + [self _findDuplicatedElementsInSubtreeWithWorkingSet:[NSHashTable hashTableWithOptions:NSHashTableObjectPointerPersonality] workingCount:&count result:&result]; return result; } @@ -219,7 +209,7 @@ ASLayoutElementStyleExtensibilityForwarding * @param workingCount The current count of the set for use in the recursion. * @param result The set into which to put the result. This initially points to @c nil to save time if no duplicates exist. */ -- (void)_findDuplicatedElementsInSubtreeWithWorkingSet:(NSMutableSet> *)workingSet workingCount:(NSUInteger *)workingCount result:(NSMutableSet> * _Nullable *)result +- (void)_findDuplicatedElementsInSubtreeWithWorkingSet:(NSHashTable> *)workingSet workingCount:(NSUInteger *)workingCount result:(NSHashTable> * _Nullable *)result { Class layoutSpecClass = [ASLayoutSpec class]; @@ -234,7 +224,7 @@ ASLayoutElementStyleExtensibilityForwarding BOOL objectAlreadyExisted = (newCount != oldCount + 1); if (objectAlreadyExisted) { if (*result == nil) { - *result = [[NSMutableSet alloc] init]; + *result = [NSHashTable hashTableWithOptions:NSHashTableObjectPointerPersonality]; } [*result addObject:child]; } else { @@ -246,6 +236,7 @@ ASLayoutElementStyleExtensibilityForwarding } } } +#endif #pragma mark - Debugging @@ -263,11 +254,21 @@ ASLayoutElementStyleExtensibilityForwarding } } -#pragma mark - Deprecated +#pragma mark - ASLayoutElementAsciiArtProtocol -- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize +- (NSString *)asciiArtString { - return [self layoutThatFits:constrainedSize]; + NSArray *children = self.children.count < 2 && self.child ? @[self.child] : self.children; + return [ASLayoutSpec asciiArtStringForChildren:children parentName:[self asciiArtName]]; +} + +- (NSString *)asciiArtName +{ + NSMutableString *result = [NSMutableString stringWithCString:object_getClassName(self) encoding:NSASCIIStringEncoding]; + if (_debugName) { + [result appendFormat:@" (%@)", _debugName]; + } + return result; } @end @@ -329,7 +330,7 @@ ASLayoutElementStyleExtensibilityForwarding @implementation ASLayoutSpec (Debugging) -#pragma mark - ASLayoutElementAsciiArtProtocol +#pragma mark - ASCII Art Helpers + (NSString *)asciiArtStringForChildren:(NSArray *)children parentName:(NSString *)parentName direction:(ASStackLayoutDirection)direction { @@ -351,21 +352,6 @@ ASLayoutElementStyleExtensibilityForwarding return [self asciiArtStringForChildren:children parentName:parentName direction:ASStackLayoutDirectionHorizontal]; } -- (NSString *)asciiArtString -{ - NSArray *children = self.children.count < 2 && self.child ? @[self.child] : self.children; - return [ASLayoutSpec asciiArtStringForChildren:children parentName:[self asciiArtName]]; -} - -- (NSString *)asciiArtName -{ - NSString *string = NSStringFromClass([self class]); - if (_debugName) { - string = [string stringByAppendingString:[NSString stringWithFormat:@" (debugName = %@)",_debugName]]; - } - return string; -} - @end #pragma mark - ASLayoutSpec (Deprecated) diff --git a/Source/Layout/ASOverlayLayoutSpec.h b/Source/Layout/ASOverlayLayoutSpec.h index 8cf4a4647f..54324f4ed3 100644 --- a/Source/Layout/ASOverlayLayoutSpec.h +++ b/Source/Layout/ASOverlayLayoutSpec.h @@ -1,11 +1,18 @@ // // ASOverlayLayoutSpec.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Layout/ASOverlayLayoutSpec.mm b/Source/Layout/ASOverlayLayoutSpec.mm index 988ddc820c..9ac5d1e567 100644 --- a/Source/Layout/ASOverlayLayoutSpec.mm +++ b/Source/Layout/ASOverlayLayoutSpec.mm @@ -1,11 +1,18 @@ // // ASOverlayLayoutSpec.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Layout/ASRatioLayoutSpec.h b/Source/Layout/ASRatioLayoutSpec.h index 5c59258e79..f5338d4dac 100644 --- a/Source/Layout/ASRatioLayoutSpec.h +++ b/Source/Layout/ASRatioLayoutSpec.h @@ -1,11 +1,18 @@ // // ASRatioLayoutSpec.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Layout/ASRatioLayoutSpec.mm b/Source/Layout/ASRatioLayoutSpec.mm index c0ba6ff329..294d11064e 100644 --- a/Source/Layout/ASRatioLayoutSpec.mm +++ b/Source/Layout/ASRatioLayoutSpec.mm @@ -1,11 +1,18 @@ // // ASRatioLayoutSpec.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Layout/ASRelativeLayoutSpec.h b/Source/Layout/ASRelativeLayoutSpec.h index d91e603457..27860b00ee 100644 --- a/Source/Layout/ASRelativeLayoutSpec.h +++ b/Source/Layout/ASRelativeLayoutSpec.h @@ -1,13 +1,18 @@ // // ASRelativeLayoutSpec.h -// AsyncDisplayKit -// -// Created by Samuel Stow on 12/31/15. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Layout/ASRelativeLayoutSpec.mm b/Source/Layout/ASRelativeLayoutSpec.mm index 26cfe2aa11..ea5f37df52 100644 --- a/Source/Layout/ASRelativeLayoutSpec.mm +++ b/Source/Layout/ASRelativeLayoutSpec.mm @@ -1,13 +1,18 @@ // // ASRelativeLayoutSpec.mm -// AsyncDisplayKit -// -// Created by Samuel Stow on 12/31/15. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Layout/ASStackLayoutDefines.h b/Source/Layout/ASStackLayoutDefines.h index 79281d73dc..8490a5389a 100644 --- a/Source/Layout/ASStackLayoutDefines.h +++ b/Source/Layout/ASStackLayoutDefines.h @@ -1,11 +1,18 @@ // // ASStackLayoutDefines.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -86,13 +93,13 @@ typedef NS_ENUM(NSUInteger, ASStackLayoutAlignSelf) { ASStackLayoutAlignSelfStretch, }; -// TODO documentation +/** Whether children are stacked into a single or multiple lines. */ typedef NS_ENUM(NSUInteger, ASStackLayoutFlexWrap) { ASStackLayoutFlexWrapNoWrap, ASStackLayoutFlexWrapWrap, }; -// TODO documentation +/** Orientation of lines along cross axis if there are multiple lines. */ typedef NS_ENUM(NSUInteger, ASStackLayoutAlignContent) { ASStackLayoutAlignContentStart, ASStackLayoutAlignContentCenter, diff --git a/Source/Layout/ASStackLayoutElement.h b/Source/Layout/ASStackLayoutElement.h index e635707df1..164817f4d0 100644 --- a/Source/Layout/ASStackLayoutElement.h +++ b/Source/Layout/ASStackLayoutElement.h @@ -1,11 +1,18 @@ // // ASStackLayoutElement.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Layout/ASStackLayoutSpec.h b/Source/Layout/ASStackLayoutSpec.h index a87673b8f0..3ac682dc64 100644 --- a/Source/Layout/ASStackLayoutSpec.h +++ b/Source/Layout/ASStackLayoutSpec.h @@ -1,11 +1,18 @@ // // ASStackLayoutSpec.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -63,7 +70,6 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, assign) ASStackLayoutFlexWrap flexWrap; /** Orientation of lines along cross axis if there are multiple lines. Defaults to ASStackLayoutAlignContentStart */ @property (nonatomic, assign) ASStackLayoutAlignContent alignContent; - /** Whether this stack can dispatch to other threads, regardless of which thread it's running on */ @property (nonatomic, assign, getter=isConcurrent) BOOL concurrent; diff --git a/Source/Layout/ASStackLayoutSpec.mm b/Source/Layout/ASStackLayoutSpec.mm index 30eea4950b..4a3de0c707 100644 --- a/Source/Layout/ASStackLayoutSpec.mm +++ b/Source/Layout/ASStackLayoutSpec.mm @@ -1,11 +1,18 @@ // // ASStackLayoutSpec.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -18,6 +25,7 @@ #import #import #import +#import #import #import @@ -127,6 +135,8 @@ return [ASLayout layoutWithLayoutElement:self size:constrainedSize.min]; } + as_activity_scope_verbose(as_activity_create("Calculate stack layout", AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT)); + as_log_verbose(ASLayoutLog(), "Stack layout %@", self); // Accessing the style and size property is pretty costly we create layout spec children we use to figure // out the layout for each child const auto stackChildren = AS::map(children, [&](const id child) -> ASStackLayoutSpecChild { @@ -170,6 +180,23 @@ } } +- (NSMutableArray *)propertiesForDescription +{ + auto result = [super propertiesForDescription]; + + // Add our direction + switch (self.direction) { + case ASStackLayoutDirectionVertical: + [result insertObject:@{ (id)kCFNull: @"vertical" } atIndex:0]; + break; + case ASStackLayoutDirectionHorizontal: + [result insertObject:@{ (id)kCFNull: @"horizontal" } atIndex:0]; + break; + } + + return result; +} + @end @implementation ASStackLayoutSpec (Debugging) diff --git a/Source/Layout/ASYogaLayoutSpec.h b/Source/Layout/ASYogaLayoutSpec.h new file mode 100644 index 0000000000..f2e13a67c0 --- /dev/null +++ b/Source/Layout/ASYogaLayoutSpec.h @@ -0,0 +1,24 @@ +// +// ASYogaLayoutSpec.h +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#if YOGA /* YOGA */ + +#import +#import + +@interface ASYogaLayoutSpec : ASLayoutSpec +@property (nonatomic, strong, nonnull) ASDisplayNode *rootNode; +@end + +#endif /* YOGA */ diff --git a/Source/Layout/ASYogaLayoutSpec.mm b/Source/Layout/ASYogaLayoutSpec.mm new file mode 100644 index 0000000000..e0cc80e512 --- /dev/null +++ b/Source/Layout/ASYogaLayoutSpec.mm @@ -0,0 +1,177 @@ +// +// ASYogaLayoutSpec.mm +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#if YOGA /* YOGA */ + +#import +#import +#import +#import +#import + +#define YOGA_LAYOUT_LOGGING 0 + +@implementation ASYogaLayoutSpec + +- (ASLayout *)layoutForYogaNode:(YGNodeRef)yogaNode +{ + BOOL isRootNode = (YGNodeGetParent(yogaNode) == NULL); + uint32_t childCount = YGNodeGetChildCount(yogaNode); + + NSMutableArray *sublayouts = [NSMutableArray arrayWithCapacity:childCount]; + for (uint32_t i = 0; i < childCount; i++) { + [sublayouts addObject:[self layoutForYogaNode:YGNodeGetChild(yogaNode, i)]]; + } + + id layoutElement = (__bridge id )YGNodeGetContext(yogaNode); + CGSize size = CGSizeMake(YGNodeLayoutGetWidth(yogaNode), YGNodeLayoutGetHeight(yogaNode)); + + if (isRootNode) { + // The layout for root should have position CGPointNull, but include the calculated size. + return [ASLayout layoutWithLayoutElement:layoutElement size:size sublayouts:sublayouts]; + } else { + CGPoint position = CGPointMake(YGNodeLayoutGetLeft(yogaNode), YGNodeLayoutGetTop(yogaNode)); + return [ASLayout layoutWithLayoutElement:layoutElement size:size position:position sublayouts:nil]; + } +} + +- (void)destroyYogaNode:(YGNodeRef)yogaNode +{ + // Release the __bridge_retained Context object. + __unused id element = (__bridge_transfer id)YGNodeGetContext(yogaNode); + YGNodeFree(yogaNode); +} + +- (void)setupYogaNode:(YGNodeRef)yogaNode forElement:(id )element withParentYogaNode:(YGNodeRef)parentYogaNode +{ + ASLayoutElementStyle *style = element.style; + + // Retain the Context object. This must be explicitly released with a __bridge_transfer; YGNodeFree() is not sufficient. + YGNodeSetContext(yogaNode, (__bridge_retained void *)element); + + YGNodeStyleSetDirection (yogaNode, style.direction); + + YGNodeStyleSetFlexWrap (yogaNode, style.flexWrap); + YGNodeStyleSetFlexGrow (yogaNode, style.flexGrow); + YGNodeStyleSetFlexShrink (yogaNode, style.flexShrink); + YGNODE_STYLE_SET_DIMENSION (yogaNode, FlexBasis, style.flexBasis); + + YGNodeStyleSetFlexDirection (yogaNode, yogaFlexDirection(style.flexDirection)); + 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; + ASEdgeInsets border = style.border; + + YGEdge edge = YGEdgeLeft; + for (int i = 0; i < YGEdgeAll + 1; ++i) { + YGNODE_STYLE_SET_DIMENSION_WITH_EDGE(yogaNode, Position, dimensionForEdgeWithEdgeInsets(edge, position), edge); + YGNODE_STYLE_SET_DIMENSION_WITH_EDGE(yogaNode, Margin, dimensionForEdgeWithEdgeInsets(edge, margin), edge); + YGNODE_STYLE_SET_DIMENSION_WITH_EDGE(yogaNode, Padding, dimensionForEdgeWithEdgeInsets(edge, padding), edge); + YGNODE_STYLE_SET_FLOAT_WITH_EDGE(yogaNode, Border, dimensionForEdgeWithEdgeInsets(edge, border), edge); + edge = (YGEdge)(edge + 1); + } + + CGFloat aspectRatio = style.aspectRatio; + if (aspectRatio > FLT_EPSILON && aspectRatio < CGFLOAT_MAX / 2.0) { + YGNodeStyleSetAspectRatio(yogaNode, aspectRatio); + } + + // For the root node, we use rootConstrainedSize above. For children, consult the style for their size. + if (parentYogaNode != NULL) { + YGNodeInsertChild(parentYogaNode, yogaNode, YGNodeGetChildCount(parentYogaNode)); + + YGNODE_STYLE_SET_DIMENSION(yogaNode, Width, style.width); + YGNODE_STYLE_SET_DIMENSION(yogaNode, Height, style.height); + + YGNODE_STYLE_SET_DIMENSION(yogaNode, MinWidth, style.minWidth); + YGNODE_STYLE_SET_DIMENSION(yogaNode, MinHeight, style.minHeight); + + YGNODE_STYLE_SET_DIMENSION(yogaNode, MaxWidth, style.maxWidth); + YGNODE_STYLE_SET_DIMENSION(yogaNode, MaxHeight, style.maxHeight); + + YGNodeSetMeasureFunc(yogaNode, &ASLayoutElementYogaMeasureFunc); + } + + // TODO(appleguy): STYLE SETTER METHODS LEFT TO IMPLEMENT: YGNodeStyleSetOverflow, YGNodeStyleSetFlex +} + +- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize + restrictedToSize:(ASLayoutElementSize)layoutElementSize + relativeToParentSize:(CGSize)parentSize +{ + ASSizeRange styleAndParentSize = ASLayoutElementSizeResolve(layoutElementSize, parentSize); + const ASSizeRange rootConstrainedSize = ASSizeRangeIntersect(constrainedSize, styleAndParentSize); + + YGNodeRef rootYogaNode = YGNodeNew(); + + // YGNodeCalculateLayout currently doesn't offer the ability to pass a minimum size (max is passed there). + // Apply the constrainedSize.min directly to the root node so that layout accounts for it. + YGNodeStyleSetMinWidth (rootYogaNode, yogaFloatForCGFloat(rootConstrainedSize.min.width)); + YGNodeStyleSetMinHeight(rootYogaNode, yogaFloatForCGFloat(rootConstrainedSize.min.height)); + + // It's crucial to set these values. YGNodeCalculateLayout has unusual behavior for its width and height parameters: + // 1. If no maximum size set, infer this means YGMeasureModeExactly. Even if a small minWidth & minHeight are set, + // these will never be used because the output size of the root will always exactly match this value. + // 2. If a maximum size is set, infer that this means YGMeasureModeAtMost, and allow down to the min* values in output. + YGNodeStyleSetMaxWidthPercent(rootYogaNode, 100.0); + YGNodeStyleSetMaxHeightPercent(rootYogaNode, 100.0); + + [self setupYogaNode:rootYogaNode forElement:self.rootNode withParentYogaNode:NULL]; + for (id child in self.children) { + YGNodeRef yogaNode = YGNodeNew(); + [self setupYogaNode:yogaNode forElement:child withParentYogaNode:rootYogaNode]; + } + + // It is crucial to use yogaFloat... to convert CGFLOAT_MAX into YGUndefined here. + YGNodeCalculateLayout(rootYogaNode, + yogaFloatForCGFloat(rootConstrainedSize.max.width), + yogaFloatForCGFloat(rootConstrainedSize.max.height), + YGDirectionInherit); + + ASLayout *layout = [self layoutForYogaNode:rootYogaNode]; + +#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(@"****************************************************************************"); + NSLog(@"node = %@", self.rootNode); + NSLog(@"style = %@", self.rootNode.style); + YGNodePrint(rootYogaNode, (YGPrintOptions)(YGPrintOptionsStyle | YGPrintOptionsLayout)); + } + NSLog(@"rootConstraint = (%@, %@), layout = %@, sublayouts = %@", NSStringFromCGSize(rootConstrainedSize.min), NSStringFromCGSize(rootConstrainedSize.max), layout, layout.sublayouts); +#endif + + while(YGNodeGetChildCount(rootYogaNode) > 0) { + YGNodeRef yogaNode = YGNodeGetChild(rootYogaNode, 0); + [self destroyYogaNode:yogaNode]; + } + [self destroyYogaNode:rootYogaNode]; + + return layout; +} + +@end + +#endif /* YOGA */ diff --git a/Source/Layout/ASYogaUtilities.h b/Source/Layout/ASYogaUtilities.h new file mode 100644 index 0000000000..2986c0195f --- /dev/null +++ b/Source/Layout/ASYogaUtilities.h @@ -0,0 +1,82 @@ +// +// ASYogaUtilities.h +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#if YOGA /* YOGA */ + +#import +#import + +#define ASYogaLog(...) //NSLog(__VA_ARGS__) + +@interface ASDisplayNode (YogaHelpers) + ++ (ASDisplayNode *)yogaNode; ++ (ASDisplayNode *)yogaSpacerNode; ++ (ASDisplayNode *)yogaVerticalStack; ++ (ASDisplayNode *)yogaHorizontalStack; + +@end + +extern void ASDisplayNodePerformBlockOnEveryYogaChild(ASDisplayNode *node, void(^block)(ASDisplayNode *node)); + +ASDISPLAYNODE_EXTERN_C_BEGIN + +#pragma mark - Yoga Type Conversion Helpers + +YGAlign yogaAlignItems(ASStackLayoutAlignItems alignItems); +YGJustify yogaJustifyContent(ASStackLayoutJustifyContent justifyContent); +YGAlign yogaAlignSelf(ASStackLayoutAlignSelf alignSelf); +YGFlexDirection yogaFlexDirection(ASStackLayoutDirection direction); +float yogaFloatForCGFloat(CGFloat value); +float yogaDimensionToPoints(ASDimension dimension); +float yogaDimensionToPercent(ASDimension dimension); +ASDimension dimensionForEdgeWithEdgeInsets(YGEdge edge, ASEdgeInsets insets); + +void ASLayoutElementYogaUpdateMeasureFunc(YGNodeRef yogaNode, id layoutElement); +YGSize ASLayoutElementYogaMeasureFunc(YGNodeRef yogaNode, + float width, YGMeasureMode widthMode, + float height, YGMeasureMode heightMode); + +#pragma mark - Yoga Style Setter Helpers + +#define YGNODE_STYLE_SET_DIMENSION(yogaNode, property, dimension) \ + if (dimension.unit == ASDimensionUnitPoints) { \ + YGNodeStyleSet##property(yogaNode, yogaDimensionToPoints(dimension)); \ + } else if (dimension.unit == ASDimensionUnitFraction) { \ + YGNodeStyleSet##property##Percent(yogaNode, yogaDimensionToPercent(dimension)); \ + } else { \ + YGNodeStyleSet##property(yogaNode, YGUndefined); \ + }\ + +#define YGNODE_STYLE_SET_DIMENSION_WITH_EDGE(yogaNode, property, dimension, edge) \ + if (dimension.unit == ASDimensionUnitPoints) { \ + YGNodeStyleSet##property(yogaNode, edge, yogaDimensionToPoints(dimension)); \ + } else if (dimension.unit == ASDimensionUnitFraction) { \ + YGNodeStyleSet##property##Percent(yogaNode, edge, yogaDimensionToPercent(dimension)); \ + } else { \ + YGNodeStyleSet##property(yogaNode, edge, YGUndefined); \ + } \ + +#define YGNODE_STYLE_SET_FLOAT_WITH_EDGE(yogaNode, property, dimension, edge) \ + if (dimension.unit == ASDimensionUnitPoints) { \ + YGNodeStyleSet##property(yogaNode, edge, yogaDimensionToPoints(dimension)); \ + } else if (dimension.unit == ASDimensionUnitFraction) { \ + ASDisplayNodeAssert(NO, @"Unexpected Fraction value in applying ##property## values to YGNode"); \ + } else { \ + YGNodeStyleSet##property(yogaNode, edge, YGUndefined); \ + } \ + +ASDISPLAYNODE_EXTERN_C_END + +#endif /* YOGA */ diff --git a/Source/Layout/ASYogaUtilities.mm b/Source/Layout/ASYogaUtilities.mm new file mode 100644 index 0000000000..566f5c3f74 --- /dev/null +++ b/Source/Layout/ASYogaUtilities.mm @@ -0,0 +1,206 @@ +// +// ASYogaUtilities.mm +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#if YOGA /* YOGA */ + +@implementation ASDisplayNode (YogaHelpers) + ++ (ASDisplayNode *)yogaNode +{ + ASDisplayNode *node = [[ASDisplayNode alloc] init]; + node.automaticallyManagesSubnodes = YES; + [node.style yogaNodeCreateIfNeeded]; + return node; +} + ++ (ASDisplayNode *)yogaSpacerNode +{ + ASDisplayNode *node = [ASDisplayNode yogaNode]; + node.style.flexGrow = 1.0f; + return node; +} + ++ (ASDisplayNode *)yogaVerticalStack +{ + ASDisplayNode *node = [self yogaNode]; + node.style.flexDirection = ASStackLayoutDirectionVertical; + return node; +} + ++ (ASDisplayNode *)yogaHorizontalStack +{ + ASDisplayNode *node = [self yogaNode]; + node.style.flexDirection = ASStackLayoutDirectionHorizontal; + return node; +} + +@end + +extern void ASDisplayNodePerformBlockOnEveryYogaChild(ASDisplayNode *node, void(^block)(ASDisplayNode *node)) +{ + if (node == nil) { + return; + } + block(node); + for (ASDisplayNode *child in [node yogaChildren]) { + ASDisplayNodePerformBlockOnEveryYogaChild(child, block); + } +} + +#pragma mark - Yoga Type Conversion Helpers + +YGAlign yogaAlignItems(ASStackLayoutAlignItems alignItems) +{ + switch (alignItems) { + case ASStackLayoutAlignItemsNotSet: return YGAlignAuto; + case ASStackLayoutAlignItemsStart: return YGAlignFlexStart; + case ASStackLayoutAlignItemsEnd: return YGAlignFlexEnd; + case ASStackLayoutAlignItemsCenter: return YGAlignCenter; + case ASStackLayoutAlignItemsStretch: return YGAlignStretch; + case ASStackLayoutAlignItemsBaselineFirst: return YGAlignBaseline; + // FIXME: WARNING, Yoga does not currently support last-baseline item alignment. + case ASStackLayoutAlignItemsBaselineLast: return YGAlignBaseline; + } +} + +YGJustify yogaJustifyContent(ASStackLayoutJustifyContent justifyContent) +{ + switch (justifyContent) { + case ASStackLayoutJustifyContentStart: return YGJustifyFlexStart; + case ASStackLayoutJustifyContentCenter: return YGJustifyCenter; + case ASStackLayoutJustifyContentEnd: return YGJustifyFlexEnd; + case ASStackLayoutJustifyContentSpaceBetween: return YGJustifySpaceBetween; + case ASStackLayoutJustifyContentSpaceAround: return YGJustifySpaceAround; + } +} + +YGAlign yogaAlignSelf(ASStackLayoutAlignSelf alignSelf) +{ + switch (alignSelf) { + case ASStackLayoutAlignSelfStart: return YGAlignFlexStart; + case ASStackLayoutAlignSelfCenter: return YGAlignCenter; + case ASStackLayoutAlignSelfEnd: return YGAlignFlexEnd; + case ASStackLayoutAlignSelfStretch: return YGAlignStretch; + case ASStackLayoutAlignSelfAuto: return YGAlignAuto; + } +} + +YGFlexDirection yogaFlexDirection(ASStackLayoutDirection direction) +{ + return direction == ASStackLayoutDirectionVertical ? YGFlexDirectionColumn : YGFlexDirectionRow; +} + +float yogaFloatForCGFloat(CGFloat value) +{ + if (value < CGFLOAT_MAX / 2) { + return value; + } else { + return YGUndefined; + } +} + +float yogaDimensionToPoints(ASDimension dimension) +{ + ASDisplayNodeCAssert(dimension.unit == ASDimensionUnitPoints, + @"Dimensions should not be type Fraction for this method: %f", dimension.value); + return yogaFloatForCGFloat(dimension.value); +} + +float yogaDimensionToPercent(ASDimension dimension) +{ + ASDisplayNodeCAssert(dimension.unit == ASDimensionUnitFraction, + @"Dimensions should not be type Points for this method: %f", dimension.value); + return 100.0 * yogaFloatForCGFloat(dimension.value); + +} + +ASDimension dimensionForEdgeWithEdgeInsets(YGEdge edge, ASEdgeInsets insets) +{ + switch (edge) { + case YGEdgeLeft: return insets.left; + case YGEdgeTop: return insets.top; + case YGEdgeRight: return insets.right; + case YGEdgeBottom: return insets.bottom; + case YGEdgeStart: return insets.start; + case YGEdgeEnd: return insets.end; + case YGEdgeHorizontal: return insets.horizontal; + case YGEdgeVertical: return insets.vertical; + case YGEdgeAll: return insets.all; + default: ASDisplayNodeCAssert(NO, @"YGEdge other than ASEdgeInsets is not supported."); + return ASDimensionAuto; + } +} + +void ASLayoutElementYogaUpdateMeasureFunc(YGNodeRef yogaNode, id layoutElement) +{ + if (yogaNode == NULL) { + return; + } + BOOL hasMeasureFunc = (YGNodeGetMeasureFunc(yogaNode) != NULL); + + if (layoutElement != nil && [layoutElement implementsLayoutMethod]) { + if (hasMeasureFunc == NO) { + // Retain the Context object. This must be explicitly released with a + // __bridge_transfer - YGNodeFree() is not sufficient. + YGNodeSetContext(yogaNode, (__bridge_retained void *)layoutElement); + YGNodeSetMeasureFunc(yogaNode, &ASLayoutElementYogaMeasureFunc); + } + ASDisplayNodeCAssert(YGNodeGetContext(yogaNode) == (__bridge void *)layoutElement, + @"Yoga node context should contain layoutElement: %@", layoutElement); + } else if (hasMeasureFunc == YES) { + // If we lack any of the conditions above, and currently have a measure func, get rid of it. + // Release the __bridge_retained Context object. + __unused id element = (__bridge_transfer id)YGNodeGetContext(yogaNode); + YGNodeSetContext(yogaNode, NULL); + YGNodeSetMeasureFunc(yogaNode, NULL); + } +} + +YGSize ASLayoutElementYogaMeasureFunc(YGNodeRef yogaNode, float width, YGMeasureMode widthMode, + float height, YGMeasureMode heightMode) +{ + id layoutElement = (__bridge id )YGNodeGetContext(yogaNode); + ASDisplayNodeCAssert([layoutElement conformsToProtocol:@protocol(ASLayoutElement)], @"Yoga context must be "); + + ASSizeRange sizeRange; + sizeRange.min = CGSizeZero; + sizeRange.max = CGSizeMake(width, height); + if (widthMode == YGMeasureModeExactly) { + sizeRange.min.width = sizeRange.max.width; + } else { + // Mode is (YGMeasureModeAtMost | YGMeasureModeUndefined) + ASDimension minWidth = layoutElement.style.minWidth; + sizeRange.min.width = (minWidth.unit == ASDimensionUnitPoints ? yogaDimensionToPoints(minWidth) : 0.0); + } + if (heightMode == YGMeasureModeExactly) { + sizeRange.min.height = sizeRange.max.height; + } else { + // Mode is (YGMeasureModeAtMost | YGMeasureModeUndefined) + ASDimension minHeight = layoutElement.style.minHeight; + sizeRange.min.height = (minHeight.unit == ASDimensionUnitPoints ? yogaDimensionToPoints(minHeight) : 0.0); + } + + ASDisplayNodeCAssert(isnan(sizeRange.min.width) == NO && isnan(sizeRange.min.height) == NO, @"Yoga size range for measurement should not have NaN in minimum"); + if (isnan(sizeRange.max.width)) { + sizeRange.max.width = CGFLOAT_MAX; + } + if (isnan(sizeRange.max.height)) { + sizeRange.max.height = CGFLOAT_MAX; + } + + CGSize size = [[layoutElement layoutThatFits:sizeRange] size]; + return (YGSize){ .width = (float)size.width, .height = (float)size.height }; +} + +#endif /* YOGA */ diff --git a/Source/Private/ASBasicImageDownloaderInternal.h b/Source/Private/ASBasicImageDownloaderInternal.h index f26ac6b732..e3d6d6dc57 100644 --- a/Source/Private/ASBasicImageDownloaderInternal.h +++ b/Source/Private/ASBasicImageDownloaderInternal.h @@ -1,11 +1,18 @@ // // ASBasicImageDownloaderInternal.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // @interface ASBasicImageDownloaderContext : NSObject diff --git a/Source/Private/ASBatchFetching.h b/Source/Private/ASBatchFetching.h index 4f3cee4a50..116217703b 100644 --- a/Source/Private/ASBatchFetching.h +++ b/Source/Private/ASBatchFetching.h @@ -1,11 +1,18 @@ // // ASBatchFetching.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK @@ -16,13 +23,17 @@ ASDISPLAYNODE_EXTERN_C_BEGIN +NS_ASSUME_NONNULL_BEGIN + @class ASBatchContext; +@protocol ASBatchFetchingDelegate; @protocol ASBatchFetchingScrollView - (BOOL)canBatchFetch; - (ASBatchContext *)batchContext; - (CGFloat)leadingScreensForBatching; +- (nullable id)batchFetchingDelegate; @end @@ -34,9 +45,14 @@ ASDISPLAYNODE_EXTERN_C_BEGIN @param scrollDirection The current scrolling direction of the scroll view. @param scrollableDirections The possible scrolling directions of the scroll view. @param contentOffset The offset that the scrollview will scroll to. + @param velocity The velocity of the scroll view (in points) at the moment the touch was released. @return Whether or not the current state should proceed with batch fetching. */ -BOOL ASDisplayShouldFetchBatchForScrollView(UIScrollView *scrollView, ASScrollDirection scrollDirection, ASScrollDirection scrollableDirections, CGPoint contentOffset); +BOOL ASDisplayShouldFetchBatchForScrollView(UIScrollView *scrollView, + ASScrollDirection scrollDirection, + ASScrollDirection scrollableDirections, + CGPoint contentOffset, + CGPoint velocity); /** @@ -49,6 +65,8 @@ BOOL ASDisplayShouldFetchBatchForScrollView(UIScrollView delegate); +NS_ASSUME_NONNULL_END ASDISPLAYNODE_EXTERN_C_END #endif diff --git a/Source/Private/ASBatchFetching.m b/Source/Private/ASBatchFetching.m index 880521c560..9365b505b1 100644 --- a/Source/Private/ASBatchFetching.m +++ b/Source/Private/ASBatchFetching.m @@ -1,18 +1,30 @@ // // ASBatchFetching.m -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK #import #import +#import -BOOL ASDisplayShouldFetchBatchForScrollView(UIScrollView *scrollView, ASScrollDirection scrollDirection, ASScrollDirection scrollableDirections, CGPoint contentOffset) +BOOL ASDisplayShouldFetchBatchForScrollView(UIScrollView *scrollView, + ASScrollDirection scrollDirection, + ASScrollDirection scrollableDirections, + CGPoint contentOffset, + CGPoint velocity) { // Don't fetch if the scroll view does not allow if (![scrollView canBatchFetch]) { @@ -24,8 +36,9 @@ BOOL ASDisplayShouldFetchBatchForScrollView(UIScrollView delegate = scrollView.batchFetchingDelegate; BOOL visible = (scrollView.window != nil); - return ASDisplayShouldFetchBatchForContext(context, scrollDirection, scrollableDirections, bounds, contentSize, contentOffset, leadingScreens, visible); + return ASDisplayShouldFetchBatchForContext(context, scrollDirection, scrollableDirections, bounds, contentSize, contentOffset, leadingScreens, visible, velocity, delegate); } BOOL ASDisplayShouldFetchBatchForContext(ASBatchContext *context, @@ -35,7 +48,9 @@ BOOL ASDisplayShouldFetchBatchForContext(ASBatchContext *context, CGSize contentSize, CGPoint targetOffset, CGFloat leadingScreens, - BOOL visible) + BOOL visible, + CGPoint velocity, + id delegate) { // Do not allow fetching if a batch is already in-flight and hasn't been completed or cancelled if ([context isFetching]) { @@ -47,16 +62,18 @@ BOOL ASDisplayShouldFetchBatchForContext(ASBatchContext *context, return NO; } - CGFloat viewLength, offset, contentLength; + CGFloat viewLength, offset, contentLength, velocityLength; if (ASScrollDirectionContainsVerticalDirection(scrollableDirections)) { viewLength = bounds.size.height; offset = targetOffset.y; contentLength = contentSize.height; + velocityLength = velocity.y; } else { // horizontal / right viewLength = bounds.size.width; offset = targetOffset.x; contentLength = contentSize.width; + velocityLength = velocity.x; } BOOL hasSmallContent = contentLength < viewLength; @@ -78,8 +95,16 @@ BOOL ASDisplayShouldFetchBatchForContext(ASBatchContext *context, CGFloat triggerDistance = viewLength * leadingScreens; CGFloat remainingDistance = contentLength - viewLength - offset; + BOOL result = remainingDistance <= triggerDistance; - return remainingDistance <= triggerDistance; + if (delegate != nil && velocityLength > 0.0) { + // Don't need to get absolute value of remaining time + // because both remainingDistance and velocityLength are positive when scrolling toward tail + NSTimeInterval remainingTime = remainingDistance / (velocityLength * 1000); + result = [delegate shouldFetchBatchWithRemainingTime:remainingTime hint:result]; + } + + return result; } #endif diff --git a/Source/Private/ASCellNode+Internal.h b/Source/Private/ASCellNode+Internal.h index 57eeacb2b8..1219dc5cd5 100644 --- a/Source/Private/ASCellNode+Internal.h +++ b/Source/Private/ASCellNode+Internal.h @@ -1,13 +1,18 @@ // // ASCellNode+Internal.h -// AsyncDisplayKit -// -// Created by Max Gu on 2/19/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK @@ -19,19 +24,6 @@ NS_ASSUME_NONNULL_BEGIN @protocol ASCellNodeInteractionDelegate -/** - * Notifies the delegate that the specified cell node has done a relayout. - * The notification is done on main thread. - * - * This will not be called due to measurement passes before the node has loaded - * its view, even if triggered by -setNeedsLayout, as it is assumed these are - * not relevant to UIKit. Indeed, these calls can cause consistency issues. - * - * @param node A node informing the delegate about the relayout. - * @param sizeChanged `YES` if the node's `calculatedSize` changed during the relayout, `NO` otherwise. - */ -- (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. * @@ -68,9 +60,9 @@ NS_ASSUME_NONNULL_BEGIN */ @property (nonatomic, strong, nullable) UICollectionViewLayoutAttributes *layoutAttributes; -@property (weak, nullable) ASCollectionElement *collectionElement; +@property (atomic, weak, nullable) ASCollectionElement *collectionElement; -@property (nonatomic, weak, nullable) ASDisplayNode *owningNode; +@property (atomic, weak, nullable) id owningNode; @property (nonatomic, assign) BOOL shouldUseUIKitCell; diff --git a/Source/Private/ASCollectionLayout.h b/Source/Private/ASCollectionLayout.h new file mode 100644 index 0000000000..ad798b6bf5 --- /dev/null +++ b/Source/Private/ASCollectionLayout.h @@ -0,0 +1,58 @@ +// +// ASCollectionLayout.h +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#ifndef MINIMAL_ASDK + +#import +#import +#import + +@protocol ASCollectionLayoutDelegate; +@class ASElementMap, ASCollectionLayout, ASCollectionNode; + +NS_ASSUME_NONNULL_BEGIN + +AS_SUBCLASSING_RESTRICTED +@interface ASCollectionLayout : UICollectionViewLayout + +/** + * The collection node object currently using this layout object. + * + * @discussion The collection node object sets the value of this property when a new layout object is assigned to it. + * + * @discussion To get the truth on the current state of the collection, call methods on the collection node or the data source rather than the collection view because: + * 1. The view might not yet be allocated. + * 2. The collection node and data source are thread-safe. + */ +@property (nonatomic, weak) ASCollectionNode *collectionNode; + +@property (nonatomic, strong, readonly) id layoutDelegate; + +/** + * Initializes with a layout delegate. + * + * @discussion For developers' convenience, the delegate is retained by this layout object, similar to UICollectionView retains its UICollectionViewLayout object. + * + * @discussion For simplicity, the delegate is read-only. If a new layout delegate is needed, construct a new layout object with that delegate and notify ASCollectionView about it. + * This ensures the underlying UICollectionView purges its cache and properly loads the new layout. + */ +- (instancetype)initWithLayoutDelegate:(id)layoutDelegate NS_DESIGNATED_INITIALIZER; + +- (instancetype)init __unavailable; + +- (instancetype)initWithCoder:(NSCoder *)aDecoder __unavailable; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/Source/Private/ASCollectionLayout.mm b/Source/Private/ASCollectionLayout.mm new file mode 100644 index 0000000000..9697753a57 --- /dev/null +++ b/Source/Private/ASCollectionLayout.mm @@ -0,0 +1,344 @@ +// +// ASCollectionLayout.mm +// Texture +// +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#ifndef MINIMAL_ASDK + +#import + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +static const ASRangeTuningParameters kASDefaultMeasureRangeTuningParameters = { + .leadingBufferScreenfuls = 2.0, + .trailingBufferScreenfuls = 2.0 +}; + +static const ASScrollDirection kASStaticScrollDirection = (ASScrollDirectionRight | ASScrollDirectionDown); + +@interface ASCollectionLayout () { + ASCollectionLayoutCache *_layoutCache; + ASCollectionLayoutState *_layout; // Main thread only. + + struct { + unsigned int implementsAdditionalInfoForLayoutWithElements:1; + } _layoutDelegateFlags; +} + +@end + +@implementation ASCollectionLayout + +- (instancetype)initWithLayoutDelegate:(id)layoutDelegate +{ + self = [super init]; + if (self) { + ASDisplayNodeAssertNotNil(layoutDelegate, @"Collection layout delegate cannot be nil"); + _layoutDelegate = layoutDelegate; + _layoutDelegateFlags.implementsAdditionalInfoForLayoutWithElements = [layoutDelegate respondsToSelector:@selector(additionalInfoForLayoutWithElements:)]; + _layoutCache = [[ASCollectionLayoutCache alloc] init]; + } + return self; +} + +#pragma mark - ASDataControllerLayoutDelegate + +- (ASCollectionLayoutContext *)layoutContextWithElements:(ASElementMap *)elements +{ + ASDisplayNodeAssertMainThread(); + CGSize viewportSize = [self _viewportSize]; + id additionalInfo = nil; + if (_layoutDelegateFlags.implementsAdditionalInfoForLayoutWithElements) { + additionalInfo = [_layoutDelegate additionalInfoForLayoutWithElements:elements]; + } + return [[ASCollectionLayoutContext alloc] initWithViewportSize:viewportSize + scrollableDirections:[_layoutDelegate scrollableDirections] + elements:elements + layoutDelegateClass:[_layoutDelegate class] + layoutCache:_layoutCache + additionalInfo:additionalInfo]; +} + ++ (ASCollectionLayoutState *)calculateLayoutWithContext:(ASCollectionLayoutContext *)context +{ + if (context.elements == nil) { + return [[ASCollectionLayoutState alloc] initWithContext:context + contentSize:CGSizeZero + elementToLayoutAttributesTable:[NSMapTable elementToLayoutAttributesTable]]; + } + + ASDisplayNodeAssertTrue([context.layoutDelegateClass conformsToProtocol:@protocol(ASCollectionLayoutDelegate)]); + ASCollectionLayoutState *layout = [context.layoutDelegateClass calculateLayoutWithContext:context]; + [context.layoutCache setLayout:layout forContext:context]; + + // Measure elements in the measure range ahead of time, block on the initial rect as it'll be visible shortly + CGSize viewportSize = context.viewportSize; + // TODO Consider content offset of the collection node + CGRect initialRect = CGRectMake(0, 0, viewportSize.width, viewportSize.height); + CGRect measureRect = CGRectExpandToRangeWithScrollableDirections(initialRect, + kASDefaultMeasureRangeTuningParameters, + context.scrollableDirections, + kASStaticScrollDirection); + [self _measureElementsInRect:measureRect blockingRect:initialRect layout:layout]; + + return layout; +} + +#pragma mark - UICollectionViewLayout overrides + +- (void)prepareLayout +{ + ASDisplayNodeAssertMainThread(); + [super prepareLayout]; + + ASCollectionLayoutContext *context = [self layoutContextWithElements:_collectionNode.visibleElements]; + if (_layout != nil && ASObjectIsEqual(_layout.context, context)) { + // The existing layout is still valid. No-op + return; + } + + if (ASCollectionLayoutState *cachedLayout = [_layoutCache layoutForContext:context]) { + _layout = cachedLayout; + } else { + // A new layout is needed now. Calculate and apply it immediately + _layout = [ASCollectionLayout calculateLayoutWithContext:context]; + } +} + +- (void)invalidateLayout +{ + ASDisplayNodeAssertMainThread(); + [super invalidateLayout]; + if (_layout != nil) { + [_layoutCache removeLayoutForContext:_layout.context]; + _layout = nil; + } +} + +- (CGSize)collectionViewContentSize +{ + ASDisplayNodeAssertMainThread(); + ASDisplayNodeAssertNotNil(_layout, @"Collection layout state should not be nil at this point"); + return _layout.contentSize; +} + +- (NSArray *)layoutAttributesForElementsInRect:(CGRect)blockingRect +{ + ASDisplayNodeAssertMainThread(); + if (CGRectIsEmpty(blockingRect)) { + return nil; + } + + // Measure elements in the measure range, block on the requested rect + CGRect measureRect = CGRectExpandToRangeWithScrollableDirections(blockingRect, + kASDefaultMeasureRangeTuningParameters, + _layout.context.scrollableDirections, + kASStaticScrollDirection); + [ASCollectionLayout _measureElementsInRect:measureRect blockingRect:blockingRect layout:_layout]; + + NSArray *result = [_layout layoutAttributesForElementsInRect:blockingRect]; + + ASElementMap *elements = _layout.context.elements; + for (UICollectionViewLayoutAttributes *attrs in result) { + ASCollectionElement *element = [elements elementForLayoutAttributes:attrs]; + ASCollectionLayoutSetSizeToElement(attrs.frame.size, element); + } + + return result; +} + +- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath +{ + ASDisplayNodeAssertMainThread(); + + ASCollectionElement *element = [_layout.context.elements elementForItemAtIndexPath:indexPath]; + UICollectionViewLayoutAttributes *attrs = [_layout layoutAttributesForElement:element]; + + ASCellNode *node = element.node; + CGSize elementSize = attrs.frame.size; + if (! CGSizeEqualToSize(elementSize, node.calculatedSize)) { + [node layoutThatFits:ASCollectionLayoutElementSizeRangeFromSize(elementSize)]; + } + + ASCollectionLayoutSetSizeToElement(attrs.frame.size, element); + return attrs; +} + +- (UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath +{ + ASCollectionElement *element = [_layout.context.elements supplementaryElementOfKind:elementKind atIndexPath:indexPath]; + UICollectionViewLayoutAttributes *attrs = [_layout layoutAttributesForElement:element]; + + ASCellNode *node = element.node; + CGSize elementSize = attrs.frame.size; + if (! CGSizeEqualToSize(elementSize, node.calculatedSize)) { + [node layoutThatFits:ASCollectionLayoutElementSizeRangeFromSize(elementSize)]; + } + + ASCollectionLayoutSetSizeToElement(attrs.frame.size, element); + return attrs; +} + +- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds +{ + return (! CGSizeEqualToSize([self _viewportSize], newBounds.size)); +} + +#pragma mark - Private methods + +- (CGSize)_viewportSize +{ + ASCollectionNode *collectionNode = _collectionNode; + if (collectionNode != nil && !collectionNode.isNodeLoaded) { + // TODO consider calculatedSize as well + return collectionNode.threadSafeBounds.size; + } else { + ASDisplayNodeAssertMainThread(); + return self.collectionView.bounds.size; + } +} + +/** + * Measures all elements in the specified rect and blocks the calling thread while measuring those in the blocking rect. + */ ++ (void)_measureElementsInRect:(CGRect)rect blockingRect:(CGRect)blockingRect layout:(ASCollectionLayoutState *)layout +{ + if (CGRectIsEmpty(rect) || layout.context.elements == nil) { + return; + } + BOOL hasBlockingRect = !CGRectIsEmpty(blockingRect); + if (hasBlockingRect && CGRectContainsRect(rect, blockingRect) == NO) { + ASDisplayNodeCAssert(NO, @"Blocking rect, if specified, must be within the other (outer) rect"); + return; + } + + // Step 1: Clamp the specified rects between the bounds of content rect + CGSize contentSize = layout.contentSize; + CGRect contentRect = CGRectMake(0, 0, contentSize.width, contentSize.height); + rect = CGRectIntersection(contentRect, rect); + if (CGRectIsNull(rect)) { + return; + } + if (hasBlockingRect) { + blockingRect = CGRectIntersection(contentRect, blockingRect); + hasBlockingRect = !CGRectIsNull(blockingRect); + } + + // Step 2: Get layout attributes of all elements within the specified outer rect + ASCollectionLayoutContext *context = layout.context; + CGSize pageSize = context.viewportSize; + ASPageToLayoutAttributesTable *attrsTable = [layout getAndRemoveUnmeasuredLayoutAttributesPageTableInRect:rect + contentSize:contentSize + pageSize:pageSize]; + if (attrsTable.count == 0) { + // No elements in this rect! Bail early + return; + } + + // Step 3: Split all those attributes into blocking and non-blocking buckets + // Use ordered sets here because some items may span multiple pages, and the sets will be accessed by indexes later on. + NSMutableOrderedSet *blockingAttrs = hasBlockingRect ? [NSMutableOrderedSet orderedSet] : nil; + NSMutableOrderedSet *nonBlockingAttrs = [NSMutableOrderedSet orderedSet]; + for (id pagePtr in attrsTable) { + ASPageCoordinate page = (ASPageCoordinate)pagePtr; + NSArray *attrsInPage = [attrsTable objectForPage:page]; + // Calculate the page's rect but only if it's going to be used. + CGRect pageRect = hasBlockingRect ? ASPageCoordinateGetPageRect(page, pageSize) : CGRectZero; + + if (hasBlockingRect && CGRectContainsRect(blockingRect, pageRect)) { + // The page fits well within the blocking rect. All attributes in this page are blocking. + [blockingAttrs addObjectsFromArray:attrsInPage]; + } else if (hasBlockingRect && CGRectIntersectsRect(blockingRect, pageRect)) { + // The page intersects the blocking rect. Some elements in this page are blocking, some are not. + for (UICollectionViewLayoutAttributes *attrs in attrsInPage) { + if (CGRectIntersectsRect(blockingRect, attrs.frame)) { + [blockingAttrs addObject:attrs]; + } else { + [nonBlockingAttrs addObject:attrs]; + } + } + } else { + // The page doesn't intersect the blocking rect. All elements in this page are non-blocking. + [nonBlockingAttrs addObjectsFromArray:attrsInPage]; + } + } + + // Step 4: Allocate and measure blocking elements' node + ASElementMap *elements = context.elements; + dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + if (NSUInteger count = blockingAttrs.count) { + ASDispatchApply(count, queue, 0, ^(size_t i) { + UICollectionViewLayoutAttributes *attrs = blockingAttrs[i]; + ASCellNode *node = [elements elementForItemAtIndexPath:attrs.indexPath].node; + CGSize expectedSize = attrs.frame.size; + if (! CGSizeEqualToSize(expectedSize, node.calculatedSize)) { + [node layoutThatFits:ASCollectionLayoutElementSizeRangeFromSize(expectedSize)]; + } + }); + } + + // Step 5: Allocate and measure non-blocking ones + if (NSUInteger count = nonBlockingAttrs.count) { + __weak ASElementMap *weakElements = elements; + ASDispatchAsync(count, queue, 0, ^(size_t i) { + __strong ASElementMap *strongElements = weakElements; + if (strongElements) { + UICollectionViewLayoutAttributes *attrs = nonBlockingAttrs[i]; + ASCellNode *node = [elements elementForItemAtIndexPath:attrs.indexPath].node; + CGSize expectedSize = attrs.frame.size; + if (! CGSizeEqualToSize(expectedSize, node.calculatedSize)) { + [node layoutThatFits:ASCollectionLayoutElementSizeRangeFromSize(expectedSize)]; + } + } + }); + } +} + +# pragma mark - Convenient inline functions + +ASDISPLAYNODE_INLINE ASSizeRange ASCollectionLayoutElementSizeRangeFromSize(CGSize size) +{ + // The layout delegate consulted us that this element must fit within this size, + // and the only way to achieve that without asking it again is to use an exact size range here. + return ASSizeRangeMake(size); +} + +ASDISPLAYNODE_INLINE void ASCollectionLayoutSetSizeToElement(CGSize size, ASCollectionElement *element) +{ + if (ASCellNode *node = element.node) { + if (! CGSizeEqualToSize(size, node.frame.size)) { + CGRect frame = CGRectZero; + frame.size = size; + node.frame = frame; + } + } +} + +@end + +#endif diff --git a/Source/Private/ASCollectionLayoutCache.h b/Source/Private/ASCollectionLayoutCache.h new file mode 100644 index 0000000000..9a71b802c0 --- /dev/null +++ b/Source/Private/ASCollectionLayoutCache.h @@ -0,0 +1,38 @@ +// +// ASCollectionLayoutCache.h +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#ifndef MINIMAL_ASDK + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@class ASCollectionLayoutContext, ASCollectionLayoutState; + +/// A thread-safe cache for ASCollectionLayoutContext-ASCollectionLayoutState pairs +AS_SUBCLASSING_RESTRICTED +@interface ASCollectionLayoutCache : NSObject + +- (nullable ASCollectionLayoutState *)layoutForContext:(ASCollectionLayoutContext *)context; + +- (void)setLayout:(ASCollectionLayoutState *)layout forContext:(ASCollectionLayoutContext *)context; + +- (void)removeLayoutForContext:(ASCollectionLayoutContext *)context; + +- (void)removeAllLayouts; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/Source/Private/ASCollectionLayoutCache.mm b/Source/Private/ASCollectionLayoutCache.mm new file mode 100644 index 0000000000..d79f512e1a --- /dev/null +++ b/Source/Private/ASCollectionLayoutCache.mm @@ -0,0 +1,94 @@ +// +// ASCollectionLayoutCache.mm +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#ifndef MINIMAL_ASDK + +#import + +#import +#import +#import +#import + +@implementation ASCollectionLayoutCache { + ASDN::Mutex __instanceLock__; + + /** + * The underlying data structure of this cache. + * + * The outer map table is a weak to strong table. That is because ASCollectionLayoutContext doesn't (and shouldn't) + * hold a strong reference on its element map. As a result, this cache should handle the case in which + * an element map no longer exists and all contexts and layouts associated with it should be cleared. + * + * The inner map table is a standard strong to strong map. + * Since different ASCollectionLayoutContext objects with the same content are considered equal, + * "object pointer personality" can't be used as a key option. + */ + NSMapTable *> *_map; +} + +- (instancetype)init +{ + self = [super init]; + if (self) { + _map = [NSMapTable mapTableWithKeyOptions:(NSMapTableWeakMemory | NSMapTableObjectPointerPersonality) valueOptions:NSMapTableStrongMemory]; + } + return self; +} + +- (ASCollectionLayoutState *)layoutForContext:(ASCollectionLayoutContext *)context +{ + ASElementMap *elements = context.elements; + if (elements == nil) { + return nil; + } + + ASDN::MutexLocker l(__instanceLock__); + return [[_map objectForKey:elements] objectForKey:context]; +} + +- (void)setLayout:(ASCollectionLayoutState *)layout forContext:(ASCollectionLayoutContext *)context +{ + ASElementMap *elements = context.elements; + if (layout == nil || elements == nil) { + return; + } + + ASDN::MutexLocker l(__instanceLock__); + auto innerMap = [_map objectForKey:elements]; + if (innerMap == nil) { + innerMap = [NSMapTable strongToStrongObjectsMapTable]; + [_map setObject:innerMap forKey:elements]; + } + [innerMap setObject:layout forKey:context]; +} + +- (void)removeLayoutForContext:(ASCollectionLayoutContext *)context +{ + ASElementMap *elements = context.elements; + if (elements == nil) { + return; + } + + ASDN::MutexLocker l(__instanceLock__); + [[_map objectForKey:elements] removeObjectForKey:context]; +} + +- (void)removeAllLayouts +{ + ASDN::MutexLocker l(__instanceLock__); + [_map removeAllObjects]; +} + +@end + +#endif diff --git a/Source/Private/ASCollectionLayoutContext+Private.h b/Source/Private/ASCollectionLayoutContext+Private.h new file mode 100644 index 0000000000..8e6e3ccffe --- /dev/null +++ b/Source/Private/ASCollectionLayoutContext+Private.h @@ -0,0 +1,43 @@ +// +// ASCollectionLayoutContext+Private.h +// Texture +// +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#ifndef MINIMAL_ASDK + +#import + +@class ASCollectionLayoutCache; +@protocol ASCollectionLayoutDelegate; + +NS_ASSUME_NONNULL_BEGIN + +@interface ASCollectionLayoutContext (Private) + +@property (nonatomic, strong, readonly) Class layoutDelegateClass; +@property (nonatomic, weak, readonly) ASCollectionLayoutCache *layoutCache; + +- (instancetype)initWithViewportSize:(CGSize)viewportSize + scrollableDirections:(ASScrollDirection)scrollableDirections + elements:(ASElementMap *)elements + layoutDelegateClass:(Class)layoutDelegateClass + layoutCache:(ASCollectionLayoutCache *)layoutCache + additionalInfo:(nullable id)additionalInfo; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/Source/Private/ASCollectionLayoutDefines.h b/Source/Private/ASCollectionLayoutDefines.h new file mode 100644 index 0000000000..07be880a0b --- /dev/null +++ b/Source/Private/ASCollectionLayoutDefines.h @@ -0,0 +1,27 @@ +// +// ASCollectionLayoutDefines.h +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +ASDISPLAYNODE_EXTERN_C_BEGIN + +FOUNDATION_EXPORT ASSizeRange ASSizeRangeForCollectionLayoutThatFitsViewportSize(CGSize viewportSize, ASScrollDirection scrollableDirections) AS_WARN_UNUSED_RESULT; + +ASDISPLAYNODE_EXTERN_C_END + +NS_ASSUME_NONNULL_END diff --git a/Source/Private/ASCollectionLayoutDefines.m b/Source/Private/ASCollectionLayoutDefines.m new file mode 100644 index 0000000000..b8c9c21ccf --- /dev/null +++ b/Source/Private/ASCollectionLayoutDefines.m @@ -0,0 +1,27 @@ +// +// ASCollectionLayoutDefines.m +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +extern ASSizeRange ASSizeRangeForCollectionLayoutThatFitsViewportSize(CGSize viewportSize, ASScrollDirection scrollableDirections) +{ + ASSizeRange sizeRange = ASSizeRangeUnconstrained; + if (ASScrollDirectionContainsVerticalDirection(scrollableDirections) == NO) { + sizeRange.min.height = viewportSize.height; + sizeRange.max.height = viewportSize.height; + } + if (ASScrollDirectionContainsHorizontalDirection(scrollableDirections) == NO) { + sizeRange.min.width = viewportSize.width; + sizeRange.max.width = viewportSize.width; + } + return sizeRange; +} diff --git a/Source/Private/ASCollectionLayoutState+Private.h b/Source/Private/ASCollectionLayoutState+Private.h new file mode 100644 index 0000000000..1423b077cb --- /dev/null +++ b/Source/Private/ASCollectionLayoutState+Private.h @@ -0,0 +1,35 @@ +// +// ASCollectionLayoutState+Private.h +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#ifndef MINIMAL_ASDK + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface ASCollectionLayoutState (Private) + +/** + * Remove and returns layout attributes for unmeasured elements that intersect the specified rect + * + * @discussion This method is atomic and thread-safe + */ +- (nullable ASPageToLayoutAttributesTable *)getAndRemoveUnmeasuredLayoutAttributesPageTableInRect:(CGRect)rect + contentSize:(CGSize)contentSize + pageSize:(CGSize)pageSize; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/Source/Private/ASCollectionView+Undeprecated.h b/Source/Private/ASCollectionView+Undeprecated.h index cfddc136c0..06c0e65fbb 100644 --- a/Source/Private/ASCollectionView+Undeprecated.h +++ b/Source/Private/ASCollectionView+Undeprecated.h @@ -1,9 +1,18 @@ // // ASCollectionView+Undeprecated.h -// AsyncDisplayKit +// Texture // -// Created by Adlai Holler on 10/10/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 /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK @@ -57,6 +66,16 @@ NS_ASSUME_NONNULL_BEGIN */ - (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout; +@property (nonatomic, assign) CGFloat leadingScreensForBatching; + +@property (nonatomic, assign) BOOL inverted; + +@property (nonatomic, readonly) ASScrollDirection scrollDirection; + +@property (nonatomic, readonly) ASScrollDirection scrollableDirections; + +@property (nonatomic, weak) id layoutInspector; + /** * Tuning parameters for a range type in full mode. * @@ -145,30 +164,6 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)performBatchUpdates:(nullable AS_NOESCAPE void (^)())updates completion:(nullable void (^)(BOOL finished))completion; -/** - * Reload everything from scratch, destroying the working range and all cached nodes. - * - * @param completion block to run on completion of asynchronous loading or nil. If supplied, the block is run on - * the main thread. - * @warning This method is substantially more expensive than UICollectionView's version. - */ -- (void)reloadDataWithCompletion:(nullable void (^)())completion; - -/** - * Reload everything from scratch, destroying the working range and all cached nodes. - * - * @warning This method is substantially more expensive than UICollectionView's version. - */ -- (void)reloadData; - -/** - * Reload everything from scratch entirely on the main thread, destroying the working range and all cached nodes. - * - * @warning This method is substantially more expensive than UICollectionView's version and will block the main thread - * while all the cells load. - */ -- (void)reloadDataImmediately; - /** * Triggers a relayout of all nodes. * diff --git a/Source/Private/ASCollectionViewFlowLayoutInspector.h b/Source/Private/ASCollectionViewFlowLayoutInspector.h index e50fdba6ab..a6beb02729 100644 --- a/Source/Private/ASCollectionViewFlowLayoutInspector.h +++ b/Source/Private/ASCollectionViewFlowLayoutInspector.h @@ -1,11 +1,18 @@ // // ASCollectionViewFlowLayoutInspector.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK diff --git a/Source/Private/ASCollectionViewFlowLayoutInspector.m b/Source/Private/ASCollectionViewFlowLayoutInspector.m index b996bfc9ff..44acad23ea 100644 --- a/Source/Private/ASCollectionViewFlowLayoutInspector.m +++ b/Source/Private/ASCollectionViewFlowLayoutInspector.m @@ -1,11 +1,18 @@ // // ASCollectionViewFlowLayoutInspector.m -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK #import diff --git a/Source/Private/ASControlTargetAction.h b/Source/Private/ASControlTargetAction.h index 78775647b4..a56ddb65ab 100644 --- a/Source/Private/ASControlTargetAction.h +++ b/Source/Private/ASControlTargetAction.h @@ -1,11 +1,18 @@ // // ASControlTargetAction.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Private/ASControlTargetAction.m b/Source/Private/ASControlTargetAction.m index 9769c571de..d3e18fb558 100644 --- a/Source/Private/ASControlTargetAction.m +++ b/Source/Private/ASControlTargetAction.m @@ -1,11 +1,18 @@ // // ASControlTargetAction.m -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Private/ASDefaultPlayButton.h b/Source/Private/ASDefaultPlayButton.h index cab4ae4231..3295fc2bbd 100644 --- a/Source/Private/ASDefaultPlayButton.h +++ b/Source/Private/ASDefaultPlayButton.h @@ -1,13 +1,18 @@ // // ASDefaultPlayButton.h -// AsyncDisplayKit -// -// Created by Luke Parham on 1/27/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Private/ASDefaultPlayButton.m b/Source/Private/ASDefaultPlayButton.m index 6078b56e0b..4cef2b35f3 100644 --- a/Source/Private/ASDefaultPlayButton.m +++ b/Source/Private/ASDefaultPlayButton.m @@ -1,13 +1,18 @@ // // ASDefaultPlayButton.m -// AsyncDisplayKit -// -// Created by Luke Parham on 1/27/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -26,7 +31,7 @@ return self; } -+ (void)drawRect:(CGRect)bounds withParameters:(id)parameters isCancelled:(asdisplaynode_iscancelled_block_t)isCancelledBlock isRasterizing:(BOOL)isRasterizing ++ (void)drawRect:(CGRect)bounds withParameters:(id)parameters isCancelled:(asdisplaynode_iscancelled_block_t)isCancelledBlock isRasterizing:(BOOL)isRasterizing { CGFloat originX = bounds.size.width/4; CGRect buttonBounds = CGRectMake(originX, bounds.size.height/4, bounds.size.width/2, bounds.size.height/2); diff --git a/Source/Private/ASDefaultPlaybackButton.h b/Source/Private/ASDefaultPlaybackButton.h index 83d3da5412..75c3707276 100644 --- a/Source/Private/ASDefaultPlaybackButton.h +++ b/Source/Private/ASDefaultPlaybackButton.h @@ -1,13 +1,18 @@ // // ASDefaultPlaybackButton.h -// AsyncDisplayKit -// -// Created by Erekle on 5/14/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Private/ASDefaultPlaybackButton.m b/Source/Private/ASDefaultPlaybackButton.m index 9379c83b7b..c2201b84a0 100644 --- a/Source/Private/ASDefaultPlaybackButton.m +++ b/Source/Private/ASDefaultPlaybackButton.m @@ -1,13 +1,18 @@ // // ASDefaultPlaybackButton.m -// AsyncDisplayKit -// -// Created by Erekle on 5/14/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -49,7 +54,7 @@ }; } -+ (void)drawRect:(CGRect)bounds withParameters:(id)parameters isCancelled:(asdisplaynode_iscancelled_block_t)isCancelledBlock isRasterizing:(BOOL)isRasterizing ++ (void)drawRect:(CGRect)bounds withParameters:(id)parameters isCancelled:(asdisplaynode_iscancelled_block_t)isCancelledBlock isRasterizing:(BOOL)isRasterizing { ASDefaultPlaybackButtonType buttonType = (ASDefaultPlaybackButtonType)[parameters[@"buttonType"] intValue]; UIColor *color = parameters[@"color"]; diff --git a/Source/Private/ASDispatch.h b/Source/Private/ASDispatch.h index 78d7f90640..4f2a1cda9e 100644 --- a/Source/Private/ASDispatch.h +++ b/Source/Private/ASDispatch.h @@ -1,12 +1,24 @@ // // ASDispatch.h -// AsyncDisplayKit +// Texture // -// Created by Adlai Holler on 8/25/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 /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import +#import + +ASDISPLAYNODE_EXTERN_C_BEGIN /** * Like dispatch_apply, but you can set the thread count. 0 means 2*active CPUs. @@ -14,19 +26,14 @@ * Note: The actual number of threads may be lower than threadCount, if libdispatch * decides the system can't handle it. In reality this rarely happens. */ -static void ASDispatchApply(size_t iterationCount, dispatch_queue_t queue, NSUInteger threadCount, void(^work)(size_t i)) { - if (threadCount == 0) { - threadCount = [NSProcessInfo processInfo].activeProcessorCount * 2; - } - dispatch_group_t group = dispatch_group_create(); - __block size_t trueI = 0; - for (NSUInteger t = 0; t < threadCount; t++) { - dispatch_group_async(group, queue, ^{ - size_t i; - while ((i = __sync_fetch_and_add(&trueI, 1)) < iterationCount) { - work(i); - } - }); - } - dispatch_group_wait(group, DISPATCH_TIME_FOREVER); -}; +void ASDispatchApply(size_t iterationCount, dispatch_queue_t queue, NSUInteger threadCount, NS_NOESCAPE void(^work)(size_t i)); + +/** + * Like dispatch_async, but you can set the thread count. 0 means 2*active CPUs. + * + * Note: The actual number of threads may be lower than threadCount, if libdispatch + * decides the system can't handle it. In reality this rarely happens. + */ +void ASDispatchAsync(size_t iterationCount, dispatch_queue_t queue, NSUInteger threadCount, NS_NOESCAPE void(^work)(size_t i)); + +ASDISPLAYNODE_EXTERN_C_END diff --git a/Source/Private/ASDispatch.m b/Source/Private/ASDispatch.m new file mode 100644 index 0000000000..14c60eb6d3 --- /dev/null +++ b/Source/Private/ASDispatch.m @@ -0,0 +1,59 @@ +// +// ASDispatch.m +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +/** + * Like dispatch_apply, but you can set the thread count. 0 means 2*active CPUs. + * + * Note: The actual number of threads may be lower than threadCount, if libdispatch + * decides the system can't handle it. In reality this rarely happens. + */ +void ASDispatchApply(size_t iterationCount, dispatch_queue_t queue, NSUInteger threadCount, NS_NOESCAPE void(^work)(size_t i)) { + if (threadCount == 0) { + threadCount = NSProcessInfo.processInfo.activeProcessorCount * 2; + } + dispatch_group_t group = dispatch_group_create(); + __block atomic_size_t counter = ATOMIC_VAR_INIT(0); + for (NSUInteger t = 0; t < threadCount; t++) { + dispatch_group_async(group, queue, ^{ + size_t i; + while ((i = atomic_fetch_add(&counter, 1)) < iterationCount) { + work(i); + } + }); + } + dispatch_group_wait(group, DISPATCH_TIME_FOREVER); +}; + +/** + * Like dispatch_async, but you can set the thread count. 0 means 2*active CPUs. + * + * Note: The actual number of threads may be lower than threadCount, if libdispatch + * decides the system can't handle it. In reality this rarely happens. + */ +void ASDispatchAsync(size_t iterationCount, dispatch_queue_t queue, NSUInteger threadCount, NS_NOESCAPE void(^work)(size_t i)) { + if (threadCount == 0) { + threadCount = NSProcessInfo.processInfo.activeProcessorCount * 2; + } + __block atomic_size_t counter = ATOMIC_VAR_INIT(0); + for (NSUInteger t = 0; t < threadCount; t++) { + dispatch_async(queue, ^{ + size_t i; + while ((i = atomic_fetch_add(&counter, 1)) < iterationCount) { + work(i); + } + }); + } +}; + diff --git a/Source/Private/ASDisplayNode+AsyncDisplay.mm b/Source/Private/ASDisplayNode+AsyncDisplay.mm index 3962fbbbff..3e0fb5ab2e 100644 --- a/Source/Private/ASDisplayNode+AsyncDisplay.mm +++ b/Source/Private/ASDisplayNode+AsyncDisplay.mm @@ -1,11 +1,18 @@ // // ASDisplayNode+AsyncDisplay.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -15,6 +22,7 @@ #import #import #import +#import @interface ASDrawingContext : NSObject @@ -227,7 +235,7 @@ static void DrawingContextDataProviderReleaseDataCallback(void *info, __unused c CGRect frame; // If this is the root container node, use a frame with a zero origin to draw into. If not, calculate the correct frame using the node's position, transform and anchorPoint. - if (self.shouldRasterizeDescendants) { + if (self.rasterizesSubtree) { frame = CGRectMake(0.0f, 0.0f, bounds.size.width, bounds.size.height); } else { CGPoint position = self.position; @@ -318,11 +326,10 @@ static void DrawingContextDataProviderReleaseDataCallback(void *info, __unused c flags = _flags; // We always create a graphics context, unless a -display method is used, OR if we are a subnode drawing into a rasterized parent. - BOOL shouldCreateGraphicsContext = (flags.implementsInstanceImageDisplay == NO && flags.implementsImageDisplay == NO && rasterizing == NO); - BOOL shouldBeginRasterizing = (rasterizing == NO && flags.shouldRasterizeDescendants); - BOOL usesInstanceMethodDisplay = (flags.implementsInstanceDrawRect || flags.implementsInstanceImageDisplay); - BOOL usesImageDisplay = (flags.implementsImageDisplay || flags.implementsInstanceImageDisplay); - BOOL usesDrawRect = (flags.implementsDrawRect || flags.implementsInstanceDrawRect); + BOOL shouldCreateGraphicsContext = (flags.implementsImageDisplay == NO && rasterizing == NO); + BOOL shouldBeginRasterizing = (rasterizing == NO && flags.rasterizesSubtree); + BOOL usesImageDisplay = flags.implementsImageDisplay; + BOOL usesDrawRect = flags.implementsDrawRect; if (usesImageDisplay == NO && usesDrawRect == NO && shouldBeginRasterizing == NO) { // Early exit before requesting more expensive properties like bounds and opaque from the layer. @@ -345,8 +352,6 @@ static void DrawingContextDataProviderReleaseDataCallback(void *info, __unused c } ASDisplayNodeAssert(contentsScaleForDisplay != 0.0, @"Invalid contents scale"); - ASDisplayNodeAssert(usesInstanceMethodDisplay == NO || (flags.implementsDrawRect == NO && flags.implementsImageDisplay == NO), - @"Node %@ should not implement both class and instance method display or draw", self); ASDisplayNodeAssert(rasterizing || !(_hierarchyState & ASHierarchyStateRasterized), @"Rasterized descendants should never display unless being drawn into the rasterized container."); @@ -415,22 +420,17 @@ static void DrawingContextDataProviderReleaseDataCallback(void *info, __unused c // For -display methods, we don't have a context, and thus will not call the _willDisplayNodeContentWithRenderingContext or // _didDisplayNodeContentWithRenderingContext blocks. It's up to the implementation of -display... to do what it needs. if (willDisplayNodeContentWithRenderingContext != nil) { - willDisplayNodeContentWithRenderingContext(currentContext); + willDisplayNodeContentWithRenderingContext(currentContext, drawParameters); } - // Decide if we use a class or instance method to draw or display. - id object = usesInstanceMethodDisplay ? self : [self class]; - if (usesImageDisplay) { // If we are using a display method, we'll get an image back directly. - image = [object displayWithParameters:drawParameters - isCancelled:isCancelledBlock]; + image = [self.class displayWithParameters:drawParameters isCancelled:isCancelledBlock]; } else if (usesDrawRect) { // If we're using a draw method, this will operate on the currentContext. - [object drawRect:bounds withParameters:drawParameters - isCancelled:isCancelledBlock isRasterizing:rasterizing]; + [self.class drawRect:bounds withParameters:drawParameters isCancelled:isCancelledBlock isRasterizing:rasterizing]; } if (didDisplayNodeContentWithRenderingContext != nil) { - didDisplayNodeContentWithRenderingContext(currentContext); + didDisplayNodeContentWithRenderingContext(currentContext, drawParameters); } if (shouldCreateGraphicsContext) { @@ -448,6 +448,20 @@ static void DrawingContextDataProviderReleaseDataCallback(void *info, __unused c }; } + /** + If we're profiling, wrap the display block with signpost start and end. + Color the interval red if cancelled, green otherwise. + */ +#if AS_KDEBUG_ENABLE + __unsafe_unretained id ptrSelf = self; + displayBlock = ^{ + ASSignpostStartCustom(ASSignpostLayerDisplay, ptrSelf, 0); + id result = displayBlock(); + ASSignpostEndCustom(ASSignpostLayerDisplay, ptrSelf, 0, isCancelledBlock() ? ASSignpostColorRed : ASSignpostColorGreen); + return result; + }; +#endif + return displayBlock; } @@ -502,7 +516,7 @@ static void DrawingContextDataProviderReleaseDataCallback(void *info, __unused c UIImage *image = (UIImage *)value; BOOL stretchable = (NO == UIEdgeInsetsEqualToEdgeInsets(image.capInsets, UIEdgeInsetsZero)); if (stretchable) { - ASDisplayNodeSetupLayerContentsWithResizableImage(layer, image); + ASDisplayNodeSetResizableContents(layer, image); } else { layer.contentsScale = self.contentsScale; layer.contents = (id)image.CGImage; diff --git a/Source/Private/ASDisplayNode+DebugTiming.h b/Source/Private/ASDisplayNode+DebugTiming.h index d331bbf035..e8f9c42a9b 100644 --- a/Source/Private/ASDisplayNode+DebugTiming.h +++ b/Source/Private/ASDisplayNode+DebugTiming.h @@ -1,11 +1,18 @@ // // ASDisplayNode+DebugTiming.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Private/ASDisplayNode+DebugTiming.mm b/Source/Private/ASDisplayNode+DebugTiming.mm index 66240d5970..39620295bb 100644 --- a/Source/Private/ASDisplayNode+DebugTiming.mm +++ b/Source/Private/ASDisplayNode+DebugTiming.mm @@ -1,11 +1,18 @@ // // ASDisplayNode+DebugTiming.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Private/ASDisplayNode+FrameworkPrivate.h b/Source/Private/ASDisplayNode+FrameworkPrivate.h index e533b200a3..ebcaf23c1c 100644 --- a/Source/Private/ASDisplayNode+FrameworkPrivate.h +++ b/Source/Private/ASDisplayNode+FrameworkPrivate.h @@ -1,11 +1,18 @@ // // ASDisplayNode+FrameworkPrivate.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // // @@ -36,7 +43,7 @@ typedef NS_OPTIONS(NSUInteger, ASHierarchyState) { /** The node may or may not have a supernode, but no supernode has a special hierarchy-influencing option enabled. */ ASHierarchyStateNormal = 0, - /** The node has a supernode with .shouldRasterizeDescendants = YES. + /** The node has a supernode with .rasterizesSubtree = YES. Note: the root node of the rasterized subtree (the one with the property set on it) will NOT have this state set. */ ASHierarchyStateRasterized = 1 << 0, /** The node or one of its supernodes is managed by a class like ASRangeController. Most commonly, these nodes are @@ -49,8 +56,6 @@ typedef NS_OPTIONS(NSUInteger, ASHierarchyState) /** One of the supernodes of this node is performing a transition. Any layout calculated during this state should not be applied immediately, but pending until later. */ ASHierarchyStateLayoutPending = 1 << 3, - ASHierarchyStateYogaLayoutEnabled = 1 << 4, - ASHierarchyStateYogaLayoutMeasuring = 1 << 5 }; ASDISPLAYNODE_INLINE BOOL ASHierarchyStateIncludesLayoutPending(ASHierarchyState hierarchyState) @@ -63,16 +68,6 @@ ASDISPLAYNODE_INLINE BOOL ASHierarchyStateIncludesRangeManaged(ASHierarchyState return ((hierarchyState & ASHierarchyStateRangeManaged) == ASHierarchyStateRangeManaged); } -ASDISPLAYNODE_INLINE BOOL ASHierarchyStateIncludesYogaLayoutMeasuring(ASHierarchyState hierarchyState) -{ - return ((hierarchyState & ASHierarchyStateYogaLayoutMeasuring) == ASHierarchyStateYogaLayoutMeasuring); -} - -ASDISPLAYNODE_INLINE BOOL ASHierarchyStateIncludesYogaLayoutEnabled(ASHierarchyState hierarchyState) -{ - return ((hierarchyState & ASHierarchyStateYogaLayoutEnabled) == ASHierarchyStateYogaLayoutEnabled); -} - ASDISPLAYNODE_INLINE BOOL ASHierarchyStateIncludesRasterized(ASHierarchyState hierarchyState) { return ((hierarchyState & ASHierarchyStateRasterized) == ASHierarchyStateRasterized); @@ -117,6 +112,9 @@ __unused static NSString * _Nonnull NSStringFromASHierarchyState(ASHierarchyStat // Thread safe way to access the bounds of the node @property (nonatomic, assign) CGRect threadSafeBounds; +// Returns the bounds of the node without reaching the view or layer +- (CGRect)_locked_threadSafeBounds; + // delegate to inform of ASInterfaceState changes (used by ASNodeController) @property (nonatomic, weak) id interfaceStateDelegate; @@ -153,6 +151,8 @@ __unused static NSString * _Nonnull NSStringFromASHierarchyState(ASHierarchyStat */ - (BOOL)supportsRangeManagedInterfaceState; +- (BOOL)_locked_displaysAsynchronously; + // The two methods below will eventually be exposed, but their names are subject to change. /** * @abstract Ensure that all rendering is complete for this node and its descendants. @@ -214,6 +214,19 @@ __unused static NSString * _Nonnull NSStringFromASHierarchyState(ASHierarchyStat */ - (BOOL)shouldScheduleDisplayWithNewInterfaceState:(ASInterfaceState)newInterfaceState; +@end + + +@interface ASDisplayNode (ASLayoutInternal) + +/** + * @abstract Informs the root node that the intrinsic size of the receiver is no longer valid. + * + * @discussion The size of a root node is determined by each subnode. Calling invalidateSize will let the root node know + * that the intrinsic size of the receiver node is no longer valid and a resizing of the root node needs to happen. + */ +- (void)_setNeedsLayoutFromAbove; + /** * @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. @@ -221,11 +234,42 @@ __unused static NSString * _Nonnull NSStringFromASHierarchyState(ASHierarchyStat - (void)_rootNodeDidInvalidateSize; /** - * @abstract Subclass hook for nodes that are acting as root nodes. This method is called after measurement - * finished in a layout transition but before the measurement completion handler is called + * 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). + */ +- (void)_locked_measureNodeWithBoundsIfNecessary:(CGRect)bounds; + +/** + * Layout all of the subnodes based on the sublayouts + */ +- (void)_layoutSublayouts; + +@end + +@interface ASDisplayNode (ASLayoutTransitionInternal) + +/** + * If one or multiple layout transitions are in flight this methods returns if the current layout transition that + * happens in in this particular thread was invalidated through another thread is starting a transition for this node + */ +- (BOOL)_isLayoutTransitionInvalid; + +/** + * Internal method that can be overriden by subclasses to add specific behavior after the measurement of a layout + * transition did finish. */ - (void)_layoutTransitionMeasurementDidFinish; +/** + * Informs the node that hte pending layout transition did complete + */ +- (void)_completePendingLayoutTransition; + +/** + * Called if the pending layout transition did complete + */ +- (void)_pendingLayoutTransitionDidComplete; + @end @interface UIView (ASDisplayNodeInternal) diff --git a/Source/Private/ASDisplayNode+FrameworkSubclasses.h b/Source/Private/ASDisplayNode+FrameworkSubclasses.h index 5e5f48eaf4..9b083c5508 100644 --- a/Source/Private/ASDisplayNode+FrameworkSubclasses.h +++ b/Source/Private/ASDisplayNode+FrameworkSubclasses.h @@ -1,11 +1,18 @@ // -// ASDisplayNode+FrameworkPrivate.h -// AsyncDisplayKit +// ASDisplayNode+FrameworkSubclasses.h +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // // diff --git a/Source/Private/ASDisplayNode+UIViewBridge.mm b/Source/Private/ASDisplayNode+UIViewBridge.mm index 913ae61448..88cb4fda2a 100644 --- a/Source/Private/ASDisplayNode+UIViewBridge.mm +++ b/Source/Private/ASDisplayNode+UIViewBridge.mm @@ -1,11 +1,18 @@ // // ASDisplayNode+UIViewBridge.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -40,7 +47,6 @@ #if DISPLAYNODE_USE_LOCKS #define _bridge_prologue_read ASDN::MutexLocker l(__instanceLock__); ASDisplayNodeAssertThreadAffinity(self) #define _bridge_prologue_write ASDN::MutexLocker l(__instanceLock__) -#define _bridge_prologue_write_unlock ASDN::MutexUnlocker u(__instanceLock__) #else #define _bridge_prologue_read ASDisplayNodeAssertThreadAffinity(self) #define _bridge_prologue_write @@ -50,6 +56,9 @@ /// Side Effect: Registers the node with the shared ASPendingStateController if /// the property cannot be immediately applied and the node does not already have pending changes. /// This function must be called with the node's lock already held (after _bridge_prologue_write). +/// *warning* the lock should *not* be released until the pending state is updated if this method +/// returns NO. Otherwise, the pending state can be scheduled and flushed *before* you get a chance +/// to apply it. ASDISPLAYNODE_INLINE BOOL ASDisplayNodeShouldApplyBridgedWriteToView(ASDisplayNode *node) { BOOL loaded = __loaded(node); if (ASDisplayNodeThreadIsMain()) { @@ -79,8 +88,6 @@ if (shouldApply) { _view.viewAndPendingViewStateProperty = (viewAndPendingViewSt #define _setToLayer(layerProperty, layerValueExpr) BOOL shouldApply = ASDisplayNodeShouldApplyBridgedWriteToView(self); \ if (shouldApply) { _layer.layerProperty = (layerValueExpr); } else { ASDisplayNodeGetPendingState(self).layerProperty = (layerValueExpr); } -#define _messageToViewOrLayer(viewAndLayerSelector) (_view ? [_view viewAndLayerSelector] : [_layer viewAndLayerSelector]) - /** * This category implements certain frequently-used properties and methods of UIView and CALayer so that ASDisplayNode clients can just call the view/layer methods on the node, * with minimal loss in performance. Unlike UIView and CALayer methods, these can be called from a non-main thread until the view or layer is created. @@ -188,6 +195,42 @@ if (shouldApply) { _layer.layerProperty = (layerValueExpr); } else { ASDisplayNo _setToLayer(cornerRadius, newCornerRadius); } +- (NSString *)contentsGravity +{ + _bridge_prologue_read; + return _getFromLayer(contentsGravity); +} + +- (void)setContentsGravity:(NSString *)newContentsGravity +{ + _bridge_prologue_write; + _setToLayer(contentsGravity, newContentsGravity); +} + +- (CGRect)contentsRect +{ + _bridge_prologue_read; + return _getFromLayer(contentsRect); +} + +- (void)setContentsRect:(CGRect)newContentsRect +{ + _bridge_prologue_write; + _setToLayer(contentsRect, newContentsRect); +} + +- (CGRect)contentsCenter +{ + _bridge_prologue_read; + return _getFromLayer(contentsCenter); +} + +- (void)setContentsCenter:(CGRect)newContentsCenter +{ + _bridge_prologue_write; + _setToLayer(contentsCenter, newContentsCenter); +} + - (CGFloat)contentsScale { _bridge_prologue_read; @@ -200,6 +243,18 @@ if (shouldApply) { _layer.layerProperty = (layerValueExpr); } else { ASDisplayNo _setToLayer(contentsScale, newContentsScale); } +- (CGFloat)rasterizationScale +{ + _bridge_prologue_read; + return _getFromLayer(rasterizationScale); +} + +- (void)setRasterizationScale:(CGFloat)newRasterizationScale +{ + _bridge_prologue_write; + _setToLayer(rasterizationScale, newRasterizationScale); +} + - (CGRect)bounds { _bridge_prologue_read; @@ -237,7 +292,7 @@ if (shouldApply) { _layer.layerProperty = (layerValueExpr); } else { ASDisplayNo // For classes like ASTableNode, ASCollectionNode, ASScrollNode and similar - make sure UIView gets setFrame: struct ASDisplayNodeFlags flags = _flags; - BOOL specialPropertiesHandling = ASDisplayNodeNeedsSpecialPropertiesHandlingForFlags(flags); + BOOL specialPropertiesHandling = ASDisplayNodeNeedsSpecialPropertiesHandling(checkFlag(Synchronous), flags.layerBacked); BOOL nodeLoaded = __loaded(self); BOOL isMainThread = ASDisplayNodeThreadIsMain(); @@ -301,17 +356,31 @@ if (shouldApply) { _layer.layerProperty = (layerValueExpr); } else { ASDisplayNo - (void)setNeedsDisplay { - _bridge_prologue_write; - if (_hierarchyState & ASHierarchyStateRasterized) { + BOOL isRasterized = NO; + BOOL shouldApply = NO; + id viewOrLayer = nil; + { + _bridge_prologue_write; + isRasterized = _hierarchyState & ASHierarchyStateRasterized; + shouldApply = ASDisplayNodeShouldApplyBridgedWriteToView(self); + viewOrLayer = _view ?: _layer; + + if (isRasterized == NO && shouldApply == NO) { + // We can't release the lock before applying to pending state, or it may be flushed before it can be applied. + [ASDisplayNodeGetPendingState(self) setNeedsDisplay]; + } + } + + if (isRasterized) { ASPerformBlockOnMainThread(^{ // The below operation must be performed on the main thread to ensure against an extremely rare deadlock, where a parent node // begins materializing the view / layer hierarchy (locking itself or a descendant) while this node walks up - // the tree and requires locking that node to access .shouldRasterizeDescendants. + // the tree and requires locking that node to access .rasterizesSubtree. // For this reason, this method should be avoided when possible. Use _hierarchyState & ASHierarchyStateRasterized. ASDisplayNodeAssertMainThread(); ASDisplayNode *rasterizedContainerNode = self.supernode; while (rasterizedContainerNode) { - if (rasterizedContainerNode.shouldRasterizeDescendants) { + if (rasterizedContainerNode.rasterizesSubtree) { break; } rasterizedContainerNode = rasterizedContainerNode.supernode; @@ -319,14 +388,11 @@ if (shouldApply) { _layer.layerProperty = (layerValueExpr); } else { ASDisplayNo [rasterizedContainerNode setNeedsDisplay]; }); } else { - BOOL shouldApply = ASDisplayNodeShouldApplyBridgedWriteToView(self); if (shouldApply) { // If not rasterized, and the node is loaded (meaning we certainly have a view or layer), send a // message to the view/layer first. This is because __setNeedsDisplay calls as scheduleNodeForDisplay, // which may call -displayIfNeeded. We want to ensure the needsDisplay flag is set now, and then cleared. - _messageToViewOrLayer(setNeedsDisplay); - } else { - [ASDisplayNodeGetPendingState(self) setNeedsDisplay]; + [viewOrLayer setNeedsDisplay]; } [self __setNeedsDisplay]; } @@ -334,29 +400,61 @@ if (shouldApply) { _layer.layerProperty = (layerValueExpr); } else { ASDisplayNo - (void)setNeedsLayout { - _bridge_prologue_write; - BOOL shouldApply = ASDisplayNodeShouldApplyBridgedWriteToView(self); + BOOL shouldApply = NO; + BOOL loaded = NO; + id viewOrLayer = nil; + { + _bridge_prologue_write; + shouldApply = ASDisplayNodeShouldApplyBridgedWriteToView(self); + loaded = __loaded(self); + viewOrLayer = _view ?: _layer; + if (shouldApply == NO && loaded) { + // The node is loaded but we're not on main. + // We will call [self __setNeedsLayout] when we apply the pending state. + // We need to call it on main if the node is loaded to support automatic subnode management. + // We can't release the lock before applying to pending state, or it may be flushed before it can be applied. + [ASDisplayNodeGetPendingState(self) setNeedsLayout]; + } + } + if (shouldApply) { // The node is loaded and we're on main. // Quite the opposite of setNeedsDisplay, we must call __setNeedsLayout before messaging // the view or layer to ensure that measurement and implicitly added subnodes have been handled. - - // Calling __setNeedsLayout while holding the property lock can cause deadlocks - _bridge_prologue_write_unlock; [self __setNeedsLayout]; - _bridge_prologue_write; - _messageToViewOrLayer(setNeedsLayout); - } else if (__loaded(self)) { - // The node is loaded but we're not on main. - // We will call [self __setNeedsLayout] when we apply - // the pending state. We need to call it on main if the node is loaded - // to support automatic subnode management. - [ASDisplayNodeGetPendingState(self) setNeedsLayout]; - } else { + [viewOrLayer setNeedsLayout]; + } else if (loaded == NO) { // The node is not loaded and we're not on main. - _bridge_prologue_write_unlock; [self __setNeedsLayout]; + } +} + +- (void)layoutIfNeeded +{ + BOOL shouldApply = NO; + BOOL loaded = NO; + id viewOrLayer = nil; + { _bridge_prologue_write; + shouldApply = ASDisplayNodeShouldApplyBridgedWriteToView(self); + loaded = __loaded(self); + viewOrLayer = _view ?: _layer; + if (shouldApply == NO && loaded) { + // The node is loaded but we're not on main. + // We will call layoutIfNeeded on the view or layer when we apply the pending state. __layout will in turn be called on us (see -[_ASDisplayLayer layoutSublayers]). + // We need to call it on main if the node is loaded to support automatic subnode management. + // We can't release the lock before applying to pending state, or it may be flushed before it can be applied. + [ASDisplayNodeGetPendingState(self) layoutIfNeeded]; + } + } + + if (shouldApply) { + // The node is loaded and we're on main. + // Message the view or layer which in turn will call __layout on us (see -[_ASDisplayLayer layoutSublayers]). + [viewOrLayer layoutIfNeeded]; + } else if (loaded == NO) { + // The node is not loaded and we're not on main. + [self __layout]; } } @@ -592,7 +690,7 @@ if (shouldApply) { _layer.layerProperty = (layerValueExpr); } else { ASDisplayNo if (shouldApply) { CGColorRef oldBackgroundCGColor = CGColorRetain(_layer.backgroundColor); - BOOL specialPropertiesHandling = ASDisplayNodeNeedsSpecialPropertiesHandlingForFlags(_flags); + BOOL specialPropertiesHandling = ASDisplayNodeNeedsSpecialPropertiesHandling(checkFlag(Synchronous), _flags.layerBacked); if (specialPropertiesHandling) { _view.backgroundColor = newBackgroundColor; } else { @@ -740,6 +838,26 @@ if (shouldApply) { _layer.layerProperty = (layerValueExpr); } else { ASDisplayNo _setToLayer(edgeAntialiasingMask, edgeAntialiasingMask); } +- (UISemanticContentAttribute)semanticContentAttribute +{ + _bridge_prologue_read; + if (AS_AT_LEAST_IOS9) { + return _getFromViewOnly(semanticContentAttribute); + } + return UISemanticContentAttributeUnspecified; +} + +- (void)setSemanticContentAttribute:(UISemanticContentAttribute)semanticContentAttribute +{ + _bridge_prologue_write; + if (AS_AT_LEAST_IOS9) { + _setToViewOnly(semanticContentAttribute, semanticContentAttribute); +#if YOGA + [self semanticContentAttributeDidChange:semanticContentAttribute]; +#endif + } +} + @end diff --git a/Source/Private/ASDisplayNodeInternal.h b/Source/Private/ASDisplayNodeInternal.h index 66e6f3b11a..7555e09389 100644 --- a/Source/Private/ASDisplayNodeInternal.h +++ b/Source/Private/ASDisplayNodeInternal.h @@ -1,11 +1,18 @@ // // ASDisplayNodeInternal.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // // @@ -27,11 +34,10 @@ NS_ASSUME_NONNULL_BEGIN @protocol _ASDisplayLayerDelegate; @class _ASDisplayLayer; @class _ASPendingState; -@class ASSentinel; struct ASDisplayNodeFlags; BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector); -BOOL ASDisplayNodeNeedsSpecialPropertiesHandlingForFlags(ASDisplayNodeFlags flags); +BOOL ASDisplayNodeNeedsSpecialPropertiesHandling(BOOL isSynchronous, BOOL isLayerBacked); /// Get the pending view state for the node, creating one if needed. _ASPendingState * ASDisplayNodeGetPendingState(ASDisplayNode * node); @@ -44,10 +50,23 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides) ASDisplayNodeMethodOverrideTouchesEnded = 1 << 2, ASDisplayNodeMethodOverrideTouchesMoved = 1 << 3, ASDisplayNodeMethodOverrideLayoutSpecThatFits = 1 << 4, - ASDisplayNodeMethodOverrideFetchData = 1 << 5, - ASDisplayNodeMethodOverrideClearFetchedData = 1 << 6 + ASDisplayNodeMethodOverrideCalcLayoutThatFits = 1 << 5, + ASDisplayNodeMethodOverrideCalcSizeThatFits = 1 << 6, + ASDisplayNodeMethodOverrideFetchData = 1 << 7, + ASDisplayNodeMethodOverrideClearFetchedData = 1 << 8 }; +typedef NS_OPTIONS(uint_least32_t, ASDisplayNodeAtomicFlags) +{ + Synchronous = 1 << 0, + YogaLayoutInProgress = 1 << 1, +}; + +#define checkFlag(flag) ((_atomicFlags.load() & flag) != 0) +// Returns the old value of the flag as a BOOL. +#define setFlag(flag, x) (((x ? _atomicFlags.fetch_or(flag) \ + : _atomicFlags.fetch_and(~flag)) & flag) != 0) + FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayScheduledNodesNotification; FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp; @@ -56,7 +75,7 @@ FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBefo #define TIME_DISPLAYNODE_OPS 0 // If you're using this information frequently, try: (DEBUG || PROFILE) -@interface ASDisplayNode () +@interface ASDisplayNode () <_ASTransitionContextCompletionDelegate> { @package _ASPendingState *_pendingViewState; @@ -64,16 +83,17 @@ FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBefo UIView *_view; CALayer *_layer; + std::atomic _atomicFlags; + struct ASDisplayNodeFlags { // public properties - unsigned synchronous:1; + unsigned viewEverHadAGestureRecognizerAttached:1; unsigned layerBacked:1; unsigned displaysAsynchronously:1; - unsigned shouldRasterizeDescendants:1; + unsigned rasterizesSubtree:1; unsigned shouldBypassEnsureDisplay:1; unsigned displaySuspended:1; unsigned shouldAnimateSizeChanges:1; - unsigned hasCustomDrawingPriority:1; // Wrapped view handling @@ -89,10 +109,7 @@ FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBefo // methods at all instead it throws away the contents of the layer and nothing will show up. unsigned canCallSetNeedsDisplayOfLayer:1; - // whether custom drawing is enabled - unsigned implementsInstanceDrawRect:1; unsigned implementsDrawRect:1; - unsigned implementsInstanceImageDisplay:1; unsigned implementsImageDisplay:1; unsigned implementsDrawParameters:1; @@ -108,13 +125,17 @@ FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBefo ASDisplayNode * __weak _supernode; NSMutableArray *_subnodes; + // Set this to nil whenever you modify _subnodes + NSArray *_cachedSubnodes; + ASLayoutElementStyle *_style; - ASPrimitiveTraitCollection _primitiveTraitCollection; + std::atomic _primitiveTraitCollection; std::atomic_uint _displaySentinel; // This is the desired contentsScale, not the scale at which the layer's contents should be displayed CGFloat _contentsScaleForDisplay; + ASDisplayNodeMethodOverrides _methodOverrides; UIEdgeInsets _hitTestSlop; @@ -128,22 +149,28 @@ FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBefo NSTimeInterval _defaultLayoutTransitionDuration; NSTimeInterval _defaultLayoutTransitionDelay; UIViewAnimationOptions _defaultLayoutTransitionOptions; - - int32_t _transitionID; - BOOL _transitionInProgress; - int32_t _pendingTransitionID; + ASLayoutSpecBlock _layoutSpecBlock; + + std::atomic _transitionID; + + std::atomic _pendingTransitionID; ASLayoutTransition *_pendingLayoutTransition; std::shared_ptr _calculatedDisplayNodeLayout; std::shared_ptr _pendingDisplayNodeLayout; + /// Sentinel for layout data. Incremented when we get -setNeedsLayout / -invalidateCalculatedLayout. + /// Starts at 1. + std::atomic _layoutVersion; + ASDisplayNodeViewBlock _viewBlock; ASDisplayNodeLayerBlock _layerBlock; NSMutableArray *_onDidLoadBlocks; - Class _viewClass; - Class _layerClass; + Class _viewClass; // nil -> _ASDisplayView + Class _layerClass; // nil -> _ASDisplayLayer UIImage *_placeholderImage; + BOOL _placeholderEnabled; CALayer *_placeholderLayer; // keeps track of nodes/subnodes that have not finished display, used with placeholders @@ -177,11 +204,18 @@ FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBefo NSInteger _layoutComputationNumberOfPasses; #if YOGA - YGNodeRef _yogaNode; - ASDisplayNode *_yogaParent; + // Only ASDisplayNodes are supported in _yogaChildren currently. This means that it is necessary to + // create ASDisplayNodes to make a stack layout when using Yoga. + // However, the implementation is mostly ready for id , with a few areas requiring updates. NSMutableArray *_yogaChildren; + __weak ASDisplayNode *_yogaParent; ASLayout *_yogaCalculatedLayout; #endif + + NSString *_debugName; + +#pragma mark - ASDisplayNode (Debugging) + ASLayout *_unflattenedLayout; #if TIME_DISPLAYNODE_OPS @public @@ -211,7 +245,9 @@ FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBefo - (void)__setNeedsDisplay; /** - * Called from [CALayer layoutSublayers:]. Executes the layout pass for the node + * Called whenever the node needs to layout its subnodes and, if it's already loaded, its subviews. Executes the layout pass for the node + * + * This method is thread-safe but asserts thread affinity. */ - (void)__layout; @@ -272,16 +308,14 @@ FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBefo - (nullable ASDisplayNode *)_supernodeWithClass:(Class)supernodeClass checkViewHierarchy:(BOOL)checkViewHierarchy; /** - * Convenience method to access this node's trait collection struct. Externally, users should interact - * with the trait collection via ASTraitCollection + * Whether this node rasterizes its descendants. See -enableSubtreeRasterization. */ -- (ASPrimitiveTraitCollection)primitiveTraitCollection; +@property (atomic, readonly) BOOL rasterizesSubtree; /** - * This is a non-deprecated internal declaration of the property. Public declaration - * is in ASDisplayNode+Beta.h + * Called if a gesture recognizer was attached to an _ASDisplayView */ -@property (nonatomic, assign) BOOL shouldRasterizeDescendants; +- (void)nodeViewDidAddGestureRecognizer; @end diff --git a/Source/Private/ASDisplayNodeLayout.h b/Source/Private/ASDisplayNodeLayout.h index 9852a2a22d..3bc7782a7f 100644 --- a/Source/Private/ASDisplayNodeLayout.h +++ b/Source/Private/ASDisplayNodeLayout.h @@ -1,13 +1,18 @@ // // ASDisplayNodeLayout.h -// AsyncDisplayKit -// -// Created by Michael Schneider on 08/26/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #pragma once @@ -25,35 +30,26 @@ struct ASDisplayNodeLayout { ASSizeRange constrainedSize; CGSize parentSize; BOOL requestedLayoutFromAbove; - BOOL _dirty; + NSUInteger version; /* * Create a new display node layout with * @param layout The layout to associate, usually returned from a call to -layoutThatFits:parentSize: * @param constrainedSize Constrained size used to create the layout * @param parentSize Parent size used to create the layout + * @param version The version of the source layout data – see ASDisplayNode's _layoutVersion. */ - ASDisplayNodeLayout(ASLayout *layout, ASSizeRange constrainedSize, CGSize parentSize) - : layout(layout), constrainedSize(constrainedSize), parentSize(parentSize), requestedLayoutFromAbove(NO), _dirty(NO) {}; + ASDisplayNodeLayout(ASLayout *layout, ASSizeRange constrainedSize, CGSize parentSize, NSUInteger version) + : layout(layout), constrainedSize(constrainedSize), parentSize(parentSize), requestedLayoutFromAbove(NO), version(version) {}; /* * Creates a layout without any layout associated. By default this display node layout is dirty. */ ASDisplayNodeLayout() - : layout(nil), constrainedSize({{0, 0}, {0, 0}}), parentSize({0, 0}), requestedLayoutFromAbove(NO), _dirty(YES) {}; + : layout(nil), constrainedSize({{0, 0}, {0, 0}}), parentSize({0, 0}), requestedLayoutFromAbove(NO), version(0) {}; /** - * Returns if the display node layout is dirty as it was invalidated or it was created without a layout. + * Returns whether this is valid for a given constrained size, parent size, and version */ - BOOL isDirty(); - - /** - * Returns if ASDisplayNode is still valid for a given constrained and parent size - */ - BOOL isValidForConstrainedSizeParentSize(ASSizeRange constrainedSize, CGSize parentSize); - - /** - * Invalidate the display node layout - */ - void invalidate(); + BOOL isValid(ASSizeRange constrainedSize, CGSize parentSize, NSUInteger version); }; diff --git a/Source/Private/ASDisplayNodeLayout.mm b/Source/Private/ASDisplayNodeLayout.mm index 697d0dea7a..694adab0b8 100644 --- a/Source/Private/ASDisplayNodeLayout.mm +++ b/Source/Private/ASDisplayNodeLayout.mm @@ -1,34 +1,26 @@ // // ASDisplayNodeLayout.mm -// AsyncDisplayKit -// -// Created by Michael Schneider on 08/26/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import -BOOL ASDisplayNodeLayout::isDirty() +BOOL ASDisplayNodeLayout::isValid(ASSizeRange theConstrainedSize, CGSize theParentSize, NSUInteger versionArg) { - return _dirty || layout == nil; -} - -BOOL ASDisplayNodeLayout::isValidForConstrainedSizeParentSize(ASSizeRange theConstrainedSize, CGSize theParentSize) -{ - // Only generate a new layout if: - // - The current layout is dirty - // - The passed constrained size is different than the original layout's parent or constrained size - return (layout != nil - && _dirty == NO - && CGSizeEqualToSize(parentSize, theParentSize) - && ASSizeRangeEqualToSizeRange(constrainedSize, theConstrainedSize)); -} - -void ASDisplayNodeLayout::invalidate() -{ - _dirty = YES; + return version >= versionArg + && layout != nil + && CGSizeEqualToSize(parentSize, theParentSize) + && ASSizeRangeEqualToSizeRange(constrainedSize, theConstrainedSize); } diff --git a/Source/Private/ASDisplayNodeTipState.h b/Source/Private/ASDisplayNodeTipState.h new file mode 100644 index 0000000000..692f3a14e8 --- /dev/null +++ b/Source/Private/ASDisplayNodeTipState.h @@ -0,0 +1,40 @@ +// +// ASDisplayNodeTipState.h +// Texture +// +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +@class ASDisplayNode, ASTipNode; + +NS_ASSUME_NONNULL_BEGIN + +AS_SUBCLASSING_RESTRICTED +@interface ASDisplayNodeTipState : NSObject + +- (instancetype)initWithNode:(ASDisplayNode *)node NS_DESIGNATED_INITIALIZER; + +- (instancetype)init NS_UNAVAILABLE; + +/// Unsafe because once the node is deallocated, we will not be able to access the tip state. +@property (nonatomic, unsafe_unretained, readonly) ASDisplayNode *node; + +/// Main-thread-only. +@property (nonatomic, strong, nullable) ASTipNode *tipNode; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Source/Private/ASDisplayNodeTipState.m b/Source/Private/ASDisplayNodeTipState.m new file mode 100644 index 0000000000..430dce5d36 --- /dev/null +++ b/Source/Private/ASDisplayNodeTipState.m @@ -0,0 +1,33 @@ +// +// ASDisplayNodeTipState.m +// Texture +// +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import "ASDisplayNodeTipState.h" + +@interface ASDisplayNodeTipState () +@end + +@implementation ASDisplayNodeTipState + +- (instancetype)initWithNode:(ASDisplayNode *)node +{ + if (self = [super init]) { + _node = node; + } + return self; +} + +@end diff --git a/Source/Private/ASIGListAdapterBasedDataSource.h b/Source/Private/ASIGListAdapterBasedDataSource.h index 9f629b7852..c46eca4b55 100644 --- a/Source/Private/ASIGListAdapterBasedDataSource.h +++ b/Source/Private/ASIGListAdapterBasedDataSource.h @@ -1,12 +1,23 @@ // // ASIGListAdapterBasedDataSource.h -// AsyncDisplayKit +// Texture // -// Created by Adlai Holler on 1/19/17. -// Copyright © 2017 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 /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // -#if IG_LIST_KIT +#import + +#if AS_IG_LIST_KIT #import #import diff --git a/Source/Private/ASIGListAdapterBasedDataSource.m b/Source/Private/ASIGListAdapterBasedDataSource.m index 912c298a1b..31a6c4582b 100644 --- a/Source/Private/ASIGListAdapterBasedDataSource.m +++ b/Source/Private/ASIGListAdapterBasedDataSource.m @@ -1,18 +1,29 @@ // // ASIGListAdapterBasedDataSource.m -// AsyncDisplayKit +// Texture // -// Created by Adlai Holler on 1/19/17. -// Copyright © 2017 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 /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // -#if IG_LIST_KIT +#import + +#if AS_IG_LIST_KIT #import "ASIGListAdapterBasedDataSource.h" #import #import -typedef IGListSectionController ASIGSectionController; +typedef IGListSectionController ASIGSectionController; /// The optional methods that a class implements from ASSectionController. /// Note: Bitfields are not supported by NSValue so we can't use them. @@ -316,4 +327,4 @@ typedef struct { @end -#endif // IG_LIST_KIT +#endif // AS_IG_LIST_KIT diff --git a/Source/Private/ASImageNode+AnimatedImagePrivate.h b/Source/Private/ASImageNode+AnimatedImagePrivate.h index bdd0a15e49..6c08ce617e 100644 --- a/Source/Private/ASImageNode+AnimatedImagePrivate.h +++ b/Source/Private/ASImageNode+AnimatedImagePrivate.h @@ -1,13 +1,18 @@ // // ASImageNode+AnimatedImagePrivate.h -// AsyncDisplayKit -// -// Created by Garrett Moon on 3/30/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Private/ASImageNode+CGExtras.h b/Source/Private/ASImageNode+CGExtras.h index 3987aa492d..f62380cd06 100644 --- a/Source/Private/ASImageNode+CGExtras.h +++ b/Source/Private/ASImageNode+CGExtras.h @@ -1,11 +1,18 @@ // // ASImageNode+CGExtras.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Private/ASImageNode+CGExtras.m b/Source/Private/ASImageNode+CGExtras.m index 2e4aaf85a2..f8c118ea05 100644 --- a/Source/Private/ASImageNode+CGExtras.m +++ b/Source/Private/ASImageNode+CGExtras.m @@ -1,11 +1,18 @@ // // ASImageNode+CGExtras.m -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Private/ASImageNode+Private.h b/Source/Private/ASImageNode+Private.h index e0880ba5a7..a4fbfcd62c 100644 --- a/Source/Private/ASImageNode+Private.h +++ b/Source/Private/ASImageNode+Private.h @@ -1,9 +1,18 @@ // // ASImageNode+Private.h -// AsyncDisplayKit +// Texture // -// Created by Michael Schneider on 3/20/17. -// Copyright © 2017 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 /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #pragma once diff --git a/Source/Private/ASInternalHelpers.h b/Source/Private/ASInternalHelpers.h index 289d0f7598..31766b366a 100644 --- a/Source/Private/ASInternalHelpers.h +++ b/Source/Private/ASInternalHelpers.h @@ -1,20 +1,23 @@ // // ASInternalHelpers.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "ASAvailability.h" -#if AS_TARGET_OS_IOS #import -#else -#import -#endif #import @@ -43,6 +46,8 @@ CGSize ASFloorSizeValues(CGSize s); CGFloat ASFloorPixelValue(CGFloat f); +CGPoint ASCeilPointValues(CGPoint p); + CGSize ASCeilSizeValues(CGSize s); CGFloat ASCeilPixelValue(CGFloat f); @@ -77,14 +82,7 @@ ASDISPLAYNODE_INLINE BOOL ASImageAlphaInfoIsOpaque(CGImageAlphaInfo info) { */ ASDISPLAYNODE_INLINE void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { if (withoutAnimation) { -#if AS_TARGET_OS_IOS [UIView performWithoutAnimation:block]; -#else - [CATransaction begin]; - [CATransaction setDisableActions: YES]; - block(); - [CATransaction commit]; -#endif } else { block(); } diff --git a/Source/Private/ASInternalHelpers.m b/Source/Private/ASInternalHelpers.m index a2c0a2f7df..088ddccea7 100644 --- a/Source/Private/ASInternalHelpers.m +++ b/Source/Private/ASInternalHelpers.m @@ -1,20 +1,23 @@ // -// ASInternalHelpers.mm -// AsyncDisplayKit +// ASInternalHelpers.m +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import -#if AS_TARGET_OS_IOS #import -#else -#import -#endif #import #import @@ -27,9 +30,7 @@ BOOL ASSubclassOverridesSelector(Class superclass, Class subclass, SEL selector) if (superclass == subclass) return NO; // Even if the class implements the selector, it doesn't override itself. Method superclassMethod = class_getInstanceMethod(superclass, selector); Method subclassMethod = class_getInstanceMethod(subclass, selector); - IMP superclassIMP = superclassMethod ? method_getImplementation(superclassMethod) : NULL; - IMP subclassIMP = subclassMethod ? method_getImplementation(subclassMethod) : NULL; - return (superclassIMP != subclassIMP); + return (superclassMethod != subclassMethod); } BOOL ASSubclassOverridesClassSelector(Class superclass, Class subclass, SEL selector) @@ -37,9 +38,7 @@ BOOL ASSubclassOverridesClassSelector(Class superclass, Class subclass, SEL sele if (superclass == subclass) return NO; // Even if the class implements the selector, it doesn't override itself. Method superclassMethod = class_getClassMethod(superclass, selector); Method subclassMethod = class_getClassMethod(subclass, selector); - IMP superclassIMP = superclassMethod ? method_getImplementation(superclassMethod) : NULL; - IMP subclassIMP = subclassMethod ? method_getImplementation(subclassMethod) : NULL; - return (superclassIMP != subclassIMP); + return (superclassMethod != subclassMethod); } IMP ASReplaceMethodWithBlock(Class c, SEL origSEL, id block) @@ -92,7 +91,6 @@ void ASPerformBackgroundDeallocation(id object) BOOL ASClassRequiresMainThreadDeallocation(Class c) { -#if AS_TARGET_OS_IOS if (c == [UIImage class] || c == [UIColor class]) { return NO; } @@ -102,19 +100,6 @@ BOOL ASClassRequiresMainThreadDeallocation(Class c) || [c isSubclassOfClass:[UIGestureRecognizer class]]) { return YES; } -#else - if (c == [NSImage class] || c == [NSColor class]) { - return NO; - } - - if ([c isSubclassOfClass:[NSResponder class]] - || [c isSubclassOfClass:[CALayer class]] - || [c isSubclassOfClass:[NSGestureRecognizer class]]) { - return YES; - } -#endif - - const char *name = class_getName(c); if (strncmp(name, "UI", 2) == 0 || strncmp(name, "AV", 2) == 0 || strncmp(name, "CA", 2) == 0) { @@ -152,11 +137,7 @@ CGFloat ASScreenScale() static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ ASDisplayNodeCAssertMainThread(); -#if AS_TARGET_OS_IOS __scale = [[UIScreen mainScreen] scale]; -#else - __scale = [[NSScreen mainScreen] backingScaleFactor]; -#endif }); return __scale; } @@ -172,6 +153,11 @@ CGFloat ASFloorPixelValue(CGFloat f) return floor(f * scale) / scale; } +CGPoint ASCeilPointValues(CGPoint p) +{ + return CGPointMake(ASCeilPixelValue(p.x), ASCeilPixelValue(p.y)); +} + CGSize ASCeilSizeValues(CGSize s) { return CGSizeMake(ASCeilPixelValue(s.width), ASCeilPixelValue(s.height)); diff --git a/Source/Private/ASLayerBackingTipProvider.h b/Source/Private/ASLayerBackingTipProvider.h new file mode 100644 index 0000000000..f2be45eb44 --- /dev/null +++ b/Source/Private/ASLayerBackingTipProvider.h @@ -0,0 +1,32 @@ +// +// ASLayerBackingTipProvider.h +// Texture +// +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import "ASTipProvider.h" +#import + +#if AS_ENABLE_TIPS + +NS_ASSUME_NONNULL_BEGIN + +AS_SUBCLASSING_RESTRICTED +@interface ASLayerBackingTipProvider : ASTipProvider + +@end + +NS_ASSUME_NONNULL_END + +#endif // AS_ENABLE_TIPS diff --git a/Source/Private/ASLayerBackingTipProvider.m b/Source/Private/ASLayerBackingTipProvider.m new file mode 100644 index 0000000000..c2d0f5aa22 --- /dev/null +++ b/Source/Private/ASLayerBackingTipProvider.m @@ -0,0 +1,53 @@ +// +// ASLayerBackingTipProvider.m +// Texture +// +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import "ASLayerBackingTipProvider.h" + +#if AS_ENABLE_TIPS + +#import +#import +#import +#import +#import + +@implementation ASLayerBackingTipProvider + +- (ASTip *)tipForNode:(ASDisplayNode *)node +{ + // Already layer-backed. + if (node.layerBacked) { + return nil; + } + + // TODO: Avoid revisiting nodes we already visited + ASDisplayNode *failNode = ASDisplayNodeFindFirstNode(node, ^BOOL(ASDisplayNode * _Nonnull node) { + return !node.supportsLayerBacking; + }); + if (failNode != nil) { + return nil; + } + + ASTip *result = [[ASTip alloc] initWithNode:node + kind:ASTipKindEnableLayerBacking + format:@"Enable layer backing to improve performance"]; + return result; +} + +@end + +#endif // AS_ENABLE_TIPS diff --git a/Source/Private/ASLayoutTransition.h b/Source/Private/ASLayoutTransition.h index f3e384e901..74a6acb984 100644 --- a/Source/Private/ASLayoutTransition.h +++ b/Source/Private/ASLayoutTransition.h @@ -1,13 +1,18 @@ // // ASLayoutTransition.h -// AsyncDisplayKit -// -// Created by Huy Nguyen on 3/8/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -25,9 +30,9 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - ASLayoutElementTransition /** - * Extend the layout element protocol to check if a the element can layout asynchronously. + * Objects conform to this project returns if it's possible to layout asynchronous */ -@protocol ASLayoutElementTransition +@protocol ASLayoutElementTransition /** * @abstract Returns if the layoutElement can be used to layout in an asynchronous way on a background thread. diff --git a/Source/Private/ASLayoutTransition.mm b/Source/Private/ASLayoutTransition.mm index 0d9af91f24..85cfcd2085 100644 --- a/Source/Private/ASLayoutTransition.mm +++ b/Source/Private/ASLayoutTransition.mm @@ -1,13 +1,18 @@ // // ASLayoutTransition.mm -// AsyncDisplayKit -// -// Created by Huy Nguyen on 3/8/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -17,6 +22,7 @@ #import #import // Required for _insertSubnode... / _removeFromSupernode. +#import #import #import @@ -101,6 +107,8 @@ static inline BOOL ASLayoutCanTransitionAsynchronous(ASLayout *layout) { ASDN::MutexSharedLocker l(__instanceLock__); [self calculateSubnodeOperationsIfNeeded]; + // Create an activity even if no subnodes affected. + as_activity_create_for_scope("Apply subnode insertions"); if (_insertedSubnodes.count == 0) { return; } @@ -116,6 +124,7 @@ static inline BOOL ASLayoutCanTransitionAsynchronous(ASLayout *layout) { - (void)applySubnodeRemovals { + as_activity_scope(as_activity_create("Apply subnode removals", AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT)); ASDN::MutexSharedLocker l(__instanceLock__); [self calculateSubnodeOperationsIfNeeded]; @@ -139,6 +148,8 @@ static inline BOOL ASLayoutCanTransitionAsynchronous(ASLayout *layout) { return; } + // Create an activity even if no subnodes affected. + as_activity_create_for_scope("Calculate subnode operations"); ASLayout *previousLayout = _previousLayout->layout; ASLayout *pendingLayout = _pendingLayout->layout; diff --git a/Source/Private/ASMutableElementMap.h b/Source/Private/ASMutableElementMap.h index fa207b5588..6d20b12e85 100644 --- a/Source/Private/ASMutableElementMap.h +++ b/Source/Private/ASMutableElementMap.h @@ -1,9 +1,18 @@ // // ASMutableElementMap.h -// AsyncDisplayKit +// Texture // -// Created by Adlai Holler on 2/23/17. -// Copyright © 2017 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 /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK @@ -11,10 +20,11 @@ #import #import #import +#import NS_ASSUME_NONNULL_BEGIN -@class ASSection, ASCollectionElement; +@class ASSection, ASCollectionElement, _ASHierarchyChangeSet; /** * This mutable version will be removed in the future. It's only here now to keep the diff small @@ -40,12 +50,18 @@ AS_SUBCLASSING_RESTRICTED - (void)removeSectionsOfItems:(NSIndexSet *)itemSections; -- (void)removeSupplementaryElementsInSections:(NSIndexSet *)sections; - - (void)insertEmptySectionsOfItemsAtIndexes:(NSIndexSet *)sections; - (void)insertElement:(ASCollectionElement *)element atIndexPath:(NSIndexPath *)indexPath; +/** + * Update the index paths for all supplementary elements to account for section-level + * deletes, moves, inserts. This must be called before adding new supplementary elements. + * + * This also deletes any supplementary elements in deleted sections. + */ +- (void)migrateSupplementaryElementsWithSectionMapping:(ASIntegerMap *)mapping; + @end @interface ASElementMap (MutableCopying) diff --git a/Source/Private/ASMutableElementMap.m b/Source/Private/ASMutableElementMap.m index fbd3ae5bdf..20e74f5f91 100644 --- a/Source/Private/ASMutableElementMap.m +++ b/Source/Private/ASMutableElementMap.m @@ -1,9 +1,18 @@ // // ASMutableElementMap.m -// AsyncDisplayKit +// Texture // -// Created by Adlai Holler on 2/23/17. -// Copyright © 2017 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 /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #ifndef MINIMAL_ASDK @@ -72,13 +81,6 @@ typedef NSMutableDictionary * _Nonnull supplementariesForKind, BOOL * _Nonnull stop) { - [supplementariesForKind removeObjectsForKeys:[sections as_filterIndexPathsBySection:supplementariesForKind]]; - }]; -} - - (void)insertEmptySectionsOfItemsAtIndexes:(NSIndexSet *)sections { [sections enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) { @@ -101,6 +103,38 @@ typedef NSMutableDictionary * _Nonnull supps, BOOL * _Nonnull stop) { + + // For each index path of that kind, move entries into a new dictionary. + // Note: it's tempting to update the dictionary in-place but because of the likely collision between old and new index paths, + // subtle bugs are possible. Note that this process is rare (only on section-level updates), + // that this work is done off-main, and that the typical supplementary element use case is just 1-per-section (header). + NSMutableDictionary *newSupps = [NSMutableDictionary dictionary]; + [supps enumerateKeysAndObjectsUsingBlock:^(NSIndexPath * _Nonnull oldIndexPath, ASCollectionElement * _Nonnull obj, BOOL * _Nonnull stop) { + NSInteger oldSection = oldIndexPath.section; + NSInteger newSection = [mapping integerForKey:oldSection]; + + if (oldSection == newSection) { + // Index path stayed the same, just copy it over. + newSupps[oldIndexPath] = obj; + } else if (newSection != NSNotFound) { + // Section index changed, move it. + NSIndexPath *newIndexPath = [NSIndexPath indexPathForItem:oldIndexPath.item inSection:newSection]; + newSupps[newIndexPath] = obj; + } + }]; + [supps setDictionary:newSupps]; + }]; +} + #pragma mark - Helpers + (ASMutableSupplementaryElementDictionary *)deepMutableCopyOfElementsDictionary:(ASSupplementaryElementDictionary *)originalDict diff --git a/Source/Private/ASPendingStateController.h b/Source/Private/ASPendingStateController.h index 0399ab9f20..259bf75c7c 100644 --- a/Source/Private/ASPendingStateController.h +++ b/Source/Private/ASPendingStateController.h @@ -1,13 +1,18 @@ // // ASPendingStateController.h -// AsyncDisplayKit -// -// Created by Adlai Holler on 1/7/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Private/ASPendingStateController.mm b/Source/Private/ASPendingStateController.mm index 371a531447..6bc2ad4978 100644 --- a/Source/Private/ASPendingStateController.mm +++ b/Source/Private/ASPendingStateController.mm @@ -1,13 +1,18 @@ // // ASPendingStateController.mm -// AsyncDisplayKit -// -// Created by Adlai Holler on 1/7/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Private/ASRectTable.h b/Source/Private/ASRectTable.h index 3617f44b17..af47f59b6a 100644 --- a/Source/Private/ASRectTable.h +++ b/Source/Private/ASRectTable.h @@ -1,9 +1,18 @@ // // ASRectTable.h -// AsyncDisplayKit +// Texture // -// Created by Adlai Holler on 2/24/17. -// Copyright © 2017 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 /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Private/ASRectTable.m b/Source/Private/ASRectTable.m index 1bd32fb53f..5a98a3f702 100644 --- a/Source/Private/ASRectTable.m +++ b/Source/Private/ASRectTable.m @@ -1,9 +1,18 @@ // // ASRectTable.m -// AsyncDisplayKit +// Texture // -// Created by Adlai Holler on 2/24/17. -// Copyright © 2017 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 /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "ASRectTable.h" @@ -16,7 +25,7 @@ static NSUInteger ASRectSize(const void *ptr) @implementation NSMapTable (ASRectTableMethods) -+ (instancetype)rectTableWithKeyPointerFunctions:(NSPointerFunctions *)keyFuncs ++ (NSMapTable *)rectTableWithKeyPointerFunctions:(NSPointerFunctions *)keyFuncs { static NSPointerFunctions *cgRectFuncs; static dispatch_once_t onceToken; @@ -28,7 +37,7 @@ static NSUInteger ASRectSize(const void *ptr) return [[NSMapTable alloc] initWithKeyPointerFunctions:keyFuncs valuePointerFunctions:cgRectFuncs capacity:0]; } -+ (instancetype)rectTableForStrongObjectPointers ++ (NSMapTable *)rectTableForStrongObjectPointers { static NSPointerFunctions *strongObjectPointerFuncs; static dispatch_once_t onceToken; @@ -38,7 +47,7 @@ static NSUInteger ASRectSize(const void *ptr) return [self rectTableWithKeyPointerFunctions:strongObjectPointerFuncs]; } -+ (instancetype)rectTableForWeakObjectPointers ++ (NSMapTable *)rectTableForWeakObjectPointers { static NSPointerFunctions *weakObjectPointerFuncs; static dispatch_once_t onceToken; diff --git a/Source/Private/ASResponderChainEnumerator.h b/Source/Private/ASResponderChainEnumerator.h index 4e292edab8..df548136b3 100644 --- a/Source/Private/ASResponderChainEnumerator.h +++ b/Source/Private/ASResponderChainEnumerator.h @@ -1,9 +1,18 @@ // // ASResponderChainEnumerator.h -// AsyncDisplayKit +// Texture // -// Created by Adlai Holler on 2/13/17. -// Copyright © 2017 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 /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Private/ASResponderChainEnumerator.m b/Source/Private/ASResponderChainEnumerator.m index 709443dc3d..35b16db0ba 100644 --- a/Source/Private/ASResponderChainEnumerator.m +++ b/Source/Private/ASResponderChainEnumerator.m @@ -1,9 +1,18 @@ // // ASResponderChainEnumerator.m -// AsyncDisplayKit +// Texture // -// Created by Adlai Holler on 2/13/17. -// Copyright © 2017 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 /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "ASResponderChainEnumerator.h" diff --git a/Source/Private/ASSection.h b/Source/Private/ASSection.h index 4944415d5b..57df74c6fa 100644 --- a/Source/Private/ASSection.h +++ b/Source/Private/ASSection.h @@ -1,14 +1,21 @@ // // ASSection.h -// AsyncDisplayKit -// -// Created by Huy Nguyen on 28/08/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. // +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#ifndef MINIMAL_ASDK #import @@ -23,3 +30,5 @@ - (nullable instancetype)initWithSectionID:(NSInteger)sectionID context:(nullable id)context NS_DESIGNATED_INITIALIZER; @end + +#endif diff --git a/Source/Private/ASSection.m b/Source/Private/ASSection.m index 6e743454ab..d3eb5adb85 100644 --- a/Source/Private/ASSection.m +++ b/Source/Private/ASSection.m @@ -1,14 +1,21 @@ // -// ASSection.h -// AsyncDisplayKit -// -// Created by Huy Nguyen on 28/08/16. +// ASSection.m +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. // +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#ifndef MINIMAL_ASDK #import #import @@ -26,3 +33,5 @@ } @end + +#endif diff --git a/Source/Private/ASTableView+Undeprecated.h b/Source/Private/ASTableView+Undeprecated.h index 6ef10380da..f7d0d2a264 100644 --- a/Source/Private/ASTableView+Undeprecated.h +++ b/Source/Private/ASTableView+Undeprecated.h @@ -1,9 +1,18 @@ // // ASTableView+Undeprecated.h -// AsyncDisplayKit +// Texture // -// Created by Adlai Holler on 10/10/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 /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -35,6 +44,10 @@ NS_ASSUME_NONNULL_BEGIN */ - (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style; +@property (nonatomic, assign) BOOL automaticallyAdjustsContentOffset; + +@property (nonatomic, assign) BOOL inverted; + /** * Tuning parameters for a range type in full mode. * diff --git a/Source/Private/ASTip.h b/Source/Private/ASTip.h new file mode 100644 index 0000000000..568f37c199 --- /dev/null +++ b/Source/Private/ASTip.h @@ -0,0 +1,57 @@ +// +// ASTip.h +// Texture +// +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +#if AS_ENABLE_TIPS + +NS_ASSUME_NONNULL_BEGIN + +@class ASDisplayNode; + +typedef NS_ENUM (NSInteger, ASTipKind) { + ASTipKindEnableLayerBacking +}; + +AS_SUBCLASSING_RESTRICTED +@interface ASTip : NSObject + +- (instancetype)initWithNode:(ASDisplayNode *)node + kind:(ASTipKind)kind + format:(NSString *)format, ... NS_FORMAT_FUNCTION(3, 4); + +/** + * The kind of tip this is. + */ +@property (nonatomic, readonly) ASTipKind kind; + +/** + * The node that this tip applies to. + */ +@property (nonatomic, strong, readonly) ASDisplayNode *node; + +/** + * The text to show the user. + */ +@property (nonatomic, strong, readonly) NSString *text; + +@end + +NS_ASSUME_NONNULL_END + +#endif // AS_ENABLE_TIPS diff --git a/Source/Private/ASTip.m b/Source/Private/ASTip.m new file mode 100644 index 0000000000..c96723b9ab --- /dev/null +++ b/Source/Private/ASTip.m @@ -0,0 +1,43 @@ +// +// ASTip.m +// Texture +// +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import "ASTip.h" + +#if AS_ENABLE_TIPS + +#import + +@implementation ASTip + +- (instancetype)initWithNode:(ASDisplayNode *)node + kind:(ASTipKind)kind + format:(NSString *)format, ... +{ + if (self = [super init]) { + _node = node; + _kind = kind; + va_list args; + va_start(args, format); + _text = [[NSString alloc] initWithFormat:format arguments:args]; + va_end(args); + } + return self; +} + +@end + +#endif // AS_ENABLE_TIPS diff --git a/Source/Private/ASTipNode.h b/Source/Private/ASTipNode.h new file mode 100644 index 0000000000..4f54d64330 --- /dev/null +++ b/Source/Private/ASTipNode.h @@ -0,0 +1,47 @@ +// +// ASTipNode.h +// Texture +// +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +#if AS_ENABLE_TIPS + +@class ASTip; + +NS_ASSUME_NONNULL_BEGIN + +/** + * ASTipNode will send these up the responder chain. + */ +@protocol ASTipNodeActions +- (void)didTapTipNode:(id)sender; +@end + +AS_SUBCLASSING_RESTRICTED +@interface ASTipNode : ASControlNode + +- (instancetype)initWithTip:(ASTip *)tip NS_DESIGNATED_INITIALIZER; + +- (instancetype)init NS_UNAVAILABLE; + +@property (nonatomic, strong, readonly) ASTip *tip; + +@end + +NS_ASSUME_NONNULL_END + +#endif // AS_ENABLE_TIPS diff --git a/Source/Private/ASTipNode.m b/Source/Private/ASTipNode.m new file mode 100644 index 0000000000..9eadb04691 --- /dev/null +++ b/Source/Private/ASTipNode.m @@ -0,0 +1,36 @@ +// +// ASTipNode.m +// Texture +// +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import "ASTipNode.h" + +#if AS_ENABLE_TIPS + +@implementation ASTipNode + +- (instancetype)initWithTip:(ASTip *)tip +{ + if (self = [super init]) { + self.backgroundColor = [UIColor colorWithRed:0 green:0.7 blue:0.2 alpha:0.3]; + _tip = tip; + [self addTarget:nil action:@selector(didTapTipNode:) forControlEvents:ASControlNodeEventTouchUpInside]; + } + return self; +} + +@end + +#endif // AS_ENABLE_TIPS diff --git a/Source/Private/ASTipProvider.h b/Source/Private/ASTipProvider.h new file mode 100644 index 0000000000..93855bcd8b --- /dev/null +++ b/Source/Private/ASTipProvider.h @@ -0,0 +1,50 @@ +// +// ASTipProvider.h +// Texture +// +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +#if AS_ENABLE_TIPS + +@class ASDisplayNode, ASTip; + +NS_ASSUME_NONNULL_BEGIN + +/** + * An abstract superclass for all tip providers. + */ +@interface ASTipProvider : NSObject + +/** + * The provider looks at the node's current situation and + * generates a tip, if any, to add to the node. + * + * Subclasses must override this. + */ +- (nullable ASTip *)tipForNode:(ASDisplayNode *)node; + +@end + +@interface ASTipProvider (Lookup) + +@property (class, nonatomic, copy, readonly) NSArray<__kindof ASTipProvider *> *all; + +@end + +NS_ASSUME_NONNULL_END + +#endif // AS_ENABLE_TIPS diff --git a/Source/Private/ASTipProvider.m b/Source/Private/ASTipProvider.m new file mode 100644 index 0000000000..a6a40457b9 --- /dev/null +++ b/Source/Private/ASTipProvider.m @@ -0,0 +1,51 @@ +// +// ASTipProvider.m +// Texture +// +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import "ASTipProvider.h" + +#if AS_ENABLE_TIPS + +#import + +// Concrete classes +#import + +@implementation ASTipProvider + +- (ASTip *)tipForNode:(ASDisplayNode *)node +{ + ASDisplayNodeFailAssert(@"Subclasses must override %@", NSStringFromSelector(_cmd)); + return nil; +} + +@end + +@implementation ASTipProvider (Lookup) + ++ (NSArray *)all +{ + static NSArray *providers; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + providers = @[ [ASLayerBackingTipProvider new] ]; + }); + return providers; +} + +@end + +#endif // AS_ENABLE_TIPS diff --git a/Source/Private/ASTipsController.h b/Source/Private/ASTipsController.h new file mode 100644 index 0000000000..6b47de6178 --- /dev/null +++ b/Source/Private/ASTipsController.h @@ -0,0 +1,48 @@ +// +// ASTipsController.h +// Texture +// +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +#if AS_ENABLE_TIPS + +@class ASDisplayNode; + +NS_ASSUME_NONNULL_BEGIN + +AS_SUBCLASSING_RESTRICTED +@interface ASTipsController : NSObject + +/** + * The shared tip controller instance. + */ +@property (class, strong, readonly) ASTipsController *shared; + +#pragma mark - Node Event Hooks + +/** + * Informs the controller that the sender did enter the visible range. + * + * The controller will run a pass with its tip providers, adding tips as needed. + */ +- (void)nodeDidAppear:(ASDisplayNode *)node; + +@end + +NS_ASSUME_NONNULL_END + +#endif // AS_ENABLE_TIPS diff --git a/Source/Private/ASTipsController.m b/Source/Private/ASTipsController.m new file mode 100644 index 0000000000..c85773dd7a --- /dev/null +++ b/Source/Private/ASTipsController.m @@ -0,0 +1,193 @@ +// +// ASTipsController.m +// Texture +// +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import "ASTipsController.h" + +#if AS_ENABLE_TIPS + +#import +#import +#import +#import +#import +#import + +@interface ASTipsController () + +/// Nil on init, updates to most recent visible window. +@property (nonatomic, strong) UIWindow *appVisibleWindow; + +/// Nil until an application window has become visible. +@property (nonatomic, strong) ASTipsWindow *tipWindow; + +/// Main-thread-only. +@property (nonatomic, strong, readonly) NSMapTable *nodeToTipStates; + +@property (nonatomic, strong) NSMutableArray *nodesThatAppearedDuringRunLoop; + +@end + +@implementation ASTipsController + +#pragma mark - Singleton + ++ (void)load +{ + [NSNotificationCenter.defaultCenter addObserver:self.shared + selector:@selector(windowDidBecomeVisibleWithNotification:) + name:UIWindowDidBecomeVisibleNotification + object:nil]; +} + ++ (ASTipsController *)shared +{ + static ASTipsController *ctrl; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + ctrl = [[ASTipsController alloc] init]; + }); + return ctrl; +} + +#pragma mark - Lifecycle + +- (instancetype)init +{ + ASDisplayNodeAssertMainThread(); + if (self = [super init]) { + _nodeToTipStates = [NSMapTable mapTableWithKeyOptions:(NSPointerFunctionsWeakMemory | NSPointerFunctionsObjectPointerPersonality) valueOptions:NSPointerFunctionsStrongMemory]; + _nodesThatAppearedDuringRunLoop = [NSMutableArray array]; + } + return self; +} + +#pragma mark - Event Handling + +- (void)nodeDidAppear:(ASDisplayNode *)node +{ + ASDisplayNodeAssertMainThread(); + // If they disabled tips on this class, bail. + if (![[node class] enableTips]) { + return; + } + + // If this node appeared in some other window (like our tips window) ignore it. + if (ASFindWindowOfLayer(node.layer) != self.appVisibleWindow) { + return; + } + + [_nodesThatAppearedDuringRunLoop addObject:node]; +} + +// If this is a main window, start watching it and clear out our tip window. +- (void)windowDidBecomeVisibleWithNotification:(NSNotification *)notification +{ + ASDisplayNodeAssertMainThread(); + UIWindow *window = notification.object; + + // If this is the same window we're already watching, bail. + if (window == self.appVisibleWindow) { + return; + } + + // Ignore windows that are not at the normal level or have empty bounds + if (window.windowLevel != UIWindowLevelNormal || CGRectIsEmpty(window.bounds)) { + return; + } + + self.appVisibleWindow = window; + + // Create the tip window if needed. + [self createTipWindowIfNeededWithFrame:window.bounds]; + + // Clear out our tip window and reset our states. + self.tipWindow.mainWindow = window; + [_nodeToTipStates removeAllObjects]; +} + +- (void)runLoopDidTick +{ + NSArray *nodes = [_nodesThatAppearedDuringRunLoop copy]; + [_nodesThatAppearedDuringRunLoop removeAllObjects]; + + // Go through the old array, removing any that have tips but aren't still visible. + for (ASDisplayNode *node in [_nodeToTipStates copy]) { + if (!node.visible) { + [_nodeToTipStates removeObjectForKey:node]; + } + } + + for (ASDisplayNode *node in nodes) { + // Get the tip state for the node. + ASDisplayNodeTipState *tipState = [_nodeToTipStates objectForKey:node]; + + // If the node already has a tip, bail. This could change. + if (tipState.tipNode != nil) { + return; + } + + for (ASTipProvider *provider in ASTipProvider.all) { + ASTip *tip = [provider tipForNode:node]; + if (!tip) { continue; } + + if (!tipState) { + tipState = [self createTipStateForNode:node]; + } + tipState.tipNode = [[ASTipNode alloc] initWithTip:tip]; + } + } + self.tipWindow.nodeToTipStates = _nodeToTipStates; + [self.tipWindow setNeedsLayout]; +} + +#pragma mark - Internal + +- (void)createTipWindowIfNeededWithFrame:(CGRect)tipWindowFrame +{ + // Lots of property accesses, but simple safe code, only run once. + if (self.tipWindow == nil) { + self.tipWindow = [[ASTipsWindow alloc] initWithFrame:tipWindowFrame]; + self.tipWindow.hidden = NO; + [self setupRunLoopObserver]; + } +} + +/** + * In order to keep the UI updated, the tips controller registers a run loop observer. + * Before the transaction commit happens, the tips controller calls -setNeedsLayout + * on the view controller's view. It will then layout the main window, and then update the frames + * for tip nodes accordingly. + */ +- (void)setupRunLoopObserver +{ + CFRunLoopObserverRef o = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopBeforeWaiting, true, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) { + [self runLoopDidTick]; + }); + CFRunLoopAddObserver(CFRunLoopGetMain(), o, kCFRunLoopCommonModes); +} + +- (ASDisplayNodeTipState *)createTipStateForNode:(ASDisplayNode *)node +{ + ASDisplayNodeAssertMainThread(); + ASDisplayNodeTipState *tipState = [[ASDisplayNodeTipState alloc] initWithNode:node]; + [_nodeToTipStates setObject:tipState forKey:node]; + return tipState; +} + +@end + +#endif // AS_ENABLE_TIPS diff --git a/Source/Private/ASTipsWindow.h b/Source/Private/ASTipsWindow.h new file mode 100644 index 0000000000..339071f2f3 --- /dev/null +++ b/Source/Private/ASTipsWindow.h @@ -0,0 +1,44 @@ +// +// ASTipsWindow.h +// Texture +// +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +#if AS_ENABLE_TIPS + +@class ASDisplayNode, ASDisplayNodeTipState; + +NS_ASSUME_NONNULL_BEGIN + +/** + * A window that shows tips. This was originally meant to be a view controller + * but UIKit will not manage view controllers in non-key windows correctly AT ALL + * as of the time of this writing. + */ +AS_SUBCLASSING_RESTRICTED +@interface ASTipsWindow : UIWindow + +/// The main application window that the tips are tracking. +@property (nonatomic, weak) UIWindow *mainWindow; + +@property (nonatomic, copy, nullable) NSMapTable *nodeToTipStates; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/Source/Private/ASTipsWindow.m b/Source/Private/ASTipsWindow.m new file mode 100644 index 0000000000..b1a96fd7e2 --- /dev/null +++ b/Source/Private/ASTipsWindow.m @@ -0,0 +1,106 @@ +// +// ASTipsWindow.m +// Texture +// +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import "ASTipsWindow.h" +#if AS_ENABLE_TIPS + +#import +#import +#import +#import + +@interface ASTipsWindow () +@property (nonatomic, strong, readonly) ASDisplayNode *node; +@end + +@implementation ASTipsWindow + +- (instancetype)initWithFrame:(CGRect)frame +{ + if (self = [super initWithFrame:frame]) { + /** + * UIKit throws an exception if you don't add a root view controller to a window, + * but if the window isn't key, then it doesn't manage the root view controller correctly! + * + * So we set a dummy root view controller and hide it. + */ + self.rootViewController = [UIViewController new]; + self.rootViewController.view.hidden = YES; + + _node = [[ASDisplayNode alloc] init]; + [self addSubnode:_node]; + + self.windowLevel = UIWindowLevelNormal + 1; + self.opaque = NO; + } + return self; +} + +- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event +{ + UIView *result = [super hitTest:point withEvent:event]; + // Ignore touches unless they hit one of my node's subnodes + if (result == _node.view) { + return nil; + } + return result; +} + +- (void)setMainWindow:(UIWindow *)mainWindow +{ + _mainWindow = mainWindow; + for (ASDisplayNode *node in _node.subnodes) { + [node removeFromSupernode]; + } +} + +- (void)didTapTipNode:(ASTipNode *)tipNode +{ + ASDisplayNode.tipDisplayBlock(tipNode.tip.node, tipNode.tip.text); +} + +- (void)layoutSubviews +{ + [super layoutSubviews]; + _node.frame = self.bounds; + + // Ensure the main window is laid out first. + [self.mainWindow layoutIfNeeded]; + + NSMutableSet *tipNodesToRemove = [NSMutableSet setWithArray:_node.subnodes]; + for (ASDisplayNodeTipState *tipState in [_nodeToTipStates objectEnumerator]) { + ASDisplayNode *node = tipState.node; + ASTipNode *tipNode = tipState.tipNode; + [tipNodesToRemove removeObject:tipNode]; + CGRect rect = node.bounds; + rect = [node.view convertRect:rect toView:nil]; + rect = [self convertRect:rect fromView:nil]; + tipNode.frame = rect; + if (tipNode.supernode != _node) { + [_node addSubnode:tipNode]; + } + } + + // Clean up any tip nodes whose target nodes have disappeared. + for (ASTipNode *tipNode in tipNodesToRemove) { + [tipNode removeFromSupernode]; + } +} + +@end + +#endif // AS_ENABLE_TIPS diff --git a/Source/Private/ASTwoDimensionalArrayUtils.h b/Source/Private/ASTwoDimensionalArrayUtils.h index c907227cc2..3c1d5c81a7 100644 --- a/Source/Private/ASTwoDimensionalArrayUtils.h +++ b/Source/Private/ASTwoDimensionalArrayUtils.h @@ -1,11 +1,18 @@ // // ASTwoDimensionalArrayUtils.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -37,6 +44,11 @@ extern void ASDeleteElementsInTwoDimensionalArrayAtIndexPaths(NSMutableArray *mu */ extern NSArray *ASIndexPathsForTwoDimensionalArray(NSArray* twoDimensionalArray) AS_WARN_UNUSED_RESULT; +/** + * Return all the elements of a two-dimensional array, in ascending order. + */ +extern NSArray *ASElementsInTwoDimensionalArray(NSArray* twoDimensionalArray) AS_WARN_UNUSED_RESULT; + /** * Attempt to get the object at the given index path. Returns @c nil if the index path is out of bounds. */ diff --git a/Source/Private/ASTwoDimensionalArrayUtils.m b/Source/Private/ASTwoDimensionalArrayUtils.m index c0d8888a3b..71a48d450b 100644 --- a/Source/Private/ASTwoDimensionalArrayUtils.m +++ b/Source/Private/ASTwoDimensionalArrayUtils.m @@ -1,11 +1,18 @@ // // ASTwoDimensionalArrayUtils.m -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -21,14 +28,15 @@ NSMutableArray *ASTwoDimensionalArrayDeepMutableCopy(NSArray *array) { NSMutableArray *newArray = [NSMutableArray arrayWithCapacity:array.count]; + NSInteger i = 0; for (NSArray *subarray in array) { ASDisplayNodeCAssert([subarray isKindOfClass:[NSArray class]], @"This function expects NSArray *"); - [newArray addObject:[subarray mutableCopy]]; + newArray[i++] = [subarray mutableCopy]; } return newArray; } -void ASDeleteElementsInTwoDimensionalArrayAtIndexPaths(NSMutableArray *mutableArray, NSArray *indexPaths) +void ASDeleteElementsInTwoDimensionalArrayAtIndexPaths(NSMutableArray *mutableArray, NSArray *indexPaths) { if (indexPaths.count == 0) { return; @@ -61,21 +69,35 @@ void ASDeleteElementsInTwoDimensionalArrayAtIndexPaths(NSMutableArray *mutableAr } } -NSArray *ASIndexPathsForTwoDimensionalArray(NSArray * twoDimensionalArray) +NSArray *ASIndexPathsForTwoDimensionalArray(NSArray * twoDimensionalArray) { NSMutableArray *result = [NSMutableArray array]; NSInteger section = 0; + NSInteger i = 0; for (NSArray *subarray in twoDimensionalArray) { ASDisplayNodeCAssert([subarray isKindOfClass:[NSArray class]], @"This function expects NSArray *"); NSInteger itemCount = subarray.count; for (NSInteger item = 0; item < itemCount; item++) { - [result addObject:[NSIndexPath indexPathForItem:item inSection:section]]; + result[i++] = [NSIndexPath indexPathForItem:item inSection:section]; } section++; } return result; } +NSArray *ASElementsInTwoDimensionalArray(NSArray * twoDimensionalArray) +{ + NSMutableArray *result = [NSMutableArray array]; + NSInteger i = 0; + for (NSArray *subarray in twoDimensionalArray) { + ASDisplayNodeCAssert([subarray isKindOfClass:[NSArray class]], @"This function expects NSArray *"); + for (id element in subarray) { + result[i++] = element; + } + } + return result; +} + id ASGetElementInTwoDimensionalArray(NSArray *array, NSIndexPath *indexPath) { ASDisplayNodeCAssertNotNil(indexPath, @"Expected non-nil index path"); diff --git a/Source/Private/ASWeakMap.h b/Source/Private/ASWeakMap.h index cbe75c9747..fb53de15a2 100644 --- a/Source/Private/ASWeakMap.h +++ b/Source/Private/ASWeakMap.h @@ -1,13 +1,18 @@ // // ASWeakMap.h -// AsyncDisplayKit -// -// Created by Chris Danford on 7/11/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Private/ASWeakMap.m b/Source/Private/ASWeakMap.m index 16b7d688d7..2d9eb55230 100644 --- a/Source/Private/ASWeakMap.m +++ b/Source/Private/ASWeakMap.m @@ -1,13 +1,18 @@ // // ASWeakMap.m -// AsyncDisplayKit -// -// Created by Chris Danford on 7/11/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Private/Layout/ASLayoutElementStylePrivate.h b/Source/Private/Layout/ASLayoutElementStylePrivate.h index 34a4852ca7..4e2fab82a7 100644 --- a/Source/Private/Layout/ASLayoutElementStylePrivate.h +++ b/Source/Private/Layout/ASLayoutElementStylePrivate.h @@ -1,11 +1,18 @@ // // ASLayoutElementStylePrivate.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #pragma once diff --git a/Source/Private/Layout/ASLayoutSpecPrivate.h b/Source/Private/Layout/ASLayoutSpecPrivate.h index 084c8bfef4..f8813afebc 100644 --- a/Source/Private/Layout/ASLayoutSpecPrivate.h +++ b/Source/Private/Layout/ASLayoutSpecPrivate.h @@ -1,34 +1,44 @@ // // ASLayoutSpecPrivate.h -// AsyncDisplayKit -// -// Created by Michael Schneider on 9/15/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // -#import #import #import +#if DEBUG + #define AS_DEDUPE_LAYOUT_SPEC_TREE 1 +#else + #define AS_DEDUPE_LAYOUT_SPEC_TREE 0 +#endif + NS_ASSUME_NONNULL_BEGIN @interface ASLayoutSpec() { ASDN::RecursiveMutex __instanceLock__; -#if AS_TARGET_OS_IOS - ASPrimitiveTraitCollection _primitiveTraitCollection; -#endif + std::atomic _primitiveTraitCollection; ASLayoutElementStyle *_style; NSMutableArray *_childrenArray; } +#if AS_DEDUPE_LAYOUT_SPEC_TREE /** * Recursively search the subtree for elements that occur more than once. */ -- (nullable NSSet> *)findDuplicatedElementsInSubtree; +- (nullable NSHashTable> *)findDuplicatedElementsInSubtree; +#endif @end diff --git a/Source/Private/Layout/ASLayoutSpecUtilities.h b/Source/Private/Layout/ASLayoutSpecUtilities.h index 3326280343..7bef9a98d1 100644 --- a/Source/Private/Layout/ASLayoutSpecUtilities.h +++ b/Source/Private/Layout/ASLayoutSpecUtilities.h @@ -1,11 +1,18 @@ // // ASLayoutSpecUtilities.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Private/Layout/ASStackLayoutSpecUtilities.h b/Source/Private/Layout/ASStackLayoutSpecUtilities.h index fbcafdff41..3b677be7e8 100644 --- a/Source/Private/Layout/ASStackLayoutSpecUtilities.h +++ b/Source/Private/Layout/ASStackLayoutSpecUtilities.h @@ -1,11 +1,18 @@ // // ASStackLayoutSpecUtilities.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Private/Layout/ASStackPositionedLayout.h b/Source/Private/Layout/ASStackPositionedLayout.h index dd0b697555..c9ed7fafdf 100644 --- a/Source/Private/Layout/ASStackPositionedLayout.h +++ b/Source/Private/Layout/ASStackPositionedLayout.h @@ -1,11 +1,18 @@ // // ASStackPositionedLayout.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Private/Layout/ASStackPositionedLayout.mm b/Source/Private/Layout/ASStackPositionedLayout.mm index c39bdcdab6..382e416860 100644 --- a/Source/Private/Layout/ASStackPositionedLayout.mm +++ b/Source/Private/Layout/ASStackPositionedLayout.mm @@ -1,11 +1,18 @@ // // ASStackPositionedLayout.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Private/Layout/ASStackUnpositionedLayout.h b/Source/Private/Layout/ASStackUnpositionedLayout.h index 88781b14c1..1f781de505 100644 --- a/Source/Private/Layout/ASStackUnpositionedLayout.h +++ b/Source/Private/Layout/ASStackUnpositionedLayout.h @@ -1,11 +1,18 @@ // // ASStackUnpositionedLayout.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Private/Layout/ASStackUnpositionedLayout.mm b/Source/Private/Layout/ASStackUnpositionedLayout.mm index 3ab2c09a4f..6f43a4535f 100644 --- a/Source/Private/Layout/ASStackUnpositionedLayout.mm +++ b/Source/Private/Layout/ASStackUnpositionedLayout.mm @@ -1,11 +1,18 @@ // // ASStackUnpositionedLayout.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Private/TextExperiment/Component/ASTextDebugOption.h b/Source/Private/TextExperiment/Component/ASTextDebugOption.h new file mode 100755 index 0000000000..1ef4ddd8be --- /dev/null +++ b/Source/Private/TextExperiment/Component/ASTextDebugOption.h @@ -0,0 +1,95 @@ +// +// ASTextDebugOption.h +// Modified from YYText +// +// Created by ibireme on 15/4/8. +// Copyright (c) 2015 ibireme. +// +// This source code is licensed under the MIT-style license found in the +// LICENSE file in the root directory of this source tree. +// + +#import + +@class ASTextDebugOption; + +NS_ASSUME_NONNULL_BEGIN + +/** + The ASTextDebugTarget protocol defines the method a debug target should implement. + A debug target can be add to the global container to receive the shared debug + option changed notification. + */ +@protocol ASTextDebugTarget + +@required +/** + When the shared debug option changed, this method would be called on main thread. + It should return as quickly as possible. The option's property should not be changed + in this method. + + @param option The shared debug option. + */ +- (void)setDebugOption:(nullable ASTextDebugOption *)option; +@end + + + +/** + The debug option for ASText. + */ +@interface ASTextDebugOption : NSObject +@property (nullable, nonatomic, strong) UIColor *baselineColor; ///< baseline color +@property (nullable, nonatomic, strong) UIColor *CTFrameBorderColor; ///< CTFrame path border color +@property (nullable, nonatomic, strong) UIColor *CTFrameFillColor; ///< CTFrame path fill color +@property (nullable, nonatomic, strong) UIColor *CTLineBorderColor; ///< CTLine bounds border color +@property (nullable, nonatomic, strong) UIColor *CTLineFillColor; ///< CTLine bounds fill color +@property (nullable, nonatomic, strong) UIColor *CTLineNumberColor; ///< CTLine line number color +@property (nullable, nonatomic, strong) UIColor *CTRunBorderColor; ///< CTRun bounds border color +@property (nullable, nonatomic, strong) UIColor *CTRunFillColor; ///< CTRun bounds fill color +@property (nullable, nonatomic, strong) UIColor *CTRunNumberColor; ///< CTRun number color +@property (nullable, nonatomic, strong) UIColor *CGGlyphBorderColor; ///< CGGlyph bounds border color +@property (nullable, nonatomic, strong) UIColor *CGGlyphFillColor; ///< CGGlyph bounds fill color + +- (BOOL)needDrawDebug; ///< `YES`: at least one debug color is visible. `NO`: all debug color is invisible/nil. +- (void)clear; ///< Set all debug color to nil. + +/** + Add a debug target. + + @discussion When `setSharedDebugOption:` is called, all added debug target will + receive `setDebugOption:` in main thread. It maintains an unsafe_unretained + reference to this target. The target must to removed before dealloc. + + @param target A debug target. + */ ++ (void)addDebugTarget:(id)target; + +/** + Remove a debug target which is added by `addDebugTarget:`. + + @param target A debug target. + */ ++ (void)removeDebugTarget:(id)target; + +/** + Returns the shared debug option. + + @return The shared debug option, default is nil. + */ ++ (nullable ASTextDebugOption *)sharedDebugOption; + +/** + Set a debug option as shared debug option. + This method must be called on main thread. + + @discussion When call this method, the new option will set to all debug target + which is added by `addDebugTarget:`. + + @param option A new debug option (nil is valid). + */ ++ (void)setSharedDebugOption:(nullable ASTextDebugOption *)option; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Source/Private/TextExperiment/Component/ASTextDebugOption.m b/Source/Private/TextExperiment/Component/ASTextDebugOption.m new file mode 100755 index 0000000000..e823d328b1 --- /dev/null +++ b/Source/Private/TextExperiment/Component/ASTextDebugOption.m @@ -0,0 +1,138 @@ +// +// ASTextDebugOption.m +// Modified from YYText +// +// Created by ibireme on 15/4/8. +// Copyright (c) 2015 ibireme. +// +// This source code is licensed under the MIT-style license found in the +// LICENSE file in the root directory of this source tree. +// + +#import "ASTextDebugOption.h" +#import + +static pthread_mutex_t _sharedDebugLock; +static CFMutableSetRef _sharedDebugTargets = nil; +static ASTextDebugOption *_sharedDebugOption = nil; + +static const void* _sharedDebugSetRetain(CFAllocatorRef allocator, const void *value) { + return value; +} + +static void _sharedDebugSetRelease(CFAllocatorRef allocator, const void *value) { +} + +void _sharedDebugSetFunction(const void *value, void *context) { + id target = (__bridge id)(value); + [target setDebugOption:_sharedDebugOption]; +} + +static void _initSharedDebug() { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + pthread_mutex_init(&_sharedDebugLock, NULL); + CFSetCallBacks callbacks = kCFTypeSetCallBacks; + callbacks.retain = _sharedDebugSetRetain; + callbacks.release = _sharedDebugSetRelease; + _sharedDebugTargets = CFSetCreateMutable(CFAllocatorGetDefault(), 0, &callbacks); + }); +} + +static void _setSharedDebugOption(ASTextDebugOption *option) { + _initSharedDebug(); + pthread_mutex_lock(&_sharedDebugLock); + _sharedDebugOption = option.copy; + CFSetApplyFunction(_sharedDebugTargets, _sharedDebugSetFunction, NULL); + pthread_mutex_unlock(&_sharedDebugLock); +} + +static ASTextDebugOption *_getSharedDebugOption() { + _initSharedDebug(); + pthread_mutex_lock(&_sharedDebugLock); + ASTextDebugOption *op = _sharedDebugOption; + pthread_mutex_unlock(&_sharedDebugLock); + return op; +} + +static void _addDebugTarget(id target) { + _initSharedDebug(); + pthread_mutex_lock(&_sharedDebugLock); + CFSetAddValue(_sharedDebugTargets, (__bridge const void *)(target)); + pthread_mutex_unlock(&_sharedDebugLock); +} + +static void _removeDebugTarget(id target) { + _initSharedDebug(); + pthread_mutex_lock(&_sharedDebugLock); + CFSetRemoveValue(_sharedDebugTargets, (__bridge const void *)(target)); + pthread_mutex_unlock(&_sharedDebugLock); +} + + +@implementation ASTextDebugOption + +- (id)copyWithZone:(NSZone *)zone { + ASTextDebugOption *op = [self.class new]; + op.baselineColor = self.baselineColor; + op.CTFrameBorderColor = self.CTFrameBorderColor; + op.CTFrameFillColor = self.CTFrameFillColor; + op.CTLineBorderColor = self.CTLineBorderColor; + op.CTLineFillColor = self.CTLineFillColor; + op.CTLineNumberColor = self.CTLineNumberColor; + op.CTRunBorderColor = self.CTRunBorderColor; + op.CTRunFillColor = self.CTRunFillColor; + op.CTRunNumberColor = self.CTRunNumberColor; + op.CGGlyphBorderColor = self.CGGlyphBorderColor; + op.CGGlyphFillColor = self.CGGlyphFillColor; + return op; +} + +- (BOOL)needDrawDebug { + if (self.baselineColor || + self.CTFrameBorderColor || + self.CTFrameFillColor || + self.CTLineBorderColor || + self.CTLineFillColor || + self.CTLineNumberColor || + self.CTRunBorderColor || + self.CTRunFillColor || + self.CTRunNumberColor || + self.CGGlyphBorderColor || + self.CGGlyphFillColor) return YES; + return NO; +} + +- (void)clear { + self.baselineColor = nil; + self.CTFrameBorderColor = nil; + self.CTFrameFillColor = nil; + self.CTLineBorderColor = nil; + self.CTLineFillColor = nil; + self.CTLineNumberColor = nil; + self.CTRunBorderColor = nil; + self.CTRunFillColor = nil; + self.CTRunNumberColor = nil; + self.CGGlyphBorderColor = nil; + self.CGGlyphFillColor = nil; +} + ++ (void)addDebugTarget:(id)target { + if (target) _addDebugTarget(target); +} + ++ (void)removeDebugTarget:(id)target { + if (target) _removeDebugTarget(target); +} + ++ (ASTextDebugOption *)sharedDebugOption { + return _getSharedDebugOption(); +} + ++ (void)setSharedDebugOption:(ASTextDebugOption *)option { + NSAssert([NSThread isMainThread], @"This method must be called on the main thread"); + _setSharedDebugOption(option); +} + +@end + diff --git a/Source/Private/TextExperiment/Component/ASTextInput.h b/Source/Private/TextExperiment/Component/ASTextInput.h new file mode 100755 index 0000000000..6b2aa79bbc --- /dev/null +++ b/Source/Private/TextExperiment/Component/ASTextInput.h @@ -0,0 +1,87 @@ +// +// ASTextInput.h +// Modified from YYText +// +// Created by ibireme on 15/4/17. +// Copyright (c) 2015 ibireme. +// +// This source code is licensed under the MIT-style license found in the +// LICENSE file in the root directory of this source tree. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + Text position affinity. For example, the offset appears after the last + character on a line is backward affinity, before the first character on + the following line is forward affinity. + */ +typedef NS_ENUM(NSInteger, ASTextAffinity) { + ASTextAffinityForward = 0, ///< offset appears before the character + ASTextAffinityBackward = 1, ///< offset appears after the character +}; + + +/** + A ASTextPosition object represents a position in a text container; in other words, + it is an index into the backing string in a text-displaying view. + + ASTextPosition has the same API as Apple's implementation in UITextView/UITextField, + so you can alse use it to interact with UITextView/UITextField. + */ +@interface ASTextPosition : UITextPosition + +@property (nonatomic, readonly) NSInteger offset; +@property (nonatomic, readonly) ASTextAffinity affinity; + ++ (instancetype)positionWithOffset:(NSInteger)offset; ++ (instancetype)positionWithOffset:(NSInteger)offset affinity:(ASTextAffinity) affinity; + +- (NSComparisonResult)compare:(id)otherPosition; + +@end + + +/** + A ASTextRange object represents a range of characters in a text container; in other words, + it identifies a starting index and an ending index in string backing a text-displaying view. + + ASTextRange has the same API as Apple's implementation in UITextView/UITextField, + so you can alse use it to interact with UITextView/UITextField. + */ +@interface ASTextRange : UITextRange + +@property (nonatomic, readonly) ASTextPosition *start; +@property (nonatomic, readonly) ASTextPosition *end; +@property (nonatomic, readonly, getter=isEmpty) BOOL empty; + ++ (instancetype)rangeWithRange:(NSRange)range; ++ (instancetype)rangeWithRange:(NSRange)range affinity:(ASTextAffinity) affinity; ++ (instancetype)rangeWithStart:(ASTextPosition *)start end:(ASTextPosition *)end; ++ (instancetype)defaultRange; ///< <{0,0} Forward> + +- (NSRange)asRange; + +@end + + +/** + A ASTextSelectionRect object encapsulates information about a selected range of + text in a text-displaying view. + + ASTextSelectionRect has the same API as Apple's implementation in UITextView/UITextField, + so you can alse use it to interact with UITextView/UITextField. + */ +@interface ASTextSelectionRect : UITextSelectionRect + +@property (nonatomic, readwrite) CGRect rect; +@property (nonatomic, readwrite) UITextWritingDirection writingDirection; +@property (nonatomic, readwrite) BOOL containsStart; +@property (nonatomic, readwrite) BOOL containsEnd; +@property (nonatomic, readwrite) BOOL isVertical; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Source/Private/TextExperiment/Component/ASTextInput.m b/Source/Private/TextExperiment/Component/ASTextInput.m new file mode 100755 index 0000000000..fa1c53b220 --- /dev/null +++ b/Source/Private/TextExperiment/Component/ASTextInput.m @@ -0,0 +1,152 @@ +// +// ASTextInput.m +// Modified from YYText +// +// Created by ibireme on 15/4/17. +// Copyright (c) 2015 ibireme. +// +// This source code is licensed under the MIT-style license found in the +// LICENSE file in the root directory of this source tree. +// + +#import +#import + + +@implementation ASTextPosition + ++ (instancetype)positionWithOffset:(NSInteger)offset { + return [self positionWithOffset:offset affinity:ASTextAffinityForward]; +} + ++ (instancetype)positionWithOffset:(NSInteger)offset affinity:(ASTextAffinity)affinity { + ASTextPosition *p = [self new]; + p->_offset = offset; + p->_affinity = affinity; + return p; +} + +- (instancetype)copyWithZone:(NSZone *)zone { + return [self.class positionWithOffset:_offset affinity:_affinity]; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"<%@: %p> (%@%@)", self.class, self, @(_offset), _affinity == ASTextAffinityForward ? @"F":@"B"]; +} + +- (NSUInteger)hash { + return _offset * 2 + (_affinity == ASTextAffinityForward ? 1 : 0); +} + +- (BOOL)isEqual:(ASTextPosition *)object { + if (!object) return NO; + return _offset == object.offset && _affinity == object.affinity; +} + +- (NSComparisonResult)compare:(ASTextPosition *)otherPosition { + if (!otherPosition) return NSOrderedAscending; + if (_offset < otherPosition.offset) return NSOrderedAscending; + if (_offset > otherPosition.offset) return NSOrderedDescending; + if (_affinity == ASTextAffinityBackward && otherPosition.affinity == ASTextAffinityForward) return NSOrderedAscending; + if (_affinity == ASTextAffinityForward && otherPosition.affinity == ASTextAffinityBackward) return NSOrderedDescending; + return NSOrderedSame; +} + +@end + + + +@implementation ASTextRange { + ASTextPosition *_start; + ASTextPosition *_end; +} + +- (instancetype)init { + self = [super init]; + if (!self) return nil; + _start = [ASTextPosition positionWithOffset:0]; + _end = [ASTextPosition positionWithOffset:0]; + return self; +} + +- (ASTextPosition *)start { + return _start; +} + +- (ASTextPosition *)end { + return _end; +} + +- (BOOL)isEmpty { + return _start.offset == _end.offset; +} + +- (NSRange)asRange { + return NSMakeRange(_start.offset, _end.offset - _start.offset); +} + ++ (instancetype)rangeWithRange:(NSRange)range { + return [self rangeWithRange:range affinity:ASTextAffinityForward]; +} + ++ (instancetype)rangeWithRange:(NSRange)range affinity:(ASTextAffinity)affinity { + ASTextPosition *start = [ASTextPosition positionWithOffset:range.location affinity:affinity]; + ASTextPosition *end = [ASTextPosition positionWithOffset:range.location + range.length affinity:affinity]; + return [self rangeWithStart:start end:end]; +} + ++ (instancetype)rangeWithStart:(ASTextPosition *)start end:(ASTextPosition *)end { + if (!start || !end) return nil; + if ([start compare:end] == NSOrderedDescending) { + ASTEXT_SWAP(start, end); + } + ASTextRange *range = [ASTextRange new]; + range->_start = start; + range->_end = end; + return range; +} + ++ (instancetype)defaultRange { + return [self new]; +} + +- (instancetype)copyWithZone:(NSZone *)zone { + return [self.class rangeWithStart:_start end:_end]; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"<%@: %p> (%@, %@)%@", self.class, self, @(_start.offset), @(_end.offset - _start.offset), _end.affinity == ASTextAffinityForward ? @"F":@"B"]; +} + +- (NSUInteger)hash { + return (sizeof(NSUInteger) == 8 ? OSSwapInt64(_start.hash) : OSSwapInt32(_start.hash)) + _end.hash; +} + +- (BOOL)isEqual:(ASTextRange *)object { + if (!object) return NO; + return [_start isEqual:object.start] && [_end isEqual:object.end]; +} + +@end + + + +@implementation ASTextSelectionRect + +@synthesize rect = _rect; +@synthesize writingDirection = _writingDirection; +@synthesize containsStart = _containsStart; +@synthesize containsEnd = _containsEnd; +@synthesize isVertical = _isVertical; + +- (id)copyWithZone:(NSZone *)zone { + ASTextSelectionRect *one = [self.class new]; + one.rect = _rect; + one.writingDirection = _writingDirection; + one.containsStart = _containsStart; + one.containsEnd = _containsEnd; + one.isVertical = _isVertical; + return one; +} + +@end diff --git a/Source/Private/TextExperiment/Component/ASTextLayout.h b/Source/Private/TextExperiment/Component/ASTextLayout.h new file mode 100755 index 0000000000..e2cd9873e4 --- /dev/null +++ b/Source/Private/TextExperiment/Component/ASTextLayout.h @@ -0,0 +1,548 @@ +// +// ASTextLayout.h +// Modified from YYText +// +// Created by ibireme on 15/3/3. +// Copyright (c) 2015 ibireme. +// +// This source code is licensed under the MIT-style license found in the +// LICENSE file in the root directory of this source tree. +// + +#import +#import + +#import "ASTextDebugOption.h" +#import "ASTextLine.h" +#import "ASTextInput.h" + +@protocol ASTextLinePositionModifier; + +NS_ASSUME_NONNULL_BEGIN + +/** + The max text container size in layout. + */ +extern const CGSize ASTextContainerMaxSize; + +/** + The ASTextContainer class defines a region in which text is laid out. + ASTextLayout class uses one or more ASTextContainer objects to generate layouts. + + A ASTextContainer defines rectangular regions (`size` and `insets`) or + nonrectangular shapes (`path`), and you can define exclusion paths inside the + text container's bounding rectangle so that text flows around the exclusion + path as it is laid out. + + All methods in this class is thread-safe. + + Example: + + ┌─────────────────────────────┐ <------- container + │ │ + │ asdfasdfasdfasdfasdfa <------------ container insets + │ asdfasdfa asdfasdfa │ + │ asdfas asdasd │ + │ asdfa <----------------------- container exclusion path + │ asdfas adfasd │ + │ asdfasdfa asdfasdfa │ + │ asdfasdfasdfasdfasdfa │ + │ │ + └─────────────────────────────┘ + */ +@interface ASTextContainer : NSObject + +/// Creates a container with the specified size. @param size The size. ++ (instancetype)containerWithSize:(CGSize)size; + +/// Creates a container with the specified size and insets. @param size The size. @param insets The text insets. ++ (instancetype)containerWithSize:(CGSize)size insets:(UIEdgeInsets)insets; + +/// Creates a container with the specified path. @param path The path. ++ (instancetype)containerWithPath:(nullable UIBezierPath *)path; + +/// The constrained size. (if the size is larger than ASTextContainerMaxSize, it will be clipped) +@property CGSize size; + +/// The insets for constrained size. The inset value should not be negative. Default is UIEdgeInsetsZero. +@property UIEdgeInsets insets; + +/// Custom constrained path. Set this property to ignore `size` and `insets`. Default is nil. +@property (nullable, copy) UIBezierPath *path; + +/// An array of `UIBezierPath` for path exclusion. Default is nil. +@property (nullable, copy) NSArray *exclusionPaths; + +/// Path line width. Default is 0; +@property CGFloat pathLineWidth; + +/// YES:(PathFillEvenOdd) Text is filled in the area that would be painted if the path were given to CGContextEOFillPath. +/// NO: (PathFillWindingNumber) Text is fill in the area that would be painted if the path were given to CGContextFillPath. +/// Default is YES; +@property (getter=isPathFillEvenOdd) BOOL pathFillEvenOdd; + +/// Whether the text is vertical form (may used for CJK text layout). Default is NO. +@property (getter=isVerticalForm) BOOL verticalForm; + +/// Maximum number of rows, 0 means no limit. Default is 0. +@property NSUInteger maximumNumberOfRows; + +/// The line truncation type, default is none. +@property ASTextTruncationType truncationType; + +/// The truncation token. If nil, the layout will use "…" instead. Default is nil. +@property (nullable, copy) NSAttributedString *truncationToken; + +/// This modifier is applied to the lines before the layout is completed, +/// give you a chance to modify the line position. Default is nil. +@property (nullable, copy) id linePositionModifier; +@end + + +/** + The ASTextLinePositionModifier protocol declares the required method to modify + the line position in text layout progress. See `ASTextLinePositionSimpleModifier` for example. + */ +@protocol ASTextLinePositionModifier +@required +/** + This method will called before layout is completed. The method should be thread-safe. + @param lines An array of ASTextLine. + @param text The full text. + @param container The layout container. + */ +- (void)modifyLines:(NSArray *)lines fromText:(NSAttributedString *)text inContainer:(ASTextContainer *)container; +@end + + +/** + A simple implementation of `ASTextLinePositionModifier`. It can fix each line's position + to a specified value, lets each line of height be the same. + */ +@interface ASTextLinePositionSimpleModifier : NSObject +@property (assign) CGFloat fixedLineHeight; ///< The fixed line height (distance between two baseline). +@end + + + +/** + ASTextLayout class is a readonly class stores text layout result. + All the property in this class is readonly, and should not be changed. + The methods in this class is thread-safe (except some of the draw methods). + + example: (layout with a circle exclusion path) + + ┌──────────────────────────┐ <------ container + │ [--------Line0--------] │ <- Row0 + │ [--------Line1--------] │ <- Row1 + │ [-Line2-] [-Line3-] │ <- Row2 + │ [-Line4] [Line5-] │ <- Row3 + │ [-Line6-] [-Line7-] │ <- Row4 + │ [--------Line8--------] │ <- Row5 + │ [--------Line9--------] │ <- Row6 + └──────────────────────────┘ + */ +@interface ASTextLayout : NSObject + + +#pragma mark - Generate text layout +///============================================================================= +/// @name Generate text layout +///============================================================================= + +/** + Generate a layout with the given container size and text. + + @param size The text container's size + @param text The text (if nil, returns nil). + @return A new layout, or nil when an error occurs. + */ ++ (nullable ASTextLayout *)layoutWithContainerSize:(CGSize)size text:(NSAttributedString *)text; + +/** + Generate a layout with the given container and text. + + @param container The text container (if nil, returns nil). + @param text The text (if nil, returns nil). + @return A new layout, or nil when an error occurs. + */ ++ (nullable ASTextLayout *)layoutWithContainer:(ASTextContainer *)container text:(NSAttributedString *)text; + +/** + Generate a layout with the given container and text. + + @param container The text container (if nil, returns nil). + @param text The text (if nil, returns nil). + @param range The text range (if out of range, returns nil). If the + length of the range is 0, it means the length is no limit. + @return A new layout, or nil when an error occurs. + */ ++ (nullable ASTextLayout *)layoutWithContainer:(ASTextContainer *)container text:(NSAttributedString *)text range:(NSRange)range; + +/** + Generate layouts with the given containers and text. + + @param containers An array of ASTextContainer object (if nil, returns nil). + @param text The text (if nil, returns nil). + @return An array of ASTextLayout object (the count is same as containers), + or nil when an error occurs. + */ ++ (nullable NSArray *)layoutWithContainers:(NSArray *)containers + text:(NSAttributedString *)text; + +/** + Generate layouts with the given containers and text. + + @param containers An array of ASTextContainer object (if nil, returns nil). + @param text The text (if nil, returns nil). + @param range The text range (if out of range, returns nil). If the + length of the range is 0, it means the length is no limit. + @return An array of ASTextLayout object (the count is same as containers), + or nil when an error occurs. + */ ++ (nullable NSArray *)layoutWithContainers:(NSArray *)containers + text:(NSAttributedString *)text + range:(NSRange)range; + +- (instancetype)init UNAVAILABLE_ATTRIBUTE; ++ (instancetype)new UNAVAILABLE_ATTRIBUTE; + + +#pragma mark - Text layout attributes +///============================================================================= +/// @name Text layout attributes +///============================================================================= + +///< The text container +@property (nonatomic, strong, readonly) ASTextContainer *container; +///< The full text +@property (nonatomic, strong, readonly) NSAttributedString *text; +///< The text range in full text +@property (nonatomic, readonly) NSRange range; +///< CTFrameSetter +@property (nonatomic, readonly) CTFramesetterRef frameSetter; +///< CTFrame +@property (nonatomic, readonly) CTFrameRef frame; +///< Array of `ASTextLine`, no truncated +@property (nonatomic, strong, readonly) NSArray *lines; +///< ASTextLine with truncated token, or nil +@property (nullable, nonatomic, strong, readonly) ASTextLine *truncatedLine; +///< Array of `ASTextAttachment` +@property (nullable, nonatomic, strong, readonly) NSArray *attachments; +///< Array of NSRange(wrapped by NSValue) in text +@property (nullable, nonatomic, strong, readonly) NSArray *attachmentRanges; +///< Array of CGRect(wrapped by NSValue) in container +@property (nullable, nonatomic, strong, readonly) NSArray *attachmentRects; +///< Set of Attachment (UIImage/UIView/CALayer) +@property (nullable, nonatomic, strong, readonly) NSSet *attachmentContentsSet; +///< Number of rows +@property (nonatomic, readonly) NSUInteger rowCount; +///< Visible text range +@property (nonatomic, readonly) NSRange visibleRange; +///< Bounding rect (glyphs) +@property (nonatomic, readonly) CGRect textBoundingRect; +///< Bounding size (glyphs and insets, ceil to pixel) +@property (nonatomic, readonly) CGSize textBoundingSize; +///< Has highlight attribute +@property (nonatomic, readonly) BOOL containsHighlight; +///< Has block border attribute +@property (nonatomic, readonly) BOOL needDrawBlockBorder; +///< Has background border attribute +@property (nonatomic, readonly) BOOL needDrawBackgroundBorder; +///< Has shadow attribute +@property (nonatomic, readonly) BOOL needDrawShadow; +///< Has underline attribute +@property (nonatomic, readonly) BOOL needDrawUnderline; +///< Has visible text +@property (nonatomic, readonly) BOOL needDrawText; +///< Has attachment attribute +@property (nonatomic, readonly) BOOL needDrawAttachment; +///< Has inner shadow attribute +@property (nonatomic, readonly) BOOL needDrawInnerShadow; +///< Has strickthrough attribute +@property (nonatomic, readonly) BOOL needDrawStrikethrough; +///< Has border attribute +@property (nonatomic, readonly) BOOL needDrawBorder; + + +#pragma mark - Query information from text layout +///============================================================================= +/// @name Query information from text layout +///============================================================================= + +/** + The first line index for row. + + @param row A row index. + @return The line index, or NSNotFound if not found. + */ +- (NSUInteger)lineIndexForRow:(NSUInteger)row; + +/** + The number of lines for row. + + @param row A row index. + @return The number of lines, or NSNotFound when an error occurs. + */ +- (NSUInteger)lineCountForRow:(NSUInteger)row; + +/** + The row index for line. + + @param line A row index. + + @return The row index, or NSNotFound if not found. + */ +- (NSUInteger)rowIndexForLine:(NSUInteger)line; + +/** + The line index for a specified point. + + @discussion It returns NSNotFound if there's no text at the point. + + @param point A point in the container. + @return The line index, or NSNotFound if not found. + */ +- (NSUInteger)lineIndexForPoint:(CGPoint)point; + +/** + The line index closest to a specified point. + + @param point A point in the container. + @return The line index, or NSNotFound if no line exist in layout. + */ +- (NSUInteger)closestLineIndexForPoint:(CGPoint)point; + +/** + The offset in container for a text position in a specified line. + + @discussion The offset is the text position's baseline point.x. + If the container is vertical form, the offset is the baseline point.y; + + @param position The text position in string. + @param lineIndex The line index. + @return The offset in container, or CGFLOAT_MAX if not found. + */ +- (CGFloat)offsetForTextPosition:(NSUInteger)position lineIndex:(NSUInteger)lineIndex; + +/** + The text position for a point in a specified line. + + @discussion This method just call CTLineGetStringIndexForPosition() and does + NOT consider the emoji, line break character, binding text... + + @param point A point in the container. + @param lineIndex The line index. + @return The text position, or NSNotFound if not found. + */ +- (NSUInteger)textPositionForPoint:(CGPoint)point lineIndex:(NSUInteger)lineIndex; + +/** + The closest text position to a specified point. + + @discussion This method takes into account the restrict of emoji, line break + character, binding text and text affinity. + + @param point A point in the container. + @return A text position, or nil if not found. + */ +- (nullable ASTextPosition *)closestPositionToPoint:(CGPoint)point; + +/** + Returns the new position when moving selection grabber in text view. + + @discussion There are two grabber in the text selection period, user can only + move one grabber at the same time. + + @param point A point in the container. + @param oldPosition The old text position for the moving grabber. + @param otherPosition The other position in text selection view. + + @return A text position, or nil if not found. + */ +- (nullable ASTextPosition *)positionForPoint:(CGPoint)point + oldPosition:(ASTextPosition *)oldPosition + otherPosition:(ASTextPosition *)otherPosition; + +/** + Returns the character or range of characters that is at a given point in the container. + If there is no text at the point, returns nil. + + @discussion This method takes into account the restrict of emoji, line break + character, binding text and text affinity. + + @param point A point in the container. + @return An object representing a range that encloses a character (or characters) + at point. Or nil if not found. + */ +- (nullable ASTextRange *)textRangeAtPoint:(CGPoint)point; + +/** + Returns the closest character or range of characters that is at a given point in + the container. + + @discussion This method takes into account the restrict of emoji, line break + character, binding text and text affinity. + + @param point A point in the container. + @return An object representing a range that encloses a character (or characters) + at point. Or nil if not found. + */ +- (nullable ASTextRange *)closestTextRangeAtPoint:(CGPoint)point; + +/** + If the position is inside an emoji, composed character sequences, line break '\\r\\n' + or custom binding range, then returns the range by extend the position. Otherwise, + returns a zero length range from the position. + + @param position A text-position object that identifies a location in layout. + + @return A text-range object that extend the position. Or nil if an error occurs + */ +- (nullable ASTextRange *)textRangeByExtendingPosition:(ASTextPosition *)position; + +/** + Returns a text range at a given offset in a specified direction from another + text position to its farthest extent in a certain direction of layout. + + @param position A text-position object that identifies a location in layout. + @param direction A constant that indicates a direction of layout (right, left, up, down). + @param offset A character offset from position. + + @return A text-range object that represents the distance from position to the + farthest extent in direction. Or nil if an error occurs. + */ +- (nullable ASTextRange *)textRangeByExtendingPosition:(ASTextPosition *)position + inDirection:(UITextLayoutDirection)direction + offset:(NSInteger)offset; + +/** + Returns the line index for a given text position. + + @discussion This method takes into account the text affinity. + + @param position A text-position object that identifies a location in layout. + @return The line index, or NSNotFound if not found. + */ +- (NSUInteger)lineIndexForPosition:(ASTextPosition *)position; + +/** + Returns the baseline position for a given text position. + + @param position An object that identifies a location in the layout. + @return The baseline position for text, or CGPointZero if not found. + */ +- (CGPoint)linePositionForPosition:(ASTextPosition *)position; + +/** + Returns a rectangle used to draw the caret at a given insertion point. + + @param position An object that identifies a location in the layout. + @return A rectangle that defines the area for drawing the caret. The width is + always zero in normal container, the height is always zero in vertical form container. + If not found, it returns CGRectNull. + */ +- (CGRect)caretRectForPosition:(ASTextPosition *)position; + +/** + Returns the first rectangle that encloses a range of text in the layout. + + @param range An object that represents a range of text in layout. + + @return The first rectangle in a range of text. You might use this rectangle to + draw a correction rectangle. The "first" in the name refers the rectangle + enclosing the first line when the range encompasses multiple lines of text. + If not found, it returns CGRectNull. + */ +- (CGRect)firstRectForRange:(ASTextRange *)range; + +/** + Returns the rectangle union that encloses a range of text in the layout. + + @param range An object that represents a range of text in layout. + + @return A rectangle that defines the area than encloses the range. + If not found, it returns CGRectNull. + */ +- (CGRect)rectForRange:(ASTextRange *)range; + +/** + Returns an array of selection rects corresponding to the range of text. + The start and end rect can be used to show grabber. + + @param range An object representing a range in text. + @return An array of `ASTextSelectionRect` objects that encompass the selection. + If not found, the array is empty. + */ +- (NSArray *)selectionRectsForRange:(ASTextRange *)range; + +/** + Returns an array of selection rects corresponding to the range of text. + + @param range An object representing a range in text. + @return An array of `ASTextSelectionRect` objects that encompass the selection. + If not found, the array is empty. + */ +- (NSArray *)selectionRectsWithoutStartAndEndForRange:(ASTextRange *)range; + +/** + Returns the start and end selection rects corresponding to the range of text. + The start and end rect can be used to show grabber. + + @param range An object representing a range in text. + @return An array of `ASTextSelectionRect` objects contains the start and end to + the selection. If not found, the array is empty. + */ +- (NSArray *)selectionRectsWithOnlyStartAndEndForRange:(ASTextRange *)range; + + +#pragma mark - Draw text layout +///============================================================================= +/// @name Draw text layout +///============================================================================= + +/** + Draw the layout and show the attachments. + + @discussion If the `view` parameter is not nil, then the attachment views will + add to this `view`, and if the `layer` parameter is not nil, then the attachment + layers will add to this `layer`. + + @warning This method should be called on main thread if `view` or `layer` parameter + is not nil and there's UIView or CALayer attachments in layout. + Otherwise, it can be called on any thread. + + @param context The draw context. Pass nil to avoid text and image drawing. + @param size The context size. + @param point The point at which to draw the layout. + @param view The attachment views will add to this view. + @param layer The attachment layers will add to this layer. + @param debug The debug option. Pass nil to avoid debug drawing. + @param cancel The cancel checker block. It will be called in drawing progress. + If it returns YES, the further draw progress will be canceled. + Pass nil to ignore this feature. + */ +- (void)drawInContext:(nullable CGContextRef)context + size:(CGSize)size + point:(CGPoint)point + view:(nullable UIView *)view + layer:(nullable CALayer *)layer + debug:(nullable ASTextDebugOption *)debug + cancel:(nullable BOOL (^)(void))cancel; + +/** + Draw the layout text and image (without view or layer attachments). + + @discussion This method is thread safe and can be called on any thread. + + @param context The draw context. Pass nil to avoid text and image drawing. + @param size The context size. + @param debug The debug option. Pass nil to avoid debug drawing. + */ +- (void)drawInContext:(nullable CGContextRef)context + size:(CGSize)size + debug:(nullable ASTextDebugOption *)debug; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Source/Private/TextExperiment/Component/ASTextLayout.m b/Source/Private/TextExperiment/Component/ASTextLayout.m new file mode 100755 index 0000000000..df8d356f6b --- /dev/null +++ b/Source/Private/TextExperiment/Component/ASTextLayout.m @@ -0,0 +1,3369 @@ +// +// ASTextLayout.m +// Modified from YYText +// +// Created by ibireme on 15/3/3. +// Copyright (c) 2015 ibireme. +// +// This source code is licensed under the MIT-style license found in the +// LICENSE file in the root directory of this source tree. +// + +#import +#import +#import +#import +#import + +const CGSize ASTextContainerMaxSize = (CGSize){0x100000, 0x100000}; + +typedef struct { + CGFloat head; + CGFloat foot; +} ASRowEdge; + +static inline CGSize ASTextClipCGSize(CGSize size) { + if (size.width > ASTextContainerMaxSize.width) size.width = ASTextContainerMaxSize.width; + if (size.height > ASTextContainerMaxSize.height) size.height = ASTextContainerMaxSize.height; + return size; +} + +static inline UIEdgeInsets UIEdgeInsetRotateVertical(UIEdgeInsets insets) { + UIEdgeInsets one; + one.top = insets.left; + one.left = insets.bottom; + one.bottom = insets.right; + one.right = insets.top; + return one; +} + +/** + Sometimes CoreText may convert CGColor to UIColor for `kCTForegroundColorAttributeName` + attribute in iOS7. This should be a bug of CoreText, and may cause crash. Here's a workaround. + */ +static CGColorRef ASTextGetCGColor(CGColorRef color) { + static UIColor *defaultColor; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + defaultColor = [UIColor blackColor]; + }); + if (!color) return defaultColor.CGColor; + if ([((__bridge NSObject *)color) respondsToSelector:@selector(CGColor)]) { + return ((__bridge UIColor *)color).CGColor; + } + return color; +} + +@implementation ASTextLinePositionSimpleModifier +- (void)modifyLines:(NSArray *)lines fromText:(NSAttributedString *)text inContainer:(ASTextContainer *)container { + if (container.verticalForm) { + for (NSUInteger i = 0, max = lines.count; i < max; i++) { + ASTextLine *line = lines[i]; + CGPoint pos = line.position; + pos.x = container.size.width - container.insets.right - line.row * _fixedLineHeight - _fixedLineHeight * 0.9; + line.position = pos; + } + } else { + for (NSUInteger i = 0, max = lines.count; i < max; i++) { + ASTextLine *line = lines[i]; + CGPoint pos = line.position; + pos.y = line.row * _fixedLineHeight + _fixedLineHeight * 0.9 + container.insets.top; + line.position = pos; + } + } +} + +- (id)copyWithZone:(NSZone *)zone { + ASTextLinePositionSimpleModifier *one = [self.class new]; + one.fixedLineHeight = _fixedLineHeight; + return one; +} +@end + + +@implementation ASTextContainer { + @package + BOOL _readonly; ///< used only in ASTextLayout.implementation + dispatch_semaphore_t _lock; + + CGSize _size; + UIEdgeInsets _insets; + UIBezierPath *_path; + NSArray *_exclusionPaths; + BOOL _pathFillEvenOdd; + CGFloat _pathLineWidth; + BOOL _verticalForm; + NSUInteger _maximumNumberOfRows; + ASTextTruncationType _truncationType; + NSAttributedString *_truncationToken; + id _linePositionModifier; +} + ++ (instancetype)containerWithSize:(CGSize)size { + return [self containerWithSize:size insets:UIEdgeInsetsZero]; +} + ++ (instancetype)containerWithSize:(CGSize)size insets:(UIEdgeInsets)insets { + ASTextContainer *one = [self new]; + one.size = ASTextClipCGSize(size); + one.insets = insets; + return one; +} + ++ (instancetype)containerWithPath:(UIBezierPath *)path { + ASTextContainer *one = [self new]; + one.path = path; + return one; +} + +- (instancetype)init { + self = [super init]; + if (!self) return nil; + _lock = dispatch_semaphore_create(1); + _pathFillEvenOdd = YES; + return self; +} + +- (id)copyWithZone:(NSZone *)zone { + ASTextContainer *one = [self.class new]; + dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER); + one->_size = _size; + one->_insets = _insets; + one->_path = _path; + one->_exclusionPaths = _exclusionPaths.copy; + one->_pathFillEvenOdd = _pathFillEvenOdd; + one->_pathLineWidth = _pathLineWidth; + one->_verticalForm = _verticalForm; + one->_maximumNumberOfRows = _maximumNumberOfRows; + one->_truncationType = _truncationType; + one->_truncationToken = _truncationToken.copy; + one->_linePositionModifier = [(NSObject *)_linePositionModifier copy]; + dispatch_semaphore_signal(_lock); + return one; +} + +- (id)mutableCopyWithZone:(nullable NSZone *)zone { + return [self copyWithZone:zone]; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:[NSValue valueWithCGSize:_size] forKey:@"size"]; + [aCoder encodeObject:[NSValue valueWithUIEdgeInsets:_insets] forKey:@"insets"]; + [aCoder encodeObject:_path forKey:@"path"]; + [aCoder encodeObject:_exclusionPaths forKey:@"exclusionPaths"]; + [aCoder encodeBool:_pathFillEvenOdd forKey:@"pathFillEvenOdd"]; + [aCoder encodeDouble:_pathLineWidth forKey:@"pathLineWidth"]; + [aCoder encodeBool:_verticalForm forKey:@"verticalForm"]; + [aCoder encodeInteger:_maximumNumberOfRows forKey:@"maximumNumberOfRows"]; + [aCoder encodeInteger:_truncationType forKey:@"truncationType"]; + [aCoder encodeObject:_truncationToken forKey:@"truncationToken"]; + if ([_linePositionModifier respondsToSelector:@selector(encodeWithCoder:)] && + [_linePositionModifier respondsToSelector:@selector(initWithCoder:)]) { + [aCoder encodeObject:_linePositionModifier forKey:@"linePositionModifier"]; + } +} + +- (id)initWithCoder:(NSCoder *)aDecoder { + self = [self init]; + _size = ((NSValue *)[aDecoder decodeObjectForKey:@"size"]).CGSizeValue; + _insets = ((NSValue *)[aDecoder decodeObjectForKey:@"insets"]).UIEdgeInsetsValue; + _path = [aDecoder decodeObjectForKey:@"path"]; + _exclusionPaths = [aDecoder decodeObjectForKey:@"exclusionPaths"]; + _pathFillEvenOdd = [aDecoder decodeBoolForKey:@"pathFillEvenOdd"]; + _pathLineWidth = [aDecoder decodeDoubleForKey:@"pathLineWidth"]; + _verticalForm = [aDecoder decodeBoolForKey:@"verticalForm"]; + _maximumNumberOfRows = [aDecoder decodeIntegerForKey:@"maximumNumberOfRows"]; + _truncationType = (ASTextTruncationType)[aDecoder decodeIntegerForKey:@"truncationType"]; + _truncationToken = [aDecoder decodeObjectForKey:@"truncationToken"]; + _linePositionModifier = [aDecoder decodeObjectForKey:@"linePositionModifier"]; + return self; +} + +#define Getter(...) \ +dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER); \ +__VA_ARGS__; \ +dispatch_semaphore_signal(_lock); + +#define Setter(...) \ +if (_readonly) { \ +@throw [NSException exceptionWithName:NSInternalInconsistencyException \ +reason:@"Cannot change the property of the 'container' in 'ASTextLayout'." userInfo:nil]; \ +return; \ +} \ +dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER); \ +__VA_ARGS__; \ +dispatch_semaphore_signal(_lock); + +- (CGSize)size { + Getter(CGSize size = _size) return size; +} + +- (void)setSize:(CGSize)size { + Setter(if(!_path) _size = ASTextClipCGSize(size)); +} + +- (UIEdgeInsets)insets { + Getter(UIEdgeInsets insets = _insets) return insets; +} + +- (void)setInsets:(UIEdgeInsets)insets { + Setter(if(!_path){ + if (insets.top < 0) insets.top = 0; + if (insets.left < 0) insets.left = 0; + if (insets.bottom < 0) insets.bottom = 0; + if (insets.right < 0) insets.right = 0; + _insets = insets; + }); +} + +- (UIBezierPath *)path { + Getter(UIBezierPath *path = _path) return path; +} + +- (void)setPath:(UIBezierPath *)path { + Setter( + _path = path.copy; + if (_path) { + CGRect bounds = _path.bounds; + CGSize size = bounds.size; + UIEdgeInsets insets = UIEdgeInsetsZero; + if (bounds.origin.x < 0) size.width += bounds.origin.x; + if (bounds.origin.x > 0) insets.left = bounds.origin.x; + if (bounds.origin.y < 0) size.height += bounds.origin.y; + if (bounds.origin.y > 0) insets.top = bounds.origin.y; + _size = size; + _insets = insets; + } + ); +} + +- (NSArray *)exclusionPaths { + Getter(NSArray *paths = _exclusionPaths) return paths; +} + +- (void)setExclusionPaths:(NSArray *)exclusionPaths { + Setter(_exclusionPaths = exclusionPaths.copy); +} + +- (BOOL)isPathFillEvenOdd { + Getter(BOOL is = _pathFillEvenOdd) return is; +} + +- (void)setPathFillEvenOdd:(BOOL)pathFillEvenOdd { + Setter(_pathFillEvenOdd = pathFillEvenOdd); +} + +- (CGFloat)pathLineWidth { + Getter(CGFloat width = _pathLineWidth) return width; +} + +- (void)setPathLineWidth:(CGFloat)pathLineWidth { + Setter(_pathLineWidth = pathLineWidth); +} + +- (BOOL)isVerticalForm { + Getter(BOOL v = _verticalForm) return v; +} + +- (void)setVerticalForm:(BOOL)verticalForm { + Setter(_verticalForm = verticalForm); +} + +- (NSUInteger)maximumNumberOfRows { + Getter(NSUInteger num = _maximumNumberOfRows) return num; +} + +- (void)setMaximumNumberOfRows:(NSUInteger)maximumNumberOfRows { + Setter(_maximumNumberOfRows = maximumNumberOfRows); +} + +- (ASTextTruncationType)truncationType { + Getter(ASTextTruncationType type = _truncationType) return type; +} + +- (void)setTruncationType:(ASTextTruncationType)truncationType { + Setter(_truncationType = truncationType); +} + +- (NSAttributedString *)truncationToken { + Getter(NSAttributedString *token = _truncationToken) return token; +} + +- (void)setTruncationToken:(NSAttributedString *)truncationToken { + Setter(_truncationToken = truncationToken.copy); +} + +- (void)setLinePositionModifier:(id)linePositionModifier { + Setter(_linePositionModifier = [(NSObject *)linePositionModifier copy]); +} + +- (id)linePositionModifier { + Getter(id m = _linePositionModifier) return m; +} + +#undef Getter +#undef Setter +@end + + + + +@interface ASTextLayout () + +@property (nonatomic, readwrite) ASTextContainer *container; +@property (nonatomic, readwrite) NSAttributedString *text; +@property (nonatomic, readwrite) NSRange range; + +@property (nonatomic, readwrite) CTFramesetterRef frameSetter; +@property (nonatomic, readwrite) CTFrameRef frame; +@property (nonatomic, readwrite) NSArray *lines; +@property (nonatomic, readwrite) ASTextLine *truncatedLine; +@property (nonatomic, readwrite) NSArray *attachments; +@property (nonatomic, readwrite) NSArray *attachmentRanges; +@property (nonatomic, readwrite) NSArray *attachmentRects; +@property (nonatomic, readwrite) NSSet *attachmentContentsSet; +@property (nonatomic, readwrite) NSUInteger rowCount; +@property (nonatomic, readwrite) NSRange visibleRange; +@property (nonatomic, readwrite) CGRect textBoundingRect; +@property (nonatomic, readwrite) CGSize textBoundingSize; + +@property (nonatomic, readwrite) BOOL containsHighlight; +@property (nonatomic, readwrite) BOOL needDrawBlockBorder; +@property (nonatomic, readwrite) BOOL needDrawBackgroundBorder; +@property (nonatomic, readwrite) BOOL needDrawShadow; +@property (nonatomic, readwrite) BOOL needDrawUnderline; +@property (nonatomic, readwrite) BOOL needDrawText; +@property (nonatomic, readwrite) BOOL needDrawAttachment; +@property (nonatomic, readwrite) BOOL needDrawInnerShadow; +@property (nonatomic, readwrite) BOOL needDrawStrikethrough; +@property (nonatomic, readwrite) BOOL needDrawBorder; + +@property (nonatomic, assign) NSUInteger *lineRowsIndex; +@property (nonatomic, assign) ASRowEdge *lineRowsEdge; ///< top-left origin + +@end + + + +@implementation ASTextLayout + +#pragma mark - Layout + +- (instancetype)_init { + self = [super init]; + return self; +} + ++ (ASTextLayout *)layoutWithContainerSize:(CGSize)size text:(NSAttributedString *)text { + ASTextContainer *container = [ASTextContainer containerWithSize:size]; + return [self layoutWithContainer:container text:text]; +} + ++ (ASTextLayout *)layoutWithContainer:(ASTextContainer *)container text:(NSAttributedString *)text { + return [self layoutWithContainer:container text:text range:NSMakeRange(0, text.length)]; +} + ++ (ASTextLayout *)layoutWithContainer:(ASTextContainer *)container text:(NSAttributedString *)text range:(NSRange)range { + ASTextLayout *layout = NULL; + CGPathRef cgPath = nil; + CGRect cgPathBox = {0}; + BOOL isVerticalForm = NO; + BOOL rowMaySeparated = NO; + NSMutableDictionary *frameAttrs = nil; + CTFramesetterRef ctSetter = NULL; + CTFrameRef ctFrame = NULL; + CFArrayRef ctLines = nil; + CGPoint *lineOrigins = NULL; + NSUInteger lineCount = 0; + NSMutableArray *lines = nil; + NSMutableArray *attachments = nil; + NSMutableArray *attachmentRanges = nil; + NSMutableArray *attachmentRects = nil; + NSMutableSet *attachmentContentsSet = nil; + BOOL needTruncation = NO; + NSAttributedString *truncationToken = nil; + ASTextLine *truncatedLine = nil; + ASRowEdge *lineRowsEdge = NULL; + NSUInteger *lineRowsIndex = NULL; + NSRange visibleRange; + NSUInteger maximumNumberOfRows = 0; + BOOL constraintSizeIsExtended = NO; + CGRect constraintRectBeforeExtended = {0}; +#define FAIL_AND_RETURN {\ + if (cgPath) CFRelease(cgPath); \ + if (ctSetter) CFRelease(ctSetter); \ + if (ctFrame) CFRelease(ctFrame); \ + if (lineOrigins) free(lineOrigins); \ + if (lineRowsEdge) free(lineRowsEdge); \ + if (lineRowsIndex) free(lineRowsIndex); \ + return nil; } + + text = text.mutableCopy; + container = container.copy; + if (!text || !container) return nil; + if (range.location + range.length > text.length) return nil; + container->_readonly = YES; + maximumNumberOfRows = container.maximumNumberOfRows; + + // CoreText bug when draw joined emoji since iOS 8.3. + // See -[NSMutableAttributedString setClearColorToJoinedEmoji] for more information. + static BOOL needFixJoinedEmojiBug = NO; + // It may use larger constraint size when create CTFrame with + // CTFramesetterCreateFrame in iOS 10. + static BOOL needFixLayoutSizeBug = NO; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + double systemVersionDouble = [UIDevice currentDevice].systemVersion.doubleValue; + if (8.3 <= systemVersionDouble && systemVersionDouble < 9) { + needFixJoinedEmojiBug = YES; + } + if (systemVersionDouble >= 10) { + needFixLayoutSizeBug = YES; + } + }); + if (needFixJoinedEmojiBug) { + [((NSMutableAttributedString *)text) as_setClearColorToJoinedEmoji]; + } + + layout = [[ASTextLayout alloc] _init]; + layout.text = text; + layout.container = container; + layout.range = range; + isVerticalForm = container.verticalForm; + + // set cgPath and cgPathBox + if (container.path == nil && container.exclusionPaths.count == 0) { + if (container.size.width <= 0 || container.size.height <= 0) FAIL_AND_RETURN + CGRect rect = (CGRect) {CGPointZero, container.size }; + if (needFixLayoutSizeBug) { + constraintSizeIsExtended = YES; + constraintRectBeforeExtended = UIEdgeInsetsInsetRect(rect, container.insets); + constraintRectBeforeExtended = CGRectStandardize(constraintRectBeforeExtended); + if (container.isVerticalForm) { + rect.size.width = ASTextContainerMaxSize.width; + } else { + rect.size.height = ASTextContainerMaxSize.height; + } + } + rect = UIEdgeInsetsInsetRect(rect, container.insets); + rect = CGRectStandardize(rect); + cgPathBox = rect; + rect = CGRectApplyAffineTransform(rect, CGAffineTransformMakeScale(1, -1)); + cgPath = CGPathCreateWithRect(rect, NULL); // let CGPathIsRect() returns true + } else if (container.path && CGPathIsRect(container.path.CGPath, &cgPathBox) && container.exclusionPaths.count == 0) { + CGRect rect = CGRectApplyAffineTransform(cgPathBox, CGAffineTransformMakeScale(1, -1)); + cgPath = CGPathCreateWithRect(rect, NULL); // let CGPathIsRect() returns true + } else { + rowMaySeparated = YES; + CGMutablePathRef path = NULL; + if (container.path) { + path = CGPathCreateMutableCopy(container.path.CGPath); + } else { + CGRect rect = (CGRect) {CGPointZero, container.size }; + rect = UIEdgeInsetsInsetRect(rect, container.insets); + CGPathRef rectPath = CGPathCreateWithRect(rect, NULL); + if (rectPath) { + path = CGPathCreateMutableCopy(rectPath); + CGPathRelease(rectPath); + } + } + if (path) { + [layout.container.exclusionPaths enumerateObjectsUsingBlock: ^(UIBezierPath *onePath, NSUInteger idx, BOOL *stop) { + CGPathAddPath(path, NULL, onePath.CGPath); + }]; + + cgPathBox = CGPathGetPathBoundingBox(path); + CGAffineTransform trans = CGAffineTransformMakeScale(1, -1); + CGMutablePathRef transPath = CGPathCreateMutableCopyByTransformingPath(path, &trans); + CGPathRelease(path); + path = transPath; + } + cgPath = path; + } + if (!cgPath) FAIL_AND_RETURN + + // frame setter config + frameAttrs = [NSMutableDictionary dictionary]; + if (container.isPathFillEvenOdd == NO) { + frameAttrs[(id)kCTFramePathFillRuleAttributeName] = @(kCTFramePathFillWindingNumber); + } + if (container.pathLineWidth > 0) { + frameAttrs[(id)kCTFramePathWidthAttributeName] = @(container.pathLineWidth); + } + if (container.isVerticalForm == YES) { + frameAttrs[(id)kCTFrameProgressionAttributeName] = @(kCTFrameProgressionRightToLeft); + } + + // create CoreText objects + ctSetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)text); + if (!ctSetter) FAIL_AND_RETURN + ctFrame = CTFramesetterCreateFrame(ctSetter, ASTextCFRangeFromNSRange(range), cgPath, (CFDictionaryRef)frameAttrs); + if (!ctFrame) FAIL_AND_RETURN + lines = [NSMutableArray new]; + ctLines = CTFrameGetLines(ctFrame); + lineCount = CFArrayGetCount(ctLines); + if (lineCount > 0) { + lineOrigins = (CGPoint *)malloc(lineCount * sizeof(CGPoint)); + if (lineOrigins == NULL) FAIL_AND_RETURN + CTFrameGetLineOrigins(ctFrame, CFRangeMake(0, lineCount), lineOrigins); + } + + CGRect textBoundingRect = CGRectZero; + CGSize textBoundingSize = CGSizeZero; + NSInteger rowIdx = -1; + NSUInteger rowCount = 0; + CGRect lastRect = CGRectMake(0, -FLT_MAX, 0, 0); + CGPoint lastPosition = CGPointMake(0, -FLT_MAX); + if (isVerticalForm) { + lastRect = CGRectMake(FLT_MAX, 0, 0, 0); + lastPosition = CGPointMake(FLT_MAX, 0); + } + + // calculate line frame + NSUInteger lineCurrentIdx = 0; + for (NSUInteger i = 0; i < lineCount; i++) { + CTLineRef ctLine = (CTLineRef)CFArrayGetValueAtIndex(ctLines, i); + CFArrayRef ctRuns = CTLineGetGlyphRuns(ctLine); + if (!ctRuns || CFArrayGetCount(ctRuns) == 0) continue; + + // CoreText coordinate system + CGPoint ctLineOrigin = lineOrigins[i]; + + // UIKit coordinate system + CGPoint position; + position.x = cgPathBox.origin.x + ctLineOrigin.x; + position.y = cgPathBox.size.height + cgPathBox.origin.y - ctLineOrigin.y; + + ASTextLine *line = [ASTextLine lineWithCTLine:ctLine position:position vertical:isVerticalForm]; + CGRect rect = line.bounds; + + if (constraintSizeIsExtended) { + if (isVerticalForm) { + if (rect.origin.x + rect.size.width > + constraintRectBeforeExtended.origin.x + + constraintRectBeforeExtended.size.width) break; + } else { + if (rect.origin.y + rect.size.height > + constraintRectBeforeExtended.origin.y + + constraintRectBeforeExtended.size.height) break; + } + } + + BOOL newRow = YES; + if (rowMaySeparated && position.x != lastPosition.x) { + if (isVerticalForm) { + if (rect.size.width > lastRect.size.width) { + if (rect.origin.x > lastPosition.x && lastPosition.x > rect.origin.x - rect.size.width) newRow = NO; + } else { + if (lastRect.origin.x > position.x && position.x > lastRect.origin.x - lastRect.size.width) newRow = NO; + } + } else { + if (rect.size.height > lastRect.size.height) { + if (rect.origin.y < lastPosition.y && lastPosition.y < rect.origin.y + rect.size.height) newRow = NO; + } else { + if (lastRect.origin.y < position.y && position.y < lastRect.origin.y + lastRect.size.height) newRow = NO; + } + } + } + + if (newRow) rowIdx++; + lastRect = rect; + lastPosition = position; + + line.index = lineCurrentIdx; + line.row = rowIdx; + [lines addObject:line]; + rowCount = rowIdx + 1; + lineCurrentIdx ++; + + if (i == 0) textBoundingRect = rect; + else { + if (maximumNumberOfRows == 0 || rowIdx < maximumNumberOfRows) { + textBoundingRect = CGRectUnion(textBoundingRect, rect); + } + } + } + + if (rowCount > 0) { + if (maximumNumberOfRows > 0) { + if (rowCount > maximumNumberOfRows) { + needTruncation = YES; + rowCount = maximumNumberOfRows; + do { + ASTextLine *line = lines.lastObject; + if (!line) break; + if (line.row < rowCount) break; + [lines removeLastObject]; + } while (1); + } + } + ASTextLine *lastLine = lines.lastObject; + if (!needTruncation && lastLine.range.location + lastLine.range.length < text.length) { + needTruncation = YES; + } + + // Give user a chance to modify the line's position. + if (container.linePositionModifier) { + [container.linePositionModifier modifyLines:lines fromText:text inContainer:container]; + textBoundingRect = CGRectZero; + for (NSUInteger i = 0, max = lines.count; i < max; i++) { + ASTextLine *line = lines[i]; + if (i == 0) textBoundingRect = line.bounds; + else textBoundingRect = CGRectUnion(textBoundingRect, line.bounds); + } + } + + lineRowsEdge = (ASRowEdge *)calloc(rowCount, sizeof(ASRowEdge)); + if (lineRowsEdge == NULL) FAIL_AND_RETURN + lineRowsIndex = (NSUInteger *)calloc(rowCount, sizeof(NSUInteger)); + if (lineRowsIndex == NULL) FAIL_AND_RETURN + NSInteger lastRowIdx = -1; + CGFloat lastHead = 0; + CGFloat lastFoot = 0; + for (NSUInteger i = 0, max = lines.count; i < max; i++) { + ASTextLine *line = lines[i]; + CGRect rect = line.bounds; + if ((NSInteger)line.row != lastRowIdx) { + if (lastRowIdx >= 0) { + lineRowsEdge[lastRowIdx] = (ASRowEdge) {.head = lastHead, .foot = lastFoot }; + } + lastRowIdx = line.row; + lineRowsIndex[lastRowIdx] = i; + if (isVerticalForm) { + lastHead = rect.origin.x + rect.size.width; + lastFoot = lastHead - rect.size.width; + } else { + lastHead = rect.origin.y; + lastFoot = lastHead + rect.size.height; + } + } else { + if (isVerticalForm) { + lastHead = MAX(lastHead, rect.origin.x + rect.size.width); + lastFoot = MIN(lastFoot, rect.origin.x); + } else { + lastHead = MIN(lastHead, rect.origin.y); + lastFoot = MAX(lastFoot, rect.origin.y + rect.size.height); + } + } + } + lineRowsEdge[lastRowIdx] = (ASRowEdge) {.head = lastHead, .foot = lastFoot }; + + for (NSUInteger i = 1; i < rowCount; i++) { + ASRowEdge v0 = lineRowsEdge[i - 1]; + ASRowEdge v1 = lineRowsEdge[i]; + lineRowsEdge[i - 1].foot = lineRowsEdge[i].head = (v0.foot + v1.head) * 0.5; + } + } + + { // calculate bounding size + CGRect rect = textBoundingRect; + if (container.path) { + if (container.pathLineWidth > 0) { + CGFloat inset = container.pathLineWidth / 2; + rect = CGRectInset(rect, -inset, -inset); + } + } else { + rect = UIEdgeInsetsInsetRect(rect,ASTextUIEdgeInsetsInvert(container.insets)); + } + rect = CGRectStandardize(rect); + CGSize size = rect.size; + if (container.verticalForm) { + size.width += container.size.width - (rect.origin.x + rect.size.width); + } else { + size.width += rect.origin.x; + } + size.height += rect.origin.y; + if (size.width < 0) size.width = 0; + if (size.height < 0) size.height = 0; + size.width = ceil(size.width); + size.height = ceil(size.height); + textBoundingSize = size; + } + + visibleRange = ASTextNSRangeFromCFRange(CTFrameGetVisibleStringRange(ctFrame)); + if (needTruncation) { + ASTextLine *lastLine = lines.lastObject; + NSRange lastRange = lastLine.range; + visibleRange.length = lastRange.location + lastRange.length - visibleRange.location; + + // create truncated line + if (container.truncationType != ASTextTruncationTypeNone) { + CTLineRef truncationTokenLine = NULL; + if (container.truncationToken) { + truncationToken = container.truncationToken; + truncationTokenLine = CTLineCreateWithAttributedString((CFAttributedStringRef)truncationToken); + } else { + CFArrayRef runs = CTLineGetGlyphRuns(lastLine.CTLine); + NSUInteger runCount = CFArrayGetCount(runs); + NSMutableDictionary *attrs = nil; + if (runCount > 0) { + CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runs, runCount - 1); + attrs = (id)CTRunGetAttributes(run); + attrs = attrs ? attrs.mutableCopy : [NSMutableArray new]; + [attrs removeObjectsForKeys:[NSMutableAttributedString as_allDiscontinuousAttributeKeys]]; + CTFontRef font = (__bridge CTFontRef)attrs[(id)kCTFontAttributeName]; + CGFloat fontSize = font ? CTFontGetSize(font) : 12.0; + UIFont *uiFont = [UIFont systemFontOfSize:fontSize * 0.9]; + if (uiFont) { + font = CTFontCreateWithName((__bridge CFStringRef)uiFont.fontName, uiFont.pointSize, NULL); + } else { + font = NULL; + } + if (font) { + attrs[(id)kCTFontAttributeName] = (__bridge id)(font); + uiFont = nil; + CFRelease(font); + } + CGColorRef color = (__bridge CGColorRef)(attrs[(id)kCTForegroundColorAttributeName]); + if (color && CFGetTypeID(color) == CGColorGetTypeID() && CGColorGetAlpha(color) == 0) { + // ignore clear color + [attrs removeObjectForKey:(id)kCTForegroundColorAttributeName]; + } + if (!attrs) attrs = [NSMutableDictionary new]; + } + truncationToken = [[NSAttributedString alloc] initWithString:ASTextTruncationToken attributes:attrs]; + truncationTokenLine = CTLineCreateWithAttributedString((CFAttributedStringRef)truncationToken); + } + if (truncationTokenLine) { + CTLineTruncationType type = kCTLineTruncationEnd; + if (container.truncationType == ASTextTruncationTypeStart) { + type = kCTLineTruncationStart; + } else if (container.truncationType == ASTextTruncationTypeMiddle) { + type = kCTLineTruncationMiddle; + } + NSMutableAttributedString *lastLineText = [text attributedSubstringFromRange:lastLine.range].mutableCopy; + [lastLineText appendAttributedString:truncationToken]; + CTLineRef ctLastLineExtend = CTLineCreateWithAttributedString((CFAttributedStringRef)lastLineText); + if (ctLastLineExtend) { + CGFloat truncatedWidth = lastLine.width; + CGRect cgPathRect = CGRectZero; + if (CGPathIsRect(cgPath, &cgPathRect)) { + if (isVerticalForm) { + truncatedWidth = cgPathRect.size.height; + } else { + truncatedWidth = cgPathRect.size.width; + } + } + CTLineRef ctTruncatedLine = CTLineCreateTruncatedLine(ctLastLineExtend, truncatedWidth, type, truncationTokenLine); + CFRelease(ctLastLineExtend); + if (ctTruncatedLine) { + truncatedLine = [ASTextLine lineWithCTLine:ctTruncatedLine position:lastLine.position vertical:isVerticalForm]; + truncatedLine.index = lastLine.index; + truncatedLine.row = lastLine.row; + CFRelease(ctTruncatedLine); + } + } + CFRelease(truncationTokenLine); + } + } + } + + if (isVerticalForm) { + NSCharacterSet *rotateCharset = ASTextVerticalFormRotateCharacterSet(); + NSCharacterSet *rotateMoveCharset = ASTextVerticalFormRotateAndMoveCharacterSet(); + + void (^lineBlock)(ASTextLine *) = ^(ASTextLine *line){ + CFArrayRef runs = CTLineGetGlyphRuns(line.CTLine); + if (!runs) return; + NSUInteger runCount = CFArrayGetCount(runs); + if (runCount == 0) return; + NSMutableArray *lineRunRanges = [NSMutableArray new]; + line.verticalRotateRange = lineRunRanges; + for (NSUInteger r = 0; r < runCount; r++) { + CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runs, r); + NSMutableArray *runRanges = [NSMutableArray new]; + [lineRunRanges addObject:runRanges]; + NSUInteger glyphCount = CTRunGetGlyphCount(run); + if (glyphCount == 0) continue; + + CFIndex runStrIdx[glyphCount + 1]; + CTRunGetStringIndices(run, CFRangeMake(0, 0), runStrIdx); + CFRange runStrRange = CTRunGetStringRange(run); + runStrIdx[glyphCount] = runStrRange.location + runStrRange.length; + CFDictionaryRef runAttrs = CTRunGetAttributes(run); + CTFontRef font = (CTFontRef)CFDictionaryGetValue(runAttrs, kCTFontAttributeName); + BOOL isColorGlyph = ASTextCTFontContainsColorBitmapGlyphs(font); + + NSUInteger prevIdx = 0; + ASTextRunGlyphDrawMode prevMode = ASTextRunGlyphDrawModeHorizontal; + NSString *layoutStr = layout.text.string; + for (NSUInteger g = 0; g < glyphCount; g++) { + BOOL glyphRotate = 0, glyphRotateMove = NO; + CFIndex runStrLen = runStrIdx[g + 1] - runStrIdx[g]; + if (isColorGlyph) { + glyphRotate = YES; + } else if (runStrLen == 1) { + unichar c = [layoutStr characterAtIndex:runStrIdx[g]]; + glyphRotate = [rotateCharset characterIsMember:c]; + if (glyphRotate) glyphRotateMove = [rotateMoveCharset characterIsMember:c]; + } else if (runStrLen > 1){ + NSString *glyphStr = [layoutStr substringWithRange:NSMakeRange(runStrIdx[g], runStrLen)]; + BOOL glyphRotate = [glyphStr rangeOfCharacterFromSet:rotateCharset].location != NSNotFound; + if (glyphRotate) glyphRotateMove = [glyphStr rangeOfCharacterFromSet:rotateMoveCharset].location != NSNotFound; + } + + ASTextRunGlyphDrawMode mode = glyphRotateMove ? ASTextRunGlyphDrawModeVerticalRotateMove : (glyphRotate ? ASTextRunGlyphDrawModeVerticalRotate : ASTextRunGlyphDrawModeHorizontal); + if (g == 0) { + prevMode = mode; + } else if (mode != prevMode) { + ASTextRunGlyphRange *aRange = [ASTextRunGlyphRange rangeWithRange:NSMakeRange(prevIdx, g - prevIdx) drawMode:prevMode]; + [runRanges addObject:aRange]; + prevIdx = g; + prevMode = mode; + } + } + if (prevIdx < glyphCount) { + ASTextRunGlyphRange *aRange = [ASTextRunGlyphRange rangeWithRange:NSMakeRange(prevIdx, glyphCount - prevIdx) drawMode:prevMode]; + [runRanges addObject:aRange]; + } + + } + }; + for (ASTextLine *line in lines) { + lineBlock(line); + } + if (truncatedLine) lineBlock(truncatedLine); + } + + if (visibleRange.length > 0) { + layout.needDrawText = YES; + + void (^block)(NSDictionary *attrs, NSRange range, BOOL *stop) = ^(NSDictionary *attrs, NSRange range, BOOL *stop) { + if (attrs[ASTextHighlightAttributeName]) layout.containsHighlight = YES; + if (attrs[ASTextBlockBorderAttributeName]) layout.needDrawBlockBorder = YES; + if (attrs[ASTextBackgroundBorderAttributeName]) layout.needDrawBackgroundBorder = YES; + if (attrs[ASTextShadowAttributeName] || attrs[NSShadowAttributeName]) layout.needDrawShadow = YES; + if (attrs[ASTextUnderlineAttributeName]) layout.needDrawUnderline = YES; + if (attrs[ASTextAttachmentAttributeName]) layout.needDrawAttachment = YES; + if (attrs[ASTextInnerShadowAttributeName]) layout.needDrawInnerShadow = YES; + if (attrs[ASTextStrikethroughAttributeName]) layout.needDrawStrikethrough = YES; + if (attrs[ASTextBorderAttributeName]) layout.needDrawBorder = YES; + }; + + [layout.text enumerateAttributesInRange:visibleRange options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired usingBlock:block]; + if (truncatedLine) { + [truncationToken enumerateAttributesInRange:NSMakeRange(0, truncationToken.length) options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired usingBlock:block]; + } + } + + attachments = [NSMutableArray new]; + attachmentRanges = [NSMutableArray new]; + attachmentRects = [NSMutableArray new]; + attachmentContentsSet = [NSMutableSet new]; + for (NSUInteger i = 0, max = lines.count; i < max; i++) { + ASTextLine *line = lines[i]; + if (truncatedLine && line.index == truncatedLine.index) line = truncatedLine; + if (line.attachments.count > 0) { + [attachments addObjectsFromArray:line.attachments]; + [attachmentRanges addObjectsFromArray:line.attachmentRanges]; + [attachmentRects addObjectsFromArray:line.attachmentRects]; + for (ASTextAttachment *attachment in line.attachments) { + if (attachment.content) { + [attachmentContentsSet addObject:attachment.content]; + } + } + } + } + if (attachments.count == 0) { + attachments = attachmentRanges = attachmentRects = nil; + } + + layout.frameSetter = ctSetter; + layout.frame = ctFrame; + layout.lines = lines; + layout.truncatedLine = truncatedLine; + layout.attachments = attachments; + layout.attachmentRanges = attachmentRanges; + layout.attachmentRects = attachmentRects; + layout.attachmentContentsSet = attachmentContentsSet; + layout.rowCount = rowCount; + layout.visibleRange = visibleRange; + layout.textBoundingRect = textBoundingRect; + layout.textBoundingSize = textBoundingSize; + layout.lineRowsEdge = lineRowsEdge; + layout.lineRowsIndex = lineRowsIndex; + CFRelease(cgPath); + CFRelease(ctSetter); + CFRelease(ctFrame); + if (lineOrigins) free(lineOrigins); + return layout; +} + ++ (NSArray *)layoutWithContainers:(NSArray *)containers text:(NSAttributedString *)text { + return [self layoutWithContainers:containers text:text range:NSMakeRange(0, text.length)]; +} + ++ (NSArray *)layoutWithContainers:(NSArray *)containers text:(NSAttributedString *)text range:(NSRange)range { + if (!containers || !text) return nil; + if (range.location + range.length > text.length) return nil; + NSMutableArray *layouts = [NSMutableArray array]; + for (NSUInteger i = 0, max = containers.count; i < max; i++) { + ASTextContainer *container = containers[i]; + ASTextLayout *layout = [self layoutWithContainer:container text:text range:range]; + if (!layout) return nil; + NSInteger length = (NSInteger)range.length - (NSInteger)layout.visibleRange.length; + if (length <= 0) { + range.length = 0; + range.location = text.length; + } else { + range.length = length; + range.location += layout.visibleRange.length; + } + } + return layouts; +} + +- (void)setFrameSetter:(CTFramesetterRef)frameSetter { + if (_frameSetter != frameSetter) { + if (frameSetter) CFRetain(frameSetter); + if (_frameSetter) CFRelease(_frameSetter); + _frameSetter = frameSetter; + } +} + +- (void)setFrame:(CTFrameRef)frame { + if (_frame != frame) { + if (frame) CFRetain(frame); + if (_frame) CFRelease(_frame); + _frame = frame; + } +} + +- (void)dealloc { + if (_frameSetter) CFRelease(_frameSetter); + if (_frame) CFRelease(_frame); + if (_lineRowsIndex) free(_lineRowsIndex); + if (_lineRowsEdge) free(_lineRowsEdge); +} + +#pragma mark - Copying + +- (id)copyWithZone:(NSZone *)zone { + return self; // readonly object +} + + +#pragma mark - Query + +/** + Get the row index with 'edge' distance. + + @param edge The distance from edge to the point. + If vertical form, the edge is left edge, otherwise the edge is top edge. + + @return Returns NSNotFound if there's no row at the point. + */ +- (NSUInteger)_rowIndexForEdge:(CGFloat)edge { + if (_rowCount == 0) return NSNotFound; + BOOL isVertical = _container.verticalForm; + NSUInteger lo = 0, hi = _rowCount - 1, mid = 0; + NSUInteger rowIdx = NSNotFound; + while (lo <= hi) { + mid = (lo + hi) / 2; + ASRowEdge oneEdge = _lineRowsEdge[mid]; + if (isVertical ? + (oneEdge.foot <= edge && edge <= oneEdge.head) : + (oneEdge.head <= edge && edge <= oneEdge.foot)) { + rowIdx = mid; + break; + } + if ((isVertical ? (edge > oneEdge.head) : (edge < oneEdge.head))) { + if (mid == 0) break; + hi = mid - 1; + } else { + lo = mid + 1; + } + } + return rowIdx; +} + +/** + Get the closest row index with 'edge' distance. + + @param edge The distance from edge to the point. + If vertical form, the edge is left edge, otherwise the edge is top edge. + + @return Returns NSNotFound if there's no line. + */ +- (NSUInteger)_closestRowIndexForEdge:(CGFloat)edge { + if (_rowCount == 0) return NSNotFound; + NSUInteger rowIdx = [self _rowIndexForEdge:edge]; + if (rowIdx == NSNotFound) { + if (_container.verticalForm) { + if (edge > _lineRowsEdge[0].head) { + rowIdx = 0; + } else if (edge < _lineRowsEdge[_rowCount - 1].foot) { + rowIdx = _rowCount - 1; + } + } else { + if (edge < _lineRowsEdge[0].head) { + rowIdx = 0; + } else if (edge > _lineRowsEdge[_rowCount - 1].foot) { + rowIdx = _rowCount - 1; + } + } + } + return rowIdx; +} + +/** + Get a CTRun from a line position. + + @param line The text line. + @param position The position in the whole text. + + @return Returns NULL if not found (no CTRun at the position). + */ +- (CTRunRef)_runForLine:(ASTextLine *)line position:(ASTextPosition *)position { + if (!line || !position) return NULL; + CFArrayRef runs = CTLineGetGlyphRuns(line.CTLine); + for (NSUInteger i = 0, max = CFArrayGetCount(runs); i < max; i++) { + CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runs, i); + CFRange range = CTRunGetStringRange(run); + if (position.affinity == ASTextAffinityBackward) { + if (range.location < position.offset && position.offset <= range.location + range.length) { + return run; + } + } else { + if (range.location <= position.offset && position.offset < range.location + range.length) { + return run; + } + } + } + return NULL; +} + +/** + Whether the position is inside a composed character sequence. + + @param line The text line. + @param position Text text position in whole text. + @param block The block to be executed before returns YES. + left: left X offset + right: right X offset + prev: left position + next: right position + */ +- (BOOL)_insideComposedCharacterSequences:(ASTextLine *)line position:(NSUInteger)position block:(void (^)(CGFloat left, CGFloat right, NSUInteger prev, NSUInteger next))block { + NSRange range = line.range; + if (range.length == 0) return NO; + __block BOOL inside = NO; + __block NSUInteger _prev, _next; + [_text.string enumerateSubstringsInRange:range options:NSStringEnumerationByComposedCharacterSequences usingBlock: ^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) { + NSUInteger prev = substringRange.location; + NSUInteger next = substringRange.location + substringRange.length; + if (prev == position || next == position) { + *stop = YES; + } + if (prev < position && position < next) { + inside = YES; + _prev = prev; + _next = next; + *stop = YES; + } + }]; + if (inside && block) { + CGFloat left = [self offsetForTextPosition:_prev lineIndex:line.index]; + CGFloat right = [self offsetForTextPosition:_next lineIndex:line.index]; + block(left, right, _prev, _next); + } + return inside; +} + +/** + Whether the position is inside an emoji (such as National Flag Emoji). + + @param line The text line. + @param position Text text position in whole text. + @param block Yhe block to be executed before returns YES. + left: emoji's left X offset + right: emoji's right X offset + prev: emoji's left position + next: emoji's right position + */ +- (BOOL)_insideEmoji:(ASTextLine *)line position:(NSUInteger)position block:(void (^)(CGFloat left, CGFloat right, NSUInteger prev, NSUInteger next))block { + if (!line) return NO; + CFArrayRef runs = CTLineGetGlyphRuns(line.CTLine); + for (NSUInteger r = 0, rMax = CFArrayGetCount(runs); r < rMax; r++) { + CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runs, r); + NSUInteger glyphCount = CTRunGetGlyphCount(run); + if (glyphCount == 0) continue; + CFRange range = CTRunGetStringRange(run); + if (range.length <= 1) continue; + if (position <= range.location || position >= range.location + range.length) continue; + CFDictionaryRef attrs = CTRunGetAttributes(run); + CTFontRef font = (CTFontRef)CFDictionaryGetValue(attrs, kCTFontAttributeName); + if (!ASTextCTFontContainsColorBitmapGlyphs(font)) continue; + + // Here's Emoji runs (larger than 1 unichar), and position is inside the range. + CFIndex indices[glyphCount]; + CTRunGetStringIndices(run, CFRangeMake(0, glyphCount), indices); + for (NSUInteger g = 0; g < glyphCount; g++) { + CFIndex prev = indices[g]; + CFIndex next = g + 1 < glyphCount ? indices[g + 1] : range.location + range.length; + if (position == prev) break; // Emoji edge + if (prev < position && position < next) { // inside an emoji (such as National Flag Emoji) + CGPoint pos = CGPointZero; + CGSize adv = CGSizeZero; + CTRunGetPositions(run, CFRangeMake(g, 1), &pos); + CTRunGetAdvances(run, CFRangeMake(g, 1), &adv); + if (block) { + block(line.position.x + pos.x, + line.position.x + pos.x + adv.width, + prev, next); + } + return YES; + } + } + } + return NO; +} +/** + Whether the write direction is RTL at the specified point + + @param line The text line + @param point The point in layout. + + @return YES if RTL. + */ +- (BOOL)_isRightToLeftInLine:(ASTextLine *)line atPoint:(CGPoint)point { + if (!line) return NO; + // get write direction + BOOL RTL = NO; + CFArrayRef runs = CTLineGetGlyphRuns(line.CTLine); + for (NSUInteger r = 0, max = CFArrayGetCount(runs); r < max; r++) { + CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runs, r); + CGPoint glyphPosition; + CTRunGetPositions(run, CFRangeMake(0, 1), &glyphPosition); + if (_container.verticalForm) { + CGFloat runX = glyphPosition.x; + runX += line.position.y; + CGFloat runWidth = CTRunGetTypographicBounds(run, CFRangeMake(0, 0), NULL, NULL, NULL); + if (runX <= point.y && point.y <= runX + runWidth) { + if (CTRunGetStatus(run) & kCTRunStatusRightToLeft) RTL = YES; + break; + } + } else { + CGFloat runX = glyphPosition.x; + runX += line.position.x; + CGFloat runWidth = CTRunGetTypographicBounds(run, CFRangeMake(0, 0), NULL, NULL, NULL); + if (runX <= point.x && point.x <= runX + runWidth) { + if (CTRunGetStatus(run) & kCTRunStatusRightToLeft) RTL = YES; + break; + } + } + } + return RTL; +} + +/** + Correct the range's edge. + */ +- (ASTextRange *)_correctedRangeWithEdge:(ASTextRange *)range { + NSRange visibleRange = self.visibleRange; + ASTextPosition *start = range.start; + ASTextPosition *end = range.end; + + if (start.offset == visibleRange.location && start.affinity == ASTextAffinityBackward) { + start = [ASTextPosition positionWithOffset:start.offset affinity:ASTextAffinityForward]; + } + + if (end.offset == visibleRange.location + visibleRange.length && start.affinity == ASTextAffinityForward) { + end = [ASTextPosition positionWithOffset:end.offset affinity:ASTextAffinityBackward]; + } + + if (start != range.start || end != range.end) { + range = [ASTextRange rangeWithStart:start end:end]; + } + return range; +} + +- (NSUInteger)lineIndexForRow:(NSUInteger)row { + if (row >= _rowCount) return NSNotFound; + return _lineRowsIndex[row]; +} + +- (NSUInteger)lineCountForRow:(NSUInteger)row { + if (row >= _rowCount) return NSNotFound; + if (row == _rowCount - 1) { + return _lines.count - _lineRowsIndex[row]; + } else { + return _lineRowsIndex[row + 1] - _lineRowsIndex[row]; + } +} + +- (NSUInteger)rowIndexForLine:(NSUInteger)line { + if (line >= _lines.count) return NSNotFound; + return ((ASTextLine *)_lines[line]).row; +} + +- (NSUInteger)lineIndexForPoint:(CGPoint)point { + if (_lines.count == 0 || _rowCount == 0) return NSNotFound; + NSUInteger rowIdx = [self _rowIndexForEdge:_container.verticalForm ? point.x : point.y]; + if (rowIdx == NSNotFound) return NSNotFound; + + NSUInteger lineIdx0 = _lineRowsIndex[rowIdx]; + NSUInteger lineIdx1 = rowIdx == _rowCount - 1 ? _lines.count - 1 : _lineRowsIndex[rowIdx + 1] - 1; + for (NSUInteger i = lineIdx0; i <= lineIdx1; i++) { + CGRect bounds = ((ASTextLine *)_lines[i]).bounds; + if (CGRectContainsPoint(bounds, point)) return i; + } + + return NSNotFound; +} + +- (NSUInteger)closestLineIndexForPoint:(CGPoint)point { + BOOL isVertical = _container.verticalForm; + if (_lines.count == 0 || _rowCount == 0) return NSNotFound; + NSUInteger rowIdx = [self _closestRowIndexForEdge:isVertical ? point.x : point.y]; + if (rowIdx == NSNotFound) return NSNotFound; + + NSUInteger lineIdx0 = _lineRowsIndex[rowIdx]; + NSUInteger lineIdx1 = rowIdx == _rowCount - 1 ? _lines.count - 1 : _lineRowsIndex[rowIdx + 1] - 1; + if (lineIdx0 == lineIdx1) return lineIdx0; + + CGFloat minDistance = CGFLOAT_MAX; + NSUInteger minIndex = lineIdx0; + for (NSUInteger i = lineIdx0; i <= lineIdx1; i++) { + CGRect bounds = ((ASTextLine *)_lines[i]).bounds; + if (isVertical) { + if (bounds.origin.y <= point.y && point.y <= bounds.origin.y + bounds.size.height) return i; + CGFloat distance; + if (point.y < bounds.origin.y) { + distance = bounds.origin.y - point.y; + } else { + distance = point.y - (bounds.origin.y + bounds.size.height); + } + if (distance < minDistance) { + minDistance = distance; + minIndex = i; + } + } else { + if (bounds.origin.x <= point.x && point.x <= bounds.origin.x + bounds.size.width) return i; + CGFloat distance; + if (point.x < bounds.origin.x) { + distance = bounds.origin.x - point.x; + } else { + distance = point.x - (bounds.origin.x + bounds.size.width); + } + if (distance < minDistance) { + minDistance = distance; + minIndex = i; + } + } + } + return minIndex; +} + +- (CGFloat)offsetForTextPosition:(NSUInteger)position lineIndex:(NSUInteger)lineIndex { + if (lineIndex >= _lines.count) return CGFLOAT_MAX; + ASTextLine *line = _lines[lineIndex]; + CFRange range = CTLineGetStringRange(line.CTLine); + if (position < range.location || position > range.location + range.length) return CGFLOAT_MAX; + + CGFloat offset = CTLineGetOffsetForStringIndex(line.CTLine, position, NULL); + return _container.verticalForm ? (offset + line.position.y) : (offset + line.position.x); +} + +- (NSUInteger)textPositionForPoint:(CGPoint)point lineIndex:(NSUInteger)lineIndex { + if (lineIndex >= _lines.count) return NSNotFound; + ASTextLine *line = _lines[lineIndex]; + if (_container.verticalForm) { + point.x = point.y - line.position.y; + point.y = 0; + } else { + point.x -= line.position.x; + point.y = 0; + } + CFIndex idx = CTLineGetStringIndexForPosition(line.CTLine, point); + if (idx == kCFNotFound) return NSNotFound; + + /* + If the emoji contains one or more variant form (such as ☔️ "\u2614\uFE0F") + and the font size is smaller than 379/15, then each variant form ("\uFE0F") + will rendered as a single blank glyph behind the emoji glyph. Maybe it's a + bug in CoreText? Seems iOS8.3 fixes this problem. + + If the point hit the blank glyph, the CTLineGetStringIndexForPosition() + returns the position before the emoji glyph, but it should returns the + position after the emoji and variant form. + + Here's a workaround. + */ + CFArrayRef runs = CTLineGetGlyphRuns(line.CTLine); + for (NSUInteger r = 0, max = CFArrayGetCount(runs); r < max; r++) { + CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runs, r); + CFRange range = CTRunGetStringRange(run); + if (range.location <= idx && idx < range.location + range.length) { + NSUInteger glyphCount = CTRunGetGlyphCount(run); + if (glyphCount == 0) break; + CFDictionaryRef attrs = CTRunGetAttributes(run); + CTFontRef font = (CTFontRef)CFDictionaryGetValue(attrs, kCTFontAttributeName); + if (!ASTextCTFontContainsColorBitmapGlyphs(font)) break; + + CFIndex indices[glyphCount]; + CGPoint positions[glyphCount]; + CTRunGetStringIndices(run, CFRangeMake(0, glyphCount), indices); + CTRunGetPositions(run, CFRangeMake(0, glyphCount), positions); + for (NSUInteger g = 0; g < glyphCount; g++) { + NSUInteger gIdx = indices[g]; + if (gIdx == idx && g + 1 < glyphCount) { + CGFloat right = positions[g + 1].x; + if (point.x < right) break; + NSUInteger next = indices[g + 1]; + do { + if (next == range.location + range.length) break; + unichar c = [_text.string characterAtIndex:next]; + if ((c == 0xFE0E || c == 0xFE0F)) { // unicode variant form for emoji style + next++; + } else break; + } + while (1); + if (next != indices[g + 1]) idx = next; + break; + } + } + break; + } + } + return idx; +} + +- (ASTextPosition *)closestPositionToPoint:(CGPoint)point { + BOOL isVertical = _container.verticalForm; + // When call CTLineGetStringIndexForPosition() on ligature such as 'fi', + // and the point `hit` the glyph's left edge, it may get the ligature inside offset. + // I don't know why, maybe it's a bug of CoreText. Try to avoid it. + if (isVertical) point.y += 0.00001234; + else point.x += 0.00001234; + + NSUInteger lineIndex = [self closestLineIndexForPoint:point]; + if (lineIndex == NSNotFound) return nil; + ASTextLine *line = _lines[lineIndex]; + __block NSUInteger position = [self textPositionForPoint:point lineIndex:lineIndex]; + if (position == NSNotFound) position = line.range.location; + if (position <= _visibleRange.location) { + return [ASTextPosition positionWithOffset:_visibleRange.location affinity:ASTextAffinityForward]; + } else if (position >= _visibleRange.location + _visibleRange.length) { + return [ASTextPosition positionWithOffset:_visibleRange.location + _visibleRange.length affinity:ASTextAffinityBackward]; + } + + ASTextAffinity finalAffinity = ASTextAffinityForward; + BOOL finalAffinityDetected = NO; + + // binding range + NSRange bindingRange; + ASTextBinding *binding = [_text attribute:ASTextBindingAttributeName atIndex:position longestEffectiveRange:&bindingRange inRange:NSMakeRange(0, _text.length)]; + if (binding && bindingRange.length > 0) { + NSUInteger headLineIdx = [self lineIndexForPosition:[ASTextPosition positionWithOffset:bindingRange.location]]; + NSUInteger tailLineIdx = [self lineIndexForPosition:[ASTextPosition positionWithOffset:bindingRange.location + bindingRange.length affinity:ASTextAffinityBackward]]; + if (headLineIdx == lineIndex && lineIndex == tailLineIdx) { // all in same line + CGFloat left = [self offsetForTextPosition:bindingRange.location lineIndex:lineIndex]; + CGFloat right = [self offsetForTextPosition:bindingRange.location + bindingRange.length lineIndex:lineIndex]; + if (left != CGFLOAT_MAX && right != CGFLOAT_MAX) { + if (_container.isVerticalForm) { + if (fabs(point.y - left) < fabs(point.y - right)) { + position = bindingRange.location; + finalAffinity = ASTextAffinityForward; + } else { + position = bindingRange.location + bindingRange.length; + finalAffinity = ASTextAffinityBackward; + } + } else { + if (fabs(point.x - left) < fabs(point.x - right)) { + position = bindingRange.location; + finalAffinity = ASTextAffinityForward; + } else { + position = bindingRange.location + bindingRange.length; + finalAffinity = ASTextAffinityBackward; + } + } + } else if (left != CGFLOAT_MAX) { + position = left; + finalAffinity = ASTextAffinityForward; + } else if (right != CGFLOAT_MAX) { + position = right; + finalAffinity = ASTextAffinityBackward; + } + finalAffinityDetected = YES; + } else if (headLineIdx == lineIndex) { + CGFloat left = [self offsetForTextPosition:bindingRange.location lineIndex:lineIndex]; + if (left != CGFLOAT_MAX) { + position = bindingRange.location; + finalAffinity = ASTextAffinityForward; + finalAffinityDetected = YES; + } + } else if (tailLineIdx == lineIndex) { + CGFloat right = [self offsetForTextPosition:bindingRange.location + bindingRange.length lineIndex:lineIndex]; + if (right != CGFLOAT_MAX) { + position = bindingRange.location + bindingRange.length; + finalAffinity = ASTextAffinityBackward; + finalAffinityDetected = YES; + } + } else { + BOOL onLeft = NO, onRight = NO; + if (headLineIdx != NSNotFound && tailLineIdx != NSNotFound) { + if (abs((int)headLineIdx - (int)lineIndex) < abs((int)tailLineIdx - (int)lineIndex)) onLeft = YES; + else onRight = YES; + } else if (headLineIdx != NSNotFound) { + onLeft = YES; + } else if (tailLineIdx != NSNotFound) { + onRight = YES; + } + + if (onLeft) { + CGFloat left = [self offsetForTextPosition:bindingRange.location lineIndex:headLineIdx]; + if (left != CGFLOAT_MAX) { + lineIndex = headLineIdx; + line = _lines[headLineIdx]; + position = bindingRange.location; + finalAffinity = ASTextAffinityForward; + finalAffinityDetected = YES; + } + } else if (onRight) { + CGFloat right = [self offsetForTextPosition:bindingRange.location + bindingRange.length lineIndex:tailLineIdx]; + if (right != CGFLOAT_MAX) { + lineIndex = tailLineIdx; + line = _lines[tailLineIdx]; + position = bindingRange.location + bindingRange.length; + finalAffinity = ASTextAffinityBackward; + finalAffinityDetected = YES; + } + } + } + } + + // empty line + if (line.range.length == 0) { + BOOL behind = (_lines.count > 1 && lineIndex == _lines.count - 1); //end line + return [ASTextPosition positionWithOffset:line.range.location affinity:behind ? ASTextAffinityBackward:ASTextAffinityForward]; + } + + // detect weather the line is a linebreak token + if (line.range.length <= 2) { + NSString *str = [_text.string substringWithRange:line.range]; + if (ASTextIsLinebreakString(str)) { // an empty line ("\r", "\n", "\r\n") + return [ASTextPosition positionWithOffset:line.range.location]; + } + } + + // above whole text frame + if (lineIndex == 0 && (isVertical ? (point.x > line.right) : (point.y < line.top))) { + position = 0; + finalAffinity = ASTextAffinityForward; + finalAffinityDetected = YES; + } + // below whole text frame + if (lineIndex == _lines.count - 1 && (isVertical ? (point.x < line.left) : (point.y > line.bottom))) { + position = line.range.location + line.range.length; + finalAffinity = ASTextAffinityBackward; + finalAffinityDetected = YES; + } + + // There must be at least one non-linebreak char, + // ignore the linebreak characters at line end if exists. + if (position >= line.range.location + line.range.length - 1) { + if (position > line.range.location) { + unichar c1 = [_text.string characterAtIndex:position - 1]; + if (ASTextIsLinebreakChar(c1)) { + position--; + if (position > line.range.location) { + unichar c0 = [_text.string characterAtIndex:position - 1]; + if (ASTextIsLinebreakChar(c0)) { + position--; + } + } + } + } + } + if (position == line.range.location) { + return [ASTextPosition positionWithOffset:position]; + } + if (position == line.range.location + line.range.length) { + return [ASTextPosition positionWithOffset:position affinity:ASTextAffinityBackward]; + } + + [self _insideComposedCharacterSequences:line position:position block: ^(CGFloat left, CGFloat right, NSUInteger prev, NSUInteger next) { + if (isVertical) { + position = fabs(left - point.y) < fabs(right - point.y) < (right ? prev : next); + } else { + position = fabs(left - point.x) < fabs(right - point.x) < (right ? prev : next); + } + }]; + + [self _insideEmoji:line position:position block: ^(CGFloat left, CGFloat right, NSUInteger prev, NSUInteger next) { + if (isVertical) { + position = fabs(left - point.y) < fabs(right - point.y) < (right ? prev : next); + } else { + position = fabs(left - point.x) < fabs(right - point.x) < (right ? prev : next); + } + }]; + + if (position < _visibleRange.location) position = _visibleRange.location; + else if (position > _visibleRange.location + _visibleRange.length) position = _visibleRange.location + _visibleRange.length; + + if (!finalAffinityDetected) { + CGFloat ofs = [self offsetForTextPosition:position lineIndex:lineIndex]; + if (ofs != CGFLOAT_MAX) { + BOOL RTL = [self _isRightToLeftInLine:line atPoint:point]; + if (position >= line.range.location + line.range.length) { + finalAffinity = RTL ? ASTextAffinityForward : ASTextAffinityBackward; + } else if (position <= line.range.location) { + finalAffinity = RTL ? ASTextAffinityBackward : ASTextAffinityForward; + } else { + finalAffinity = (ofs < (isVertical ? point.y : point.x) && !RTL) ? ASTextAffinityForward : ASTextAffinityBackward; + } + } + } + + return [ASTextPosition positionWithOffset:position affinity:finalAffinity]; +} + +- (ASTextPosition *)positionForPoint:(CGPoint)point + oldPosition:(ASTextPosition *)oldPosition + otherPosition:(ASTextPosition *)otherPosition { + if (!oldPosition || !otherPosition) { + return oldPosition; + } + ASTextPosition *newPos = [self closestPositionToPoint:point]; + if (!newPos) return oldPosition; + if ([newPos compare:otherPosition] == [oldPosition compare:otherPosition] && + newPos.offset != otherPosition.offset) { + return newPos; + } + NSUInteger lineIndex = [self lineIndexForPosition:otherPosition]; + if (lineIndex == NSNotFound) return oldPosition; + ASTextLine *line = _lines[lineIndex]; + ASRowEdge vertical = _lineRowsEdge[line.row]; + if (_container.verticalForm) { + point.x = (vertical.head + vertical.foot) * 0.5; + } else { + point.y = (vertical.head + vertical.foot) * 0.5; + } + newPos = [self closestPositionToPoint:point]; + if ([newPos compare:otherPosition] == [oldPosition compare:otherPosition] && + newPos.offset != otherPosition.offset) { + return newPos; + } + + if (_container.isVerticalForm) { + if ([oldPosition compare:otherPosition] == NSOrderedAscending) { // search backward + ASTextRange *range = [self textRangeByExtendingPosition:otherPosition inDirection:UITextLayoutDirectionUp offset:1]; + if (range) return range.start; + } else { // search forward + ASTextRange *range = [self textRangeByExtendingPosition:otherPosition inDirection:UITextLayoutDirectionDown offset:1]; + if (range) return range.end; + } + } else { + if ([oldPosition compare:otherPosition] == NSOrderedAscending) { // search backward + ASTextRange *range = [self textRangeByExtendingPosition:otherPosition inDirection:UITextLayoutDirectionLeft offset:1]; + if (range) return range.start; + } else { // search forward + ASTextRange *range = [self textRangeByExtendingPosition:otherPosition inDirection:UITextLayoutDirectionRight offset:1]; + if (range) return range.end; + } + } + + return oldPosition; +} + +- (ASTextRange *)textRangeAtPoint:(CGPoint)point { + NSUInteger lineIndex = [self lineIndexForPoint:point]; + if (lineIndex == NSNotFound) return nil; + NSUInteger textPosition = [self textPositionForPoint:point lineIndex:[self lineIndexForPoint:point]]; + if (textPosition == NSNotFound) return nil; + ASTextPosition *pos = [self closestPositionToPoint:point]; + if (!pos) return nil; + + // get write direction + BOOL RTL = [self _isRightToLeftInLine:_lines[lineIndex] atPoint:point]; + CGRect rect = [self caretRectForPosition:pos]; + if (CGRectIsNull(rect)) return nil; + + if (_container.verticalForm) { + ASTextRange *range = [self textRangeByExtendingPosition:pos inDirection:(rect.origin.y >= point.y && !RTL) ? UITextLayoutDirectionUp:UITextLayoutDirectionDown offset:1]; + return range; + } else { + ASTextRange *range = [self textRangeByExtendingPosition:pos inDirection:(rect.origin.x >= point.x && !RTL) ? UITextLayoutDirectionLeft:UITextLayoutDirectionRight offset:1]; + return range; + } +} + +- (ASTextRange *)closestTextRangeAtPoint:(CGPoint)point { + ASTextPosition *pos = [self closestPositionToPoint:point]; + if (!pos) return nil; + NSUInteger lineIndex = [self lineIndexForPosition:pos]; + if (lineIndex == NSNotFound) return nil; + ASTextLine *line = _lines[lineIndex]; + BOOL RTL = [self _isRightToLeftInLine:line atPoint:point]; + CGRect rect = [self caretRectForPosition:pos]; + if (CGRectIsNull(rect)) return nil; + + UITextLayoutDirection direction = UITextLayoutDirectionRight; + if (pos.offset >= line.range.location + line.range.length) { + if (direction != RTL) { + direction = _container.verticalForm ? UITextLayoutDirectionUp : UITextLayoutDirectionLeft; + } else { + direction = _container.verticalForm ? UITextLayoutDirectionDown : UITextLayoutDirectionRight; + } + } else if (pos.offset <= line.range.location) { + if (direction != RTL) { + direction = _container.verticalForm ? UITextLayoutDirectionDown : UITextLayoutDirectionRight; + } else { + direction = _container.verticalForm ? UITextLayoutDirectionUp : UITextLayoutDirectionLeft; + } + } else { + if (_container.verticalForm) { + direction = (rect.origin.y >= point.y && !RTL) ? UITextLayoutDirectionUp:UITextLayoutDirectionDown; + } else { + direction = (rect.origin.x >= point.x && !RTL) ? UITextLayoutDirectionLeft:UITextLayoutDirectionRight; + } + } + + ASTextRange *range = [self textRangeByExtendingPosition:pos inDirection:direction offset:1]; + return range; +} + +- (ASTextRange *)textRangeByExtendingPosition:(ASTextPosition *)position { + NSUInteger visibleStart = _visibleRange.location; + NSUInteger visibleEnd = _visibleRange.location + _visibleRange.length; + + if (!position) return nil; + if (position.offset < visibleStart || position.offset > visibleEnd) return nil; + + // head or tail, returns immediately + if (position.offset == visibleStart) { + return [ASTextRange rangeWithRange:NSMakeRange(position.offset, 0)]; + } else if (position.offset == visibleEnd) { + return [ASTextRange rangeWithRange:NSMakeRange(position.offset, 0) affinity:ASTextAffinityBackward]; + } + + // binding range + NSRange tRange; + ASTextBinding *binding = [_text attribute:ASTextBindingAttributeName atIndex:position.offset longestEffectiveRange:&tRange inRange:_visibleRange]; + if (binding && tRange.length > 0 && tRange.location < position.offset) { + return [ASTextRange rangeWithRange:tRange]; + } + + // inside emoji or composed character sequences + NSUInteger lineIndex = [self lineIndexForPosition:position]; + if (lineIndex != NSNotFound) { + __block NSUInteger _prev, _next; + BOOL emoji = NO, seq = NO; + + ASTextLine *line = _lines[lineIndex]; + emoji = [self _insideEmoji:line position:position.offset block: ^(CGFloat left, CGFloat right, NSUInteger prev, NSUInteger next) { + _prev = prev; + _next = next; + }]; + if (!emoji) { + seq = [self _insideComposedCharacterSequences:line position:position.offset block: ^(CGFloat left, CGFloat right, NSUInteger prev, NSUInteger next) { + _prev = prev; + _next = next; + }]; + } + if (emoji || seq) { + return [ASTextRange rangeWithRange:NSMakeRange(_prev, _next - _prev)]; + } + } + + // inside linebreak '\r\n' + if (position.offset > visibleStart && position.offset < visibleEnd) { + unichar c0 = [_text.string characterAtIndex:position.offset - 1]; + if ((c0 == '\r') && position.offset < visibleEnd) { + unichar c1 = [_text.string characterAtIndex:position.offset]; + if (c1 == '\n') { + return [ASTextRange rangeWithStart:[ASTextPosition positionWithOffset:position.offset - 1] end:[ASTextPosition positionWithOffset:position.offset + 1]]; + } + } + if (ASTextIsLinebreakChar(c0) && position.affinity == ASTextAffinityBackward) { + NSString *str = [_text.string substringToIndex:position.offset]; + NSUInteger len = ASTextLinebreakTailLength(str); + return [ASTextRange rangeWithStart:[ASTextPosition positionWithOffset:position.offset - len] end:[ASTextPosition positionWithOffset:position.offset]]; + } + } + + return [ASTextRange rangeWithRange:NSMakeRange(position.offset, 0) affinity:position.affinity]; +} + +- (ASTextRange *)textRangeByExtendingPosition:(ASTextPosition *)position + inDirection:(UITextLayoutDirection)direction + offset:(NSInteger)offset { + NSInteger visibleStart = _visibleRange.location; + NSInteger visibleEnd = _visibleRange.location + _visibleRange.length; + + if (!position) return nil; + if (position.offset < visibleStart || position.offset > visibleEnd) return nil; + if (offset == 0) return [self textRangeByExtendingPosition:position]; + + BOOL isVerticalForm = _container.verticalForm; + BOOL verticalMove, forwardMove; + + if (isVerticalForm) { + verticalMove = direction == UITextLayoutDirectionLeft || direction == UITextLayoutDirectionRight; + forwardMove = direction == UITextLayoutDirectionLeft || direction == UITextLayoutDirectionDown; + } else { + verticalMove = direction == UITextLayoutDirectionUp || direction == UITextLayoutDirectionDown; + forwardMove = direction == UITextLayoutDirectionDown || direction == UITextLayoutDirectionRight; + } + + if (offset < 0) { + forwardMove = !forwardMove; + offset = -offset; + } + + // head or tail, returns immediately + if (!forwardMove && position.offset == visibleStart) { + return [ASTextRange rangeWithRange:NSMakeRange(_visibleRange.location, 0)]; + } else if (forwardMove && position.offset == visibleEnd) { + return [ASTextRange rangeWithRange:NSMakeRange(position.offset, 0) affinity:ASTextAffinityBackward]; + } + + // extend from position + ASTextRange *fromRange = [self textRangeByExtendingPosition:position]; + if (!fromRange) return nil; + ASTextRange *allForward = [ASTextRange rangeWithStart:fromRange.start end:[ASTextPosition positionWithOffset:visibleEnd]]; + ASTextRange *allBackward = [ASTextRange rangeWithStart:[ASTextPosition positionWithOffset:visibleStart] end:fromRange.end]; + + if (verticalMove) { // up/down in text layout + NSInteger lineIndex = [self lineIndexForPosition:position]; + if (lineIndex == NSNotFound) return nil; + + ASTextLine *line = _lines[lineIndex]; + NSInteger moveToRowIndex = (NSInteger)line.row + (forwardMove ? offset : -offset); + if (moveToRowIndex < 0) return allBackward; + else if (moveToRowIndex >= (NSInteger)_rowCount) return allForward; + + CGFloat ofs = [self offsetForTextPosition:position.offset lineIndex:lineIndex]; + if (ofs == CGFLOAT_MAX) return nil; + + NSUInteger moveToLineFirstIndex = [self lineIndexForRow:moveToRowIndex]; + NSUInteger moveToLineCount = [self lineCountForRow:moveToRowIndex]; + if (moveToLineFirstIndex == NSNotFound || moveToLineCount == NSNotFound || moveToLineCount == 0) return nil; + CGFloat mostLeft = CGFLOAT_MAX, mostRight = -CGFLOAT_MAX; + ASTextLine *mostLeftLine = nil, *mostRightLine = nil; + NSUInteger insideIndex = NSNotFound; + for (NSUInteger i = 0; i < moveToLineCount; i++) { + NSUInteger lineIndex = moveToLineFirstIndex + i; + ASTextLine *line = _lines[lineIndex]; + if (isVerticalForm) { + if (line.top <= ofs && ofs <= line.bottom) { + insideIndex = line.index; + break; + } + if (line.top < mostLeft) { + mostLeft = line.top; + mostLeftLine = line; + } + if (line.bottom > mostRight) { + mostRight = line.bottom; + mostRightLine = line; + } + } else { + if (line.left <= ofs && ofs <= line.right) { + insideIndex = line.index; + break; + } + if (line.left < mostLeft) { + mostLeft = line.left; + mostLeftLine = line; + } + if (line.right > mostRight) { + mostRight = line.right; + mostRightLine = line; + } + } + } + BOOL afinityEdge = NO; + if (insideIndex == NSNotFound) { + if (ofs <= mostLeft) { + insideIndex = mostLeftLine.index; + } else { + insideIndex = mostRightLine.index; + } + afinityEdge = YES; + } + ASTextLine *insideLine = _lines[insideIndex]; + NSUInteger pos; + if (isVerticalForm) { + pos = [self textPositionForPoint:CGPointMake(insideLine.position.x, ofs) lineIndex:insideIndex]; + } else { + pos = [self textPositionForPoint:CGPointMake(ofs, insideLine.position.y) lineIndex:insideIndex]; + } + if (pos == NSNotFound) return nil; + ASTextPosition *extPos; + if (afinityEdge) { + if (pos == insideLine.range.location + insideLine.range.length) { + NSString *subStr = [_text.string substringWithRange:insideLine.range]; + NSUInteger lineBreakLen = ASTextLinebreakTailLength(subStr); + extPos = [ASTextPosition positionWithOffset:pos - lineBreakLen]; + } else { + extPos = [ASTextPosition positionWithOffset:pos]; + } + } else { + extPos = [ASTextPosition positionWithOffset:pos]; + } + ASTextRange *ext = [self textRangeByExtendingPosition:extPos]; + if (!ext) return nil; + if (forwardMove) { + return [ASTextRange rangeWithStart:fromRange.start end:ext.end]; + } else { + return [ASTextRange rangeWithStart:ext.start end:fromRange.end]; + } + + } else { // left/right in text layout + ASTextPosition *toPosition = [ASTextPosition positionWithOffset:position.offset + (forwardMove ? offset : -offset)]; + if (toPosition.offset <= visibleStart) return allBackward; + else if (toPosition.offset >= visibleEnd) return allForward; + + ASTextRange *toRange = [self textRangeByExtendingPosition:toPosition]; + if (!toRange) return nil; + + NSInteger start = MIN(fromRange.start.offset, toRange.start.offset); + NSInteger end = MAX(fromRange.end.offset, toRange.end.offset); + return [ASTextRange rangeWithRange:NSMakeRange(start, end - start)]; + } +} + +- (NSUInteger)lineIndexForPosition:(ASTextPosition *)position { + if (!position) return NSNotFound; + if (_lines.count == 0) return NSNotFound; + NSUInteger location = position.offset; + NSInteger lo = 0, hi = _lines.count - 1, mid = 0; + if (position.affinity == ASTextAffinityBackward) { + while (lo <= hi) { + mid = (lo + hi) / 2; + ASTextLine *line = _lines[mid]; + NSRange range = line.range; + if (range.location < location && location <= range.location + range.length) { + return mid; + } + if (location <= range.location) { + hi = mid - 1; + } else { + lo = mid + 1; + } + } + } else { + while (lo <= hi) { + mid = (lo + hi) / 2; + ASTextLine *line = _lines[mid]; + NSRange range = line.range; + if (range.location <= location && location < range.location + range.length) { + return mid; + } + if (location < range.location) { + hi = mid - 1; + } else { + lo = mid + 1; + } + } + } + return NSNotFound; +} + +- (CGPoint)linePositionForPosition:(ASTextPosition *)position { + NSUInteger lineIndex = [self lineIndexForPosition:position]; + if (lineIndex == NSNotFound) return CGPointZero; + ASTextLine *line = _lines[lineIndex]; + CGFloat offset = [self offsetForTextPosition:position.offset lineIndex:lineIndex]; + if (offset == CGFLOAT_MAX) return CGPointZero; + if (_container.verticalForm) { + return CGPointMake(line.position.x, offset); + } else { + return CGPointMake(offset, line.position.y); + } +} + +- (CGRect)caretRectForPosition:(ASTextPosition *)position { + NSUInteger lineIndex = [self lineIndexForPosition:position]; + if (lineIndex == NSNotFound) return CGRectNull; + ASTextLine *line = _lines[lineIndex]; + CGFloat offset = [self offsetForTextPosition:position.offset lineIndex:lineIndex]; + if (offset == CGFLOAT_MAX) return CGRectNull; + if (_container.verticalForm) { + return CGRectMake(line.bounds.origin.x, offset, line.bounds.size.width, 0); + } else { + return CGRectMake(offset, line.bounds.origin.y, 0, line.bounds.size.height); + } +} + +- (CGRect)firstRectForRange:(ASTextRange *)range { + range = [self _correctedRangeWithEdge:range]; + + NSUInteger startLineIndex = [self lineIndexForPosition:range.start]; + NSUInteger endLineIndex = [self lineIndexForPosition:range.end]; + if (startLineIndex == NSNotFound || endLineIndex == NSNotFound) return CGRectNull; + if (startLineIndex > endLineIndex) return CGRectNull; + ASTextLine *startLine = _lines[startLineIndex]; + ASTextLine *endLine = _lines[endLineIndex]; + NSMutableArray *lines = [NSMutableArray new]; + for (NSUInteger i = startLineIndex; i <= startLineIndex; i++) { + ASTextLine *line = _lines[i]; + if (line.row != startLine.row) break; + [lines addObject:line]; + } + if (_container.verticalForm) { + if (lines.count == 1) { + CGFloat top = [self offsetForTextPosition:range.start.offset lineIndex:startLineIndex]; + CGFloat bottom; + if (startLine == endLine) { + bottom = [self offsetForTextPosition:range.end.offset lineIndex:startLineIndex]; + } else { + bottom = startLine.bottom; + } + if (top == CGFLOAT_MAX || bottom == CGFLOAT_MAX) return CGRectNull; + if (top > bottom) ASTEXT_SWAP(top, bottom); + return CGRectMake(startLine.left, top, startLine.width, bottom - top); + } else { + CGFloat top = [self offsetForTextPosition:range.start.offset lineIndex:startLineIndex]; + CGFloat bottom = startLine.bottom; + if (top == CGFLOAT_MAX || bottom == CGFLOAT_MAX) return CGRectNull; + if (top > bottom) ASTEXT_SWAP(top, bottom); + CGRect rect = CGRectMake(startLine.left, top, startLine.width, bottom - top); + for (NSUInteger i = 1; i < lines.count; i++) { + ASTextLine *line = lines[i]; + rect = CGRectUnion(rect, line.bounds); + } + return rect; + } + } else { + if (lines.count == 1) { + CGFloat left = [self offsetForTextPosition:range.start.offset lineIndex:startLineIndex]; + CGFloat right; + if (startLine == endLine) { + right = [self offsetForTextPosition:range.end.offset lineIndex:startLineIndex]; + } else { + right = startLine.right; + } + if (left == CGFLOAT_MAX || right == CGFLOAT_MAX) return CGRectNull; + if (left > right) ASTEXT_SWAP(left, right); + return CGRectMake(left, startLine.top, right - left, startLine.height); + } else { + CGFloat left = [self offsetForTextPosition:range.start.offset lineIndex:startLineIndex]; + CGFloat right = startLine.right; + if (left == CGFLOAT_MAX || right == CGFLOAT_MAX) return CGRectNull; + if (left > right) ASTEXT_SWAP(left, right); + CGRect rect = CGRectMake(left, startLine.top, right - left, startLine.height); + for (NSUInteger i = 1; i < lines.count; i++) { + ASTextLine *line = lines[i]; + rect = CGRectUnion(rect, line.bounds); + } + return rect; + } + } +} + +- (CGRect)rectForRange:(ASTextRange *)range { + NSArray *rects = [self selectionRectsForRange:range]; + if (rects.count == 0) return CGRectNull; + CGRect rectUnion = ((ASTextSelectionRect *)rects.firstObject).rect; + for (NSUInteger i = 1; i < rects.count; i++) { + ASTextSelectionRect *rect = rects[i]; + rectUnion = CGRectUnion(rectUnion, rect.rect); + } + return rectUnion; +} + +- (NSArray *)selectionRectsForRange:(ASTextRange *)range { + range = [self _correctedRangeWithEdge:range]; + + BOOL isVertical = _container.verticalForm; + NSMutableArray *rects = [NSMutableArray array]; + if (!range) return rects; + + NSUInteger startLineIndex = [self lineIndexForPosition:range.start]; + NSUInteger endLineIndex = [self lineIndexForPosition:range.end]; + if (startLineIndex == NSNotFound || endLineIndex == NSNotFound) return rects; + if (startLineIndex > endLineIndex) ASTEXT_SWAP(startLineIndex, endLineIndex); + ASTextLine *startLine = _lines[startLineIndex]; + ASTextLine *endLine = _lines[endLineIndex]; + CGFloat offsetStart = [self offsetForTextPosition:range.start.offset lineIndex:startLineIndex]; + CGFloat offsetEnd = [self offsetForTextPosition:range.end.offset lineIndex:endLineIndex]; + + ASTextSelectionRect *start = [ASTextSelectionRect new]; + if (isVertical) { + start.rect = CGRectMake(startLine.left, offsetStart, startLine.width, 0); + } else { + start.rect = CGRectMake(offsetStart, startLine.top, 0, startLine.height); + } + start.containsStart = YES; + start.isVertical = isVertical; + [rects addObject:start]; + + ASTextSelectionRect *end = [ASTextSelectionRect new]; + if (isVertical) { + end.rect = CGRectMake(endLine.left, offsetEnd, endLine.width, 0); + } else { + end.rect = CGRectMake(offsetEnd, endLine.top, 0, endLine.height); + } + end.containsEnd = YES; + end.isVertical = isVertical; + [rects addObject:end]; + + if (startLine.row == endLine.row) { // same row + if (offsetStart > offsetEnd) ASTEXT_SWAP(offsetStart, offsetEnd); + ASTextSelectionRect *rect = [ASTextSelectionRect new]; + if (isVertical) { + rect.rect = CGRectMake(startLine.bounds.origin.x, offsetStart, MAX(startLine.width, endLine.width), offsetEnd - offsetStart); + } else { + rect.rect = CGRectMake(offsetStart, startLine.bounds.origin.y, offsetEnd - offsetStart, MAX(startLine.height, endLine.height)); + } + rect.isVertical = isVertical; + [rects addObject:rect]; + + } else { // more than one row + + // start line select rect + ASTextSelectionRect *topRect = [ASTextSelectionRect new]; + topRect.isVertical = isVertical; + CGFloat topOffset = [self offsetForTextPosition:range.start.offset lineIndex:startLineIndex]; + CTRunRef topRun = [self _runForLine:startLine position:range.start]; + if (topRun && (CTRunGetStatus(topRun) & kCTRunStatusRightToLeft)) { + if (isVertical) { + topRect.rect = CGRectMake(startLine.left, _container.path ? startLine.top : _container.insets.top, startLine.width, topOffset - startLine.top); + } else { + topRect.rect = CGRectMake(_container.path ? startLine.left : _container.insets.left, startLine.top, topOffset - startLine.left, startLine.height); + } + topRect.writingDirection = UITextWritingDirectionRightToLeft; + } else { + if (isVertical) { + topRect.rect = CGRectMake(startLine.left, topOffset, startLine.width, (_container.path ? startLine.bottom : _container.size.height - _container.insets.bottom) - topOffset); + } else { + topRect.rect = CGRectMake(topOffset, startLine.top, (_container.path ? startLine.right : _container.size.width - _container.insets.right) - topOffset, startLine.height); + } + } + [rects addObject:topRect]; + + // end line select rect + ASTextSelectionRect *bottomRect = [ASTextSelectionRect new]; + bottomRect.isVertical = isVertical; + CGFloat bottomOffset = [self offsetForTextPosition:range.end.offset lineIndex:endLineIndex]; + CTRunRef bottomRun = [self _runForLine:endLine position:range.end]; + if (bottomRun && (CTRunGetStatus(bottomRun) & kCTRunStatusRightToLeft)) { + if (isVertical) { + bottomRect.rect = CGRectMake(endLine.left, bottomOffset, endLine.width, (_container.path ? endLine.bottom : _container.size.height - _container.insets.bottom) - bottomOffset); + } else { + bottomRect.rect = CGRectMake(bottomOffset, endLine.top, (_container.path ? endLine.right : _container.size.width - _container.insets.right) - bottomOffset, endLine.height); + } + bottomRect.writingDirection = UITextWritingDirectionRightToLeft; + } else { + if (isVertical) { + CGFloat top = _container.path ? endLine.top : _container.insets.top; + bottomRect.rect = CGRectMake(endLine.left, top, endLine.width, bottomOffset - top); + } else { + CGFloat left = _container.path ? endLine.left : _container.insets.left; + bottomRect.rect = CGRectMake(left, endLine.top, bottomOffset - left, endLine.height); + } + } + [rects addObject:bottomRect]; + + if (endLineIndex - startLineIndex >= 2) { + CGRect r = CGRectZero; + BOOL startLineDetected = NO; + for (NSUInteger l = startLineIndex + 1; l < endLineIndex; l++) { + ASTextLine *line = _lines[l]; + if (line.row == startLine.row || line.row == endLine.row) continue; + if (!startLineDetected) { + r = line.bounds; + startLineDetected = YES; + } else { + r = CGRectUnion(r, line.bounds); + } + } + if (startLineDetected) { + if (isVertical) { + if (!_container.path) { + r.origin.y = _container.insets.top; + r.size.height = _container.size.height - _container.insets.bottom - _container.insets.top; + } + r.size.width = CGRectGetMinX(topRect.rect) - CGRectGetMaxX(bottomRect.rect); + r.origin.x = CGRectGetMaxX(bottomRect.rect); + } else { + if (!_container.path) { + r.origin.x = _container.insets.left; + r.size.width = _container.size.width - _container.insets.right - _container.insets.left; + } + r.origin.y = CGRectGetMaxY(topRect.rect); + r.size.height = bottomRect.rect.origin.y - r.origin.y; + } + + ASTextSelectionRect *rect = [ASTextSelectionRect new]; + rect.rect = r; + rect.isVertical = isVertical; + [rects addObject:rect]; + } + } else { + if (isVertical) { + CGRect r0 = bottomRect.rect; + CGRect r1 = topRect.rect; + CGFloat mid = (CGRectGetMaxX(r0) + CGRectGetMinX(r1)) * 0.5; + r0.size.width = mid - r0.origin.x; + CGFloat r1ofs = r1.origin.x - mid; + r1.origin.x -= r1ofs; + r1.size.width += r1ofs; + topRect.rect = r1; + bottomRect.rect = r0; + } else { + CGRect r0 = topRect.rect; + CGRect r1 = bottomRect.rect; + CGFloat mid = (CGRectGetMaxY(r0) + CGRectGetMinY(r1)) * 0.5; + r0.size.height = mid - r0.origin.y; + CGFloat r1ofs = r1.origin.y - mid; + r1.origin.y -= r1ofs; + r1.size.height += r1ofs; + topRect.rect = r0; + bottomRect.rect = r1; + } + } + } + return rects; +} + +- (NSArray *)selectionRectsWithoutStartAndEndForRange:(ASTextRange *)range { + NSMutableArray *rects = [self selectionRectsForRange:range].mutableCopy; + for (NSInteger i = 0, max = rects.count; i < max; i++) { + ASTextSelectionRect *rect = rects[i]; + if (rect.containsStart || rect.containsEnd) { + [rects removeObjectAtIndex:i]; + i--; + max--; + } + } + return rects; +} + +- (NSArray *)selectionRectsWithOnlyStartAndEndForRange:(ASTextRange *)range { + NSMutableArray *rects = [self selectionRectsForRange:range].mutableCopy; + for (NSInteger i = 0, max = rects.count; i < max; i++) { + ASTextSelectionRect *rect = rects[i]; + if (!rect.containsStart && !rect.containsEnd) { + [rects removeObjectAtIndex:i]; + i--; + max--; + } + } + return rects; +} + + +#pragma mark - Draw + + +typedef NS_OPTIONS(NSUInteger, ASTextDecorationType) { + ASTextDecorationTypeUnderline = 1 << 0, + ASTextDecorationTypeStrikethrough = 1 << 1, +}; + +typedef NS_OPTIONS(NSUInteger, ASTextBorderType) { + ASTextBorderTypeBackgound = 1 << 0, + ASTextBorderTypeNormal = 1 << 1, +}; + +static CGRect ASTextMergeRectInSameLine(CGRect rect1, CGRect rect2, BOOL isVertical) { + if (isVertical) { + CGFloat top = MIN(rect1.origin.y, rect2.origin.y); + CGFloat bottom = MAX(rect1.origin.y + rect1.size.height, rect2.origin.y + rect2.size.height); + CGFloat width = MAX(rect1.size.width, rect2.size.width); + return CGRectMake(rect1.origin.x, top, width, bottom - top); + } else { + CGFloat left = MIN(rect1.origin.x, rect2.origin.x); + CGFloat right = MAX(rect1.origin.x + rect1.size.width, rect2.origin.x + rect2.size.width); + CGFloat height = MAX(rect1.size.height, rect2.size.height); + return CGRectMake(left, rect1.origin.y, right - left, height); + } +} + +static void ASTextGetRunsMaxMetric(CFArrayRef runs, CGFloat *xHeight, CGFloat *underlinePosition, CGFloat *lineThickness) { + CGFloat maxXHeight = 0; + CGFloat maxUnderlinePos = 0; + CGFloat maxLineThickness = 0; + for (NSUInteger i = 0, max = CFArrayGetCount(runs); i < max; i++) { + CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runs, i); + CFDictionaryRef attrs = CTRunGetAttributes(run); + if (attrs) { + CTFontRef font = (CTFontRef)CFDictionaryGetValue(attrs, kCTFontAttributeName); + if (font) { + CGFloat xHeight = CTFontGetXHeight(font); + if (xHeight > maxXHeight) maxXHeight = xHeight; + CGFloat underlinePos = CTFontGetUnderlinePosition(font); + if (underlinePos < maxUnderlinePos) maxUnderlinePos = underlinePos; + CGFloat lineThickness = CTFontGetUnderlineThickness(font); + if (lineThickness > maxLineThickness) maxLineThickness = lineThickness; + } + } + } + if (xHeight) *xHeight = maxXHeight; + if (underlinePosition) *underlinePosition = maxUnderlinePos; + if (lineThickness) *lineThickness = maxLineThickness; +} + +static void ASTextDrawRun(ASTextLine *line, CTRunRef run, CGContextRef context, CGSize size, BOOL isVertical, NSArray *runRanges, CGFloat verticalOffset) { + CGAffineTransform runTextMatrix = CTRunGetTextMatrix(run); + BOOL runTextMatrixIsID = CGAffineTransformIsIdentity(runTextMatrix); + + CFDictionaryRef runAttrs = CTRunGetAttributes(run); + NSValue *glyphTransformValue = (NSValue *)CFDictionaryGetValue(runAttrs, (__bridge const void *)(ASTextGlyphTransformAttributeName)); + if (!isVertical && !glyphTransformValue) { // draw run + if (!runTextMatrixIsID) { + CGContextSaveGState(context); + CGAffineTransform trans = CGContextGetTextMatrix(context); + CGContextSetTextMatrix(context, CGAffineTransformConcat(trans, runTextMatrix)); + } + CTRunDraw(run, context, CFRangeMake(0, 0)); + if (!runTextMatrixIsID) { + CGContextRestoreGState(context); + } + } else { // draw glyph + CTFontRef runFont = (CTFontRef)CFDictionaryGetValue(runAttrs, kCTFontAttributeName); + if (!runFont) return; + NSUInteger glyphCount = CTRunGetGlyphCount(run); + if (glyphCount <= 0) return; + + CGGlyph glyphs[glyphCount]; + CGPoint glyphPositions[glyphCount]; + CTRunGetGlyphs(run, CFRangeMake(0, 0), glyphs); + CTRunGetPositions(run, CFRangeMake(0, 0), glyphPositions); + + CGColorRef fillColor = (CGColorRef)CFDictionaryGetValue(runAttrs, kCTForegroundColorAttributeName); + fillColor = ASTextGetCGColor(fillColor); + NSNumber *strokeWidth = (NSNumber *)CFDictionaryGetValue(runAttrs, kCTStrokeWidthAttributeName); + + CGContextSaveGState(context); { + CGContextSetFillColorWithColor(context, fillColor); + if (!strokeWidth || strokeWidth.floatValue == 0) { + CGContextSetTextDrawingMode(context, kCGTextFill); + } else { + CGColorRef strokeColor = (CGColorRef)CFDictionaryGetValue(runAttrs, kCTStrokeColorAttributeName); + if (!strokeColor) strokeColor = fillColor; + CGContextSetStrokeColorWithColor(context, strokeColor); + CGContextSetLineWidth(context, CTFontGetSize(runFont) * fabs(strokeWidth.floatValue * 0.01)); + if (strokeWidth.floatValue > 0) { + CGContextSetTextDrawingMode(context, kCGTextStroke); + } else { + CGContextSetTextDrawingMode(context, kCGTextFillStroke); + } + } + + if (isVertical) { + CFIndex runStrIdx[glyphCount + 1]; + CTRunGetStringIndices(run, CFRangeMake(0, 0), runStrIdx); + CFRange runStrRange = CTRunGetStringRange(run); + runStrIdx[glyphCount] = runStrRange.location + runStrRange.length; + CGSize glyphAdvances[glyphCount]; + CTRunGetAdvances(run, CFRangeMake(0, 0), glyphAdvances); + CGFloat ascent = CTFontGetAscent(runFont); + CGFloat descent = CTFontGetDescent(runFont); + CGAffineTransform glyphTransform = glyphTransformValue.CGAffineTransformValue; + CGPoint zeroPoint = CGPointZero; + + for (ASTextRunGlyphRange *oneRange in runRanges) { + NSRange range = oneRange.glyphRangeInRun; + NSUInteger rangeMax = range.location + range.length; + ASTextRunGlyphDrawMode mode = oneRange.drawMode; + + for (NSUInteger g = range.location; g < rangeMax; g++) { + CGContextSaveGState(context); { + CGContextSetTextMatrix(context, CGAffineTransformIdentity); + if (glyphTransformValue) { + CGContextSetTextMatrix(context, glyphTransform); + } + if (mode) { // CJK glyph, need rotated + CGFloat ofs = (ascent - descent) * 0.5; + CGFloat w = glyphAdvances[g].width * 0.5; + CGFloat x = x = line.position.x + verticalOffset + glyphPositions[g].y + (ofs - w); + CGFloat y = -line.position.y + size.height - glyphPositions[g].x - (ofs + w); + if (mode == ASTextRunGlyphDrawModeVerticalRotateMove) { + x += w; + y += w; + } + CGContextSetTextPosition(context, x, y); + } else { + CGContextRotateCTM(context, -M_PI_2); + CGContextSetTextPosition(context, + line.position.y - size.height + glyphPositions[g].x, + line.position.x + verticalOffset + glyphPositions[g].y); + } + + if (ASTextCTFontContainsColorBitmapGlyphs(runFont)) { + CTFontDrawGlyphs(runFont, glyphs + g, &zeroPoint, 1, context); + } else { + CGFontRef cgFont = CTFontCopyGraphicsFont(runFont, NULL); + CGContextSetFont(context, cgFont); + CGContextSetFontSize(context, CTFontGetSize(runFont)); + CGContextShowGlyphsAtPositions(context, glyphs + g, &zeroPoint, 1); + CGFontRelease(cgFont); + } + } CGContextRestoreGState(context); + } + } + } else { // not vertical + if (glyphTransformValue) { + CFIndex runStrIdx[glyphCount + 1]; + CTRunGetStringIndices(run, CFRangeMake(0, 0), runStrIdx); + CFRange runStrRange = CTRunGetStringRange(run); + runStrIdx[glyphCount] = runStrRange.location + runStrRange.length; + CGSize glyphAdvances[glyphCount]; + CTRunGetAdvances(run, CFRangeMake(0, 0), glyphAdvances); + CGAffineTransform glyphTransform = glyphTransformValue.CGAffineTransformValue; + CGPoint zeroPoint = CGPointZero; + + for (NSUInteger g = 0; g < glyphCount; g++) { + CGContextSaveGState(context); { + CGContextSetTextMatrix(context, CGAffineTransformIdentity); + CGContextSetTextMatrix(context, glyphTransform); + CGContextSetTextPosition(context, + line.position.x + glyphPositions[g].x, + size.height - (line.position.y + glyphPositions[g].y)); + + if (ASTextCTFontContainsColorBitmapGlyphs(runFont)) { + CTFontDrawGlyphs(runFont, glyphs + g, &zeroPoint, 1, context); + } else { + CGFontRef cgFont = CTFontCopyGraphicsFont(runFont, NULL); + CGContextSetFont(context, cgFont); + CGContextSetFontSize(context, CTFontGetSize(runFont)); + CGContextShowGlyphsAtPositions(context, glyphs + g, &zeroPoint, 1); + CGFontRelease(cgFont); + } + } CGContextRestoreGState(context); + } + } else { + if (ASTextCTFontContainsColorBitmapGlyphs(runFont)) { + CTFontDrawGlyphs(runFont, glyphs, glyphPositions, glyphCount, context); + } else { + CGFontRef cgFont = CTFontCopyGraphicsFont(runFont, NULL); + CGContextSetFont(context, cgFont); + CGContextSetFontSize(context, CTFontGetSize(runFont)); + CGContextShowGlyphsAtPositions(context, glyphs, glyphPositions, glyphCount); + CGFontRelease(cgFont); + } + } + } + + } CGContextRestoreGState(context); + } +} + +static void ASTextSetLinePatternInContext(ASTextLineStyle style, CGFloat width, CGFloat phase, CGContextRef context){ + CGContextSetLineWidth(context, width); + CGContextSetLineCap(context, kCGLineCapButt); + CGContextSetLineJoin(context, kCGLineJoinMiter); + + CGFloat dash = 12, dot = 5, space = 3; + NSUInteger pattern = style & 0xF00; + if (pattern == ASTextLineStylePatternSolid) { + CGContextSetLineDash(context, phase, NULL, 0); + } else if (pattern == ASTextLineStylePatternDot) { + CGFloat lengths[2] = {width * dot, width * space}; + CGContextSetLineDash(context, phase, lengths, 2); + } else if (pattern == ASTextLineStylePatternDash) { + CGFloat lengths[2] = {width * dash, width * space}; + CGContextSetLineDash(context, phase, lengths, 2); + } else if (pattern == ASTextLineStylePatternDashDot) { + CGFloat lengths[4] = {width * dash, width * space, width * dot, width * space}; + CGContextSetLineDash(context, phase, lengths, 4); + } else if (pattern == ASTextLineStylePatternDashDotDot) { + CGFloat lengths[6] = {width * dash, width * space,width * dot, width * space, width * dot, width * space}; + CGContextSetLineDash(context, phase, lengths, 6); + } else if (pattern == ASTextLineStylePatternCircleDot) { + CGFloat lengths[2] = {width * 0, width * 3}; + CGContextSetLineDash(context, phase, lengths, 2); + CGContextSetLineCap(context, kCGLineCapRound); + CGContextSetLineJoin(context, kCGLineJoinRound); + } +} + + +static void ASTextDrawBorderRects(CGContextRef context, CGSize size, ASTextBorder *border, NSArray *rects, BOOL isVertical) { + if (rects.count == 0) return; + + ASTextShadow *shadow = border.shadow; + if (shadow.color) { + CGContextSaveGState(context); + CGContextSetShadowWithColor(context, shadow.offset, shadow.radius, shadow.color.CGColor); + CGContextBeginTransparencyLayer(context, NULL); + } + + NSMutableArray *paths = [NSMutableArray new]; + for (NSValue *value in rects) { + CGRect rect = value.CGRectValue; + if (isVertical) { + rect = UIEdgeInsetsInsetRect(rect, UIEdgeInsetRotateVertical(border.insets)); + } else { + rect = UIEdgeInsetsInsetRect(rect, border.insets); + } + rect = ASTextCGRectPixelRound(rect); + UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:border.cornerRadius]; + [path closePath]; + [paths addObject:path]; + } + + if (border.fillColor) { + CGContextSaveGState(context); + CGContextSetFillColorWithColor(context, border.fillColor.CGColor); + for (UIBezierPath *path in paths) { + CGContextAddPath(context, path.CGPath); + } + CGContextFillPath(context); + CGContextRestoreGState(context); + } + + if (border.strokeColor && border.lineStyle > 0 && border.strokeWidth > 0) { + + //-------------------------- single line ------------------------------// + CGContextSaveGState(context); + for (UIBezierPath *path in paths) { + CGRect bounds = CGRectUnion(path.bounds, (CGRect){CGPointZero, size}); + bounds = CGRectInset(bounds, -2 * border.strokeWidth, -2 * border.strokeWidth); + CGContextAddRect(context, bounds); + CGContextAddPath(context, path.CGPath); + CGContextEOClip(context); + } + [border.strokeColor setStroke]; + ASTextSetLinePatternInContext(border.lineStyle, border.strokeWidth, 0, context); + CGFloat inset = -border.strokeWidth * 0.5; + if ((border.lineStyle & 0xFF) == ASTextLineStyleThick) { + inset *= 2; + CGContextSetLineWidth(context, border.strokeWidth * 2); + } + CGFloat radiusDelta = -inset; + if (border.cornerRadius <= 0) { + radiusDelta = 0; + } + CGContextSetLineJoin(context, border.lineJoin); + for (NSValue *value in rects) { + CGRect rect = value.CGRectValue; + if (isVertical) { + rect = UIEdgeInsetsInsetRect(rect, UIEdgeInsetRotateVertical(border.insets)); + } else { + rect = UIEdgeInsetsInsetRect(rect, border.insets); + } + rect = CGRectInset(rect, inset, inset); + UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:border.cornerRadius + radiusDelta]; + [path closePath]; + CGContextAddPath(context, path.CGPath); + } + CGContextStrokePath(context); + CGContextRestoreGState(context); + + //------------------------- second line ------------------------------// + if ((border.lineStyle & 0xFF) == ASTextLineStyleDouble) { + CGContextSaveGState(context); + CGFloat inset = -border.strokeWidth * 2; + for (NSValue *value in rects) { + CGRect rect = value.CGRectValue; + rect = UIEdgeInsetsInsetRect(rect, border.insets); + rect = CGRectInset(rect, inset, inset); + UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:border.cornerRadius + 2 * border.strokeWidth]; + [path closePath]; + + CGRect bounds = CGRectUnion(path.bounds, (CGRect){CGPointZero, size}); + bounds = CGRectInset(bounds, -2 * border.strokeWidth, -2 * border.strokeWidth); + CGContextAddRect(context, bounds); + CGContextAddPath(context, path.CGPath); + CGContextEOClip(context); + } + CGContextSetStrokeColorWithColor(context, border.strokeColor.CGColor); + ASTextSetLinePatternInContext(border.lineStyle, border.strokeWidth, 0, context); + CGContextSetLineJoin(context, border.lineJoin); + inset = -border.strokeWidth * 2.5; + radiusDelta = border.strokeWidth * 2; + if (border.cornerRadius <= 0) { + radiusDelta = 0; + } + for (NSValue *value in rects) { + CGRect rect = value.CGRectValue; + rect = UIEdgeInsetsInsetRect(rect, border.insets); + rect = CGRectInset(rect, inset, inset); + UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:border.cornerRadius + radiusDelta]; + [path closePath]; + CGContextAddPath(context, path.CGPath); + } + CGContextStrokePath(context); + CGContextRestoreGState(context); + } + } + + if (shadow.color) { + CGContextEndTransparencyLayer(context); + CGContextRestoreGState(context); + } +} + +static void ASTextDrawLineStyle(CGContextRef context, CGFloat length, CGFloat lineWidth, ASTextLineStyle style, CGPoint position, CGColorRef color, BOOL isVertical) { + NSUInteger styleBase = style & 0xFF; + if (styleBase == 0) return; + + CGContextSaveGState(context); { + if (isVertical) { + CGFloat x, y1, y2, w; + y1 = ASRoundPixelValue(position.y); + y2 = ASRoundPixelValue(position.y + length); + w = (styleBase == ASTextLineStyleThick ? lineWidth * 2 : lineWidth); + + CGFloat linePixel = ASTextCGFloatToPixel(w); + if (fabs(linePixel - floor(linePixel)) < 0.1) { + int iPixel = linePixel; + if (iPixel == 0 || (iPixel % 2)) { // odd line pixel + x = ASTextCGFloatPixelHalf(position.x); + } else { + x = ASFloorPixelValue(position.x); + } + } else { + x = position.x; + } + + CGContextSetStrokeColorWithColor(context, color); + ASTextSetLinePatternInContext(style, lineWidth, position.y, context); + CGContextSetLineWidth(context, w); + if (styleBase == ASTextLineStyleSingle) { + CGContextMoveToPoint(context, x, y1); + CGContextAddLineToPoint(context, x, y2); + CGContextStrokePath(context); + } else if (styleBase == ASTextLineStyleThick) { + CGContextMoveToPoint(context, x, y1); + CGContextAddLineToPoint(context, x, y2); + CGContextStrokePath(context); + } else if (styleBase == ASTextLineStyleDouble) { + CGContextMoveToPoint(context, x - w, y1); + CGContextAddLineToPoint(context, x - w, y2); + CGContextStrokePath(context); + CGContextMoveToPoint(context, x + w, y1); + CGContextAddLineToPoint(context, x + w, y2); + CGContextStrokePath(context); + } + } else { + CGFloat x1, x2, y, w; + x1 = ASRoundPixelValue(position.x); + x2 = ASRoundPixelValue(position.x + length); + w = (styleBase == ASTextLineStyleThick ? lineWidth * 2 : lineWidth); + + CGFloat linePixel = ASTextCGFloatToPixel(w); + if (fabs(linePixel - floor(linePixel)) < 0.1) { + int iPixel = linePixel; + if (iPixel == 0 || (iPixel % 2)) { // odd line pixel + y = ASTextCGFloatPixelHalf(position.y); + } else { + y = ASFloorPixelValue(position.y); + } + } else { + y = position.y; + } + + CGContextSetStrokeColorWithColor(context, color); + ASTextSetLinePatternInContext(style, lineWidth, position.x, context); + CGContextSetLineWidth(context, w); + if (styleBase == ASTextLineStyleSingle) { + CGContextMoveToPoint(context, x1, y); + CGContextAddLineToPoint(context, x2, y); + CGContextStrokePath(context); + } else if (styleBase == ASTextLineStyleThick) { + CGContextMoveToPoint(context, x1, y); + CGContextAddLineToPoint(context, x2, y); + CGContextStrokePath(context); + } else if (styleBase == ASTextLineStyleDouble) { + CGContextMoveToPoint(context, x1, y - w); + CGContextAddLineToPoint(context, x2, y - w); + CGContextStrokePath(context); + CGContextMoveToPoint(context, x1, y + w); + CGContextAddLineToPoint(context, x2, y + w); + CGContextStrokePath(context); + } + } + } CGContextRestoreGState(context); +} + +static void ASTextDrawText(ASTextLayout *layout, CGContextRef context, CGSize size, CGPoint point, BOOL (^cancel)(void)) { + CGContextSaveGState(context); { + + CGContextTranslateCTM(context, point.x, point.y); + CGContextTranslateCTM(context, 0, size.height); + CGContextScaleCTM(context, 1, -1); + + BOOL isVertical = layout.container.verticalForm; + CGFloat verticalOffset = isVertical ? (size.width - layout.container.size.width) : 0; + + NSArray *lines = layout.lines; + for (NSUInteger l = 0, lMax = lines.count; l < lMax; l++) { + ASTextLine *line = lines[l]; + if (layout.truncatedLine && layout.truncatedLine.index == line.index) line = layout.truncatedLine; + NSArray *lineRunRanges = line.verticalRotateRange; + CGFloat posX = line.position.x + verticalOffset; + CGFloat posY = size.height - line.position.y; + CFArrayRef runs = CTLineGetGlyphRuns(line.CTLine); + for (NSUInteger r = 0, rMax = CFArrayGetCount(runs); r < rMax; r++) { + CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runs, r); + CGContextSetTextMatrix(context, CGAffineTransformIdentity); + CGContextSetTextPosition(context, posX, posY); + ASTextDrawRun(line, run, context, size, isVertical, lineRunRanges[r], verticalOffset); + } + if (cancel && cancel()) break; + } + + // Use this to draw frame for test/debug. + // CGContextTranslateCTM(context, verticalOffset, size.height); + // CTFrameDraw(layout.frame, context); + + } CGContextRestoreGState(context); +} + +static void ASTextDrawBlockBorder(ASTextLayout *layout, CGContextRef context, CGSize size, CGPoint point, BOOL (^cancel)(void)) { + CGContextSaveGState(context); + CGContextTranslateCTM(context, point.x, point.y); + + BOOL isVertical = layout.container.verticalForm; + CGFloat verticalOffset = isVertical ? (size.width - layout.container.size.width) : 0; + + NSArray *lines = layout.lines; + for (NSInteger l = 0, lMax = lines.count; l < lMax; l++) { + if (cancel && cancel()) break; + + ASTextLine *line = lines[l]; + if (layout.truncatedLine && layout.truncatedLine.index == line.index) line = layout.truncatedLine; + CFArrayRef runs = CTLineGetGlyphRuns(line.CTLine); + for (NSInteger r = 0, rMax = CFArrayGetCount(runs); r < rMax; r++) { + CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runs, r); + CFIndex glyphCount = CTRunGetGlyphCount(run); + if (glyphCount == 0) continue; + NSDictionary *attrs = (id)CTRunGetAttributes(run); + ASTextBorder *border = attrs[ASTextBlockBorderAttributeName]; + if (!border) continue; + + NSUInteger lineStartIndex = line.index; + while (lineStartIndex > 0) { + if (((ASTextLine *)lines[lineStartIndex - 1]).row == line.row) lineStartIndex--; + else break; + } + + CGRect unionRect = CGRectZero; + NSUInteger lineStartRow = ((ASTextLine *)lines[lineStartIndex]).row; + NSUInteger lineContinueIndex = lineStartIndex; + NSUInteger lineContinueRow = lineStartRow; + do { + ASTextLine *one = lines[lineContinueIndex]; + if (lineContinueIndex == lineStartIndex) { + unionRect = one.bounds; + } else { + unionRect = CGRectUnion(unionRect, one.bounds); + } + if (lineContinueIndex + 1 == lMax) break; + ASTextLine *next = lines[lineContinueIndex + 1]; + if (next.row != lineContinueRow) { + ASTextBorder *nextBorder = [layout.text as_attribute:ASTextBlockBorderAttributeName atIndex:next.range.location]; + if ([nextBorder isEqual:border]) { + lineContinueRow++; + } else { + break; + } + } + lineContinueIndex++; + } while (true); + + if (isVertical) { + UIEdgeInsets insets = layout.container.insets; + unionRect.origin.y = insets.top; + unionRect.size.height = layout.container.size.height -insets.top - insets.bottom; + } else { + UIEdgeInsets insets = layout.container.insets; + unionRect.origin.x = insets.left; + unionRect.size.width = layout.container.size.width -insets.left - insets.right; + } + unionRect.origin.x += verticalOffset; + ASTextDrawBorderRects(context, size, border, @[[NSValue valueWithCGRect:unionRect]], isVertical); + + l = lineContinueIndex; + break; + } + } + + + CGContextRestoreGState(context); +} + +static void ASTextDrawBorder(ASTextLayout *layout, CGContextRef context, CGSize size, CGPoint point, ASTextBorderType type, BOOL (^cancel)(void)) { + CGContextSaveGState(context); + CGContextTranslateCTM(context, point.x, point.y); + + BOOL isVertical = layout.container.verticalForm; + CGFloat verticalOffset = isVertical ? (size.width - layout.container.size.width) : 0; + + NSArray *lines = layout.lines; + NSString *borderKey = (type == ASTextBorderTypeNormal ? ASTextBorderAttributeName : ASTextBackgroundBorderAttributeName); + + BOOL needJumpRun = NO; + NSUInteger jumpRunIndex = 0; + + for (NSInteger l = 0, lMax = lines.count; l < lMax; l++) { + if (cancel && cancel()) break; + + ASTextLine *line = lines[l]; + if (layout.truncatedLine && layout.truncatedLine.index == line.index) line = layout.truncatedLine; + CFArrayRef runs = CTLineGetGlyphRuns(line.CTLine); + for (NSInteger r = 0, rMax = CFArrayGetCount(runs); r < rMax; r++) { + if (needJumpRun) { + needJumpRun = NO; + r = jumpRunIndex + 1; + if (r >= rMax) break; + } + + CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runs, r); + CFIndex glyphCount = CTRunGetGlyphCount(run); + if (glyphCount == 0) continue; + + NSDictionary *attrs = (id)CTRunGetAttributes(run); + ASTextBorder *border = attrs[borderKey]; + if (!border) continue; + + CFRange runRange = CTRunGetStringRange(run); + if (runRange.location == kCFNotFound || runRange.length == 0) continue; + if (runRange.location + runRange.length > layout.text.length) continue; + + NSMutableArray *runRects = [NSMutableArray new]; + NSInteger endLineIndex = l; + NSInteger endRunIndex = r; + BOOL endFound = NO; + for (NSInteger ll = l; ll < lMax; ll++) { + if (endFound) break; + ASTextLine *iLine = lines[ll]; + CFArrayRef iRuns = CTLineGetGlyphRuns(iLine.CTLine); + + CGRect extLineRect = CGRectNull; + for (NSInteger rr = (ll == l) ? r : 0, rrMax = CFArrayGetCount(iRuns); rr < rrMax; rr++) { + CTRunRef iRun = (CTRunRef)CFArrayGetValueAtIndex(iRuns, rr); + NSDictionary *iAttrs = (id)CTRunGetAttributes(iRun); + ASTextBorder *iBorder = iAttrs[borderKey]; + if (![border isEqual:iBorder]) { + endFound = YES; + break; + } + endLineIndex = ll; + endRunIndex = rr; + + CGPoint iRunPosition = CGPointZero; + CTRunGetPositions(iRun, CFRangeMake(0, 1), &iRunPosition); + CGFloat ascent, descent; + CGFloat iRunWidth = CTRunGetTypographicBounds(iRun, CFRangeMake(0, 0), &ascent, &descent, NULL); + + if (isVertical) { + ASTEXT_SWAP(iRunPosition.x, iRunPosition.y); + iRunPosition.y += iLine.position.y; + CGRect iRect = CGRectMake(verticalOffset + line.position.x - descent, iRunPosition.y, ascent + descent, iRunWidth); + if (CGRectIsNull(extLineRect)) { + extLineRect = iRect; + } else { + extLineRect = CGRectUnion(extLineRect, iRect); + } + } else { + iRunPosition.x += iLine.position.x; + CGRect iRect = CGRectMake(iRunPosition.x, iLine.position.y - ascent, iRunWidth, ascent + descent); + if (CGRectIsNull(extLineRect)) { + extLineRect = iRect; + } else { + extLineRect = CGRectUnion(extLineRect, iRect); + } + } + } + + if (!CGRectIsNull(extLineRect)) { + [runRects addObject:[NSValue valueWithCGRect:extLineRect]]; + } + } + + NSMutableArray *drawRects = [NSMutableArray new]; + CGRect curRect= ((NSValue *)[runRects firstObject]).CGRectValue; + for (NSInteger re = 0, reMax = runRects.count; re < reMax; re++) { + CGRect rect = ((NSValue *)runRects[re]).CGRectValue; + if (isVertical) { + if (fabs(rect.origin.x - curRect.origin.x) < 1) { + curRect = ASTextMergeRectInSameLine(rect, curRect, isVertical); + } else { + [drawRects addObject:[NSValue valueWithCGRect:curRect]]; + curRect = rect; + } + } else { + if (fabs(rect.origin.y - curRect.origin.y) < 1) { + curRect = ASTextMergeRectInSameLine(rect, curRect, isVertical); + } else { + [drawRects addObject:[NSValue valueWithCGRect:curRect]]; + curRect = rect; + } + } + } + if (!CGRectEqualToRect(curRect, CGRectZero)) { + [drawRects addObject:[NSValue valueWithCGRect:curRect]]; + } + + ASTextDrawBorderRects(context, size, border, drawRects, isVertical); + + if (l == endLineIndex) { + r = endRunIndex; + } else { + l = endLineIndex - 1; + needJumpRun = YES; + jumpRunIndex = endRunIndex; + break; + } + + } + } + + CGContextRestoreGState(context); +} + +static void ASTextDrawDecoration(ASTextLayout *layout, CGContextRef context, CGSize size, CGPoint point, ASTextDecorationType type, BOOL (^cancel)(void)) { + NSArray *lines = layout.lines; + + CGContextSaveGState(context); + CGContextTranslateCTM(context, point.x, point.y); + + BOOL isVertical = layout.container.verticalForm; + CGFloat verticalOffset = isVertical ? (size.width - layout.container.size.width) : 0; + CGContextTranslateCTM(context, verticalOffset, 0); + + for (NSUInteger l = 0, lMax = layout.lines.count; l < lMax; l++) { + if (cancel && cancel()) break; + + ASTextLine *line = lines[l]; + if (layout.truncatedLine && layout.truncatedLine.index == line.index) line = layout.truncatedLine; + CFArrayRef runs = CTLineGetGlyphRuns(line.CTLine); + for (NSUInteger r = 0, rMax = CFArrayGetCount(runs); r < rMax; r++) { + CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runs, r); + CFIndex glyphCount = CTRunGetGlyphCount(run); + if (glyphCount == 0) continue; + + NSDictionary *attrs = (id)CTRunGetAttributes(run); + ASTextDecoration *underline = attrs[ASTextUnderlineAttributeName]; + ASTextDecoration *strikethrough = attrs[ASTextStrikethroughAttributeName]; + + BOOL needDrawUnderline = NO, needDrawStrikethrough = NO; + if ((type & ASTextDecorationTypeUnderline) && underline.style > 0) { + needDrawUnderline = YES; + } + if ((type & ASTextDecorationTypeStrikethrough) && strikethrough.style > 0) { + needDrawStrikethrough = YES; + } + if (!needDrawUnderline && !needDrawStrikethrough) continue; + + CFRange runRange = CTRunGetStringRange(run); + if (runRange.location == kCFNotFound || runRange.length == 0) continue; + if (runRange.location + runRange.length > layout.text.length) continue; + NSString *runStr = [layout.text attributedSubstringFromRange:NSMakeRange(runRange.location, runRange.length)].string; + if (ASTextIsLinebreakString(runStr)) continue; // may need more checks... + + CGFloat xHeight, underlinePosition, lineThickness; + ASTextGetRunsMaxMetric(runs, &xHeight, &underlinePosition, &lineThickness); + + CGPoint underlineStart, strikethroughStart; + CGFloat length; + + if (isVertical) { + underlineStart.x = line.position.x + underlinePosition; + strikethroughStart.x = line.position.x + xHeight / 2; + + CGPoint runPosition = CGPointZero; + CTRunGetPositions(run, CFRangeMake(0, 1), &runPosition); + underlineStart.y = strikethroughStart.y = runPosition.x + line.position.y; + length = CTRunGetTypographicBounds(run, CFRangeMake(0, 0), NULL, NULL, NULL); + + } else { + underlineStart.y = line.position.y - underlinePosition; + strikethroughStart.y = line.position.y - xHeight / 2; + + CGPoint runPosition = CGPointZero; + CTRunGetPositions(run, CFRangeMake(0, 1), &runPosition); + underlineStart.x = strikethroughStart.x = runPosition.x + line.position.x; + length = CTRunGetTypographicBounds(run, CFRangeMake(0, 0), NULL, NULL, NULL); + } + + if (needDrawUnderline) { + CGColorRef color = underline.color.CGColor; + if (!color) { + color = (__bridge CGColorRef)(attrs[(id)kCTForegroundColorAttributeName]); + color = ASTextGetCGColor(color); + } + CGFloat thickness = underline.width ? underline.width.floatValue : lineThickness; + ASTextShadow *shadow = underline.shadow; + while (shadow) { + if (!shadow.color) { + shadow = shadow.subShadow; + continue; + } + CGFloat offsetAlterX = size.width + 0xFFFF; + CGContextSaveGState(context); { + CGSize offset = shadow.offset; + offset.width -= offsetAlterX; + CGContextSaveGState(context); { + CGContextSetShadowWithColor(context, offset, shadow.radius, shadow.color.CGColor); + CGContextSetBlendMode(context, shadow.blendMode); + CGContextTranslateCTM(context, offsetAlterX, 0); + ASTextDrawLineStyle(context, length, thickness, underline.style, underlineStart, color, isVertical); + } CGContextRestoreGState(context); + } CGContextRestoreGState(context); + shadow = shadow.subShadow; + } + ASTextDrawLineStyle(context, length, thickness, underline.style, underlineStart, color, isVertical); + } + + if (needDrawStrikethrough) { + CGColorRef color = strikethrough.color.CGColor; + if (!color) { + color = (__bridge CGColorRef)(attrs[(id)kCTForegroundColorAttributeName]); + color = ASTextGetCGColor(color); + } + CGFloat thickness = strikethrough.width ? strikethrough.width.floatValue : lineThickness; + ASTextShadow *shadow = underline.shadow; + while (shadow) { + if (!shadow.color) { + shadow = shadow.subShadow; + continue; + } + CGFloat offsetAlterX = size.width + 0xFFFF; + CGContextSaveGState(context); { + CGSize offset = shadow.offset; + offset.width -= offsetAlterX; + CGContextSaveGState(context); { + CGContextSetShadowWithColor(context, offset, shadow.radius, shadow.color.CGColor); + CGContextSetBlendMode(context, shadow.blendMode); + CGContextTranslateCTM(context, offsetAlterX, 0); + ASTextDrawLineStyle(context, length, thickness, underline.style, underlineStart, color, isVertical); + } CGContextRestoreGState(context); + } CGContextRestoreGState(context); + shadow = shadow.subShadow; + } + ASTextDrawLineStyle(context, length, thickness, strikethrough.style, strikethroughStart, color, isVertical); + } + } + } + CGContextRestoreGState(context); +} + +static void ASTextDrawAttachment(ASTextLayout *layout, CGContextRef context, CGSize size, CGPoint point, UIView *targetView, CALayer *targetLayer, BOOL (^cancel)(void)) { + + BOOL isVertical = layout.container.verticalForm; + CGFloat verticalOffset = isVertical ? (size.width - layout.container.size.width) : 0; + + for (NSUInteger i = 0, max = layout.attachments.count; i < max; i++) { + ASTextAttachment *a = layout.attachments[i]; + if (!a.content) continue; + + UIImage *image = nil; + UIView *view = nil; + CALayer *layer = nil; + if ([a.content isKindOfClass:[UIImage class]]) { + image = a.content; + } else if ([a.content isKindOfClass:[UIView class]]) { + view = a.content; + } else if ([a.content isKindOfClass:[CALayer class]]) { + layer = a.content; + } + if (!image && !view && !layer) continue; + if (image && !context) continue; + if (view && !targetView) continue; + if (layer && !targetLayer) continue; + if (cancel && cancel()) break; + + CGSize asize = image ? image.size : view ? view.frame.size : layer.frame.size; + CGRect rect = ((NSValue *)layout.attachmentRects[i]).CGRectValue; + if (isVertical) { + rect = UIEdgeInsetsInsetRect(rect, UIEdgeInsetRotateVertical(a.contentInsets)); + } else { + rect = UIEdgeInsetsInsetRect(rect, a.contentInsets); + } + rect = ASTextCGRectFitWithContentMode(rect, asize, a.contentMode); + rect = ASTextCGRectPixelRound(rect); + rect = CGRectStandardize(rect); + rect.origin.x += point.x + verticalOffset; + rect.origin.y += point.y; + if (image) { + CGImageRef ref = image.CGImage; + if (ref) { + CGContextSaveGState(context); + CGContextTranslateCTM(context, 0, CGRectGetMaxY(rect) + CGRectGetMinY(rect)); + CGContextScaleCTM(context, 1, -1); + CGContextDrawImage(context, rect, ref); + CGContextRestoreGState(context); + } + } else if (view) { + view.frame = rect; + [targetView addSubview:view]; + } else if (layer) { + layer.frame = rect; + [targetLayer addSublayer:layer]; + } + } +} + +static void ASTextDrawShadow(ASTextLayout *layout, CGContextRef context, CGSize size, CGPoint point, BOOL (^cancel)(void)) { + //move out of context. (0xFFFF is just a random large number) + CGFloat offsetAlterX = size.width + 0xFFFF; + + BOOL isVertical = layout.container.verticalForm; + CGFloat verticalOffset = isVertical ? (size.width - layout.container.size.width) : 0; + + CGContextSaveGState(context); { + CGContextTranslateCTM(context, point.x, point.y); + CGContextTranslateCTM(context, 0, size.height); + CGContextScaleCTM(context, 1, -1); + NSArray *lines = layout.lines; + for (NSUInteger l = 0, lMax = layout.lines.count; l < lMax; l++) { + if (cancel && cancel()) break; + ASTextLine *line = lines[l]; + if (layout.truncatedLine && layout.truncatedLine.index == line.index) line = layout.truncatedLine; + NSArray *lineRunRanges = line.verticalRotateRange; + CGFloat linePosX = line.position.x; + CGFloat linePosY = size.height - line.position.y; + CFArrayRef runs = CTLineGetGlyphRuns(line.CTLine); + for (NSUInteger r = 0, rMax = CFArrayGetCount(runs); r < rMax; r++) { + CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runs, r); + CGContextSetTextMatrix(context, CGAffineTransformIdentity); + CGContextSetTextPosition(context, linePosX, linePosY); + NSDictionary *attrs = (id)CTRunGetAttributes(run); + ASTextShadow *shadow = attrs[ASTextShadowAttributeName]; + ASTextShadow *nsShadow = [ASTextShadow shadowWithNSShadow:attrs[NSShadowAttributeName]]; // NSShadow compatible + if (nsShadow) { + nsShadow.subShadow = shadow; + shadow = nsShadow; + } + while (shadow) { + if (!shadow.color) { + shadow = shadow.subShadow; + continue; + } + CGSize offset = shadow.offset; + offset.width -= offsetAlterX; + CGContextSaveGState(context); { + CGContextSetShadowWithColor(context, offset, shadow.radius, shadow.color.CGColor); + CGContextSetBlendMode(context, shadow.blendMode); + CGContextTranslateCTM(context, offsetAlterX, 0); + ASTextDrawRun(line, run, context, size, isVertical, lineRunRanges[r], verticalOffset); + } CGContextRestoreGState(context); + shadow = shadow.subShadow; + } + } + } + } CGContextRestoreGState(context); +} + +static void ASTextDrawInnerShadow(ASTextLayout *layout, CGContextRef context, CGSize size, CGPoint point, BOOL (^cancel)(void)) { + CGContextSaveGState(context); + CGContextTranslateCTM(context, point.x, point.y); + CGContextTranslateCTM(context, 0, size.height); + CGContextScaleCTM(context, 1, -1); + CGContextSetTextMatrix(context, CGAffineTransformIdentity); + + BOOL isVertical = layout.container.verticalForm; + CGFloat verticalOffset = isVertical ? (size.width - layout.container.size.width) : 0; + + NSArray *lines = layout.lines; + for (NSUInteger l = 0, lMax = lines.count; l < lMax; l++) { + if (cancel && cancel()) break; + + ASTextLine *line = lines[l]; + if (layout.truncatedLine && layout.truncatedLine.index == line.index) line = layout.truncatedLine; + NSArray *lineRunRanges = line.verticalRotateRange; + CGFloat linePosX = line.position.x; + CGFloat linePosY = size.height - line.position.y; + CFArrayRef runs = CTLineGetGlyphRuns(line.CTLine); + for (NSUInteger r = 0, rMax = CFArrayGetCount(runs); r < rMax; r++) { + CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runs, r); + if (CTRunGetGlyphCount(run) == 0) continue; + CGContextSetTextMatrix(context, CGAffineTransformIdentity); + CGContextSetTextPosition(context, linePosX, linePosY); + NSDictionary *attrs = (id)CTRunGetAttributes(run); + ASTextShadow *shadow = attrs[ASTextInnerShadowAttributeName]; + while (shadow) { + if (!shadow.color) { + shadow = shadow.subShadow; + continue; + } + CGPoint runPosition = CGPointZero; + CTRunGetPositions(run, CFRangeMake(0, 1), &runPosition); + CGRect runImageBounds = CTRunGetImageBounds(run, context, CFRangeMake(0, 0)); + runImageBounds.origin.x += runPosition.x; + if (runImageBounds.size.width < 0.1 || runImageBounds.size.height < 0.1) continue; + + CFDictionaryRef runAttrs = CTRunGetAttributes(run); + NSValue *glyphTransformValue = (NSValue *)CFDictionaryGetValue(runAttrs, (__bridge const void *)(ASTextGlyphTransformAttributeName)); + if (glyphTransformValue) { + runImageBounds = CGRectMake(0, 0, size.width, size.height); + } + + // text inner shadow + CGContextSaveGState(context); { + CGContextSetBlendMode(context, shadow.blendMode); + CGContextSetShadowWithColor(context, CGSizeZero, 0, NULL); + CGContextSetAlpha(context, CGColorGetAlpha(shadow.color.CGColor)); + CGContextClipToRect(context, runImageBounds); + CGContextBeginTransparencyLayer(context, NULL); { + UIColor *opaqueShadowColor = [shadow.color colorWithAlphaComponent:1]; + CGContextSetShadowWithColor(context, shadow.offset, shadow.radius, opaqueShadowColor.CGColor); + CGContextSetFillColorWithColor(context, opaqueShadowColor.CGColor); + CGContextSetBlendMode(context, kCGBlendModeSourceOut); + CGContextBeginTransparencyLayer(context, NULL); { + CGContextFillRect(context, runImageBounds); + CGContextSetBlendMode(context, kCGBlendModeDestinationIn); + CGContextBeginTransparencyLayer(context, NULL); { + ASTextDrawRun(line, run, context, size, isVertical, lineRunRanges[r], verticalOffset); + } CGContextEndTransparencyLayer(context); + } CGContextEndTransparencyLayer(context); + } CGContextEndTransparencyLayer(context); + } CGContextRestoreGState(context); + shadow = shadow.subShadow; + } + } + } + + CGContextRestoreGState(context); +} + +static void ASTextDrawDebug(ASTextLayout *layout, CGContextRef context, CGSize size, CGPoint point, ASTextDebugOption *op) { + UIGraphicsPushContext(context); + CGContextSaveGState(context); + CGContextTranslateCTM(context, point.x, point.y); + CGContextSetLineWidth(context, 1.0 / ASScreenScale()); + CGContextSetLineDash(context, 0, NULL, 0); + CGContextSetLineJoin(context, kCGLineJoinMiter); + CGContextSetLineCap(context, kCGLineCapButt); + + BOOL isVertical = layout.container.verticalForm; + CGFloat verticalOffset = isVertical ? (size.width - layout.container.size.width) : 0; + CGContextTranslateCTM(context, verticalOffset, 0); + + if (op.CTFrameBorderColor || op.CTFrameFillColor) { + UIBezierPath *path = layout.container.path; + if (!path) { + CGRect rect = (CGRect){CGPointZero, layout.container.size}; + rect = UIEdgeInsetsInsetRect(rect, layout.container.insets); + if (op.CTFrameBorderColor) rect = ASTextCGRectPixelHalf(rect); + else rect = ASTextCGRectPixelRound(rect); + path = [UIBezierPath bezierPathWithRect:rect]; + } + [path closePath]; + + for (UIBezierPath *ex in layout.container.exclusionPaths) { + [path appendPath:ex]; + } + if (op.CTFrameFillColor) { + [op.CTFrameFillColor setFill]; + if (layout.container.pathLineWidth > 0) { + CGContextSaveGState(context); { + CGContextBeginTransparencyLayer(context, NULL); { + CGContextAddPath(context, path.CGPath); + if (layout.container.pathFillEvenOdd) { + CGContextEOFillPath(context); + } else { + CGContextFillPath(context); + } + CGContextSetBlendMode(context, kCGBlendModeDestinationOut); + [[UIColor blackColor] setFill]; + CGPathRef cgPath = CGPathCreateCopyByStrokingPath(path.CGPath, NULL, layout.container.pathLineWidth, kCGLineCapButt, kCGLineJoinMiter, 0); + if (cgPath) { + CGContextAddPath(context, cgPath); + CGContextFillPath(context); + } + CGPathRelease(cgPath); + } CGContextEndTransparencyLayer(context); + } CGContextRestoreGState(context); + } else { + CGContextAddPath(context, path.CGPath); + if (layout.container.pathFillEvenOdd) { + CGContextEOFillPath(context); + } else { + CGContextFillPath(context); + } + } + } + if (op.CTFrameBorderColor) { + CGContextSaveGState(context); { + if (layout.container.pathLineWidth > 0) { + CGContextSetLineWidth(context, layout.container.pathLineWidth); + } + [op.CTFrameBorderColor setStroke]; + CGContextAddPath(context, path.CGPath); + CGContextStrokePath(context); + } CGContextRestoreGState(context); + } + } + + NSArray *lines = layout.lines; + for (NSUInteger l = 0, lMax = lines.count; l < lMax; l++) { + ASTextLine *line = lines[l]; + if (layout.truncatedLine && layout.truncatedLine.index == line.index) line = layout.truncatedLine; + CGRect lineBounds = line.bounds; + if (op.CTLineFillColor) { + [op.CTLineFillColor setFill]; + CGContextAddRect(context, ASTextCGRectPixelRound(lineBounds)); + CGContextFillPath(context); + } + if (op.CTLineBorderColor) { + [op.CTLineBorderColor setStroke]; + CGContextAddRect(context, ASTextCGRectPixelHalf(lineBounds)); + CGContextStrokePath(context); + } + if (op.baselineColor) { + [op.baselineColor setStroke]; + if (isVertical) { + CGFloat x = ASTextCGFloatPixelHalf(line.position.x); + CGFloat y1 = ASTextCGFloatPixelHalf(line.top); + CGFloat y2 = ASTextCGFloatPixelHalf(line.bottom); + CGContextMoveToPoint(context, x, y1); + CGContextAddLineToPoint(context, x, y2); + CGContextStrokePath(context); + } else { + CGFloat x1 = ASTextCGFloatPixelHalf(lineBounds.origin.x); + CGFloat x2 = ASTextCGFloatPixelHalf(lineBounds.origin.x + lineBounds.size.width); + CGFloat y = ASTextCGFloatPixelHalf(line.position.y); + CGContextMoveToPoint(context, x1, y); + CGContextAddLineToPoint(context, x2, y); + CGContextStrokePath(context); + } + } + if (op.CTLineNumberColor) { + [op.CTLineNumberColor set]; + NSMutableAttributedString *num = [[NSMutableAttributedString alloc] initWithString:@(l).description]; + num.as_color = op.CTLineNumberColor; + num.as_font = [UIFont systemFontOfSize:6]; + [num drawAtPoint:CGPointMake(line.position.x, line.position.y - (isVertical ? 1 : 6))]; + } + if (op.CTRunFillColor || op.CTRunBorderColor || op.CTRunNumberColor || op.CGGlyphFillColor || op.CGGlyphBorderColor) { + CFArrayRef runs = CTLineGetGlyphRuns(line.CTLine); + for (NSUInteger r = 0, rMax = CFArrayGetCount(runs); r < rMax; r++) { + CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runs, r); + CFIndex glyphCount = CTRunGetGlyphCount(run); + if (glyphCount == 0) continue; + + CGPoint glyphPositions[glyphCount]; + CTRunGetPositions(run, CFRangeMake(0, glyphCount), glyphPositions); + + CGSize glyphAdvances[glyphCount]; + CTRunGetAdvances(run, CFRangeMake(0, glyphCount), glyphAdvances); + + CGPoint runPosition = glyphPositions[0]; + if (isVertical) { + ASTEXT_SWAP(runPosition.x, runPosition.y); + runPosition.x = line.position.x; + runPosition.y += line.position.y; + } else { + runPosition.x += line.position.x; + runPosition.y = line.position.y - runPosition.y; + } + + CGFloat ascent, descent, leading; + CGFloat width = CTRunGetTypographicBounds(run, CFRangeMake(0, 0), &ascent, &descent, &leading); + CGRect runTypoBounds; + if (isVertical) { + runTypoBounds = CGRectMake(runPosition.x - descent, runPosition.y, ascent + descent, width); + } else { + runTypoBounds = CGRectMake(runPosition.x, line.position.y - ascent, width, ascent + descent); + } + + if (op.CTRunFillColor) { + [op.CTRunFillColor setFill]; + CGContextAddRect(context, ASTextCGRectPixelRound(runTypoBounds)); + CGContextFillPath(context); + } + if (op.CTRunBorderColor) { + [op.CTRunBorderColor setStroke]; + CGContextAddRect(context, ASTextCGRectPixelHalf(runTypoBounds)); + CGContextStrokePath(context); + } + if (op.CTRunNumberColor) { + [op.CTRunNumberColor set]; + NSMutableAttributedString *num = [[NSMutableAttributedString alloc] initWithString:@(r).description]; + num.as_color = op.CTRunNumberColor; + num.as_font = [UIFont systemFontOfSize:6]; + [num drawAtPoint:CGPointMake(runTypoBounds.origin.x, runTypoBounds.origin.y - 1)]; + } + if (op.CGGlyphBorderColor || op.CGGlyphFillColor) { + for (NSUInteger g = 0; g < glyphCount; g++) { + CGPoint pos = glyphPositions[g]; + CGSize adv = glyphAdvances[g]; + CGRect rect; + if (isVertical) { + ASTEXT_SWAP(pos.x, pos.y); + pos.x = runPosition.x; + pos.y += line.position.y; + rect = CGRectMake(pos.x - descent, pos.y, runTypoBounds.size.width, adv.width); + } else { + pos.x += line.position.x; + pos.y = runPosition.y; + rect = CGRectMake(pos.x, pos.y - ascent, adv.width, runTypoBounds.size.height); + } + if (op.CGGlyphFillColor) { + [op.CGGlyphFillColor setFill]; + CGContextAddRect(context, ASTextCGRectPixelRound(rect)); + CGContextFillPath(context); + } + if (op.CGGlyphBorderColor) { + [op.CGGlyphBorderColor setStroke]; + CGContextAddRect(context, ASTextCGRectPixelHalf(rect)); + CGContextStrokePath(context); + } + } + } + } + } + } + CGContextRestoreGState(context); + UIGraphicsPopContext(); +} + + +- (void)drawInContext:(CGContextRef)context + size:(CGSize)size + point:(CGPoint)point + view:(UIView *)view + layer:(CALayer *)layer + debug:(ASTextDebugOption *)debug + cancel:(BOOL (^)(void))cancel{ + @autoreleasepool { + if (self.needDrawBlockBorder && context) { + if (cancel && cancel()) return; + ASTextDrawBlockBorder(self, context, size, point, cancel); + } + if (self.needDrawBackgroundBorder && context) { + if (cancel && cancel()) return; + ASTextDrawBorder(self, context, size, point, ASTextBorderTypeBackgound, cancel); + } + if (self.needDrawShadow && context) { + if (cancel && cancel()) return; + ASTextDrawShadow(self, context, size, point, cancel); + } + if (self.needDrawUnderline && context) { + if (cancel && cancel()) return; + ASTextDrawDecoration(self, context, size, point, ASTextDecorationTypeUnderline, cancel); + } + if (self.needDrawText && context) { + if (cancel && cancel()) return; + ASTextDrawText(self, context, size, point, cancel); + } + if (self.needDrawAttachment && (context || view || layer)) { + if (cancel && cancel()) return; + ASTextDrawAttachment(self, context, size, point, view, layer, cancel); + } + if (self.needDrawInnerShadow && context) { + if (cancel && cancel()) return; + ASTextDrawInnerShadow(self, context, size, point, cancel); + } + if (self.needDrawStrikethrough && context) { + if (cancel && cancel()) return; + ASTextDrawDecoration(self, context, size, point, ASTextDecorationTypeStrikethrough, cancel); + } + if (self.needDrawBorder && context) { + if (cancel && cancel()) return; + ASTextDrawBorder(self, context, size, point, ASTextBorderTypeNormal, cancel); + } + if (debug.needDrawDebug && context) { + if (cancel && cancel()) return; + ASTextDrawDebug(self, context, size, point, debug); + } + } +} + +- (void)drawInContext:(CGContextRef)context + size:(CGSize)size + debug:(ASTextDebugOption *)debug { + [self drawInContext:context size:size point:CGPointZero view:nil layer:nil debug:debug cancel:nil]; +} + +@end diff --git a/Source/Private/TextExperiment/Component/ASTextLine.h b/Source/Private/TextExperiment/Component/ASTextLine.h new file mode 100755 index 0000000000..10d2685dbc --- /dev/null +++ b/Source/Private/TextExperiment/Component/ASTextLine.h @@ -0,0 +1,79 @@ +// +// ASTextLine.h +// Modified from YYText +// +// Created by ibireme on 15/3/10. +// Copyright (c) 2015 ibireme. +// +// This source code is licensed under the MIT-style license found in the +// LICENSE file in the root directory of this source tree. +// + +#import +#import +#import + +@class ASTextRunGlyphRange; + +NS_ASSUME_NONNULL_BEGIN + +/** + A text line object wrapped `CTLineRef`, see `ASTextLayout` for more. + */ +@interface ASTextLine : NSObject + ++ (instancetype)lineWithCTLine:(CTLineRef)CTLine position:(CGPoint)position vertical:(BOOL)isVertical; + +@property (nonatomic) NSUInteger index; ///< line index +@property (nonatomic) NSUInteger row; ///< line row +@property (nullable, nonatomic, strong) NSArray *> *verticalRotateRange; ///< Run rotate range + +@property (nonatomic, readonly) CTLineRef CTLine; ///< CoreText line +@property (nonatomic, readonly) NSRange range; ///< string range +@property (nonatomic, readonly) BOOL vertical; ///< vertical form + +@property (nonatomic, readonly) CGRect bounds; ///< bounds (ascent + descent) +@property (nonatomic, readonly) CGSize size; ///< bounds.size +@property (nonatomic, readonly) CGFloat width; ///< bounds.size.width +@property (nonatomic, readonly) CGFloat height; ///< bounds.size.height +@property (nonatomic, readonly) CGFloat top; ///< bounds.origin.y +@property (nonatomic, readonly) CGFloat bottom; ///< bounds.origin.y + bounds.size.height +@property (nonatomic, readonly) CGFloat left; ///< bounds.origin.x +@property (nonatomic, readonly) CGFloat right; ///< bounds.origin.x + bounds.size.width + +@property (nonatomic) CGPoint position; ///< baseline position +@property (nonatomic, readonly) CGFloat ascent; ///< line ascent +@property (nonatomic, readonly) CGFloat descent; ///< line descent +@property (nonatomic, readonly) CGFloat leading; ///< line leading +@property (nonatomic, readonly) CGFloat lineWidth; ///< line width +@property (nonatomic, readonly) CGFloat trailingWhitespaceWidth; + +@property (nullable, nonatomic, readonly) NSArray *attachments; ///< ASTextAttachment +@property (nullable, nonatomic, readonly) NSArray *attachmentRanges; ///< NSRange(NSValue) +@property (nullable, nonatomic, readonly) NSArray *attachmentRects; ///< CGRect(NSValue) + +@end + + +typedef NS_ENUM(NSUInteger, ASTextRunGlyphDrawMode) { + /// No rotate. + ASTextRunGlyphDrawModeHorizontal = 0, + + /// Rotate vertical for single glyph. + ASTextRunGlyphDrawModeVerticalRotate = 1, + + /// Rotate vertical for single glyph, and move the glyph to a better position, + /// such as fullwidth punctuation. + ASTextRunGlyphDrawModeVerticalRotateMove = 2, +}; + +/** + A range in CTRun, used for vertical form. + */ +@interface ASTextRunGlyphRange : NSObject +@property (nonatomic) NSRange glyphRangeInRun; +@property (nonatomic) ASTextRunGlyphDrawMode drawMode; ++ (instancetype)rangeWithRange:(NSRange)range drawMode:(ASTextRunGlyphDrawMode)mode; +@end + +NS_ASSUME_NONNULL_END diff --git a/Source/Private/TextExperiment/Component/ASTextLine.m b/Source/Private/TextExperiment/Component/ASTextLine.m new file mode 100755 index 0000000000..14d9f7fd24 --- /dev/null +++ b/Source/Private/TextExperiment/Component/ASTextLine.m @@ -0,0 +1,166 @@ +// +// ASYTextLine.m +// Modified from YYText +// +// Created by ibireme on 15/3/3. +// Copyright (c) 2015 ibireme. +// +// This source code is licensed under the MIT-style license found in the +// LICENSE file in the root directory of this source tree. +// + +#import +#import + +@implementation ASTextLine { + CGFloat _firstGlyphPos; // first glyph position for baseline, typically 0. +} + ++ (instancetype)lineWithCTLine:(CTLineRef)CTLine position:(CGPoint)position vertical:(BOOL)isVertical { + if (!CTLine) return nil; + ASTextLine *line = [self new]; + line->_position = position; + line->_vertical = isVertical; + [line setCTLine:CTLine]; + return line; +} + +- (void)dealloc { + if (_CTLine) CFRelease(_CTLine); +} + +- (void)setCTLine:(_Nonnull CTLineRef)CTLine { + if (_CTLine != CTLine) { + if (CTLine) CFRetain(CTLine); + if (_CTLine) CFRelease(_CTLine); + _CTLine = CTLine; + if (_CTLine) { + _lineWidth = CTLineGetTypographicBounds(_CTLine, &_ascent, &_descent, &_leading); + CFRange range = CTLineGetStringRange(_CTLine); + _range = NSMakeRange(range.location, range.length); + if (CTLineGetGlyphCount(_CTLine) > 0) { + CFArrayRef runs = CTLineGetGlyphRuns(_CTLine); + CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runs, 0); + CGPoint pos; + CTRunGetPositions(run, CFRangeMake(0, 1), &pos); + _firstGlyphPos = pos.x; + } else { + _firstGlyphPos = 0; + } + _trailingWhitespaceWidth = CTLineGetTrailingWhitespaceWidth(_CTLine); + } else { + _lineWidth = _ascent = _descent = _leading = _firstGlyphPos = _trailingWhitespaceWidth = 0; + _range = NSMakeRange(0, 0); + } + [self reloadBounds]; + } +} + +- (void)setPosition:(CGPoint)position { + _position = position; + [self reloadBounds]; +} + +- (void)reloadBounds { + if (_vertical) { + _bounds = CGRectMake(_position.x - _descent, _position.y, _ascent + _descent, _lineWidth); + _bounds.origin.y += _firstGlyphPos; + } else { + _bounds = CGRectMake(_position.x, _position.y - _ascent, _lineWidth, _ascent + _descent); + _bounds.origin.x += _firstGlyphPos; + } + + _attachments = nil; + _attachmentRanges = nil; + _attachmentRects = nil; + if (!_CTLine) return; + CFArrayRef runs = CTLineGetGlyphRuns(_CTLine); + NSUInteger runCount = CFArrayGetCount(runs); + if (runCount == 0) return; + + NSMutableArray *attachments = [NSMutableArray new]; + NSMutableArray *attachmentRanges = [NSMutableArray new]; + NSMutableArray *attachmentRects = [NSMutableArray new]; + for (NSUInteger r = 0; r < runCount; r++) { + CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runs, r); + CFIndex glyphCount = CTRunGetGlyphCount(run); + if (glyphCount == 0) continue; + NSDictionary *attrs = (id)CTRunGetAttributes(run); + ASTextAttachment *attachment = attrs[ASTextAttachmentAttributeName]; + if (attachment) { + CGPoint runPosition = CGPointZero; + CTRunGetPositions(run, CFRangeMake(0, 1), &runPosition); + + CGFloat ascent, descent, leading, runWidth; + CGRect runTypoBounds; + runWidth = CTRunGetTypographicBounds(run, CFRangeMake(0, 0), &ascent, &descent, &leading); + + if (_vertical) { + ASTEXT_SWAP(runPosition.x, runPosition.y); + runPosition.y = _position.y + runPosition.y; + runTypoBounds = CGRectMake(_position.x + runPosition.x - descent, runPosition.y , ascent + descent, runWidth); + } else { + runPosition.x += _position.x; + runPosition.y = _position.y - runPosition.y; + runTypoBounds = CGRectMake(runPosition.x, runPosition.y - ascent, runWidth, ascent + descent); + } + + NSRange runRange = ASTextNSRangeFromCFRange(CTRunGetStringRange(run)); + [attachments addObject:attachment]; + [attachmentRanges addObject:[NSValue valueWithRange:runRange]]; + [attachmentRects addObject:[NSValue valueWithCGRect:runTypoBounds]]; + } + } + _attachments = attachments.count ? attachments : nil; + _attachmentRanges = attachmentRanges.count ? attachmentRanges : nil; + _attachmentRects = attachmentRects.count ? attachmentRects : nil; +} + +- (CGSize)size { + return _bounds.size; +} + +- (CGFloat)width { + return CGRectGetWidth(_bounds); +} + +- (CGFloat)height { + return CGRectGetHeight(_bounds); +} + +- (CGFloat)top { + return CGRectGetMinY(_bounds); +} + +- (CGFloat)bottom { + return CGRectGetMaxY(_bounds); +} + +- (CGFloat)left { + return CGRectGetMinX(_bounds); +} + +- (CGFloat)right { + return CGRectGetMaxX(_bounds); +} + +- (NSString *)description { + NSMutableString *desc = @"".mutableCopy; + NSRange range = self.range; + [desc appendFormat:@" row:%zd range:%tu,%tu",self, self.row, range.location, range.length]; + [desc appendFormat:@" position:%@",NSStringFromCGPoint(self.position)]; + [desc appendFormat:@" bounds:%@",NSStringFromCGRect(self.bounds)]; + return desc; +} + +@end + + +@implementation ASTextRunGlyphRange ++ (instancetype)rangeWithRange:(NSRange)range drawMode:(ASTextRunGlyphDrawMode)mode { + ASTextRunGlyphRange *one = [self new]; + one.glyphRangeInRun = range; + one.drawMode = mode; + return one; +} +@end diff --git a/Source/Private/TextExperiment/String/ASTextAttribute.h b/Source/Private/TextExperiment/String/ASTextAttribute.h new file mode 100755 index 0000000000..80cdf64ec8 --- /dev/null +++ b/Source/Private/TextExperiment/String/ASTextAttribute.h @@ -0,0 +1,347 @@ +// +// ASTextAttribute.h +// Modified from YYText +// +// Created by ibireme on 14/10/26. +// Copyright (c) 2015 ibireme. +// +// This source code is licensed under the MIT-style license found in the +// LICENSE file in the root directory of this source tree. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +#pragma mark - Enum Define + +/// The attribute type +typedef NS_OPTIONS(NSInteger, ASTextAttributeType) { + ASTextAttributeTypeNone = 0, + ASTextAttributeTypeUIKit = 1 << 0, ///< UIKit attributes, such as UILabel/UITextField/drawInRect. + ASTextAttributeTypeCoreText = 1 << 1, ///< CoreText attributes, used by CoreText. + ASTextAttributeTypeASText = 1 << 2, ///< ASText attributes, used by ASText. +}; + +/// Get the attribute type from an attribute name. +extern ASTextAttributeType ASTextAttributeGetType(NSString *attributeName); + +/** + Line style in ASText (similar to NSUnderlineStyle). + */ +typedef NS_OPTIONS (NSInteger, ASTextLineStyle) { + // basic style (bitmask:0xFF) + ASTextLineStyleNone = 0x00, ///< ( ) Do not draw a line (Default). + ASTextLineStyleSingle = 0x01, ///< (──────) Draw a single line. + ASTextLineStyleThick = 0x02, ///< (━━━━━━━) Draw a thick line. + ASTextLineStyleDouble = 0x09, ///< (══════) Draw a double line. + + // style pattern (bitmask:0xF00) + ASTextLineStylePatternSolid = 0x000, ///< (────────) Draw a solid line (Default). + ASTextLineStylePatternDot = 0x100, ///< (‑ ‑ ‑ ‑ ‑ ‑) Draw a line of dots. + ASTextLineStylePatternDash = 0x200, ///< (— — — —) Draw a line of dashes. + ASTextLineStylePatternDashDot = 0x300, ///< (— ‑ — ‑ — ‑) Draw a line of alternating dashes and dots. + ASTextLineStylePatternDashDotDot = 0x400, ///< (— ‑ ‑ — ‑ ‑) Draw a line of alternating dashes and two dots. + ASTextLineStylePatternCircleDot = 0x900, ///< (••••••••••••) Draw a line of small circle dots. +}; + +/** + Text vertical alignment. + */ +typedef NS_ENUM(NSInteger, ASTextVerticalAlignment) { + ASTextVerticalAlignmentTop = 0, ///< Top alignment. + ASTextVerticalAlignmentCenter = 1, ///< Center alignment. + ASTextVerticalAlignmentBottom = 2, ///< Bottom alignment. +}; + +/** + The direction define in ASText. + */ +typedef NS_OPTIONS(NSUInteger, ASTextDirection) { + ASTextDirectionNone = 0, + ASTextDirectionTop = 1 << 0, + ASTextDirectionRight = 1 << 1, + ASTextDirectionBottom = 1 << 2, + ASTextDirectionLeft = 1 << 3, +}; + +/** + The trunction type, tells the truncation engine which type of truncation is being requested. + */ +typedef NS_ENUM (NSUInteger, ASTextTruncationType) { + /// No truncate. + ASTextTruncationTypeNone = 0, + + /// Truncate at the beginning of the line, leaving the end portion visible. + ASTextTruncationTypeStart = 1, + + /// Truncate at the end of the line, leaving the start portion visible. + ASTextTruncationTypeEnd = 2, + + /// Truncate in the middle of the line, leaving both the start and the end portions visible. + ASTextTruncationTypeMiddle = 3, +}; + + + +#pragma mark - Attribute Name Defined in ASText + +/// The value of this attribute is a `ASTextBackedString` object. +/// Use this attribute to store the original plain text if it is replaced by something else (such as attachment). +UIKIT_EXTERN NSString *const ASTextBackedStringAttributeName; + +/// The value of this attribute is a `ASTextBinding` object. +/// Use this attribute to bind a range of text together, as if it was a single charactor. +UIKIT_EXTERN NSString *const ASTextBindingAttributeName; + +/// The value of this attribute is a `ASTextShadow` object. +/// Use this attribute to add shadow to a range of text. +/// Shadow will be drawn below text glyphs. Use ASTextShadow.subShadow to add multi-shadow. +UIKIT_EXTERN NSString *const ASTextShadowAttributeName; + +/// The value of this attribute is a `ASTextShadow` object. +/// Use this attribute to add inner shadow to a range of text. +/// Inner shadow will be drawn above text glyphs. Use ASTextShadow.subShadow to add multi-shadow. +UIKIT_EXTERN NSString *const ASTextInnerShadowAttributeName; + +/// The value of this attribute is a `ASTextDecoration` object. +/// Use this attribute to add underline to a range of text. +/// The underline will be drawn below text glyphs. +UIKIT_EXTERN NSString *const ASTextUnderlineAttributeName; + +/// The value of this attribute is a `ASTextDecoration` object. +/// Use this attribute to add strikethrough (delete line) to a range of text. +/// The strikethrough will be drawn above text glyphs. +UIKIT_EXTERN NSString *const ASTextStrikethroughAttributeName; + +/// The value of this attribute is a `ASTextBorder` object. +/// Use this attribute to add cover border or cover color to a range of text. +/// The border will be drawn above the text glyphs. +UIKIT_EXTERN NSString *const ASTextBorderAttributeName; + +/// The value of this attribute is a `ASTextBorder` object. +/// Use this attribute to add background border or background color to a range of text. +/// The border will be drawn below the text glyphs. +UIKIT_EXTERN NSString *const ASTextBackgroundBorderAttributeName; + +/// The value of this attribute is a `ASTextBorder` object. +/// Use this attribute to add a code block border to one or more line of text. +/// The border will be drawn below the text glyphs. +UIKIT_EXTERN NSString *const ASTextBlockBorderAttributeName; + +/// The value of this attribute is a `ASTextAttachment` object. +/// Use this attribute to add attachment to text. +/// It should be used in conjunction with a CTRunDelegate. +UIKIT_EXTERN NSString *const ASTextAttachmentAttributeName; + +/// The value of this attribute is a `ASTextHighlight` object. +/// Use this attribute to add a touchable highlight state to a range of text. +UIKIT_EXTERN NSString *const ASTextHighlightAttributeName; + +/// The value of this attribute is a `NSValue` object stores CGAffineTransform. +/// Use this attribute to add transform to each glyph in a range of text. +UIKIT_EXTERN NSString *const ASTextGlyphTransformAttributeName; + + + +#pragma mark - String Token Define + +UIKIT_EXTERN NSString *const ASTextAttachmentToken; ///< Object replacement character (U+FFFC), used for text attachment. +UIKIT_EXTERN NSString *const ASTextTruncationToken; ///< Horizontal ellipsis (U+2026), used for text truncation "…". + + + +#pragma mark - Attribute Value Define + +/** + The tap/long press action callback defined in ASText. + + @param containerView The text container view (such as ASLabel/ASTextView). + @param text The whole text. + @param range The text range in `text` (if no range, the range.location is NSNotFound). + @param rect The text frame in `containerView` (if no data, the rect is CGRectNull). + */ +typedef void(^ASTextAction)(UIView *containerView, NSAttributedString *text, NSRange range, CGRect rect); + + +/** + ASTextBackedString objects are used by the NSAttributedString class cluster + as the values for text backed string attributes (stored in the attributed + string under the key named ASTextBackedStringAttributeName). + + It may used for copy/paste plain text from attributed string. + Example: If :) is replace by a custom emoji (such as😊), the backed string can be set to @":)". + */ +@interface ASTextBackedString : NSObject ++ (instancetype)stringWithString:(nullable NSString *)string; +@property (nullable, nonatomic, copy) NSString *string; ///< backed string +@end + + +/** + ASTextBinding objects are used by the NSAttributedString class cluster + as the values for shadow attributes (stored in the attributed string under + the key named ASTextBindingAttributeName). + + Add this to a range of text will make the specified characters 'binding together'. + ASTextView will treat the range of text as a single character during text + selection and edit. + */ +@interface ASTextBinding : NSObject ++ (instancetype)bindingWithDeleteConfirm:(BOOL)deleteConfirm; +@property (nonatomic) BOOL deleteConfirm; ///< confirm the range when delete in ASTextView +@end + + +/** + ASTextShadow objects are used by the NSAttributedString class cluster + as the values for shadow attributes (stored in the attributed string under + the key named ASTextShadowAttributeName or ASTextInnerShadowAttributeName). + + It's similar to `NSShadow`, but offers more options. + */ +@interface ASTextShadow : NSObject ++ (instancetype)shadowWithColor:(nullable UIColor *)color offset:(CGSize)offset radius:(CGFloat)radius; + +@property (nullable, nonatomic, strong) UIColor *color; ///< shadow color +@property (nonatomic) CGSize offset; ///< shadow offset +@property (nonatomic) CGFloat radius; ///< shadow blur radius +@property (nonatomic) CGBlendMode blendMode; ///< shadow blend mode +@property (nullable, nonatomic, strong) ASTextShadow *subShadow; ///< a sub shadow which will be added above the parent shadow + ++ (instancetype)shadowWithNSShadow:(NSShadow *)nsShadow; ///< convert NSShadow to ASTextShadow +- (NSShadow *)nsShadow; ///< convert ASTextShadow to NSShadow +@end + + +/** + ASTextDecorationLine objects are used by the NSAttributedString class cluster + as the values for decoration line attributes (stored in the attributed string under + the key named ASTextUnderlineAttributeName or ASTextStrikethroughAttributeName). + + When it's used as underline, the line is drawn below text glyphs; + when it's used as strikethrough, the line is drawn above text glyphs. + */ +@interface ASTextDecoration : NSObject ++ (instancetype)decorationWithStyle:(ASTextLineStyle)style; ++ (instancetype)decorationWithStyle:(ASTextLineStyle)style width:(nullable NSNumber *)width color:(nullable UIColor *)color; +@property (nonatomic) ASTextLineStyle style; ///< line style +@property (nullable, nonatomic, strong) NSNumber *width; ///< line width (nil means automatic width) +@property (nullable, nonatomic, strong) UIColor *color; ///< line color (nil means automatic color) +@property (nullable, nonatomic, strong) ASTextShadow *shadow; ///< line shadow +@end + + +/** + ASTextBorder objects are used by the NSAttributedString class cluster + as the values for border attributes (stored in the attributed string under + the key named ASTextBorderAttributeName or ASTextBackgroundBorderAttributeName). + + It can be used to draw a border around a range of text, or draw a background + to a range of text. + + Example: + ╭──────╮ + │ Text │ + ╰──────╯ + */ +@interface ASTextBorder : NSObject ++ (instancetype)borderWithLineStyle:(ASTextLineStyle)lineStyle lineWidth:(CGFloat)width strokeColor:(nullable UIColor *)color; ++ (instancetype)borderWithFillColor:(nullable UIColor *)color cornerRadius:(CGFloat)cornerRadius; +@property (nonatomic) ASTextLineStyle lineStyle; ///< border line style +@property (nonatomic) CGFloat strokeWidth; ///< border line width +@property (nullable, nonatomic, strong) UIColor *strokeColor; ///< border line color +@property (nonatomic) CGLineJoin lineJoin; ///< border line join +@property (nonatomic) UIEdgeInsets insets; ///< border insets for text bounds +@property (nonatomic) CGFloat cornerRadius; ///< border corder radius +@property (nullable, nonatomic, strong) ASTextShadow *shadow; ///< border shadow +@property (nullable, nonatomic, strong) UIColor *fillColor; ///< inner fill color +@end + + +/** + ASTextAttachment objects are used by the NSAttributedString class cluster + as the values for attachment attributes (stored in the attributed string under + the key named ASTextAttachmentAttributeName). + + When display an attributed string which contains `ASTextAttachment` object, + the content will be placed in text metric. If the content is `UIImage`, + then it will be drawn to CGContext; if the content is `UIView` or `CALayer`, + then it will be added to the text container's view or layer. + */ +@interface ASTextAttachment : NSObject ++ (instancetype)attachmentWithContent:(nullable id)content; +@property (nullable, nonatomic, strong) id content; ///< Supported type: UIImage, UIView, CALayer +@property (nonatomic) UIViewContentMode contentMode; ///< Content display mode. +@property (nonatomic) UIEdgeInsets contentInsets; ///< The insets when drawing content. +@property (nullable, nonatomic, strong) NSDictionary *userInfo; ///< The user information dictionary. +@end + + +/** + ASTextHighlight objects are used by the NSAttributedString class cluster + as the values for touchable highlight attributes (stored in the attributed string + under the key named ASTextHighlightAttributeName). + + When display an attributed string in `ASLabel` or `ASTextView`, the range of + highlight text can be toucheds down by users. If a range of text is turned into + highlighted state, the `attributes` in `ASTextHighlight` will be used to modify + (set or remove) the original attributes in the range for display. + */ +@interface ASTextHighlight : NSObject + +/** + Attributes that you can apply to text in an attributed string when highlight. + Key: Same as CoreText/ASText Attribute Name. + Value: Modify attribute value when highlight (NSNull for remove attribute). + */ +@property (nullable, nonatomic, copy) NSDictionary *attributes; + +/** + Creates a highlight object with specified attributes. + + @param attributes The attributes which will replace original attributes when highlight, + If the value is NSNull, it will removed when highlight. + */ ++ (instancetype)highlightWithAttributes:(nullable NSDictionary *)attributes; + +/** + Convenience methods to create a default highlight with the specifeid background color. + + @param color The background border color. + */ ++ (instancetype)highlightWithBackgroundColor:(nullable UIColor *)color; + +// Convenience methods below to set the `attributes`. +- (void)setFont:(nullable UIFont *)font; +- (void)setColor:(nullable UIColor *)color; +- (void)setStrokeWidth:(nullable NSNumber *)width; +- (void)setStrokeColor:(nullable UIColor *)color; +- (void)setShadow:(nullable ASTextShadow *)shadow; +- (void)setInnerShadow:(nullable ASTextShadow *)shadow; +- (void)setUnderline:(nullable ASTextDecoration *)underline; +- (void)setStrikethrough:(nullable ASTextDecoration *)strikethrough; +- (void)setBackgroundBorder:(nullable ASTextBorder *)border; +- (void)setBorder:(nullable ASTextBorder *)border; +- (void)setAttachment:(nullable ASTextAttachment *)attachment; + +/** + The user information dictionary, default is nil. + */ +@property (nullable, nonatomic, copy) NSDictionary *userInfo; + +/** + Tap action when user tap the highlight, default is nil. + If the value is nil, ASTextView or ASLabel will ask it's delegate to handle the tap action. + */ +@property (nullable, nonatomic, copy) ASTextAction tapAction; + +/** + Long press action when user long press the highlight, default is nil. + If the value is nil, ASTextView or ASLabel will ask it's delegate to handle the long press action. + */ +@property (nullable, nonatomic, copy) ASTextAction longPressAction; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Source/Private/TextExperiment/String/ASTextAttribute.m b/Source/Private/TextExperiment/String/ASTextAttribute.m new file mode 100755 index 0000000000..084bdcf820 --- /dev/null +++ b/Source/Private/TextExperiment/String/ASTextAttribute.m @@ -0,0 +1,485 @@ +// +// ASTextAttribute.m +// Modified from YYText +// +// Created by ibireme on 14/10/26. +// Copyright (c) 2015 ibireme. +// +// This source code is licensed under the MIT-style license found in the +// LICENSE file in the root directory of this source tree. +// + +#import "ASTextAttribute.h" +#import +#import +#import + +NSString *const ASTextBackedStringAttributeName = @"ASTextBackedString"; +NSString *const ASTextBindingAttributeName = @"ASTextBinding"; +NSString *const ASTextShadowAttributeName = @"ASTextShadow"; +NSString *const ASTextInnerShadowAttributeName = @"ASTextInnerShadow"; +NSString *const ASTextUnderlineAttributeName = @"ASTextUnderline"; +NSString *const ASTextStrikethroughAttributeName = @"ASTextStrikethrough"; +NSString *const ASTextBorderAttributeName = @"ASTextBorder"; +NSString *const ASTextBackgroundBorderAttributeName = @"ASTextBackgroundBorder"; +NSString *const ASTextBlockBorderAttributeName = @"ASTextBlockBorder"; +NSString *const ASTextAttachmentAttributeName = @"ASTextAttachment"; +NSString *const ASTextHighlightAttributeName = @"ASTextHighlight"; +NSString *const ASTextGlyphTransformAttributeName = @"ASTextGlyphTransform"; + +NSString *const ASTextAttachmentToken = @"\uFFFC"; +NSString *const ASTextTruncationToken = @"\u2026"; + + +ASTextAttributeType ASTextAttributeGetType(NSString *name){ + if (name.length == 0) return ASTextAttributeTypeNone; + + static NSMutableDictionary *dic; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + dic = [NSMutableDictionary new]; + NSNumber *All = @(ASTextAttributeTypeUIKit | ASTextAttributeTypeCoreText | ASTextAttributeTypeASText); + NSNumber *CoreText_ASText = @(ASTextAttributeTypeCoreText | ASTextAttributeTypeASText); + NSNumber *UIKit_ASText = @(ASTextAttributeTypeUIKit | ASTextAttributeTypeASText); + NSNumber *UIKit_CoreText = @(ASTextAttributeTypeUIKit | ASTextAttributeTypeCoreText); + NSNumber *UIKit = @(ASTextAttributeTypeUIKit); + NSNumber *CoreText = @(ASTextAttributeTypeCoreText); + NSNumber *ASText = @(ASTextAttributeTypeASText); + + dic[NSFontAttributeName] = All; + dic[NSKernAttributeName] = All; + dic[NSForegroundColorAttributeName] = UIKit; + dic[(id)kCTForegroundColorAttributeName] = CoreText; + dic[(id)kCTForegroundColorFromContextAttributeName] = CoreText; + dic[NSBackgroundColorAttributeName] = UIKit; + dic[NSStrokeWidthAttributeName] = All; + dic[NSStrokeColorAttributeName] = UIKit; + dic[(id)kCTStrokeColorAttributeName] = CoreText_ASText; + dic[NSShadowAttributeName] = UIKit_ASText; + dic[NSStrikethroughStyleAttributeName] = UIKit; + dic[NSUnderlineStyleAttributeName] = UIKit_CoreText; + dic[(id)kCTUnderlineColorAttributeName] = CoreText; + dic[NSLigatureAttributeName] = All; + dic[(id)kCTSuperscriptAttributeName] = UIKit; //it's a CoreText attrubite, but only supported by UIKit... + dic[NSVerticalGlyphFormAttributeName] = All; + dic[(id)kCTGlyphInfoAttributeName] = CoreText_ASText; + dic[(id)kCTCharacterShapeAttributeName] = CoreText_ASText; + dic[(id)kCTRunDelegateAttributeName] = CoreText_ASText; + dic[(id)kCTBaselineClassAttributeName] = CoreText_ASText; + dic[(id)kCTBaselineInfoAttributeName] = CoreText_ASText; + dic[(id)kCTBaselineReferenceInfoAttributeName] = CoreText_ASText; + dic[(id)kCTWritingDirectionAttributeName] = CoreText_ASText; + dic[NSParagraphStyleAttributeName] = All; + + dic[NSStrikethroughColorAttributeName] = UIKit; + dic[NSUnderlineColorAttributeName] = UIKit; + dic[NSTextEffectAttributeName] = UIKit; + dic[NSObliquenessAttributeName] = UIKit; + dic[NSExpansionAttributeName] = UIKit; + dic[(id)kCTLanguageAttributeName] = CoreText_ASText; + dic[NSBaselineOffsetAttributeName] = UIKit; + dic[NSWritingDirectionAttributeName] = All; + dic[NSAttachmentAttributeName] = UIKit; + dic[NSLinkAttributeName] = UIKit; + dic[(id)kCTRubyAnnotationAttributeName] = CoreText; + + dic[ASTextBackedStringAttributeName] = ASText; + dic[ASTextBindingAttributeName] = ASText; + dic[ASTextShadowAttributeName] = ASText; + dic[ASTextInnerShadowAttributeName] = ASText; + dic[ASTextUnderlineAttributeName] = ASText; + dic[ASTextStrikethroughAttributeName] = ASText; + dic[ASTextBorderAttributeName] = ASText; + dic[ASTextBackgroundBorderAttributeName] = ASText; + dic[ASTextBlockBorderAttributeName] = ASText; + dic[ASTextAttachmentAttributeName] = ASText; + dic[ASTextHighlightAttributeName] = ASText; + dic[ASTextGlyphTransformAttributeName] = ASText; + }); + NSNumber *num = dic[name]; + if (num) return num.integerValue; + return ASTextAttributeTypeNone; +} + + +@implementation ASTextBackedString + ++ (instancetype)stringWithString:(NSString *)string { + ASTextBackedString *one = [self new]; + one.string = string; + return one; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:self.string forKey:@"string"]; +} + +- (id)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + _string = [aDecoder decodeObjectForKey:@"string"]; + return self; +} + +- (id)copyWithZone:(NSZone *)zone { + __typeof__(self) one = [self.class new]; + one.string = self.string; + return one; +} + +@end + + +@implementation ASTextBinding + ++ (instancetype)bindingWithDeleteConfirm:(BOOL)deleteConfirm { + ASTextBinding *one = [self new]; + one.deleteConfirm = deleteConfirm; + return one; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:@(self.deleteConfirm) forKey:@"deleteConfirm"]; +} + +- (id)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + _deleteConfirm = ((NSNumber *)[aDecoder decodeObjectForKey:@"deleteConfirm"]).boolValue; + return self; +} + +- (id)copyWithZone:(NSZone *)zone { + __typeof__(self) one = [self.class new]; + one.deleteConfirm = self.deleteConfirm; + return one; +} + +@end + + +@implementation ASTextShadow + ++ (instancetype)shadowWithColor:(UIColor *)color offset:(CGSize)offset radius:(CGFloat)radius { + ASTextShadow *one = [self new]; + one.color = color; + one.offset = offset; + one.radius = radius; + return one; +} + ++ (instancetype)shadowWithNSShadow:(NSShadow *)nsShadow { + if (!nsShadow) return nil; + ASTextShadow *shadow = [self new]; + shadow.offset = nsShadow.shadowOffset; + shadow.radius = nsShadow.shadowBlurRadius; + id color = nsShadow.shadowColor; + if (color) { + if (CGColorGetTypeID() == CFGetTypeID((__bridge CFTypeRef)(color))) { + color = [UIColor colorWithCGColor:(__bridge CGColorRef)(color)]; + } + if ([color isKindOfClass:[UIColor class]]) { + shadow.color = color; + } + } + return shadow; +} + +- (NSShadow *)nsShadow { + NSShadow *shadow = [NSShadow new]; + shadow.shadowOffset = self.offset; + shadow.shadowBlurRadius = self.radius; + shadow.shadowColor = self.color; + return shadow; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:self.color forKey:@"color"]; + [aCoder encodeObject:@(self.radius) forKey:@"radius"]; + [aCoder encodeObject:[NSValue valueWithCGSize:self.offset] forKey:@"offset"]; + [aCoder encodeObject:self.subShadow forKey:@"subShadow"]; +} + +- (id)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + _color = [aDecoder decodeObjectForKey:@"color"]; + _radius = ((NSNumber *)[aDecoder decodeObjectForKey:@"radius"]).floatValue; + _offset = ((NSValue *)[aDecoder decodeObjectForKey:@"offset"]).CGSizeValue; + _subShadow = [aDecoder decodeObjectForKey:@"subShadow"]; + return self; +} + +- (id)copyWithZone:(NSZone *)zone { + __typeof__(self) one = [self.class new]; + one.color = self.color; + one.radius = self.radius; + one.offset = self.offset; + one.subShadow = self.subShadow.copy; + return one; +} + +@end + + +@implementation ASTextDecoration + +- (instancetype)init { + self = [super init]; + _style = ASTextLineStyleSingle; + return self; +} + ++ (instancetype)decorationWithStyle:(ASTextLineStyle)style { + ASTextDecoration *one = [self new]; + one.style = style; + return one; +} ++ (instancetype)decorationWithStyle:(ASTextLineStyle)style width:(NSNumber *)width color:(UIColor *)color { + ASTextDecoration *one = [self new]; + one.style = style; + one.width = width; + one.color = color; + return one; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:@(self.style) forKey:@"style"]; + [aCoder encodeObject:self.width forKey:@"width"]; + [aCoder encodeObject:self.color forKey:@"color"]; +} + +- (id)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + self.style = ((NSNumber *)[aDecoder decodeObjectForKey:@"style"]).unsignedIntegerValue; + self.width = [aDecoder decodeObjectForKey:@"width"]; + self.color = [aDecoder decodeObjectForKey:@"color"]; + return self; +} + +- (id)copyWithZone:(NSZone *)zone { + __typeof__(self) one = [self.class new]; + one.style = self.style; + one.width = self.width; + one.color = self.color; + return one; +} + +@end + + +@implementation ASTextBorder + ++ (instancetype)borderWithLineStyle:(ASTextLineStyle)lineStyle lineWidth:(CGFloat)width strokeColor:(UIColor *)color { + ASTextBorder *one = [self new]; + one.lineStyle = lineStyle; + one.strokeWidth = width; + one.strokeColor = color; + return one; +} + ++ (instancetype)borderWithFillColor:(UIColor *)color cornerRadius:(CGFloat)cornerRadius { + ASTextBorder *one = [self new]; + one.fillColor = color; + one.cornerRadius = cornerRadius; + one.insets = UIEdgeInsetsMake(-2, 0, 0, -2); + return one; +} + +- (instancetype)init { + self = [super init]; + self.lineStyle = ASTextLineStyleSingle; + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:@(self.lineStyle) forKey:@"lineStyle"]; + [aCoder encodeObject:@(self.strokeWidth) forKey:@"strokeWidth"]; + [aCoder encodeObject:self.strokeColor forKey:@"strokeColor"]; + [aCoder encodeObject:@(self.lineJoin) forKey:@"lineJoin"]; + [aCoder encodeObject:[NSValue valueWithUIEdgeInsets:self.insets] forKey:@"insets"]; + [aCoder encodeObject:@(self.cornerRadius) forKey:@"cornerRadius"]; + [aCoder encodeObject:self.shadow forKey:@"shadow"]; + [aCoder encodeObject:self.fillColor forKey:@"fillColor"]; +} + +- (id)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + _lineStyle = ((NSNumber *)[aDecoder decodeObjectForKey:@"lineStyle"]).unsignedIntegerValue; + _strokeWidth = ((NSNumber *)[aDecoder decodeObjectForKey:@"strokeWidth"]).doubleValue; + _strokeColor = [aDecoder decodeObjectForKey:@"strokeColor"]; + _lineJoin = (CGLineJoin)((NSNumber *)[aDecoder decodeObjectForKey:@"join"]).unsignedIntegerValue; + _insets = ((NSValue *)[aDecoder decodeObjectForKey:@"insets"]).UIEdgeInsetsValue; + _cornerRadius = ((NSNumber *)[aDecoder decodeObjectForKey:@"cornerRadius"]).doubleValue; + _shadow = [aDecoder decodeObjectForKey:@"shadow"]; + _fillColor = [aDecoder decodeObjectForKey:@"fillColor"]; + return self; +} + +- (id)copyWithZone:(NSZone *)zone { + __typeof__(self) one = [self.class new]; + one.lineStyle = self.lineStyle; + one.strokeWidth = self.strokeWidth; + one.strokeColor = self.strokeColor; + one.lineJoin = self.lineJoin; + one.insets = self.insets; + one.cornerRadius = self.cornerRadius; + one.shadow = self.shadow.copy; + one.fillColor = self.fillColor; + return one; +} + +@end + + +@implementation ASTextAttachment + ++ (instancetype)attachmentWithContent:(id)content { + ASTextAttachment *one = [self new]; + one.content = content; + return one; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:self.content forKey:@"content"]; + [aCoder encodeObject:[NSValue valueWithUIEdgeInsets:self.contentInsets] forKey:@"contentInsets"]; + [aCoder encodeObject:self.userInfo forKey:@"userInfo"]; +} + +- (id)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + _content = [aDecoder decodeObjectForKey:@"content"]; + _contentInsets = ((NSValue *)[aDecoder decodeObjectForKey:@"contentInsets"]).UIEdgeInsetsValue; + _userInfo = [aDecoder decodeObjectForKey:@"userInfo"]; + return self; +} + +- (id)copyWithZone:(NSZone *)zone { + __typeof__(self) one = [self.class new]; + if ([self.content respondsToSelector:@selector(copy)]) { + one.content = [self.content copy]; + } else { + one.content = self.content; + } + one.contentInsets = self.contentInsets; + one.userInfo = self.userInfo.copy; + return one; +} + +@end + + +@implementation ASTextHighlight + ++ (instancetype)highlightWithAttributes:(NSDictionary *)attributes { + ASTextHighlight *one = [self new]; + one.attributes = attributes; + return one; +} + ++ (instancetype)highlightWithBackgroundColor:(UIColor *)color { + ASTextBorder *highlightBorder = [ASTextBorder new]; + highlightBorder.insets = UIEdgeInsetsMake(-2, -1, -2, -1); + highlightBorder.cornerRadius = 3; + highlightBorder.fillColor = color; + + ASTextHighlight *one = [self new]; + [one setBackgroundBorder:highlightBorder]; + return one; +} + +- (void)setAttributes:(NSDictionary *)attributes { + _attributes = attributes.mutableCopy; +} + +- (id)copyWithZone:(NSZone *)zone { + __typeof__(self) one = [self.class new]; + one.attributes = self.attributes.mutableCopy; + return one; +} + +- (void)_makeMutableAttributes { + if (!_attributes) { + _attributes = [NSMutableDictionary new]; + } else if (![_attributes isKindOfClass:[NSMutableDictionary class]]) { + _attributes = _attributes.mutableCopy; + } +} + +- (void)setFont:(UIFont *)font { + [self _makeMutableAttributes]; + if (font == (id)[NSNull null] || font == nil) { + ((NSMutableDictionary *)_attributes)[(id)kCTFontAttributeName] = [NSNull null]; + } else { + CTFontRef ctFont = CTFontCreateWithName((__bridge CFStringRef)font.fontName, font.pointSize, NULL); + if (ctFont) { + ((NSMutableDictionary *)_attributes)[(id)kCTFontAttributeName] = (__bridge id)(ctFont); + CFRelease(ctFont); + } + } +} + +- (void)setColor:(UIColor *)color { + [self _makeMutableAttributes]; + if (color == (id)[NSNull null] || color == nil) { + ((NSMutableDictionary *)_attributes)[(id)kCTForegroundColorAttributeName] = [NSNull null]; + ((NSMutableDictionary *)_attributes)[NSForegroundColorAttributeName] = [NSNull null]; + } else { + ((NSMutableDictionary *)_attributes)[(id)kCTForegroundColorAttributeName] = (__bridge id)(color.CGColor); + ((NSMutableDictionary *)_attributes)[NSForegroundColorAttributeName] = color; + } +} + +- (void)setStrokeWidth:(NSNumber *)width { + [self _makeMutableAttributes]; + if (width == (id)[NSNull null] || width == nil) { + ((NSMutableDictionary *)_attributes)[(id)kCTStrokeWidthAttributeName] = [NSNull null]; + } else { + ((NSMutableDictionary *)_attributes)[(id)kCTStrokeWidthAttributeName] = width; + } +} + +- (void)setStrokeColor:(UIColor *)color { + [self _makeMutableAttributes]; + if (color == (id)[NSNull null] || color == nil) { + ((NSMutableDictionary *)_attributes)[(id)kCTStrokeColorAttributeName] = [NSNull null]; + ((NSMutableDictionary *)_attributes)[NSStrokeColorAttributeName] = [NSNull null]; + } else { + ((NSMutableDictionary *)_attributes)[(id)kCTStrokeColorAttributeName] = (__bridge id)(color.CGColor); + ((NSMutableDictionary *)_attributes)[NSStrokeColorAttributeName] = color; + } +} + +- (void)setTextAttribute:(NSString *)attribute value:(id)value { + [self _makeMutableAttributes]; + if (value == nil) value = [NSNull null]; + ((NSMutableDictionary *)_attributes)[attribute] = value; +} + +- (void)setShadow:(ASTextShadow *)shadow { + [self setTextAttribute:ASTextShadowAttributeName value:shadow]; +} + +- (void)setInnerShadow:(ASTextShadow *)shadow { + [self setTextAttribute:ASTextInnerShadowAttributeName value:shadow]; +} + +- (void)setUnderline:(ASTextDecoration *)underline { + [self setTextAttribute:ASTextUnderlineAttributeName value:underline]; +} + +- (void)setStrikethrough:(ASTextDecoration *)strikethrough { + [self setTextAttribute:ASTextStrikethroughAttributeName value:strikethrough]; +} + +- (void)setBackgroundBorder:(ASTextBorder *)border { + [self setTextAttribute:ASTextBackgroundBorderAttributeName value:border]; +} + +- (void)setBorder:(ASTextBorder *)border { + [self setTextAttribute:ASTextBorderAttributeName value:border]; +} + +- (void)setAttachment:(ASTextAttachment *)attachment { + [self setTextAttribute:ASTextAttachmentAttributeName value:attachment]; +} + +@end + diff --git a/Source/Private/TextExperiment/String/ASTextRunDelegate.h b/Source/Private/TextExperiment/String/ASTextRunDelegate.h new file mode 100755 index 0000000000..80bbc83aec --- /dev/null +++ b/Source/Private/TextExperiment/String/ASTextRunDelegate.h @@ -0,0 +1,68 @@ +// +// ASTextRunDelegate.h +// ASText +// +// Created by ibireme on 14/10/14. +// Copyright (c) 2015 ibireme. +// +// This source code is licensed under the MIT-style license found in the +// LICENSE file in the root directory of this source tree. +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + Wrapper for CTRunDelegateRef. + + Example: + + ASTextRunDelegate *delegate = [ASTextRunDelegate new]; + delegate.ascent = 20; + delegate.descent = 4; + delegate.width = 20; + CTRunDelegateRef ctRunDelegate = delegate.CTRunDelegate; + if (ctRunDelegate) { + /// add to attributed string + CFRelease(ctRunDelegate); + } + + */ +@interface ASTextRunDelegate : NSObject + +/** + Creates and returns the CTRunDelegate. + + @discussion You need call CFRelease() after used. + The CTRunDelegateRef has a strong reference to this ASTextRunDelegate object. + In CoreText, use CTRunDelegateGetRefCon() to get this ASTextRunDelegate object. + + @return The CTRunDelegate object. + */ +- (nullable CTRunDelegateRef)CTRunDelegate CF_RETURNS_RETAINED; + +/** + Additional information about the the run delegate. + */ +@property (nullable, nonatomic, strong) NSDictionary *userInfo; + +/** + The typographic ascent of glyphs in the run. + */ +@property (nonatomic) CGFloat ascent; + +/** + The typographic descent of glyphs in the run. + */ +@property (nonatomic) CGFloat descent; + +/** + The typographic width of glyphs in the run. + */ +@property (nonatomic) CGFloat width; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Source/Private/TextExperiment/String/ASTextRunDelegate.m b/Source/Private/TextExperiment/String/ASTextRunDelegate.m new file mode 100755 index 0000000000..f7739f0c8d --- /dev/null +++ b/Source/Private/TextExperiment/String/ASTextRunDelegate.m @@ -0,0 +1,71 @@ +// +// ASTextRunDelegate.m +// ASText +// +// Created by ibireme on 14/10/14. +// Copyright (c) 2015 ibireme. +// +// This source code is licensed under the MIT-style license found in the +// LICENSE file in the root directory of this source tree. +// + +#import + +static void DeallocCallback(void *ref) { + ASTextRunDelegate *self = (__bridge_transfer ASTextRunDelegate *)(ref); + self = nil; // release +} + +static CGFloat GetAscentCallback(void *ref) { + ASTextRunDelegate *self = (__bridge ASTextRunDelegate *)(ref); + return self.ascent; +} + +static CGFloat GetDecentCallback(void *ref) { + ASTextRunDelegate *self = (__bridge ASTextRunDelegate *)(ref); + return self.descent; +} + +static CGFloat GetWidthCallback(void *ref) { + ASTextRunDelegate *self = (__bridge ASTextRunDelegate *)(ref); + return self.width; +} + +@implementation ASTextRunDelegate + +- (CTRunDelegateRef)CTRunDelegate CF_RETURNS_RETAINED { + CTRunDelegateCallbacks callbacks; + callbacks.version = kCTRunDelegateCurrentVersion; + callbacks.dealloc = DeallocCallback; + callbacks.getAscent = GetAscentCallback; + callbacks.getDescent = GetDecentCallback; + callbacks.getWidth = GetWidthCallback; + return CTRunDelegateCreate(&callbacks, (__bridge_retained void *)(self.copy)); +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:@(_ascent) forKey:@"ascent"]; + [aCoder encodeObject:@(_descent) forKey:@"descent"]; + [aCoder encodeObject:@(_width) forKey:@"width"]; + [aCoder encodeObject:_userInfo forKey:@"userInfo"]; +} + +- (id)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + _ascent = ((NSNumber *)[aDecoder decodeObjectForKey:@"ascent"]).floatValue; + _descent = ((NSNumber *)[aDecoder decodeObjectForKey:@"descent"]).floatValue; + _width = ((NSNumber *)[aDecoder decodeObjectForKey:@"width"]).floatValue; + _userInfo = [aDecoder decodeObjectForKey:@"userInfo"]; + return self; +} + +- (id)copyWithZone:(NSZone *)zone { + __typeof__(self) one = [self.class new]; + one.ascent = self.ascent; + one.descent = self.descent; + one.width = self.width; + one.userInfo = self.userInfo; + return one; +} + +@end diff --git a/Source/Private/TextExperiment/Utility/ASTextUtilities.h b/Source/Private/TextExperiment/Utility/ASTextUtilities.h new file mode 100755 index 0000000000..c434cc9012 --- /dev/null +++ b/Source/Private/TextExperiment/Utility/ASTextUtilities.h @@ -0,0 +1,319 @@ +// +// ASTextUtilities.h +// Modified from YYText +// +// Created by ibireme on 15/4/6. +// Copyright (c) 2015 ibireme. +// +// This source code is licensed under the MIT-style license found in the +// LICENSE file in the root directory of this source tree. +// + +#import +#import +#import +#import +#import + + +#ifndef ASTEXT_CLAMP // return the clamped value +#define ASTEXT_CLAMP(_x_, _low_, _high_) (((_x_) > (_high_)) ? (_high_) : (((_x_) < (_low_)) ? (_low_) : (_x_))) +#endif + +#ifndef ASTEXT_SWAP // swap two value +#define ASTEXT_SWAP(_a_, _b_) do { __typeof__(_a_) _tmp_ = (_a_); (_a_) = (_b_); (_b_) = _tmp_; } while (0) +#endif + +NS_ASSUME_NONNULL_BEGIN + +/** + Whether the character is 'line break char': + U+000D (\\r or CR) + U+2028 (Unicode line separator) + U+000A (\\n or LF) + U+2029 (Unicode paragraph separator) + + @param c A character + @return YES or NO. + */ +static inline BOOL ASTextIsLinebreakChar(unichar c) { + switch (c) { + case 0x000D: + case 0x2028: + case 0x000A: + case 0x2029: + return YES; + default: + return NO; + } +} + +/** + Whether the string is a 'line break': + U+000D (\\r or CR) + U+2028 (Unicode line separator) + U+000A (\\n or LF) + U+2029 (Unicode paragraph separator) + \\r\\n, in that order (also known as CRLF) + + @param str A string + @return YES or NO. + */ +static inline BOOL ASTextIsLinebreakString(NSString * _Nullable str) { + if (str.length > 2 || str.length == 0) return NO; + if (str.length == 1) { + unichar c = [str characterAtIndex:0]; + return ASTextIsLinebreakChar(c); + } else { + return ([str characterAtIndex:0] == '\r') && ([str characterAtIndex:1] == '\n'); + } +} + +/** + If the string has a 'line break' suffix, return the 'line break' length. + + @param str A string. + @return The length of the tail line break: 0, 1 or 2. + */ +static inline NSUInteger ASTextLinebreakTailLength(NSString * _Nullable str) { + if (str.length >= 2) { + unichar c2 = [str characterAtIndex:str.length - 1]; + if (ASTextIsLinebreakChar(c2)) { + unichar c1 = [str characterAtIndex:str.length - 2]; + if (c1 == '\r' && c2 == '\n') return 2; + else return 1; + } else { + return 0; + } + } else if (str.length == 1) { + return ASTextIsLinebreakChar([str characterAtIndex:0]) ? 1 : 0; + } else { + return 0; + } +} + +/** + Whether the font contains color bitmap glyphs. + + @discussion Only `AppleColorEmoji` contains color bitmap glyphs in iOS system fonts. + @param font A font. + @return YES: the font contains color bitmap glyphs, NO: the font has no color bitmap glyph. + */ +static inline BOOL ASTextCTFontContainsColorBitmapGlyphs(CTFontRef font) { + return (CTFontGetSymbolicTraits(font) & kCTFontTraitColorGlyphs) != 0; +} + +/** + Get the `AppleColorEmoji` font's ascent with a specified font size. + It may used to create custom emoji. + + @param fontSize The specified font size. + @return The font ascent. + */ +static inline CGFloat ASTextEmojiGetAscentWithFontSize(CGFloat fontSize) { + if (fontSize < 16) { + return 1.25 * fontSize; + } else if (16 <= fontSize && fontSize <= 24) { + return 0.5 * fontSize + 12; + } else { + return fontSize; + } +} + +/** + Get the `AppleColorEmoji` font's descent with a specified font size. + It may used to create custom emoji. + + @param fontSize The specified font size. + @return The font descent. + */ +static inline CGFloat ASTextEmojiGetDescentWithFontSize(CGFloat fontSize) { + if (fontSize < 16) { + return 0.390625 * fontSize; + } else if (16 <= fontSize && fontSize <= 24) { + return 0.15625 * fontSize + 3.75; + } else { + return 0.3125 * fontSize; + } + return 0; +} + +/** + Get the `AppleColorEmoji` font's glyph bounding rect with a specified font size. + It may used to create custom emoji. + + @param fontSize The specified font size. + @return The font glyph bounding rect. + */ +static inline CGRect ASTextEmojiGetGlyphBoundingRectWithFontSize(CGFloat fontSize) { + CGRect rect; + rect.origin.x = 0.75; + rect.size.width = rect.size.height = ASTextEmojiGetAscentWithFontSize(fontSize); + if (fontSize < 16) { + rect.origin.y = -0.2525 * fontSize; + } else if (16 <= fontSize && fontSize <= 24) { + rect.origin.y = 0.1225 * fontSize -6; + } else { + rect.origin.y = -0.1275 * fontSize; + } + return rect; +} + + +/** + Get the character set which should rotate in vertical form. + @return The shared character set. + */ +NSCharacterSet *ASTextVerticalFormRotateCharacterSet(); + +/** + Get the character set which should rotate and move in vertical form. + @return The shared character set. + */ +NSCharacterSet *ASTextVerticalFormRotateAndMoveCharacterSet(); + + +/// Get the transform rotation. +/// @return the rotation in radians [-PI,PI] ([-180°,180°]) +static inline CGFloat ASTextCGAffineTransformGetRotation(CGAffineTransform transform) { + return atan2(transform.b, transform.a); +} + +/// Negates/inverts a UIEdgeInsets. +static inline UIEdgeInsets ASTextUIEdgeInsetsInvert(UIEdgeInsets insets) { + return UIEdgeInsetsMake(-insets.top, -insets.left, -insets.bottom, -insets.right); +} + +/** + Returns a rectangle to fit `rect` with specified content mode. + + @param rect The constraint rect + @param size The content size + @param mode The content mode + @return A rectangle for the given content mode. + @discussion UIViewContentModeRedraw is same as UIViewContentModeScaleToFill. + */ +CGRect ASTextCGRectFitWithContentMode(CGRect rect, CGSize size, UIViewContentMode mode); + +/// Returns the center for the rectangle. +static inline CGPoint ASTextCGRectGetCenter(CGRect rect) { + return CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect)); +} + +/// Returns the area of the rectangle. +static inline CGFloat ASTextCGRectGetArea(CGRect rect) { + if (CGRectIsNull(rect)) return 0; + rect = CGRectStandardize(rect); + return rect.size.width * rect.size.height; +} + +/// Returns the minmium distance between a point to a rectangle. +static inline CGFloat ASTextCGPointGetDistanceToRect(CGPoint p, CGRect r) { + r = CGRectStandardize(r); + if (CGRectContainsPoint(r, p)) return 0; + CGFloat distV, distH; + if (CGRectGetMinY(r) <= p.y && p.y <= CGRectGetMaxY(r)) { + distV = 0; + } else { + distV = p.y < CGRectGetMinY(r) ? CGRectGetMinY(r) - p.y : p.y - CGRectGetMaxY(r); + } + if (CGRectGetMinX(r) <= p.x && p.x <= CGRectGetMaxX(r)) { + distH = 0; + } else { + distH = p.x < CGRectGetMinX(r) ? CGRectGetMinX(r) - p.x : p.x - CGRectGetMaxX(r); + } + return MAX(distV, distH); +} + +/// Convert point to pixel. +static inline CGFloat ASTextCGFloatToPixel(CGFloat value) { + return value * ASScreenScale(); +} + +/// Convert pixel to point. +static inline CGFloat ASTextCGFloatFromPixel(CGFloat value) { + return value / ASScreenScale(); +} + +/// round point value to .5 pixel for path stroke (odd pixel line width pixel-aligned) +static inline CGFloat ASTextCGFloatPixelHalf(CGFloat value) { + CGFloat scale = ASScreenScale(); + return (floor(value * scale) + 0.5) / scale; +} + +/// floor point value for pixel-aligned +static inline CGPoint ASTextCGPointPixelFloor(CGPoint point) { + CGFloat scale = ASScreenScale(); + return CGPointMake(floor(point.x * scale) / scale, + floor(point.y * scale) / scale); +} + +/// round point value for pixel-aligned +static inline CGPoint ASTextCGPointPixelRound(CGPoint point) { + CGFloat scale = ASScreenScale(); + return CGPointMake(round(point.x * scale) / scale, + round(point.y * scale) / scale); +} + +/// ceil point value for pixel-aligned +static inline CGPoint ASTextCGPointPixelCeil(CGPoint point) { + CGFloat scale = ASScreenScale(); + return CGPointMake(ceil(point.x * scale) / scale, + ceil(point.y * scale) / scale); +} + +/// round point value to .5 pixel for path stroke (odd pixel line width pixel-aligned) +static inline CGPoint ASTextCGPointPixelHalf(CGPoint point) { + CGFloat scale = ASScreenScale(); + return CGPointMake((floor(point.x * scale) + 0.5) / scale, + (floor(point.y * scale) + 0.5) / scale); +} + +/// round point value for pixel-aligned +static inline CGRect ASTextCGRectPixelRound(CGRect rect) { + CGPoint origin = ASTextCGPointPixelRound(rect.origin); + CGPoint corner = ASTextCGPointPixelRound(CGPointMake(rect.origin.x + rect.size.width, + rect.origin.y + rect.size.height)); + return CGRectMake(origin.x, origin.y, corner.x - origin.x, corner.y - origin.y); +} + +/// round point value to .5 pixel for path stroke (odd pixel line width pixel-aligned) +static inline CGRect ASTextCGRectPixelHalf(CGRect rect) { + CGPoint origin = ASTextCGPointPixelHalf(rect.origin); + CGPoint corner = ASTextCGPointPixelHalf(CGPointMake(rect.origin.x + rect.size.width, + rect.origin.y + rect.size.height)); + return CGRectMake(origin.x, origin.y, corner.x - origin.x, corner.y - origin.y); +} + + +static inline UIFont * _Nullable ASTextFontWithBold(UIFont *font) { + return [UIFont fontWithDescriptor:[font.fontDescriptor fontDescriptorWithSymbolicTraits:UIFontDescriptorTraitBold] size:font.pointSize]; +} + +static inline UIFont * _Nullable ASTextFontWithItalic(UIFont *font) { + return [UIFont fontWithDescriptor:[font.fontDescriptor fontDescriptorWithSymbolicTraits:UIFontDescriptorTraitItalic] size:font.pointSize]; +} + +static inline UIFont * _Nullable ASTextFontWithBoldItalic(UIFont *font) { + return [UIFont fontWithDescriptor:[font.fontDescriptor fontDescriptorWithSymbolicTraits:UIFontDescriptorTraitBold | UIFontDescriptorTraitItalic] size:font.pointSize]; +} + + + +/** + Convert CFRange to NSRange + @param range CFRange @return NSRange + */ +static inline NSRange ASTextNSRangeFromCFRange(CFRange range) { + return NSMakeRange(range.location, range.length); +} + +/** + Convert NSRange to CFRange + @param range NSRange @return CFRange + */ +static inline CFRange ASTextCFRangeFromNSRange(NSRange range) { + return CFRangeMake(range.location, range.length); +} + +NS_ASSUME_NONNULL_END diff --git a/Source/Private/TextExperiment/Utility/ASTextUtilities.m b/Source/Private/TextExperiment/Utility/ASTextUtilities.m new file mode 100755 index 0000000000..17e6971355 --- /dev/null +++ b/Source/Private/TextExperiment/Utility/ASTextUtilities.m @@ -0,0 +1,146 @@ +// +// ASTextUtilities.m +// Modified from YYText +// +// Created by ibireme on 15/4/6. +// Copyright (c) 2015 ibireme. +// +// This source code is licensed under the MIT-style license found in the +// LICENSE file in the root directory of this source tree. +// + +#import "ASTextUtilities.h" +#import + +NSCharacterSet *ASTextVerticalFormRotateCharacterSet() { + static NSMutableCharacterSet *set; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + set = [NSMutableCharacterSet new]; + [set addCharactersInRange:NSMakeRange(0x1100, 256)]; // Hangul Jamo + [set addCharactersInRange:NSMakeRange(0x2460, 160)]; // Enclosed Alphanumerics + [set addCharactersInRange:NSMakeRange(0x2600, 256)]; // Miscellaneous Symbols + [set addCharactersInRange:NSMakeRange(0x2700, 192)]; // Dingbats + [set addCharactersInRange:NSMakeRange(0x2E80, 128)]; // CJK Radicals Supplement + [set addCharactersInRange:NSMakeRange(0x2F00, 224)]; // Kangxi Radicals + [set addCharactersInRange:NSMakeRange(0x2FF0, 16)]; // Ideographic Description Characters + [set addCharactersInRange:NSMakeRange(0x3000, 64)]; // CJK Symbols and Punctuation + [set removeCharactersInRange:NSMakeRange(0x3008, 10)]; + [set removeCharactersInRange:NSMakeRange(0x3014, 12)]; + [set addCharactersInRange:NSMakeRange(0x3040, 96)]; // Hiragana + [set addCharactersInRange:NSMakeRange(0x30A0, 96)]; // Katakana + [set addCharactersInRange:NSMakeRange(0x3100, 48)]; // Bopomofo + [set addCharactersInRange:NSMakeRange(0x3130, 96)]; // Hangul Compatibility Jamo + [set addCharactersInRange:NSMakeRange(0x3190, 16)]; // Kanbun + [set addCharactersInRange:NSMakeRange(0x31A0, 32)]; // Bopomofo Extended + [set addCharactersInRange:NSMakeRange(0x31C0, 48)]; // CJK Strokes + [set addCharactersInRange:NSMakeRange(0x31F0, 16)]; // Katakana Phonetic Extensions + [set addCharactersInRange:NSMakeRange(0x3200, 256)]; // Enclosed CJK Letters and Months + [set addCharactersInRange:NSMakeRange(0x3300, 256)]; // CJK Compatibility + [set addCharactersInRange:NSMakeRange(0x3400, 2582)]; // CJK Unified Ideographs Extension A + [set addCharactersInRange:NSMakeRange(0x4E00, 20941)]; // CJK Unified Ideographs + [set addCharactersInRange:NSMakeRange(0xAC00, 11172)]; // Hangul Syllables + [set addCharactersInRange:NSMakeRange(0xD7B0, 80)]; // Hangul Jamo Extended-B + [set addCharactersInString:@""]; // U+F8FF (Private Use Area) + [set addCharactersInRange:NSMakeRange(0xF900, 512)]; // CJK Compatibility Ideographs + [set addCharactersInRange:NSMakeRange(0xFE10, 16)]; // Vertical Forms + [set addCharactersInRange:NSMakeRange(0xFF00, 240)]; // Halfwidth and Fullwidth Forms + [set addCharactersInRange:NSMakeRange(0x1F200, 256)]; // Enclosed Ideographic Supplement + [set addCharactersInRange:NSMakeRange(0x1F300, 768)]; // Enclosed Ideographic Supplement + [set addCharactersInRange:NSMakeRange(0x1F600, 80)]; // Emoticons (Emoji) + [set addCharactersInRange:NSMakeRange(0x1F680, 128)]; // Transport and Map Symbols + + // See http://unicode-table.com/ for more information. + }); + return set; +} + +NSCharacterSet *ASTextVerticalFormRotateAndMoveCharacterSet() { + static NSMutableCharacterSet *set; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + set = [NSMutableCharacterSet new]; + [set addCharactersInString:@",。、."]; + }); + return set; +} + +CGRect ASTextCGRectFitWithContentMode(CGRect rect, CGSize size, UIViewContentMode mode) { + rect = CGRectStandardize(rect); + size.width = size.width < 0 ? -size.width : size.width; + size.height = size.height < 0 ? -size.height : size.height; + CGPoint center = CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect)); + switch (mode) { + case UIViewContentModeScaleAspectFit: + case UIViewContentModeScaleAspectFill: { + if (rect.size.width < 0.01 || rect.size.height < 0.01 || + size.width < 0.01 || size.height < 0.01) { + rect.origin = center; + rect.size = CGSizeZero; + } else { + CGFloat scale; + if (mode == UIViewContentModeScaleAspectFit) { + if (size.width / size.height < rect.size.width / rect.size.height) { + scale = rect.size.height / size.height; + } else { + scale = rect.size.width / size.width; + } + } else { + if (size.width / size.height < rect.size.width / rect.size.height) { + scale = rect.size.width / size.width; + } else { + scale = rect.size.height / size.height; + } + } + size.width *= scale; + size.height *= scale; + rect.size = size; + rect.origin = CGPointMake(center.x - size.width * 0.5, center.y - size.height * 0.5); + } + } break; + case UIViewContentModeCenter: { + rect.size = size; + rect.origin = CGPointMake(center.x - size.width * 0.5, center.y - size.height * 0.5); + } break; + case UIViewContentModeTop: { + rect.origin.x = center.x - size.width * 0.5; + rect.size = size; + } break; + case UIViewContentModeBottom: { + rect.origin.x = center.x - size.width * 0.5; + rect.origin.y += rect.size.height - size.height; + rect.size = size; + } break; + case UIViewContentModeLeft: { + rect.origin.y = center.y - size.height * 0.5; + rect.size = size; + } break; + case UIViewContentModeRight: { + rect.origin.y = center.y - size.height * 0.5; + rect.origin.x += rect.size.width - size.width; + rect.size = size; + } break; + case UIViewContentModeTopLeft: { + rect.size = size; + } break; + case UIViewContentModeTopRight: { + rect.origin.x += rect.size.width - size.width; + rect.size = size; + } break; + case UIViewContentModeBottomLeft: { + rect.origin.y += rect.size.height - size.height; + rect.size = size; + } break; + case UIViewContentModeBottomRight: { + rect.origin.x += rect.size.width - size.width; + rect.origin.y += rect.size.height - size.height; + rect.size = size; + } break; + case UIViewContentModeScaleToFill: + case UIViewContentModeRedraw: + default: { + rect = rect; + } + } + return rect; +} diff --git a/Source/Private/TextExperiment/Utility/NSAttributedString+ASText.h b/Source/Private/TextExperiment/Utility/NSAttributedString+ASText.h new file mode 100755 index 0000000000..9fc8ec2228 --- /dev/null +++ b/Source/Private/TextExperiment/Utility/NSAttributedString+ASText.h @@ -0,0 +1,1393 @@ +// +// NSAttributedString+ASText.h +// Modified from YYText +// +// Created by ibireme on 14/10/7. +// Copyright (c) 2015 ibireme. +// +// This source code is licensed under the MIT-style license found in the +// LICENSE file in the root directory of this source tree. +// + +#import +#import + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + Get pre-defined attributes from attributed string. + All properties defined in UIKit, CoreText and ASText are included. + */ +@interface NSAttributedString (ASText) + +#pragma mark - Retrieving character attribute information +///============================================================================= +/// @name Retrieving character attribute information +///============================================================================= + +/** + Returns the attributes at first charactor. + */ +@property (nullable, nonatomic, copy, readonly) NSDictionary *as_attributes; + +/** + Returns the attributes for the character at a given index. + + @discussion Raises an `NSRangeException` if index lies beyond the end of the + receiver's characters. + + @param index The index for which to return attributes. + This value must lie within the bounds of the receiver. + + @return The attributes for the character at index. + */ +- (nullable NSDictionary *)as_attributesAtIndex:(NSUInteger)index; + +/** + Returns the value for an attribute with a given name of the character at a given index. + + @discussion Raises an `NSRangeException` if index lies beyond the end of the + receiver's characters. + + @param attributeName The name of an attribute. + @param index The index for which to return attributes. + This value must not exceed the bounds of the receiver. + + @return The value for the attribute named `attributeName` of the character at + index `index`, or nil if there is no such attribute. + */ +- (nullable id)as_attribute:(NSString *)attributeName atIndex:(NSUInteger)index; + + +#pragma mark - Get character attribute as property +///============================================================================= +/// @name Get character attribute as property +///============================================================================= + +/** + The font of the text. (read-only) + + @discussion Default is Helvetica (Neue) 12. + @discussion Get this property returns the first character's attribute. + @since CoreText:3.2 UIKit:6.0 ASText:6.0 + */ +@property (nullable, nonatomic, strong, readonly) UIFont *as_font; +- (nullable UIFont *)as_fontAtIndex:(NSUInteger)index; + +/** + A kerning adjustment. (read-only) + + @discussion Default is standard kerning. The kerning attribute indicate how many + points the following character should be shifted from its default offset as + defined by the current character's font in points; a positive kern indicates a + shift farther along and a negative kern indicates a shift closer to the current + character. If this attribute is not present, standard kerning will be used. + If this attribute is set to 0.0, no kerning will be done at all. + @discussion Get this property returns the first character's attribute. + @since CoreText:3.2 UIKit:6.0 ASText:6.0 + */ +@property (nullable, nonatomic, strong, readonly) NSNumber *as_kern; +- (nullable NSNumber *)as_kernAtIndex:(NSUInteger)index; + +/** + The foreground color. (read-only) + + @discussion Default is Black. + @discussion Get this property returns the first character's attribute. + @since CoreText:3.2 UIKit:6.0 ASText:6.0 + */ +@property (nullable, nonatomic, strong, readonly) UIColor *as_color; +- (nullable UIColor *)as_colorAtIndex:(NSUInteger)index; + +/** + The background color. (read-only) + + @discussion Default is nil (or no background). + @discussion Get this property returns the first character's attribute. + @since UIKit:6.0 + */ +@property (nullable, nonatomic, strong, readonly) UIColor *as_backgroundColor; +- (nullable UIColor *)as_backgroundColorAtIndex:(NSUInteger)index; + +/** + The stroke width. (read-only) + + @discussion Default value is 0.0 (no stroke). This attribute, interpreted as + a percentage of font point size, controls the text drawing mode: positive + values effect drawing with stroke only; negative values are for stroke and fill. + A typical value for outlined text is 3.0. + @discussion Get this property returns the first character's attribute. + @since CoreText:3.2 UIKit:6.0 + */ +@property (nullable, nonatomic, strong, readonly) NSNumber *as_strokeWidth; +- (nullable NSNumber *)as_strokeWidthAtIndex:(NSUInteger)index; + +/** + The stroke color. (read-only) + + @discussion Default value is nil (same as foreground color). + @discussion Get this property returns the first character's attribute. + @since CoreText:3.2 UIKit:6.0 + */ +@property (nullable, nonatomic, strong, readonly) UIColor *as_strokeColor; +- (nullable UIColor *)as_strokeColorAtIndex:(NSUInteger)index; + +/** + The text shadow. (read-only) + + @discussion Default value is nil (no shadow). + @discussion Get this property returns the first character's attribute. + @since UIKit:6.0 ASText:6.0 + */ +@property (nullable, nonatomic, strong, readonly) NSShadow *as_shadow; +- (nullable NSShadow *)as_shadowAtIndex:(NSUInteger)index; + +/** + The strikethrough style. (read-only) + + @discussion Default value is NSUnderlineStyleNone (no strikethrough). + @discussion Get this property returns the first character's attribute. + @since UIKit:6.0 + */ +@property (nonatomic, readonly) NSUnderlineStyle as_strikethroughStyle; +- (NSUnderlineStyle)as_strikethroughStyleAtIndex:(NSUInteger)index; + +/** + The strikethrough color. (read-only) + + @discussion Default value is nil (same as foreground color). + @discussion Get this property returns the first character's attribute. + @since UIKit:7.0 + */ +@property (nullable, nonatomic, strong, readonly) UIColor *as_strikethroughColor; +- (nullable UIColor *)as_strikethroughColorAtIndex:(NSUInteger)index; + +/** + The underline style. (read-only) + + @discussion Default value is NSUnderlineStyleNone (no underline). + @discussion Get this property returns the first character's attribute. + @since CoreText:3.2 UIKit:6.0 + */ +@property (nonatomic, readonly) NSUnderlineStyle as_underlineStyle; +- (NSUnderlineStyle)as_underlineStyleAtIndex:(NSUInteger)index; + +/** + The underline color. (read-only) + + @discussion Default value is nil (same as foreground color). + @discussion Get this property returns the first character's attribute. + @since CoreText:3.2 UIKit:7.0 + */ +@property (nullable, nonatomic, strong, readonly) UIColor *as_underlineColor; +- (nullable UIColor *)as_underlineColorAtIndex:(NSUInteger)index; + +/** + Ligature formation control. (read-only) + + @discussion Default is int value 1. The ligature attribute determines what kinds + of ligatures should be used when displaying the string. A value of 0 indicates + that only ligatures essential for proper rendering of text should be used, + 1 indicates that standard ligatures should be used, and 2 indicates that all + available ligatures should be used. Which ligatures are standard depends on the + script and possibly the font. + @discussion Get this property returns the first character's attribute. + @since CoreText:3.2 UIKit:6.0 ASText:6.0 + */ +@property (nullable, nonatomic, strong, readonly) NSNumber *as_ligature; +- (nullable NSNumber *)as_ligatureAtIndex:(NSUInteger)index; + +/** + The text effect. (read-only) + + @discussion Default is nil (no effect). The only currently supported value + is NSTextEffectLetterpressStyle. + @discussion Get this property returns the first character's attribute. + @since UIKit:7.0 + */ +@property (nullable, nonatomic, strong, readonly) NSString *as_textEffect; +- (nullable NSString *)as_textEffectAtIndex:(NSUInteger)index; + +/** + The skew to be applied to glyphs. (read-only) + + @discussion Default is 0 (no skew). + @discussion Get this property returns the first character's attribute. + @since UIKit:7.0 + */ +@property (nullable, nonatomic, strong, readonly) NSNumber *as_obliqueness; +- (nullable NSNumber *)as_obliquenessAtIndex:(NSUInteger)index; + +/** + The log of the expansion factor to be applied to glyphs. (read-only) + + @discussion Default is 0 (no expansion). + @discussion Get this property returns the first character's attribute. + @since UIKit:7.0 + */ +@property (nullable, nonatomic, strong, readonly) NSNumber *as_expansion; +- (nullable NSNumber *)as_expansionAtIndex:(NSUInteger)index; + +/** + The character's offset from the baseline, in points. (read-only) + + @discussion Default is 0. + @discussion Get this property returns the first character's attribute. + @since UIKit:7.0 + */ +@property (nullable, nonatomic, strong, readonly) NSNumber *as_baselineOffset; +- (nullable NSNumber *)as_baselineOffsetAtIndex:(NSUInteger)index; + +/** + Glyph orientation control. (read-only) + + @discussion Default is NO. A value of NO indicates that horizontal glyph forms + are to be used, YES indicates that vertical glyph forms are to be used. + @discussion Get this property returns the first character's attribute. + @since CoreText:4.3 ASText:6.0 + */ +@property (nonatomic, readonly) BOOL as_verticalGlyphForm; +- (BOOL)as_verticalGlyphFormAtIndex:(NSUInteger)index; + +/** + Specifies text language. (read-only) + + @discussion Value must be a NSString containing a locale identifier. Default is + unset. When this attribute is set to a valid identifier, it will be used to select + localized glyphs (if supported by the font) and locale-specific line breaking rules. + @discussion Get this property returns the first character's attribute. + @since CoreText:7.0 ASText:7.0 + */ +@property (nullable, nonatomic, strong, readonly) NSString *as_language; +- (nullable NSString *)as_languageAtIndex:(NSUInteger)index; + +/** + Specifies a bidirectional override or embedding. (read-only) + + @discussion See alse NSWritingDirection and NSWritingDirectionAttributeName. + @discussion Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:7.0 ASText:6.0 + */ +@property (nullable, nonatomic, strong, readonly) NSArray *as_writingDirection; +- (nullable NSArray *)as_writingDirectionAtIndex:(NSUInteger)index; + +/** + An NSParagraphStyle object which is used to specify things like + line alignment, tab rulers, writing direction, etc. (read-only) + + @discussion Default is nil ([NSParagraphStyle defaultParagraphStyle]). + @discussion Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nullable, nonatomic, strong, readonly) NSParagraphStyle *as_paragraphStyle; +- (nullable NSParagraphStyle *)as_paragraphStyleAtIndex:(NSUInteger)index; + +#pragma mark - Get paragraph attribute as property +///============================================================================= +/// @name Get paragraph attribute as property +///============================================================================= + +/** + The text alignment (A wrapper for NSParagraphStyle). (read-only) + + @discussion Natural text alignment is realized as left or right alignment + depending on the line sweep direction of the first script contained in the paragraph. + @discussion Default is NSTextAlignmentNatural. + @discussion Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nonatomic, readonly) NSTextAlignment as_alignment; +- (NSTextAlignment)as_alignmentAtIndex:(NSUInteger)index; + +/** + The mode that should be used to break lines (A wrapper for NSParagraphStyle). (read-only) + + @discussion This property contains the line break mode to be used laying out the paragraph's text. + @discussion Default is NSLineBreakByWordWrapping. + @discussion Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nonatomic, readonly) NSLineBreakMode as_lineBreakMode; +- (NSLineBreakMode)as_lineBreakModeAtIndex:(NSUInteger)index; + +/** + The distance in points between the bottom of one line fragment and the top of the next. + (A wrapper for NSParagraphStyle) (read-only) + + @discussion This value is always nonnegative. This value is included in the line + fragment heights in the layout manager. + @discussion Default is 0. + @discussion Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nonatomic, readonly) CGFloat as_lineSpacing; +- (CGFloat)as_lineSpacingAtIndex:(NSUInteger)index; + +/** + The space after the end of the paragraph (A wrapper for NSParagraphStyle). (read-only) + + @discussion This property contains the space (measured in points) added at the + end of the paragraph to separate it from the following paragraph. This value must + be nonnegative. The space between paragraphs is determined by adding the previous + paragraph's paragraphSpacing and the current paragraph's paragraphSpacingBefore. + @discussion Default is 0. + @discussion Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nonatomic, readonly) CGFloat as_paragraphSpacing; +- (CGFloat)as_paragraphSpacingAtIndex:(NSUInteger)index; + +/** + The distance between the paragraph's top and the beginning of its text content. + (A wrapper for NSParagraphStyle). (read-only) + + @discussion This property contains the space (measured in points) between the + paragraph's top and the beginning of its text content. + @discussion Default is 0. + @discussion Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nonatomic, readonly) CGFloat as_paragraphSpacingBefore; +- (CGFloat)as_paragraphSpacingBeforeAtIndex:(NSUInteger)index; + +/** + The indentation of the first line (A wrapper for NSParagraphStyle). (read-only) + + @discussion This property contains the distance (in points) from the leading margin + of a text container to the beginning of the paragraph's first line. This value + is always nonnegative. + @discussion Default is 0. + @discussion Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nonatomic, readonly) CGFloat as_firstLineHeadIndent; +- (CGFloat)as_firstLineHeadIndentAtIndex:(NSUInteger)index; + +/** + The indentation of the receiver's lines other than the first. (A wrapper for NSParagraphStyle). (read-only) + + @discussion This property contains the distance (in points) from the leading margin + of a text container to the beginning of lines other than the first. This value is + always nonnegative. + @discussion Default is 0. + @discussion Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nonatomic, readonly) CGFloat as_headIndent; +- (CGFloat)as_headIndentAtIndex:(NSUInteger)index; + +/** + The trailing indentation (A wrapper for NSParagraphStyle). (read-only) + + @discussion If positive, this value is the distance from the leading margin + (for example, the left margin in left-to-right text). If 0 or negative, it's the + distance from the trailing margin. + @discussion Default is 0. + @discussion Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nonatomic, readonly) CGFloat as_tailIndent; +- (CGFloat)as_tailIndentAtIndex:(NSUInteger)index; + +/** + The receiver's minimum height (A wrapper for NSParagraphStyle). (read-only) + + @discussion This property contains the minimum height in points that any line in + the receiver will occupy, regardless of the font size or size of any attached graphic. + This value must be nonnegative. + @discussion Default is 0. + @discussion Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nonatomic, readonly) CGFloat as_minimumLineHeight; +- (CGFloat)as_minimumLineHeightAtIndex:(NSUInteger)index; + +/** + The receiver's maximum line height (A wrapper for NSParagraphStyle). (read-only) + + @discussion This property contains the maximum height in points that any line in + the receiver will occupy, regardless of the font size or size of any attached graphic. + This value is always nonnegative. Glyphs and graphics exceeding this height will + overlap neighboring lines; however, a maximum height of 0 implies no line height limit. + Although this limit applies to the line itself, line spacing adds extra space between adjacent lines. + @discussion Default is 0 (no limit). + @discussion Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nonatomic, readonly) CGFloat as_maximumLineHeight; +- (CGFloat)as_maximumLineHeightAtIndex:(NSUInteger)index; + +/** + The line height multiple (A wrapper for NSParagraphStyle). (read-only) + + @discussion This property contains the line break mode to be used laying out the paragraph's text. + @discussion Default is 0 (no multiple). + @discussion Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nonatomic, readonly) CGFloat as_lineHeightMultiple; +- (CGFloat)as_lineHeightMultipleAtIndex:(NSUInteger)index; + +/** + The base writing direction (A wrapper for NSParagraphStyle). (read-only) + + @discussion If you specify NSWritingDirectionNaturalDirection, the receiver resolves + the writing direction to either NSWritingDirectionLeftToRight or NSWritingDirectionRightToLeft, + depending on the direction for the user's `language` preference setting. + @discussion Default is NSWritingDirectionNatural. + @discussion Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nonatomic, readonly) NSWritingDirection as_baseWritingDirection; +- (NSWritingDirection)as_baseWritingDirectionAtIndex:(NSUInteger)index; + +/** + The paragraph's threshold for hyphenation. (A wrapper for NSParagraphStyle). (read-only) + + @discussion Valid values lie between 0.0 and 1.0 inclusive. Hyphenation is attempted + when the ratio of the text width (as broken without hyphenation) to the width of the + line fragment is less than the hyphenation factor. When the paragraph's hyphenation + factor is 0.0, the layout manager's hyphenation factor is used instead. When both + are 0.0, hyphenation is disabled. + @discussion Default is 0. + @discussion Get this property returns the first character's attribute. + @since UIKit:6.0 + */ +@property (nonatomic, readonly) float as_hyphenationFactor; +- (float)as_hyphenationFactorAtIndex:(NSUInteger)index; + +/** + The document-wide default tab interval (A wrapper for NSParagraphStyle). (read-only) + + @discussion This property represents the default tab interval in points. Tabs after the + last specified in tabStops are placed at integer multiples of this distance (if positive). + @discussion Default is 0. + @discussion Get this property returns the first character's attribute. + @since CoreText:7.0 UIKit:7.0 ASText:7.0 + */ +@property (nonatomic, readonly) CGFloat as_defaultTabInterval; +- (CGFloat)as_defaultTabIntervalAtIndex:(NSUInteger)index; + +/** + An array of NSTextTab objects representing the receiver's tab stops. + (A wrapper for NSParagraphStyle). (read-only) + + @discussion The NSTextTab objects, sorted by location, define the tab stops for + the paragraph style. + @discussion Default is 12 TabStops with 28.0 tab interval. + @discussion Get this property returns the first character's attribute. + @since CoreText:7.0 UIKit:7.0 ASText:7.0 + */ +@property (nullable, nonatomic, copy, readonly) NSArray *as_tabStops; +- (nullable NSArray *)as_tabStopsAtIndex:(NSUInteger)index; + +#pragma mark - Get ASText attribute as property +///============================================================================= +/// @name Get ASText attribute as property +///============================================================================= + +/** + The text shadow. (read-only) + + @discussion Default value is nil (no shadow). + @discussion Get this property returns the first character's attribute. + @since ASText:6.0 + */ +@property (nullable, nonatomic, strong, readonly) ASTextShadow *as_textShadow; +- (nullable ASTextShadow *)as_textShadowAtIndex:(NSUInteger)index; + +/** + The text inner shadow. (read-only) + + @discussion Default value is nil (no shadow). + @discussion Get this property returns the first character's attribute. + @since ASText:6.0 + */ +@property (nullable, nonatomic, strong, readonly) ASTextShadow *as_textInnerShadow; +- (nullable ASTextShadow *)as_textInnerShadowAtIndex:(NSUInteger)index; + +/** + The text underline. (read-only) + + @discussion Default value is nil (no underline). + @discussion Get this property returns the first character's attribute. + @since ASText:6.0 + */ +@property (nullable, nonatomic, strong, readonly) ASTextDecoration *as_textUnderline; +- (nullable ASTextDecoration *)as_textUnderlineAtIndex:(NSUInteger)index; + +/** + The text strikethrough. (read-only) + + @discussion Default value is nil (no strikethrough). + @discussion Get this property returns the first character's attribute. + @since ASText:6.0 + */ +@property (nullable, nonatomic, strong, readonly) ASTextDecoration *as_textStrikethrough; +- (nullable ASTextDecoration *)as_textStrikethroughAtIndex:(NSUInteger)index; + +/** + The text border. (read-only) + + @discussion Default value is nil (no border). + @discussion Get this property returns the first character's attribute. + @since ASText:6.0 + */ +@property (nullable, nonatomic, strong, readonly) ASTextBorder *as_textBorder; +- (nullable ASTextBorder *)as_textBorderAtIndex:(NSUInteger)index; + +/** + The text background border. (read-only) + + @discussion Default value is nil (no background border). + @discussion Get this property returns the first character's attribute. + @since ASText:6.0 + */ +@property (nullable, nonatomic, strong, readonly) ASTextBorder *as_textBackgroundBorder; +- (nullable ASTextBorder *)as_textBackgroundBorderAtIndex:(NSUInteger)index; + +/** + The glyph transform. (read-only) + + @discussion Default value is CGAffineTransformIdentity (no transform). + @discussion Get this property returns the first character's attribute. + @since ASText:6.0 + */ +@property (nonatomic, readonly) CGAffineTransform as_textGlyphTransform; +- (CGAffineTransform)as_textGlyphTransformAtIndex:(NSUInteger)index; + + +#pragma mark - Query for ASText +///============================================================================= +/// @name Query for ASText +///============================================================================= + +/** + Returns the plain text from a range. + If there's `ASTextBackedStringAttributeName` attribute, the backed string will + replace the attributed string range. + + @param range A range in receiver. + @return The plain text. + */ +- (nullable NSString *)as_plainTextForRange:(NSRange)range; + + +#pragma mark - Create attachment string for ASText +///============================================================================= +/// @name Create attachment string for ASText +///============================================================================= + +/** + Creates and returns an attachment. + + @param content The attachment (UIImage/UIView/CALayer). + @param contentMode The attachment's content mode. + @param width The attachment's container width in layout. + @param ascent The attachment's container ascent in layout. + @param descent The attachment's container descent in layout. + + @return An attributed string, or nil if an error occurs. + @since ASText:6.0 + */ ++ (NSMutableAttributedString *)as_attachmentStringWithContent:(nullable id)content + contentMode:(UIViewContentMode)contentMode + width:(CGFloat)width + ascent:(CGFloat)ascent + descent:(CGFloat)descent; + +/** + Creates and returns an attachment. + + + Example: ContentMode:bottom Alignment:Top. + + The text The attachment holder + ↓ ↓ + ─────────┌──────────────────────┐─────── + / \ │ │ / ___| + / _ \ │ │| | + / ___ \ │ │| |___ ←── The text line + /_/ \_\│ ██████████████ │ \____| + ─────────│ ██████████████ │─────── + │ ██████████████ │ + │ ██████████████ ←───────────────── The attachment content + │ ██████████████ │ + └──────────────────────┘ + + @param content The attachment (UIImage/UIView/CALayer). + @param contentMode The attachment's content mode in attachment holder + @param attachmentSize The attachment holder's size in text layout. + @param font The attachment will align to this font. + @param alignment The attachment holder's alignment to text line. + + @return An attributed string, or nil if an error occurs. + @since ASText:6.0 + */ ++ (NSMutableAttributedString *)as_attachmentStringWithContent:(nullable id)content + contentMode:(UIViewContentMode)contentMode + attachmentSize:(CGSize)attachmentSize + alignToFont:(UIFont *)font + alignment:(ASTextVerticalAlignment)alignment; + +/** + Creates and returns an attahment from a fourquare image as if it was an emoji. + + @param image A fourquare image. + @param fontSize The font size. + + @return An attributed string, or nil if an error occurs. + @since ASText:6.0 + */ ++ (nullable NSMutableAttributedString *)as_attachmentStringWithEmojiImage:(UIImage *)image + fontSize:(CGFloat)fontSize; + +#pragma mark - Utility +///============================================================================= +/// @name Utility +///============================================================================= + +/** + Returns NSMakeRange(0, self.length). + */ +- (NSRange)as_rangeOfAll; + +/** + If YES, it share the same attribute in entire text range. + */ +- (BOOL)as_isSharedAttributesInAllRange; + +/** + If YES, it can be drawn with the [drawWithRect:options:context:] method or displayed with UIKit. + If NO, it should be drawn with CoreText or ASText. + + @discussion If the method returns NO, it means that there's at least one attribute + which is not supported by UIKit (such as CTParagraphStyleRef). If display this string + in UIKit, it may lose some attribute, or even crash the app. + */ +- (BOOL)as_canDrawWithUIKit; + +@end + + + + +/** + Set pre-defined attributes to attributed string. + All properties defined in UIKit, CoreText and ASText are included. + */ +@interface NSMutableAttributedString (ASText) + +#pragma mark - Set character attribute +///============================================================================= +/// @name Set character attribute +///============================================================================= + +/** + Sets the attributes to the entire text string. + + @discussion The old attributes will be removed. + + @param attributes A dictionary containing the attributes to set, or nil to remove all attributes. + */ +- (void)as_setAttributes:(nullable NSDictionary *)attributes; +- (void)setAs_attributes:(nullable NSDictionary *)attributes; + +/** + Sets an attribute with the given name and value to the entire text string. + + @param name A string specifying the attribute name. + @param value The attribute value associated with name. Pass `nil` or `NSNull` to + remove the attribute. + */ +- (void)as_setAttribute:(NSString *)name value:(nullable id)value; + +/** + Sets an attribute with the given name and value to the characters in the specified range. + + @param name A string specifying the attribute name. + @param value The attribute value associated with name. Pass `nil` or `NSNull` to + remove the attribute. + @param range The range of characters to which the specified attribute/value pair applies. + */ +- (void)as_setAttribute:(NSString *)name value:(nullable id)value range:(NSRange)range; + +/** + Removes all attributes in the specified range. + + @param range The range of characters. + */ +- (void)as_removeAttributesInRange:(NSRange)range; + + +#pragma mark - Set character attribute as property +///============================================================================= +/// @name Set character attribute as property +///============================================================================= + +/** + The font of the text. + + @discussion Default is Helvetica (Neue) 12. + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:3.2 UIKit:6.0 ASText:6.0 + */ +@property (nullable, nonatomic, strong, readwrite) UIFont *as_font; +- (void)as_setFont:(nullable UIFont *)font range:(NSRange)range; + +/** + A kerning adjustment. + + @discussion Default is standard kerning. The kerning attribute indicate how many + points the following character should be shifted from its default offset as + defined by the current character's font in points; a positive kern indicates a + shift farther along and a negative kern indicates a shift closer to the current + character. If this attribute is not present, standard kerning will be used. + If this attribute is set to 0.0, no kerning will be done at all. + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:3.2 UIKit:6.0 ASText:6.0 + */ +@property (nullable, nonatomic, strong, readwrite) NSNumber *as_kern; +- (void)as_setKern:(nullable NSNumber *)kern range:(NSRange)range; + +/** + The foreground color. + + @discussion Default is Black. + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:3.2 UIKit:6.0 ASText:6.0 + */ +@property (nullable, nonatomic, strong, readwrite) UIColor *as_color; +- (void)as_setColor:(nullable UIColor *)color range:(NSRange)range; + +/** + The background color. + + @discussion Default is nil (or no background). + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since UIKit:6.0 + */ +@property (nullable, nonatomic, strong, readwrite) UIColor *as_backgroundColor; +- (void)as_setBackgroundColor:(nullable UIColor *)backgroundColor range:(NSRange)range; + +/** + The stroke width. + + @discussion Default value is 0.0 (no stroke). This attribute, interpreted as + a percentage of font point size, controls the text drawing mode: positive + values effect drawing with stroke only; negative values are for stroke and fill. + A typical value for outlined text is 3.0. + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:3.2 UIKit:6.0 ASText:6.0 + */ +@property (nullable, nonatomic, strong, readwrite) NSNumber *as_strokeWidth; +- (void)as_setStrokeWidth:(nullable NSNumber *)strokeWidth range:(NSRange)range; + +/** + The stroke color. + + @discussion Default value is nil (same as foreground color). + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:3.2 UIKit:6.0 ASText:6.0 + */ +@property (nullable, nonatomic, strong, readwrite) UIColor *as_strokeColor; +- (void)as_setStrokeColor:(nullable UIColor *)strokeColor range:(NSRange)range; + +/** + The text shadow. + + @discussion Default value is nil (no shadow). + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since UIKit:6.0 ASText:6.0 + */ +@property (nullable, nonatomic, strong, readwrite) NSShadow *as_shadow; +- (void)as_setShadow:(nullable NSShadow *)shadow range:(NSRange)range; + +/** + The strikethrough style. + + @discussion Default value is NSUnderlineStyleNone (no strikethrough). + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since UIKit:6.0 + */ +@property (nonatomic, readwrite) NSUnderlineStyle as_strikethroughStyle; +- (void)as_setStrikethroughStyle:(NSUnderlineStyle)strikethroughStyle range:(NSRange)range; + +/** + The strikethrough color. + + @discussion Default value is nil (same as foreground color). + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since UIKit:7.0 + */ +@property (nullable, nonatomic, strong, readwrite) UIColor *as_strikethroughColor; +- (void)as_setStrikethroughColor:(nullable UIColor *)strikethroughColor range:(NSRange)range NS_AVAILABLE_IOS(7_0); + +/** + The underline style. + + @discussion Default value is NSUnderlineStyleNone (no underline). + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:3.2 UIKit:6.0 + */ +@property (nonatomic, readwrite) NSUnderlineStyle as_underlineStyle; +- (void)as_setUnderlineStyle:(NSUnderlineStyle)underlineStyle range:(NSRange)range; + +/** + The underline color. + + @discussion Default value is nil (same as foreground color). + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:3.2 UIKit:7.0 + */ +@property (nullable, nonatomic, strong, readwrite) UIColor *as_underlineColor; +- (void)as_setUnderlineColor:(nullable UIColor *)underlineColor range:(NSRange)range; + +/** + Ligature formation control. + + @discussion Default is int value 1. The ligature attribute determines what kinds + of ligatures should be used when displaying the string. A value of 0 indicates + that only ligatures essential for proper rendering of text should be used, + 1 indicates that standard ligatures should be used, and 2 indicates that all + available ligatures should be used. Which ligatures are standard depends on the + script and possibly the font. + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:3.2 UIKit:6.0 ASText:6.0 + */ +@property (nullable, nonatomic, strong, readwrite) NSNumber *as_ligature; +- (void)as_setLigature:(nullable NSNumber *)ligature range:(NSRange)range; + +/** + The text effect. + + @discussion Default is nil (no effect). The only currently supported value + is NSTextEffectLetterpressStyle. + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since UIKit:7.0 + */ +@property (nullable, nonatomic, strong, readwrite) NSString *as_textEffect; +- (void)as_setTextEffect:(nullable NSString *)textEffect range:(NSRange)range NS_AVAILABLE_IOS(7_0); + +/** + The skew to be applied to glyphs. + + @discussion Default is 0 (no skew). + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since UIKit:7.0 + */ +@property (nullable, nonatomic, strong, readwrite) NSNumber *as_obliqueness; +- (void)as_setObliqueness:(nullable NSNumber *)obliqueness range:(NSRange)range NS_AVAILABLE_IOS(7_0); + +/** + The log of the expansion factor to be applied to glyphs. + + @discussion Default is 0 (no expansion). + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since UIKit:7.0 + */ +@property (nullable, nonatomic, strong, readwrite) NSNumber *as_expansion; +- (void)as_setExpansion:(nullable NSNumber *)expansion range:(NSRange)range NS_AVAILABLE_IOS(7_0); + +/** + The character's offset from the baseline, in points. + + @discussion Default is 0. + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since UIKit:7.0 + */ +@property (nullable, nonatomic, strong, readwrite) NSNumber *as_baselineOffset; +- (void)as_setBaselineOffset:(nullable NSNumber *)baselineOffset range:(NSRange)range NS_AVAILABLE_IOS(7_0); + +/** + Glyph orientation control. + + @discussion Default is NO. A value of NO indicates that horizontal glyph forms + are to be used, YES indicates that vertical glyph forms are to be used. + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:4.3 ASText:6.0 + */ +@property (nonatomic, readwrite) BOOL as_verticalGlyphForm; +- (void)as_setVerticalGlyphForm:(BOOL)verticalGlyphForm range:(NSRange)range; + +/** + Specifies text language. + + @discussion Value must be a NSString containing a locale identifier. Default is + unset. When this attribute is set to a valid identifier, it will be used to select + localized glyphs (if supported by the font) and locale-specific line breaking rules. + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:7.0 ASText:7.0 + */ +@property (nullable, nonatomic, strong, readwrite) NSString *as_language; +- (void)as_setLanguage:(nullable NSString *)language range:(NSRange)range NS_AVAILABLE_IOS(7_0); + +/** + Specifies a bidirectional override or embedding. + + @discussion See alse NSWritingDirection and NSWritingDirectionAttributeName. + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:7.0 ASText:6.0 + */ +@property (nullable, nonatomic, strong, readwrite) NSArray *as_writingDirection; +- (void)as_setWritingDirection:(nullable NSArray *)writingDirection range:(NSRange)range; + +/** + An NSParagraphStyle object which is used to specify things like + line alignment, tab rulers, writing direction, etc. + + @discussion Default is nil ([NSParagraphStyle defaultParagraphStyle]). + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nullable, nonatomic, strong, readwrite) NSParagraphStyle *as_paragraphStyle; +- (void)as_setParagraphStyle:(nullable NSParagraphStyle *)paragraphStyle range:(NSRange)range; + + +#pragma mark - Set paragraph attribute as property +///============================================================================= +/// @name Set paragraph attribute as property +///============================================================================= + +/** + The text alignment (A wrapper for NSParagraphStyle). + + @discussion Natural text alignment is realized as left or right alignment + depending on the line sweep direction of the first script contained in the paragraph. + @discussion Default is NSTextAlignmentNatural. + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nonatomic, readwrite) NSTextAlignment as_alignment; +- (void)as_setAlignment:(NSTextAlignment)alignment range:(NSRange)range; + +/** + The mode that should be used to break lines (A wrapper for NSParagraphStyle). + + @discussion This property contains the line break mode to be used laying out the paragraph's text. + @discussion Default is NSLineBreakByWordWrapping. + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nonatomic, readwrite) NSLineBreakMode as_lineBreakMode; +- (void)as_setLineBreakMode:(NSLineBreakMode)lineBreakMode range:(NSRange)range; + +/** + The distance in points between the bottom of one line fragment and the top of the next. + (A wrapper for NSParagraphStyle) + + @discussion This value is always nonnegative. This value is included in the line + fragment heights in the layout manager. + @discussion Default is 0. + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nonatomic, readwrite) CGFloat as_lineSpacing; +- (void)as_setLineSpacing:(CGFloat)lineSpacing range:(NSRange)range; + +/** + The space after the end of the paragraph (A wrapper for NSParagraphStyle). + + @discussion This property contains the space (measured in points) added at the + end of the paragraph to separate it from the following paragraph. This value must + be nonnegative. The space between paragraphs is determined by adding the previous + paragraph's paragraphSpacing and the current paragraph's paragraphSpacingBefore. + @discussion Default is 0. + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nonatomic, readwrite) CGFloat as_paragraphSpacing; +- (void)as_setParagraphSpacing:(CGFloat)paragraphSpacing range:(NSRange)range; + +/** + The distance between the paragraph's top and the beginning of its text content. + (A wrapper for NSParagraphStyle). + + @discussion This property contains the space (measured in points) between the + paragraph's top and the beginning of its text content. + @discussion Default is 0. + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nonatomic, readwrite) CGFloat as_paragraphSpacingBefore; +- (void)as_setParagraphSpacingBefore:(CGFloat)paragraphSpacingBefore range:(NSRange)range; + +/** + The indentation of the first line (A wrapper for NSParagraphStyle). + + @discussion This property contains the distance (in points) from the leading margin + of a text container to the beginning of the paragraph's first line. This value + is always nonnegative. + @discussion Default is 0. + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nonatomic, readwrite) CGFloat as_firstLineHeadIndent; +- (void)as_setFirstLineHeadIndent:(CGFloat)firstLineHeadIndent range:(NSRange)range; + +/** + The indentation of the receiver's lines other than the first. (A wrapper for NSParagraphStyle). + + @discussion This property contains the distance (in points) from the leading margin + of a text container to the beginning of lines other than the first. This value is + always nonnegative. + @discussion Default is 0. + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nonatomic, readwrite) CGFloat as_headIndent; +- (void)as_setHeadIndent:(CGFloat)headIndent range:(NSRange)range; + +/** + The trailing indentation (A wrapper for NSParagraphStyle). + + @discussion If positive, this value is the distance from the leading margin + (for example, the left margin in left-to-right text). If 0 or negative, it's the + distance from the trailing margin. + @discussion Default is 0. + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nonatomic, readwrite) CGFloat as_tailIndent; +- (void)as_setTailIndent:(CGFloat)tailIndent range:(NSRange)range; + +/** + The receiver's minimum height (A wrapper for NSParagraphStyle). + + @discussion This property contains the minimum height in points that any line in + the receiver will occupy, regardless of the font size or size of any attached graphic. + This value must be nonnegative. + @discussion Default is 0. + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nonatomic, readwrite) CGFloat as_minimumLineHeight; +- (void)as_setMinimumLineHeight:(CGFloat)minimumLineHeight range:(NSRange)range; + +/** + The receiver's maximum line height (A wrapper for NSParagraphStyle). + + @discussion This property contains the maximum height in points that any line in + the receiver will occupy, regardless of the font size or size of any attached graphic. + This value is always nonnegative. Glyphs and graphics exceeding this height will + overlap neighboring lines; however, a maximum height of 0 implies no line height limit. + Although this limit applies to the line itself, line spacing adds extra space between adjacent lines. + @discussion Default is 0 (no limit). + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nonatomic, readwrite) CGFloat as_maximumLineHeight; +- (void)as_setMaximumLineHeight:(CGFloat)maximumLineHeight range:(NSRange)range; + +/** + The line height multiple (A wrapper for NSParagraphStyle). + + @discussion This property contains the line break mode to be used laying out the paragraph's text. + @discussion Default is 0 (no multiple). + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nonatomic, readwrite) CGFloat as_lineHeightMultiple; +- (void)as_setLineHeightMultiple:(CGFloat)lineHeightMultiple range:(NSRange)range; + +/** + The base writing direction (A wrapper for NSParagraphStyle). + + @discussion If you specify NSWritingDirectionNaturalDirection, the receiver resolves + the writing direction to either NSWritingDirectionLeftToRight or NSWritingDirectionRightToLeft, + depending on the direction for the user's `language` preference setting. + @discussion Default is NSWritingDirectionNatural. + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nonatomic, readwrite) NSWritingDirection as_baseWritingDirection; +- (void)as_setBaseWritingDirection:(NSWritingDirection)baseWritingDirection range:(NSRange)range; + +/** + The paragraph's threshold for hyphenation. (A wrapper for NSParagraphStyle). + + @discussion Valid values lie between 0.0 and 1.0 inclusive. Hyphenation is attempted + when the ratio of the text width (as broken without hyphenation) to the width of the + line fragment is less than the hyphenation factor. When the paragraph's hyphenation + factor is 0.0, the layout manager's hyphenation factor is used instead. When both + are 0.0, hyphenation is disabled. + @discussion Default is 0. + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since UIKit:6.0 + */ +@property (nonatomic, readwrite) float as_hyphenationFactor; +- (void)as_setHyphenationFactor:(float)hyphenationFactor range:(NSRange)range; + +/** + The document-wide default tab interval (A wrapper for NSParagraphStyle). + + @discussion This property represents the default tab interval in points. Tabs after the + last specified in tabStops are placed at integer multiples of this distance (if positive). + @discussion Default is 0. + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:7.0 UIKit:7.0 ASText:7.0 + */ +@property (nonatomic, readwrite) CGFloat as_defaultTabInterval; +- (void)as_setDefaultTabInterval:(CGFloat)defaultTabInterval range:(NSRange)range NS_AVAILABLE_IOS(7_0); + +/** + An array of NSTextTab objects representing the receiver's tab stops. + (A wrapper for NSParagraphStyle). + + @discussion The NSTextTab objects, sorted by location, define the tab stops for + the paragraph style. + @discussion Default is 12 TabStops with 28.0 tab interval. + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:7.0 UIKit:7.0 ASText:7.0 + */ +@property (nullable, nonatomic, copy, readwrite) NSArray *as_tabStops; +- (void)as_setTabStops:(nullable NSArray *)tabStops range:(NSRange)range NS_AVAILABLE_IOS(7_0); + +#pragma mark - Set ASText attribute as property +///============================================================================= +/// @name Set ASText attribute as property +///============================================================================= + +/** + The text shadow. + + @discussion Default value is nil (no shadow). + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since ASText:6.0 + */ +@property (nullable, nonatomic, strong, readwrite) ASTextShadow *as_textShadow; +- (void)as_setTextShadow:(nullable ASTextShadow *)textShadow range:(NSRange)range; + +/** + The text inner shadow. + + @discussion Default value is nil (no shadow). + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since ASText:6.0 + */ +@property (nullable, nonatomic, strong, readwrite) ASTextShadow *as_textInnerShadow; +- (void)as_setTextInnerShadow:(nullable ASTextShadow *)textInnerShadow range:(NSRange)range; + +/** + The text underline. + + @discussion Default value is nil (no underline). + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since ASText:6.0 + */ +@property (nullable, nonatomic, strong, readwrite) ASTextDecoration *as_textUnderline; +- (void)as_setTextUnderline:(nullable ASTextDecoration *)textUnderline range:(NSRange)range; + +/** + The text strikethrough. + + @discussion Default value is nil (no strikethrough). + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since ASText:6.0 + */ +@property (nullable, nonatomic, strong, readwrite) ASTextDecoration *as_textStrikethrough; +- (void)as_setTextStrikethrough:(nullable ASTextDecoration *)textStrikethrough range:(NSRange)range; + +/** + The text border. + + @discussion Default value is nil (no border). + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since ASText:6.0 + */ +@property (nullable, nonatomic, strong, readwrite) ASTextBorder *as_textBorder; +- (void)as_setTextBorder:(nullable ASTextBorder *)textBorder range:(NSRange)range; + +/** + The text background border. + + @discussion Default value is nil (no background border). + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since ASText:6.0 + */ +@property (nullable, nonatomic, strong, readwrite) ASTextBorder *as_textBackgroundBorder; +- (void)as_setTextBackgroundBorder:(nullable ASTextBorder *)textBackgroundBorder range:(NSRange)range; + +/** + The glyph transform. + + @discussion Default value is CGAffineTransformIdentity (no transform). + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since ASText:6.0 + */ +@property (nonatomic, readwrite) CGAffineTransform as_textGlyphTransform; +- (void)as_setTextGlyphTransform:(CGAffineTransform)textGlyphTransform range:(NSRange)range; + + +#pragma mark - Set discontinuous attribute for range +///============================================================================= +/// @name Set discontinuous attribute for range +///============================================================================= + +- (void)as_setSuperscript:(nullable NSNumber *)superscript range:(NSRange)range; +- (void)as_setGlyphInfo:(nullable CTGlyphInfoRef)glyphInfo range:(NSRange)range; +- (void)as_setCharacterShape:(nullable NSNumber *)characterShape range:(NSRange)range; +- (void)as_setRunDelegate:(nullable CTRunDelegateRef)runDelegate range:(NSRange)range; +- (void)as_setBaselineClass:(nullable CFStringRef)baselineClass range:(NSRange)range; +- (void)as_setBaselineInfo:(nullable CFDictionaryRef)baselineInfo range:(NSRange)range; +- (void)as_setBaselineReferenceInfo:(nullable CFDictionaryRef)referenceInfo range:(NSRange)range; +- (void)as_setRubyAnnotation:(nullable CTRubyAnnotationRef)ruby range:(NSRange)range NS_AVAILABLE_IOS(8_0); +- (void)as_setAttachment:(nullable NSTextAttachment *)attachment range:(NSRange)range NS_AVAILABLE_IOS(7_0); +- (void)as_setLink:(nullable id)link range:(NSRange)range NS_AVAILABLE_IOS(7_0); +- (void)as_setTextBackedString:(nullable ASTextBackedString *)textBackedString range:(NSRange)range; +- (void)as_setTextBinding:(nullable ASTextBinding *)textBinding range:(NSRange)range; +- (void)as_setTextAttachment:(nullable ASTextAttachment *)textAttachment range:(NSRange)range; +- (void)as_setTextHighlight:(nullable ASTextHighlight *)textHighlight range:(NSRange)range; +- (void)as_setTextBlockBorder:(nullable ASTextBorder *)textBlockBorder range:(NSRange)range; + + +#pragma mark - Convenience methods for text highlight +///============================================================================= +/// @name Convenience methods for text highlight +///============================================================================= + +/** + Convenience method to set text highlight + + @param range text range + @param color text color (pass nil to ignore) + @param backgroundColor text background color when highlight + @param userInfo user information dictionary (pass nil to ignore) + @param tapAction tap action when user tap the highlight (pass nil to ignore) + @param longPressAction long press action when user long press the highlight (pass nil to ignore) + */ +- (void)as_setTextHighlightRange:(NSRange)range + color:(nullable UIColor *)color + backgroundColor:(nullable UIColor *)backgroundColor + userInfo:(nullable NSDictionary *)userInfo + tapAction:(nullable ASTextAction)tapAction + longPressAction:(nullable ASTextAction)longPressAction; + +/** + Convenience method to set text highlight + + @param range text range + @param color text color (pass nil to ignore) + @param backgroundColor text background color when highlight + @param tapAction tap action when user tap the highlight (pass nil to ignore) + */ +- (void)as_setTextHighlightRange:(NSRange)range + color:(nullable UIColor *)color + backgroundColor:(nullable UIColor *)backgroundColor + tapAction:(nullable ASTextAction)tapAction; + +/** + Convenience method to set text highlight + + @param range text range + @param color text color (pass nil to ignore) + @param backgroundColor text background color when highlight + @param userInfo tap action when user tap the highlight (pass nil to ignore) + */ +- (void)as_setTextHighlightRange:(NSRange)range + color:(nullable UIColor *)color + backgroundColor:(nullable UIColor *)backgroundColor + userInfo:(nullable NSDictionary *)userInfo; + +#pragma mark - Utilities +///============================================================================= +/// @name Utilities +///============================================================================= + +/** + Inserts into the receiver the characters of a given string at a given location. + The new string inherit the attributes of the first replaced character from location. + + @param string The string to insert into the receiver, must not be nil. + @param location The location at which string is inserted. The location must not + exceed the bounds of the receiver. + @throw Raises an NSRangeException if the location out of bounds. + */ +- (void)as_insertString:(NSString *)string atIndex:(NSUInteger)location; + +/** + Adds to the end of the receiver the characters of a given string. + The new string inherit the attributes of the receiver's tail. + + @param string The string to append to the receiver, must not be nil. + */ +- (void)as_appendString:(NSString *)string; + +/** + Set foreground color with [UIColor clearColor] in joined-emoji range. + Emoji drawing will not be affected by the foreground color. + + @discussion In iOS 8.3, Apple releases some new diversified emojis. + There's some single emoji which can be assembled to a new 'joined-emoji'. + The joiner is unicode character 'ZERO WIDTH JOINER' (U+200D). + For example: 👨👩👧👧 -> 👨‍👩‍👧‍👧. + + When there are more than 5 'joined-emoji' in a same CTLine, CoreText may render some + extra glyphs above the emoji. It's a bug in CoreText, try this method to avoid. + This bug is fixed in iOS 9. + */ +- (void)as_setClearColorToJoinedEmoji; + +/** + Removes all discontinuous attributes in a specified range. + See `allDiscontinuousAttributeKeys`. + + @param range A text range. + */ +- (void)as_removeDiscontinuousAttributesInRange:(NSRange)range; + +/** + Returns all discontinuous attribute keys, such as RunDelegate/Attachment/Ruby. + + @discussion These attributes can only set to a specified range of text, and + should not extend to other range when editing text. + */ ++ (NSArray *)as_allDiscontinuousAttributeKeys; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Source/Private/TextExperiment/Utility/NSAttributedString+ASText.m b/Source/Private/TextExperiment/Utility/NSAttributedString+ASText.m new file mode 100755 index 0000000000..d67dd15d2c --- /dev/null +++ b/Source/Private/TextExperiment/Utility/NSAttributedString+ASText.m @@ -0,0 +1,1248 @@ +// +// NSAttributedString+ASText.m +// Modified from YYText +// +// Created by ibireme on 14/10/7. +// Copyright (c) 2015 ibireme. +// +// This source code is licensed under the MIT-style license found in the +// LICENSE file in the root directory of this source tree. +// + +#import +#import +#import +#import +#import + + +// Dummy class for category +@interface NSAttributedString_ASText : NSObject @end +@implementation NSAttributedString_ASText @end + + +@implementation NSAttributedString (ASText) + +- (NSDictionary *)as_attributesAtIndex:(NSUInteger)index { + if (index > self.length || self.length == 0) return nil; + if (self.length > 0 && index == self.length) index--; + return [self attributesAtIndex:index effectiveRange:NULL]; +} + +- (id)as_attribute:(NSString *)attributeName atIndex:(NSUInteger)index { + if (!attributeName) return nil; + if (index > self.length || self.length == 0) return nil; + if (self.length > 0 && index == self.length) index--; + return [self attribute:attributeName atIndex:index effectiveRange:NULL]; +} + +- (NSDictionary *)as_attributes { + return [self as_attributesAtIndex:0]; +} + +- (UIFont *)as_font { + return [self as_fontAtIndex:0]; +} + +- (UIFont *)as_fontAtIndex:(NSUInteger)index { + return [self as_attribute:NSFontAttributeName atIndex:index]; +} + +- (NSNumber *)as_kern { + return [self as_kernAtIndex:0]; +} + +- (NSNumber *)as_kernAtIndex:(NSUInteger)index { + return [self as_attribute:NSKernAttributeName atIndex:index]; +} + +- (UIColor *)as_color { + return [self as_colorAtIndex:0]; +} + +- (UIColor *)as_colorAtIndex:(NSUInteger)index { + UIColor *color = [self as_attribute:NSForegroundColorAttributeName atIndex:index]; + if (!color) { + CGColorRef ref = (__bridge CGColorRef)([self as_attribute:(NSString *)kCTForegroundColorAttributeName atIndex:index]); + color = [UIColor colorWithCGColor:ref]; + } + if (color && ![color isKindOfClass:[UIColor class]]) { + if (CFGetTypeID((__bridge CFTypeRef)(color)) == CGColorGetTypeID()) { + color = [UIColor colorWithCGColor:(__bridge CGColorRef)(color)]; + } else { + color = nil; + } + } + return color; +} + +- (UIColor *)as_backgroundColor { + return [self as_backgroundColorAtIndex:0]; +} + +- (UIColor *)as_backgroundColorAtIndex:(NSUInteger)index { + return [self as_attribute:NSBackgroundColorAttributeName atIndex:index]; +} + +- (NSNumber *)as_strokeWidth { + return [self as_strokeWidthAtIndex:0]; +} + +- (NSNumber *)as_strokeWidthAtIndex:(NSUInteger)index { + return [self as_attribute:NSStrokeWidthAttributeName atIndex:index]; +} + +- (UIColor *)as_strokeColor { + return [self as_strokeColorAtIndex:0]; +} + +- (UIColor *)as_strokeColorAtIndex:(NSUInteger)index { + UIColor *color = [self as_attribute:NSStrokeColorAttributeName atIndex:index]; + if (!color) { + CGColorRef ref = (__bridge CGColorRef)([self as_attribute:(NSString *)kCTStrokeColorAttributeName atIndex:index]); + color = [UIColor colorWithCGColor:ref]; + } + return color; +} + +- (NSShadow *)as_shadow { + return [self as_shadowAtIndex:0]; +} + +- (NSShadow *)as_shadowAtIndex:(NSUInteger)index { + return [self as_attribute:NSShadowAttributeName atIndex:index]; +} + +- (NSUnderlineStyle)as_strikethroughStyle { + return [self as_strikethroughStyleAtIndex:0]; +} + +- (NSUnderlineStyle)as_strikethroughStyleAtIndex:(NSUInteger)index { + NSNumber *style = [self as_attribute:NSStrikethroughStyleAttributeName atIndex:index]; + return (NSUnderlineStyle)style.integerValue; +} + +- (UIColor *)as_strikethroughColor { + return [self as_strikethroughColorAtIndex:0]; +} + +- (UIColor *)as_strikethroughColorAtIndex:(NSUInteger)index { + return [self as_attribute:NSStrikethroughColorAttributeName atIndex:index]; +} + +- (NSUnderlineStyle)as_underlineStyle { + return [self as_underlineStyleAtIndex:0]; +} + +- (NSUnderlineStyle)as_underlineStyleAtIndex:(NSUInteger)index { + NSNumber *style = [self as_attribute:NSUnderlineStyleAttributeName atIndex:index]; + return (NSUnderlineStyle)style.integerValue; +} + +- (UIColor *)as_underlineColor { + return [self as_underlineColorAtIndex:0]; +} + +- (UIColor *)as_underlineColorAtIndex:(NSUInteger)index { + UIColor *color = [self as_attribute:NSUnderlineColorAttributeName atIndex:index]; + if (!color) { + CGColorRef ref = (__bridge CGColorRef)([self as_attribute:(NSString *)kCTUnderlineColorAttributeName atIndex:index]); + color = [UIColor colorWithCGColor:ref]; + } + return color; +} + +- (NSNumber *)as_ligature { + return [self as_ligatureAtIndex:0]; +} + +- (NSNumber *)as_ligatureAtIndex:(NSUInteger)index { + return [self as_attribute:NSLigatureAttributeName atIndex:index]; +} + +- (NSString *)as_textEffect { + return [self as_textEffectAtIndex:0]; +} + +- (NSString *)as_textEffectAtIndex:(NSUInteger)index { + return [self as_attribute:NSTextEffectAttributeName atIndex:index]; +} + +- (NSNumber *)as_obliqueness { + return [self as_obliquenessAtIndex:0]; +} + +- (NSNumber *)as_obliquenessAtIndex:(NSUInteger)index { + return [self as_attribute:NSObliquenessAttributeName atIndex:index]; +} + +- (NSNumber *)as_expansion { + return [self as_expansionAtIndex:0]; +} + +- (NSNumber *)as_expansionAtIndex:(NSUInteger)index { + return [self as_attribute:NSExpansionAttributeName atIndex:index]; +} + +- (NSNumber *)as_baselineOffset { + return [self as_baselineOffsetAtIndex:0]; +} + +- (NSNumber *)as_baselineOffsetAtIndex:(NSUInteger)index { + return [self as_attribute:NSBaselineOffsetAttributeName atIndex:index]; +} + +- (BOOL)as_verticalGlyphForm { + return [self as_verticalGlyphFormAtIndex:0]; +} + +- (BOOL)as_verticalGlyphFormAtIndex:(NSUInteger)index { + NSNumber *num = [self as_attribute:NSVerticalGlyphFormAttributeName atIndex:index]; + return num.boolValue; +} + +- (NSString *)as_language { + return [self as_languageAtIndex:0]; +} + +- (NSString *)as_languageAtIndex:(NSUInteger)index { + return [self as_attribute:(id)kCTLanguageAttributeName atIndex:index]; +} + +- (NSArray *)as_writingDirection { + return [self as_writingDirectionAtIndex:0]; +} + +- (NSArray *)as_writingDirectionAtIndex:(NSUInteger)index { + return [self as_attribute:(id)kCTWritingDirectionAttributeName atIndex:index]; +} + +- (NSParagraphStyle *)as_paragraphStyle { + return [self as_paragraphStyleAtIndex:0]; +} + +- (NSParagraphStyle *)as_paragraphStyleAtIndex:(NSUInteger)index { + /* + NSParagraphStyle is NOT toll-free bridged to CTParagraphStyleRef. + + CoreText can use both NSParagraphStyle and CTParagraphStyleRef, + but UILabel/UITextView can only use NSParagraphStyle. + + We use NSParagraphStyle in both CoreText and UIKit. + */ + NSParagraphStyle *style = [self as_attribute:NSParagraphStyleAttributeName atIndex:index]; + if (style) { + if (CFGetTypeID((__bridge CFTypeRef)(style)) == CTParagraphStyleGetTypeID()) { \ + style = [NSParagraphStyle as_styleWithCTStyle:(__bridge CTParagraphStyleRef)(style)]; + } + } + return style; +} + +#define ParagraphAttribute(_attr_) \ +NSParagraphStyle *style = self.as_paragraphStyle; \ +if (!style) style = [NSParagraphStyle defaultParagraphStyle]; \ +return style. _attr_; + +#define ParagraphAttributeAtIndex(_attr_) \ +NSParagraphStyle *style = [self as_paragraphStyleAtIndex:index]; \ +if (!style) style = [NSParagraphStyle defaultParagraphStyle]; \ +return style. _attr_; + +- (NSTextAlignment)as_alignment { + ParagraphAttribute(alignment); +} + +- (NSLineBreakMode)as_lineBreakMode { + ParagraphAttribute(lineBreakMode); +} + +- (CGFloat)as_lineSpacing { + ParagraphAttribute(lineSpacing); +} + +- (CGFloat)as_paragraphSpacing { + ParagraphAttribute(paragraphSpacing); +} + +- (CGFloat)as_paragraphSpacingBefore { + ParagraphAttribute(paragraphSpacingBefore); +} + +- (CGFloat)as_firstLineHeadIndent { + ParagraphAttribute(firstLineHeadIndent); +} + +- (CGFloat)as_headIndent { + ParagraphAttribute(headIndent); +} + +- (CGFloat)as_tailIndent { + ParagraphAttribute(tailIndent); +} + +- (CGFloat)as_minimumLineHeight { + ParagraphAttribute(minimumLineHeight); +} + +- (CGFloat)as_maximumLineHeight { + ParagraphAttribute(maximumLineHeight); +} + +- (CGFloat)as_lineHeightMultiple { + ParagraphAttribute(lineHeightMultiple); +} + +- (NSWritingDirection)as_baseWritingDirection { + ParagraphAttribute(baseWritingDirection); +} + +- (float)as_hyphenationFactor { + ParagraphAttribute(hyphenationFactor); +} + +- (CGFloat)as_defaultTabInterval { + ParagraphAttribute(defaultTabInterval); +} + +- (NSArray *)as_tabStops { + ParagraphAttribute(tabStops); +} + +- (NSTextAlignment)as_alignmentAtIndex:(NSUInteger)index { + ParagraphAttributeAtIndex(alignment); +} + +- (NSLineBreakMode)as_lineBreakModeAtIndex:(NSUInteger)index { + ParagraphAttributeAtIndex(lineBreakMode); +} + +- (CGFloat)as_lineSpacingAtIndex:(NSUInteger)index { + ParagraphAttributeAtIndex(lineSpacing); +} + +- (CGFloat)as_paragraphSpacingAtIndex:(NSUInteger)index { + ParagraphAttributeAtIndex(paragraphSpacing); +} + +- (CGFloat)as_paragraphSpacingBeforeAtIndex:(NSUInteger)index { + ParagraphAttributeAtIndex(paragraphSpacingBefore); +} + +- (CGFloat)as_firstLineHeadIndentAtIndex:(NSUInteger)index { + ParagraphAttributeAtIndex(firstLineHeadIndent); +} + +- (CGFloat)as_headIndentAtIndex:(NSUInteger)index { + ParagraphAttributeAtIndex(headIndent); +} + +- (CGFloat)as_tailIndentAtIndex:(NSUInteger)index { + ParagraphAttributeAtIndex(tailIndent); +} + +- (CGFloat)as_minimumLineHeightAtIndex:(NSUInteger)index { + ParagraphAttributeAtIndex(minimumLineHeight); +} + +- (CGFloat)as_maximumLineHeightAtIndex:(NSUInteger)index { + ParagraphAttributeAtIndex(maximumLineHeight); +} + +- (CGFloat)as_lineHeightMultipleAtIndex:(NSUInteger)index { + ParagraphAttributeAtIndex(lineHeightMultiple); +} + +- (NSWritingDirection)as_baseWritingDirectionAtIndex:(NSUInteger)index { + ParagraphAttributeAtIndex(baseWritingDirection); +} + +- (float)as_hyphenationFactorAtIndex:(NSUInteger)index { + ParagraphAttributeAtIndex(hyphenationFactor); +} + +- (CGFloat)as_defaultTabIntervalAtIndex:(NSUInteger)index { + ParagraphAttributeAtIndex(defaultTabInterval); +} + +- (NSArray *)as_tabStopsAtIndex:(NSUInteger)index { + ParagraphAttributeAtIndex(tabStops); +} + +#undef ParagraphAttribute +#undef ParagraphAttributeAtIndex + +- (ASTextShadow *)as_textShadow { + return [self as_textShadowAtIndex:0]; +} + +- (ASTextShadow *)as_textShadowAtIndex:(NSUInteger)index { + return [self as_attribute:ASTextShadowAttributeName atIndex:index]; +} + +- (ASTextShadow *)as_textInnerShadow { + return [self as_textInnerShadowAtIndex:0]; +} + +- (ASTextShadow *)as_textInnerShadowAtIndex:(NSUInteger)index { + return [self as_attribute:ASTextInnerShadowAttributeName atIndex:index]; +} + +- (ASTextDecoration *)as_textUnderline { + return [self as_textUnderlineAtIndex:0]; +} + +- (ASTextDecoration *)as_textUnderlineAtIndex:(NSUInteger)index { + return [self as_attribute:ASTextUnderlineAttributeName atIndex:index]; +} + +- (ASTextDecoration *)as_textStrikethrough { + return [self as_textStrikethroughAtIndex:0]; +} + +- (ASTextDecoration *)as_textStrikethroughAtIndex:(NSUInteger)index { + return [self as_attribute:ASTextStrikethroughAttributeName atIndex:index]; +} + +- (ASTextBorder *)as_textBorder { + return [self as_textBorderAtIndex:0]; +} + +- (ASTextBorder *)as_textBorderAtIndex:(NSUInteger)index { + return [self as_attribute:ASTextBorderAttributeName atIndex:index]; +} + +- (ASTextBorder *)as_textBackgroundBorder { + return [self as_textBackgroundBorderAtIndex:0]; +} + +- (ASTextBorder *)as_textBackgroundBorderAtIndex:(NSUInteger)index { + return [self as_attribute:ASTextBackedStringAttributeName atIndex:index]; +} + +- (CGAffineTransform)as_textGlyphTransform { + return [self as_textGlyphTransformAtIndex:0]; +} + +- (CGAffineTransform)as_textGlyphTransformAtIndex:(NSUInteger)index { + NSValue *value = [self as_attribute:ASTextGlyphTransformAttributeName atIndex:index]; + if (!value) return CGAffineTransformIdentity; + return [value CGAffineTransformValue]; +} + +- (NSString *)as_plainTextForRange:(NSRange)range { + if (range.location == NSNotFound ||range.length == NSNotFound) return nil; + NSMutableString *result = [NSMutableString string]; + if (range.length == 0) return result; + NSString *string = self.string; + [self enumerateAttribute:ASTextBackedStringAttributeName inRange:range options:kNilOptions usingBlock:^(id value, NSRange range, BOOL *stop) { + ASTextBackedString *backed = value; + if (backed && backed.string) { + [result appendString:backed.string]; + } else { + [result appendString:[string substringWithRange:range]]; + } + }]; + return result; +} + ++ (NSMutableAttributedString *)as_attachmentStringWithContent:(id)content + contentMode:(UIViewContentMode)contentMode + width:(CGFloat)width + ascent:(CGFloat)ascent + descent:(CGFloat)descent { + NSMutableAttributedString *atr = [[NSMutableAttributedString alloc] initWithString:ASTextAttachmentToken]; + + ASTextAttachment *attach = [ASTextAttachment new]; + attach.content = content; + attach.contentMode = contentMode; + [atr as_setTextAttachment:attach range:NSMakeRange(0, atr.length)]; + + ASTextRunDelegate *delegate = [ASTextRunDelegate new]; + delegate.width = width; + delegate.ascent = ascent; + delegate.descent = descent; + CTRunDelegateRef delegateRef = delegate.CTRunDelegate; + [atr as_setRunDelegate:delegateRef range:NSMakeRange(0, atr.length)]; + if (delegate) CFRelease(delegateRef); + + return atr; +} + ++ (NSMutableAttributedString *)as_attachmentStringWithContent:(id)content + contentMode:(UIViewContentMode)contentMode + attachmentSize:(CGSize)attachmentSize + alignToFont:(UIFont *)font + alignment:(ASTextVerticalAlignment)alignment { + NSMutableAttributedString *atr = [[NSMutableAttributedString alloc] initWithString:ASTextAttachmentToken]; + + ASTextAttachment *attach = [ASTextAttachment new]; + attach.content = content; + attach.contentMode = contentMode; + [atr as_setTextAttachment:attach range:NSMakeRange(0, atr.length)]; + + ASTextRunDelegate *delegate = [ASTextRunDelegate new]; + delegate.width = attachmentSize.width; + switch (alignment) { + case ASTextVerticalAlignmentTop: { + delegate.ascent = font.ascender; + delegate.descent = attachmentSize.height - font.ascender; + if (delegate.descent < 0) { + delegate.descent = 0; + delegate.ascent = attachmentSize.height; + } + } break; + case ASTextVerticalAlignmentCenter: { + CGFloat fontHeight = font.ascender - font.descender; + CGFloat yOffset = font.ascender - fontHeight * 0.5; + delegate.ascent = attachmentSize.height * 0.5 + yOffset; + delegate.descent = attachmentSize.height - delegate.ascent; + if (delegate.descent < 0) { + delegate.descent = 0; + delegate.ascent = attachmentSize.height; + } + } break; + case ASTextVerticalAlignmentBottom: { + delegate.ascent = attachmentSize.height + font.descender; + delegate.descent = -font.descender; + if (delegate.ascent < 0) { + delegate.ascent = 0; + delegate.descent = attachmentSize.height; + } + } break; + default: { + delegate.ascent = attachmentSize.height; + delegate.descent = 0; + } break; + } + + CTRunDelegateRef delegateRef = delegate.CTRunDelegate; + [atr as_setRunDelegate:delegateRef range:NSMakeRange(0, atr.length)]; + if (delegate) CFRelease(delegateRef); + + return atr; +} + ++ (NSMutableAttributedString *)as_attachmentStringWithEmojiImage:(UIImage *)image + fontSize:(CGFloat)fontSize { + if (!image || fontSize <= 0) return nil; + + BOOL hasAnim = NO; + if (image.images.count > 1) { + hasAnim = YES; + } else if (NSProtocolFromString(@"ASAnimatedImage") && + [image conformsToProtocol:NSProtocolFromString(@"ASAnimatedImage")]) { + NSNumber *frameCount = [image valueForKey:@"animatedImageFrameCount"]; + if (frameCount.intValue > 1) hasAnim = YES; + } + + CGFloat ascent = ASTextEmojiGetAscentWithFontSize(fontSize); + CGFloat descent = ASTextEmojiGetDescentWithFontSize(fontSize); + CGRect bounding = ASTextEmojiGetGlyphBoundingRectWithFontSize(fontSize); + + ASTextRunDelegate *delegate = [ASTextRunDelegate new]; + delegate.ascent = ascent; + delegate.descent = descent; + delegate.width = bounding.size.width + 2 * bounding.origin.x; + + ASTextAttachment *attachment = [ASTextAttachment new]; + attachment.contentMode = UIViewContentModeScaleAspectFit; + attachment.contentInsets = UIEdgeInsetsMake(ascent - (bounding.size.height + bounding.origin.y), bounding.origin.x, descent + bounding.origin.y, bounding.origin.x); + if (hasAnim) { + Class imageClass = NSClassFromString(@"ASAnimatedImageView"); + if (!imageClass) imageClass = [UIImageView class]; + UIImageView *view = (id)[imageClass new]; + view.frame = bounding; + view.image = image; + view.contentMode = UIViewContentModeScaleAspectFit; + attachment.content = view; + } else { + attachment.content = image; + } + + NSMutableAttributedString *atr = [[NSMutableAttributedString alloc] initWithString:ASTextAttachmentToken]; + [atr as_setTextAttachment:attachment range:NSMakeRange(0, atr.length)]; + CTRunDelegateRef ctDelegate = delegate.CTRunDelegate; + [atr as_setRunDelegate:ctDelegate range:NSMakeRange(0, atr.length)]; + if (ctDelegate) CFRelease(ctDelegate); + + return atr; +} + +- (NSRange)as_rangeOfAll { + return NSMakeRange(0, self.length); +} + +- (BOOL)as_isSharedAttributesInAllRange { + __block BOOL shared = YES; + __block NSDictionary *firstAttrs = nil; + [self enumerateAttributesInRange:self.as_rangeOfAll options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop) { + if (range.location == 0) { + firstAttrs = attrs; + } else { + if (firstAttrs.count != attrs.count) { + shared = NO; + *stop = YES; + } else if (firstAttrs) { + if (![firstAttrs isEqualToDictionary:attrs]) { + shared = NO; + *stop = YES; + } + } + } + }]; + return shared; +} + +- (BOOL)as_canDrawWithUIKit { + static NSMutableSet *failSet; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + failSet = [NSMutableSet new]; + [failSet addObject:(id)kCTGlyphInfoAttributeName]; + [failSet addObject:(id)kCTCharacterShapeAttributeName]; + [failSet addObject:(id)kCTLanguageAttributeName]; + [failSet addObject:(id)kCTRunDelegateAttributeName]; + [failSet addObject:(id)kCTBaselineClassAttributeName]; + [failSet addObject:(id)kCTBaselineInfoAttributeName]; + [failSet addObject:(id)kCTBaselineReferenceInfoAttributeName]; + [failSet addObject:(id)kCTRubyAnnotationAttributeName]; + [failSet addObject:ASTextShadowAttributeName]; + [failSet addObject:ASTextInnerShadowAttributeName]; + [failSet addObject:ASTextUnderlineAttributeName]; + [failSet addObject:ASTextStrikethroughAttributeName]; + [failSet addObject:ASTextBorderAttributeName]; + [failSet addObject:ASTextBackgroundBorderAttributeName]; + [failSet addObject:ASTextBlockBorderAttributeName]; + [failSet addObject:ASTextAttachmentAttributeName]; + [failSet addObject:ASTextHighlightAttributeName]; + [failSet addObject:ASTextGlyphTransformAttributeName]; + }); + +#define Fail { result = NO; *stop = YES; return; } + __block BOOL result = YES; + [self enumerateAttributesInRange:self.as_rangeOfAll options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop) { + if (attrs.count == 0) return; + for (NSString *str in attrs.allKeys) { + if ([failSet containsObject:str]) Fail; + } + if (attrs[(id)kCTForegroundColorAttributeName] && !attrs[NSForegroundColorAttributeName]) Fail; + if (attrs[(id)kCTStrokeColorAttributeName] && !attrs[NSStrokeColorAttributeName]) Fail; + if (attrs[(id)kCTUnderlineColorAttributeName]) { + if (!attrs[NSUnderlineColorAttributeName]) Fail; + } + NSParagraphStyle *style = attrs[NSParagraphStyleAttributeName]; + if (style && CFGetTypeID((__bridge CFTypeRef)(style)) == CTParagraphStyleGetTypeID()) Fail; + }]; + return result; +#undef Fail +} + +@end + +@implementation NSMutableAttributedString (ASText) + +- (void)as_setAttributes:(NSDictionary *)attributes { + [self setAs_attributes:attributes]; +} + +- (void)setAs_attributes:(NSDictionary *)attributes { + if (attributes == (id)[NSNull null]) attributes = nil; + [self setAttributes:@{} range:NSMakeRange(0, self.length)]; + [attributes enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + [self as_setAttribute:key value:obj]; + }]; +} + +- (void)as_setAttribute:(NSString *)name value:(id)value { + [self as_setAttribute:name value:value range:NSMakeRange(0, self.length)]; +} + +- (void)as_setAttribute:(NSString *)name value:(id)value range:(NSRange)range { + if (!name || [NSNull isEqual:name]) return; + if (value && ![NSNull isEqual:value]) [self addAttribute:name value:value range:range]; + else [self removeAttribute:name range:range]; +} + +- (void)as_removeAttributesInRange:(NSRange)range { + [self setAttributes:nil range:range]; +} + +#pragma mark - Property Setter + +- (void)setAs_font:(UIFont *)font { + /* + In iOS7 and later, UIFont is toll-free bridged to CTFontRef, + although Apple does not mention it in documentation. + + In iOS6, UIFont is a wrapper for CTFontRef, so CoreText can alse use UIfont, + but UILabel/UITextView cannot use CTFontRef. + + We use UIFont for both CoreText and UIKit. + */ + [self as_setFont:font range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_kern:(NSNumber *)kern { + [self as_setKern:kern range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_color:(UIColor *)color { + [self as_setColor:color range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_backgroundColor:(UIColor *)backgroundColor { + [self as_setBackgroundColor:backgroundColor range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_strokeWidth:(NSNumber *)strokeWidth { + [self as_setStrokeWidth:strokeWidth range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_strokeColor:(UIColor *)strokeColor { + [self as_setStrokeColor:strokeColor range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_shadow:(NSShadow *)shadow { + [self as_setShadow:shadow range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_strikethroughStyle:(NSUnderlineStyle)strikethroughStyle { + [self as_setStrikethroughStyle:strikethroughStyle range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_strikethroughColor:(UIColor *)strikethroughColor { + [self as_setStrikethroughColor:strikethroughColor range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_underlineStyle:(NSUnderlineStyle)underlineStyle { + [self as_setUnderlineStyle:underlineStyle range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_underlineColor:(UIColor *)underlineColor { + [self as_setUnderlineColor:underlineColor range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_ligature:(NSNumber *)ligature { + [self as_setLigature:ligature range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_textEffect:(NSString *)textEffect { + [self as_setTextEffect:textEffect range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_obliqueness:(NSNumber *)obliqueness { + [self as_setObliqueness:obliqueness range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_expansion:(NSNumber *)expansion { + [self as_setExpansion:expansion range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_baselineOffset:(NSNumber *)baselineOffset { + [self as_setBaselineOffset:baselineOffset range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_verticalGlyphForm:(BOOL)verticalGlyphForm { + [self as_setVerticalGlyphForm:verticalGlyphForm range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_language:(NSString *)language { + [self as_setLanguage:language range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_writingDirection:(NSArray *)writingDirection { + [self as_setWritingDirection:writingDirection range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_paragraphStyle:(NSParagraphStyle *)paragraphStyle { + /* + NSParagraphStyle is NOT toll-free bridged to CTParagraphStyleRef. + + CoreText can use both NSParagraphStyle and CTParagraphStyleRef, + but UILabel/UITextView can only use NSParagraphStyle. + + We use NSParagraphStyle in both CoreText and UIKit. + */ + [self as_setParagraphStyle:paragraphStyle range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_alignment:(NSTextAlignment)alignment { + [self as_setAlignment:alignment range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_baseWritingDirection:(NSWritingDirection)baseWritingDirection { + [self as_setBaseWritingDirection:baseWritingDirection range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_lineSpacing:(CGFloat)lineSpacing { + [self as_setLineSpacing:lineSpacing range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_paragraphSpacing:(CGFloat)paragraphSpacing { + [self as_setParagraphSpacing:paragraphSpacing range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_paragraphSpacingBefore:(CGFloat)paragraphSpacingBefore { + [self as_setParagraphSpacing:paragraphSpacingBefore range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_firstLineHeadIndent:(CGFloat)firstLineHeadIndent { + [self as_setFirstLineHeadIndent:firstLineHeadIndent range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_headIndent:(CGFloat)headIndent { + [self as_setHeadIndent:headIndent range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_tailIndent:(CGFloat)tailIndent { + [self as_setTailIndent:tailIndent range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_lineBreakMode:(NSLineBreakMode)lineBreakMode { + [self as_setLineBreakMode:lineBreakMode range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_minimumLineHeight:(CGFloat)minimumLineHeight { + [self as_setMinimumLineHeight:minimumLineHeight range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_maximumLineHeight:(CGFloat)maximumLineHeight { + [self as_setMaximumLineHeight:maximumLineHeight range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_lineHeightMultiple:(CGFloat)lineHeightMultiple { + [self as_setLineHeightMultiple:lineHeightMultiple range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_hyphenationFactor:(float)hyphenationFactor { + [self as_setHyphenationFactor:hyphenationFactor range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_defaultTabInterval:(CGFloat)defaultTabInterval { + [self as_setDefaultTabInterval:defaultTabInterval range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_tabStops:(NSArray *)tabStops { + [self as_setTabStops:tabStops range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_textShadow:(ASTextShadow *)textShadow { + [self as_setTextShadow:textShadow range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_textInnerShadow:(ASTextShadow *)textInnerShadow { + [self as_setTextInnerShadow:textInnerShadow range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_textUnderline:(ASTextDecoration *)textUnderline { + [self as_setTextUnderline:textUnderline range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_textStrikethrough:(ASTextDecoration *)textStrikethrough { + [self as_setTextStrikethrough:textStrikethrough range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_textBorder:(ASTextBorder *)textBorder { + [self as_setTextBorder:textBorder range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_textBackgroundBorder:(ASTextBorder *)textBackgroundBorder { + [self as_setTextBackgroundBorder:textBackgroundBorder range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_textGlyphTransform:(CGAffineTransform)textGlyphTransform { + [self as_setTextGlyphTransform:textGlyphTransform range:NSMakeRange(0, self.length)]; +} + +#pragma mark - Range Setter + +- (void)as_setFont:(UIFont *)font range:(NSRange)range { + [self as_setAttribute:NSFontAttributeName value:font range:range]; +} + +- (void)as_setKern:(NSNumber *)kern range:(NSRange)range { + [self as_setAttribute:NSKernAttributeName value:kern range:range]; +} + +- (void)as_setColor:(UIColor *)color range:(NSRange)range { + [self as_setAttribute:(id)kCTForegroundColorAttributeName value:(id)color.CGColor range:range]; + [self as_setAttribute:NSForegroundColorAttributeName value:color range:range]; +} + +- (void)as_setBackgroundColor:(UIColor *)backgroundColor range:(NSRange)range { + [self as_setAttribute:NSBackgroundColorAttributeName value:backgroundColor range:range]; +} + +- (void)as_setStrokeWidth:(NSNumber *)strokeWidth range:(NSRange)range { + [self as_setAttribute:NSStrokeWidthAttributeName value:strokeWidth range:range]; +} + +- (void)as_setStrokeColor:(UIColor *)strokeColor range:(NSRange)range { + [self as_setAttribute:(id)kCTStrokeColorAttributeName value:(id)strokeColor.CGColor range:range]; + [self as_setAttribute:NSStrokeColorAttributeName value:strokeColor range:range]; +} + +- (void)as_setShadow:(NSShadow *)shadow range:(NSRange)range { + [self as_setAttribute:NSShadowAttributeName value:shadow range:range]; +} + +- (void)as_setStrikethroughStyle:(NSUnderlineStyle)strikethroughStyle range:(NSRange)range { + NSNumber *style = strikethroughStyle == 0 ? nil : @(strikethroughStyle); + [self as_setAttribute:NSStrikethroughStyleAttributeName value:style range:range]; +} + +- (void)as_setStrikethroughColor:(UIColor *)strikethroughColor range:(NSRange)range { + [self as_setAttribute:NSStrikethroughColorAttributeName value:strikethroughColor range:range]; +} + +- (void)as_setUnderlineStyle:(NSUnderlineStyle)underlineStyle range:(NSRange)range { + NSNumber *style = underlineStyle == 0 ? nil : @(underlineStyle); + [self as_setAttribute:NSUnderlineStyleAttributeName value:style range:range]; +} + +- (void)as_setUnderlineColor:(UIColor *)underlineColor range:(NSRange)range { + [self as_setAttribute:(id)kCTUnderlineColorAttributeName value:(id)underlineColor.CGColor range:range]; + [self as_setAttribute:NSUnderlineColorAttributeName value:underlineColor range:range]; +} + +- (void)as_setLigature:(NSNumber *)ligature range:(NSRange)range { + [self as_setAttribute:NSLigatureAttributeName value:ligature range:range]; +} + +- (void)as_setTextEffect:(NSString *)textEffect range:(NSRange)range { + [self as_setAttribute:NSTextEffectAttributeName value:textEffect range:range]; +} + +- (void)as_setObliqueness:(NSNumber *)obliqueness range:(NSRange)range { + [self as_setAttribute:NSObliquenessAttributeName value:obliqueness range:range]; +} + +- (void)as_setExpansion:(NSNumber *)expansion range:(NSRange)range { + [self as_setAttribute:NSExpansionAttributeName value:expansion range:range]; +} + +- (void)as_setBaselineOffset:(NSNumber *)baselineOffset range:(NSRange)range { + [self as_setAttribute:NSBaselineOffsetAttributeName value:baselineOffset range:range]; +} + +- (void)as_setVerticalGlyphForm:(BOOL)verticalGlyphForm range:(NSRange)range { + NSNumber *v = verticalGlyphForm ? @(YES) : nil; + [self as_setAttribute:NSVerticalGlyphFormAttributeName value:v range:range]; +} + +- (void)as_setLanguage:(NSString *)language range:(NSRange)range { + [self as_setAttribute:(id)kCTLanguageAttributeName value:language range:range]; +} + +- (void)as_setWritingDirection:(NSArray *)writingDirection range:(NSRange)range { + [self as_setAttribute:(id)kCTWritingDirectionAttributeName value:writingDirection range:range]; +} + +- (void)as_setParagraphStyle:(NSParagraphStyle *)paragraphStyle range:(NSRange)range { + /* + NSParagraphStyle is NOT toll-free bridged to CTParagraphStyleRef. + + CoreText can use both NSParagraphStyle and CTParagraphStyleRef, + but UILabel/UITextView can only use NSParagraphStyle. + + We use NSParagraphStyle in both CoreText and UIKit. + */ + [self as_setAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:range]; +} + +#define ParagraphStyleSet(_attr_) \ +[self enumerateAttribute:NSParagraphStyleAttributeName \ +inRange:range \ +options:kNilOptions \ +usingBlock: ^(NSParagraphStyle *value, NSRange subRange, BOOL *stop) { \ +NSMutableParagraphStyle *style = nil; \ +if (value) { \ +if (CFGetTypeID((__bridge CFTypeRef)(value)) == CTParagraphStyleGetTypeID()) { \ +value = [NSParagraphStyle as_styleWithCTStyle:(__bridge CTParagraphStyleRef)(value)]; \ +} \ +if (value. _attr_ == _attr_) return; \ +if ([value isKindOfClass:[NSMutableParagraphStyle class]]) { \ +style = (id)value; \ +} else { \ +style = value.mutableCopy; \ +} \ +} else { \ +if ([NSParagraphStyle defaultParagraphStyle]. _attr_ == _attr_) return; \ +style = [NSParagraphStyle defaultParagraphStyle].mutableCopy; \ +} \ +style. _attr_ = _attr_; \ +[self as_setParagraphStyle:style range:subRange]; \ +}]; + +- (void)as_setAlignment:(NSTextAlignment)alignment range:(NSRange)range { + ParagraphStyleSet(alignment); +} + +- (void)as_setBaseWritingDirection:(NSWritingDirection)baseWritingDirection range:(NSRange)range { + ParagraphStyleSet(baseWritingDirection); +} + +- (void)as_setLineSpacing:(CGFloat)lineSpacing range:(NSRange)range { + ParagraphStyleSet(lineSpacing); +} + +- (void)as_setParagraphSpacing:(CGFloat)paragraphSpacing range:(NSRange)range { + ParagraphStyleSet(paragraphSpacing); +} + +- (void)as_setParagraphSpacingBefore:(CGFloat)paragraphSpacingBefore range:(NSRange)range { + ParagraphStyleSet(paragraphSpacingBefore); +} + +- (void)as_setFirstLineHeadIndent:(CGFloat)firstLineHeadIndent range:(NSRange)range { + ParagraphStyleSet(firstLineHeadIndent); +} + +- (void)as_setHeadIndent:(CGFloat)headIndent range:(NSRange)range { + ParagraphStyleSet(headIndent); +} + +- (void)as_setTailIndent:(CGFloat)tailIndent range:(NSRange)range { + ParagraphStyleSet(tailIndent); +} + +- (void)as_setLineBreakMode:(NSLineBreakMode)lineBreakMode range:(NSRange)range { + ParagraphStyleSet(lineBreakMode); +} + +- (void)as_setMinimumLineHeight:(CGFloat)minimumLineHeight range:(NSRange)range { + ParagraphStyleSet(minimumLineHeight); +} + +- (void)as_setMaximumLineHeight:(CGFloat)maximumLineHeight range:(NSRange)range { + ParagraphStyleSet(maximumLineHeight); +} + +- (void)as_setLineHeightMultiple:(CGFloat)lineHeightMultiple range:(NSRange)range { + ParagraphStyleSet(lineHeightMultiple); +} + +- (void)as_setHyphenationFactor:(float)hyphenationFactor range:(NSRange)range { + ParagraphStyleSet(hyphenationFactor); +} + +- (void)as_setDefaultTabInterval:(CGFloat)defaultTabInterval range:(NSRange)range { + ParagraphStyleSet(defaultTabInterval); +} + +- (void)as_setTabStops:(NSArray *)tabStops range:(NSRange)range { + ParagraphStyleSet(tabStops); +} + +#undef ParagraphStyleSet + +- (void)as_setSuperscript:(NSNumber *)superscript range:(NSRange)range { + if ([superscript isEqualToNumber:@(0)]) { + superscript = nil; + } + [self as_setAttribute:(id)kCTSuperscriptAttributeName value:superscript range:range]; +} + +- (void)as_setGlyphInfo:(CTGlyphInfoRef)glyphInfo range:(NSRange)range { + [self as_setAttribute:(id)kCTGlyphInfoAttributeName value:(__bridge id)glyphInfo range:range]; +} + +- (void)as_setCharacterShape:(NSNumber *)characterShape range:(NSRange)range { + [self as_setAttribute:(id)kCTCharacterShapeAttributeName value:characterShape range:range]; +} + +- (void)as_setRunDelegate:(CTRunDelegateRef)runDelegate range:(NSRange)range { + [self as_setAttribute:(id)kCTRunDelegateAttributeName value:(__bridge id)runDelegate range:range]; +} + +- (void)as_setBaselineClass:(CFStringRef)baselineClass range:(NSRange)range { + [self as_setAttribute:(id)kCTBaselineClassAttributeName value:(__bridge id)baselineClass range:range]; +} + +- (void)as_setBaselineInfo:(CFDictionaryRef)baselineInfo range:(NSRange)range { + [self as_setAttribute:(id)kCTBaselineInfoAttributeName value:(__bridge id)baselineInfo range:range]; +} + +- (void)as_setBaselineReferenceInfo:(CFDictionaryRef)referenceInfo range:(NSRange)range { + [self as_setAttribute:(id)kCTBaselineReferenceInfoAttributeName value:(__bridge id)referenceInfo range:range]; +} + +- (void)as_setRubyAnnotation:(CTRubyAnnotationRef)ruby range:(NSRange)range { + [self as_setAttribute:(id)kCTRubyAnnotationAttributeName value:(__bridge id)ruby range:range]; +} + +- (void)as_setAttachment:(NSTextAttachment *)attachment range:(NSRange)range { + [self as_setAttribute:NSAttachmentAttributeName value:attachment range:range]; +} + +- (void)as_setLink:(id)link range:(NSRange)range { + [self as_setAttribute:NSLinkAttributeName value:link range:range]; +} + +- (void)as_setTextBackedString:(ASTextBackedString *)textBackedString range:(NSRange)range { + [self as_setAttribute:ASTextBackedStringAttributeName value:textBackedString range:range]; +} + +- (void)as_setTextBinding:(ASTextBinding *)textBinding range:(NSRange)range { + [self as_setAttribute:ASTextBindingAttributeName value:textBinding range:range]; +} + +- (void)as_setTextShadow:(ASTextShadow *)textShadow range:(NSRange)range { + [self as_setAttribute:ASTextShadowAttributeName value:textShadow range:range]; +} + +- (void)as_setTextInnerShadow:(ASTextShadow *)textInnerShadow range:(NSRange)range { + [self as_setAttribute:ASTextInnerShadowAttributeName value:textInnerShadow range:range]; +} + +- (void)as_setTextUnderline:(ASTextDecoration *)textUnderline range:(NSRange)range { + [self as_setAttribute:ASTextUnderlineAttributeName value:textUnderline range:range]; +} + +- (void)as_setTextStrikethrough:(ASTextDecoration *)textStrikethrough range:(NSRange)range { + [self as_setAttribute:ASTextStrikethroughAttributeName value:textStrikethrough range:range]; +} + +- (void)as_setTextBorder:(ASTextBorder *)textBorder range:(NSRange)range { + [self as_setAttribute:ASTextBorderAttributeName value:textBorder range:range]; +} + +- (void)as_setTextBackgroundBorder:(ASTextBorder *)textBackgroundBorder range:(NSRange)range { + [self as_setAttribute:ASTextBackgroundBorderAttributeName value:textBackgroundBorder range:range]; +} + +- (void)as_setTextAttachment:(ASTextAttachment *)textAttachment range:(NSRange)range { + [self as_setAttribute:ASTextAttachmentAttributeName value:textAttachment range:range]; +} + +- (void)as_setTextHighlight:(ASTextHighlight *)textHighlight range:(NSRange)range { + [self as_setAttribute:ASTextHighlightAttributeName value:textHighlight range:range]; +} + +- (void)as_setTextBlockBorder:(ASTextBorder *)textBlockBorder range:(NSRange)range { + [self as_setAttribute:ASTextBlockBorderAttributeName value:textBlockBorder range:range]; +} + +- (void)as_setTextGlyphTransform:(CGAffineTransform)textGlyphTransform range:(NSRange)range { + NSValue *value = CGAffineTransformIsIdentity(textGlyphTransform) ? nil : [NSValue valueWithCGAffineTransform:textGlyphTransform]; + [self as_setAttribute:ASTextGlyphTransformAttributeName value:value range:range]; +} + +- (void)as_setTextHighlightRange:(NSRange)range + color:(UIColor *)color + backgroundColor:(UIColor *)backgroundColor + userInfo:(NSDictionary *)userInfo + tapAction:(ASTextAction)tapAction + longPressAction:(ASTextAction)longPressAction { + ASTextHighlight *highlight = [ASTextHighlight highlightWithBackgroundColor:backgroundColor]; + highlight.userInfo = userInfo; + highlight.tapAction = tapAction; + highlight.longPressAction = longPressAction; + if (color) [self as_setColor:color range:range]; + [self as_setTextHighlight:highlight range:range]; +} + +- (void)as_setTextHighlightRange:(NSRange)range + color:(UIColor *)color + backgroundColor:(UIColor *)backgroundColor + tapAction:(ASTextAction)tapAction { + [self as_setTextHighlightRange:range + color:color + backgroundColor:backgroundColor + userInfo:nil + tapAction:tapAction + longPressAction:nil]; +} + +- (void)as_setTextHighlightRange:(NSRange)range + color:(UIColor *)color + backgroundColor:(UIColor *)backgroundColor + userInfo:(NSDictionary *)userInfo { + [self as_setTextHighlightRange:range + color:color + backgroundColor:backgroundColor + userInfo:userInfo + tapAction:nil + longPressAction:nil]; +} + +- (void)as_insertString:(NSString *)string atIndex:(NSUInteger)location { + [self replaceCharactersInRange:NSMakeRange(location, 0) withString:string]; + [self as_removeDiscontinuousAttributesInRange:NSMakeRange(location, string.length)]; +} + +- (void)as_appendString:(NSString *)string { + NSUInteger length = self.length; + [self replaceCharactersInRange:NSMakeRange(length, 0) withString:string]; + [self as_removeDiscontinuousAttributesInRange:NSMakeRange(length, string.length)]; +} + +- (void)as_setClearColorToJoinedEmoji { + NSString *str = self.string; + if (str.length < 8) return; + + // Most string do not contains the joined-emoji, test the joiner first. + BOOL containsJoiner = NO; + { + CFStringRef cfStr = (__bridge CFStringRef)str; + BOOL needFree = NO; + UniChar *chars = NULL; + chars = (UniChar *)CFStringGetCharactersPtr(cfStr); + if (!chars) { + chars = (UniChar *)malloc(str.length * sizeof(UniChar)); + if (chars) { + needFree = YES; + CFStringGetCharacters(cfStr, CFRangeMake(0, str.length), chars); + } + } + if (!chars) { // fail to get unichar.. + containsJoiner = YES; + } else { + for (int i = 0, max = (int)str.length; i < max; i++) { + if (chars[i] == 0x200D) { // 'ZERO WIDTH JOINER' (U+200D) + containsJoiner = YES; + break; + } + } + if (needFree) free(chars); + } + } + if (!containsJoiner) return; + + // NSRegularExpression is designed to be immutable and thread safe. + static NSRegularExpression *regex; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + regex = [NSRegularExpression regularExpressionWithPattern:@"((👨‍👩‍👧‍👦|👨‍👩‍👦‍👦|👨‍👩‍👧‍👧|👩‍👩‍👧‍👦|👩‍👩‍👦‍👦|👩‍👩‍👧‍👧|👨‍👨‍👧‍👦|👨‍👨‍👦‍👦|👨‍👨‍👧‍👧)+|(👨‍👩‍👧|👩‍👩‍👦|👩‍👩‍👧|👨‍👨‍👦|👨‍👨‍👧))" options:kNilOptions error:nil]; + }); + + UIColor *clear = [UIColor clearColor]; + [regex enumerateMatchesInString:str options:kNilOptions range:NSMakeRange(0, str.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) { + [self as_setColor:clear range:result.range]; + }]; +} + +- (void)as_removeDiscontinuousAttributesInRange:(NSRange)range { + NSArray *keys = [NSMutableAttributedString as_allDiscontinuousAttributeKeys]; + for (NSString *key in keys) { + [self removeAttribute:key range:range]; + } +} + ++ (NSArray *)as_allDiscontinuousAttributeKeys { + static NSArray *keys; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + keys = @[(id)kCTSuperscriptAttributeName, + (id)kCTRunDelegateAttributeName, + ASTextBackedStringAttributeName, + ASTextBindingAttributeName, + ASTextAttachmentAttributeName, + (id)kCTRubyAnnotationAttributeName, + NSAttachmentAttributeName]; + }); + return keys; +} + +@end diff --git a/Source/Private/TextExperiment/Utility/NSParagraphStyle+ASText.h b/Source/Private/TextExperiment/Utility/NSParagraphStyle+ASText.h new file mode 100755 index 0000000000..b7c21f8121 --- /dev/null +++ b/Source/Private/TextExperiment/Utility/NSParagraphStyle+ASText.h @@ -0,0 +1,37 @@ +// +// NSParagraphStyle+ASText.h +// Modified from YYText +// +// Created by ibireme on 14/10/7. +// Copyright (c) 2015 ibireme. +// +// This source code is licensed under the MIT-style license found in the +// LICENSE file in the root directory of this source tree. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + Provides extensions for `NSParagraphStyle` to work with CoreText. + */ +@interface NSParagraphStyle (ASText) + +/** + Creates a new NSParagraphStyle object from the CoreText Style. + + @param CTStyle CoreText Paragraph Style. + + @return a new NSParagraphStyle + */ ++ (nullable NSParagraphStyle *)as_styleWithCTStyle:(CTParagraphStyleRef)CTStyle; + +/** + Creates and returns a CoreText Paragraph Style. (need call CFRelease() after used) + */ +- (nullable CTParagraphStyleRef)as_CTStyle CF_RETURNS_RETAINED; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Source/Private/TextExperiment/Utility/NSParagraphStyle+ASText.m b/Source/Private/TextExperiment/Utility/NSParagraphStyle+ASText.m new file mode 100755 index 0000000000..3401fcc829 --- /dev/null +++ b/Source/Private/TextExperiment/Utility/NSParagraphStyle+ASText.m @@ -0,0 +1,218 @@ +// +// NSParagraphStyle+ASText.m +// Modified from YYText +// +// Created by ibireme on 14/10/7. +// Copyright (c) 2015 ibireme. +// +// This source code is licensed under the MIT-style license found in the +// LICENSE file in the root directory of this source tree. +// + +#import +#import +#import + +// Dummy class for category +@interface NSParagraphStyle_ASText : NSObject @end +@implementation NSParagraphStyle_ASText @end + + +@implementation NSParagraphStyle (ASText) + ++ (NSParagraphStyle *)as_styleWithCTStyle:(CTParagraphStyleRef)CTStyle { + if (CTStyle == NULL) return nil; + + NSMutableParagraphStyle *style = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + CGFloat lineSpacing; + if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierLineSpacing, sizeof(CGFloat), &lineSpacing)) { + style.lineSpacing = lineSpacing; + } +#pragma clang diagnostic pop + + CGFloat paragraphSpacing; + if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierParagraphSpacing, sizeof(CGFloat), ¶graphSpacing)) { + style.paragraphSpacing = paragraphSpacing; + } + + CTTextAlignment alignment; + if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierAlignment, sizeof(CTTextAlignment), &alignment)) { + style.alignment = NSTextAlignmentFromCTTextAlignment(alignment); + } + + CGFloat firstLineHeadIndent; + if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierFirstLineHeadIndent, sizeof(CGFloat), &firstLineHeadIndent)) { + style.firstLineHeadIndent = firstLineHeadIndent; + } + + CGFloat headIndent; + if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierHeadIndent, sizeof(CGFloat), &headIndent)) { + style.headIndent = headIndent; + } + + CGFloat tailIndent; + if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierTailIndent, sizeof(CGFloat), &tailIndent)) { + style.tailIndent = tailIndent; + } + + CTLineBreakMode lineBreakMode; + if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierLineBreakMode, sizeof(CTLineBreakMode), &lineBreakMode)) { + style.lineBreakMode = (NSLineBreakMode)lineBreakMode; + } + + CGFloat minimumLineHeight; + if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierMinimumLineHeight, sizeof(CGFloat), &minimumLineHeight)) { + style.minimumLineHeight = minimumLineHeight; + } + + CGFloat maximumLineHeight; + if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierMaximumLineHeight, sizeof(CGFloat), &maximumLineHeight)) { + style.maximumLineHeight = maximumLineHeight; + } + + CTWritingDirection baseWritingDirection; + if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierBaseWritingDirection, sizeof(CTWritingDirection), &baseWritingDirection)) { + style.baseWritingDirection = (NSWritingDirection)baseWritingDirection; + } + + CGFloat lineHeightMultiple; + if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierLineHeightMultiple, sizeof(CGFloat), &lineHeightMultiple)) { + style.lineHeightMultiple = lineHeightMultiple; + } + + CGFloat paragraphSpacingBefore; + if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierParagraphSpacingBefore, sizeof(CGFloat), ¶graphSpacingBefore)) { + style.paragraphSpacingBefore = paragraphSpacingBefore; + } + + CFArrayRef tabStops; + if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierTabStops, sizeof(CFArrayRef), &tabStops)) { + NSMutableArray *tabs = [NSMutableArray new]; + [((__bridge NSArray *)(tabStops))enumerateObjectsUsingBlock : ^(id obj, NSUInteger idx, BOOL *stop) { + CTTextTabRef ctTab = (__bridge CTTextTabRef)obj; + + NSTextTab *tab = [[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentFromCTTextAlignment(CTTextTabGetAlignment(ctTab)) location:CTTextTabGetLocation(ctTab) options:(__bridge id)CTTextTabGetOptions(ctTab)]; + [tabs addObject:tab]; + }]; + if (tabs.count) { + style.tabStops = tabs; + } + } + + CGFloat defaultTabInterval; + if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierDefaultTabInterval, sizeof(CGFloat), &defaultTabInterval)) { + style.defaultTabInterval = defaultTabInterval; + } + + return style; +} + +- (CTParagraphStyleRef)as_CTStyle CF_RETURNS_RETAINED { + CTParagraphStyleSetting set[kCTParagraphStyleSpecifierCount] = { }; + int count = 0; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + CGFloat lineSpacing = self.lineSpacing; + set[count].spec = kCTParagraphStyleSpecifierLineSpacing; + set[count].valueSize = sizeof(CGFloat); + set[count].value = &lineSpacing; + count++; +#pragma clang diagnostic pop + + CGFloat paragraphSpacing = self.paragraphSpacing; + set[count].spec = kCTParagraphStyleSpecifierParagraphSpacing; + set[count].valueSize = sizeof(CGFloat); + set[count].value = ¶graphSpacing; + count++; + + CTTextAlignment alignment = NSTextAlignmentToCTTextAlignment(self.alignment); + set[count].spec = kCTParagraphStyleSpecifierAlignment; + set[count].valueSize = sizeof(CTTextAlignment); + set[count].value = &alignment; + count++; + + CGFloat firstLineHeadIndent = self.firstLineHeadIndent; + set[count].spec = kCTParagraphStyleSpecifierFirstLineHeadIndent; + set[count].valueSize = sizeof(CGFloat); + set[count].value = &firstLineHeadIndent; + count++; + + CGFloat headIndent = self.headIndent; + set[count].spec = kCTParagraphStyleSpecifierHeadIndent; + set[count].valueSize = sizeof(CGFloat); + set[count].value = &headIndent; + count++; + + CGFloat tailIndent = self.tailIndent; + set[count].spec = kCTParagraphStyleSpecifierTailIndent; + set[count].valueSize = sizeof(CGFloat); + set[count].value = &tailIndent; + count++; + + CTLineBreakMode paraLineBreak = (CTLineBreakMode)self.lineBreakMode; + set[count].spec = kCTParagraphStyleSpecifierLineBreakMode; + set[count].valueSize = sizeof(CTLineBreakMode); + set[count].value = ¶LineBreak; + count++; + + CGFloat minimumLineHeight = self.minimumLineHeight; + set[count].spec = kCTParagraphStyleSpecifierMinimumLineHeight; + set[count].valueSize = sizeof(CGFloat); + set[count].value = &minimumLineHeight; + count++; + + CGFloat maximumLineHeight = self.maximumLineHeight; + set[count].spec = kCTParagraphStyleSpecifierMaximumLineHeight; + set[count].valueSize = sizeof(CGFloat); + set[count].value = &maximumLineHeight; + count++; + + CTWritingDirection paraWritingDirection = (CTWritingDirection)self.baseWritingDirection; + set[count].spec = kCTParagraphStyleSpecifierBaseWritingDirection; + set[count].valueSize = sizeof(CTWritingDirection); + set[count].value = ¶WritingDirection; + count++; + + CGFloat lineHeightMultiple = self.lineHeightMultiple; + set[count].spec = kCTParagraphStyleSpecifierLineHeightMultiple; + set[count].valueSize = sizeof(CGFloat); + set[count].value = &lineHeightMultiple; + count++; + + CGFloat paragraphSpacingBefore = self.paragraphSpacingBefore; + set[count].spec = kCTParagraphStyleSpecifierParagraphSpacingBefore; + set[count].valueSize = sizeof(CGFloat); + set[count].value = ¶graphSpacingBefore; + count++; + + NSMutableArray *tabs = [NSMutableArray array]; + NSInteger numTabs = self.tabStops.count; + if (numTabs) { + [self.tabStops enumerateObjectsUsingBlock: ^(NSTextTab *tab, NSUInteger idx, BOOL *stop) { + CTTextTabRef ctTab = CTTextTabCreate(NSTextAlignmentToCTTextAlignment(tab.alignment), tab.location, (__bridge CFDictionaryRef)tab.options); + [tabs addObject:(__bridge id)ctTab]; + CFRelease(ctTab); + }]; + + CFArrayRef tabStops = (__bridge CFArrayRef)(tabs); + set[count].spec = kCTParagraphStyleSpecifierTabStops; + set[count].valueSize = sizeof(CFArrayRef); + set[count].value = &tabStops; + count++; + } + + CGFloat defaultTabInterval = self.defaultTabInterval; + set[count].spec = kCTParagraphStyleSpecifierDefaultTabInterval; + set[count].valueSize = sizeof(CGFloat); + set[count].value = &defaultTabInterval; + count++; + + CTParagraphStyleRef style = CTParagraphStyleCreate(set, count); + return style; +} + +@end diff --git a/Source/Private/_ASCollectionGalleryLayoutItem.h b/Source/Private/_ASCollectionGalleryLayoutItem.h new file mode 100644 index 0000000000..9cd25bf5f6 --- /dev/null +++ b/Source/Private/_ASCollectionGalleryLayoutItem.h @@ -0,0 +1,42 @@ +// +// _ASCollectionGalleryLayoutItem.h +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#ifndef MINIMAL_ASDK + +#import +#import +#import + +@class ASCollectionElement; + +NS_ASSUME_NONNULL_BEGIN + +/** + * A dummy item that represents a collection element to participate in the collection layout calculation process + * without triggering measurement on the actual node of the collection element. + * + * This item always has a fixed size that is the item size passed to it. + */ +AS_SUBCLASSING_RESTRICTED +@interface _ASGalleryLayoutItem : NSObject + +@property (nonatomic, assign, readonly) CGSize itemSize; +@property (nonatomic, weak, readonly) ASCollectionElement *collectionElement; + +- (instancetype)initWithItemSize:(CGSize)itemSize collectionElement:(ASCollectionElement *)collectionElement; +- (instancetype)init __unavailable; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/Source/Private/_ASCollectionGalleryLayoutItem.mm b/Source/Private/_ASCollectionGalleryLayoutItem.mm new file mode 100644 index 0000000000..8a5605593d --- /dev/null +++ b/Source/Private/_ASCollectionGalleryLayoutItem.mm @@ -0,0 +1,90 @@ +// +// _ASCollectionGalleryLayoutItem.mm +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#ifndef MINIMAL_ASDK + +#import + +#import +#import +#import +#import +#import + +@implementation _ASGalleryLayoutItem { + std::atomic _primitiveTraitCollection; +} + +@synthesize style; + +- (instancetype)initWithItemSize:(CGSize)itemSize collectionElement:(ASCollectionElement *)collectionElement +{ + self = [super init]; + if (self) { + ASDisplayNodeAssert(! CGSizeEqualToSize(CGSizeZero, itemSize), @"Item size should not be zero"); + ASDisplayNodeAssertNotNil(collectionElement, @"Collection element should not be nil"); + _itemSize = itemSize; + _collectionElement = collectionElement; + } + return self; +} + +ASLayoutElementStyleExtensibilityForwarding +ASPrimitiveTraitCollectionDefaults +ASPrimitiveTraitCollectionDeprecatedImplementation + +- (ASTraitCollection *)asyncTraitCollection +{ + ASDisplayNodeAssertNotSupported(); + return nil; +} + +- (ASLayoutElementType)layoutElementType +{ + return ASLayoutElementTypeLayoutSpec; +} + +- (NSArray> *)sublayoutElements +{ + ASDisplayNodeAssertNotSupported(); + return nil; +} + +- (BOOL)implementsLayoutMethod +{ + return YES; +} + +ASLayoutElementLayoutCalculationDefaults + +- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize +{ + ASDisplayNodeAssert(CGSizeEqualToSize(_itemSize, ASSizeRangeClamp(constrainedSize, _itemSize)), + @"Item size %@ can't fit within the bounds of constrained size %@", NSStringFromCGSize(_itemSize), NSStringFromASSizeRange(constrainedSize)); + return [ASLayout layoutWithLayoutElement:self size:_itemSize]; +} + +#pragma mark - ASLayoutElementAsciiArtProtocol + +- (NSString *)asciiArtString +{ + return [ASLayoutSpec asciiArtStringForChildren:@[] parentName:[self asciiArtName]]; +} + +- (NSString *)asciiArtName +{ + return [NSMutableString stringWithCString:object_getClassName(self) encoding:NSASCIIStringEncoding]; +} + +@end + +#endif diff --git a/Source/Private/_ASCoreAnimationExtras.h b/Source/Private/_ASCoreAnimationExtras.h index 7dcdf063b6..3272549173 100644 --- a/Source/Private/_ASCoreAnimationExtras.h +++ b/Source/Private/_ASCoreAnimationExtras.h @@ -1,20 +1,45 @@ // // _ASCoreAnimationExtras.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import #import +#import ASDISPLAYNODE_EXTERN_C_BEGIN -extern void ASDisplayNodeSetupLayerContentsWithResizableImage(CALayer *layer, UIImage *image); +// This protocol defines the core properties that ASDisplayNode and CALayer share, for managing contents. +@protocol ASResizableContents +@required +@property id contents; +@property CGRect contentsRect; +@property CGRect contentsCenter; +@property CGFloat contentsScale; +@property CGFloat rasterizationScale; +@property NSString *contentsGravity; +@end + +@interface CALayer (ASResizableContents) +@end +@interface ASDisplayNode (ASResizableContents) +@end + +// This function can operate on either an ASDisplayNode (including un-loaded) or CALayer directly. +extern void ASDisplayNodeSetResizableContents(id obj, UIImage *image); /** Turns a value of UIViewContentMode to a string for debugging or serialization @@ -60,4 +85,7 @@ extern UIImage *ASDisplayNodeStretchableBoxContentsWithColor(UIColor *color, CGS */ extern BOOL ASDisplayNodeLayerHasAnimations(CALayer *layer); +// This function is a less generalized version of ASDisplayNodeSetResizableContents. +extern void ASDisplayNodeSetupLayerContentsWithResizableImage(CALayer *layer, UIImage *image) ASDISPLAYNODE_DEPRECATED; + ASDISPLAYNODE_EXTERN_C_END diff --git a/Source/Private/_ASCoreAnimationExtras.mm b/Source/Private/_ASCoreAnimationExtras.mm index d953ba2ff2..3cd18c8e85 100644 --- a/Source/Private/_ASCoreAnimationExtras.mm +++ b/Source/Private/_ASCoreAnimationExtras.mm @@ -1,11 +1,18 @@ // // _ASCoreAnimationExtras.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -13,17 +20,21 @@ #import extern void ASDisplayNodeSetupLayerContentsWithResizableImage(CALayer *layer, UIImage *image) +{ + ASDisplayNodeSetResizableContents(layer, image); +} + +extern void ASDisplayNodeSetResizableContents(id obj, UIImage *image) { // FIXME: This method does not currently handle UIImageResizingModeTile, which is the default on iOS 6. // I'm not sure of a way to use CALayer directly to perform such tiling on the GPU, though the stretch is handled by the GPU, // and CALayer.h documents the fact that contentsCenter is used to stretch the pixels. if (image) { - // Image may not actually be stretchable in one or both dimensions; this is handled - layer.contents = (id)[image CGImage]; - layer.contentsScale = [image scale]; - layer.rasterizationScale = [image scale]; + obj.contents = (id)[image CGImage]; + obj.contentsScale = [image scale]; + obj.rasterizationScale = [image scale]; CGSize imageSize = [image size]; ASDisplayNodeCAssert(image.resizingMode == UIImageResizingModeStretch || UIEdgeInsetsEqualToEdgeInsets(image.capInsets, UIEdgeInsetsZero), @@ -44,11 +55,11 @@ extern void ASDisplayNodeSetupLayerContentsWithResizableImage(CALayer *layer, UI contentsCenter.origin.y = ((insets.top + halfPixelFudge) / imageSize.height); contentsCenter.size.height = (imageSize.height - (insets.top + insets.bottom + 1.f) + otherPixelFudge) / imageSize.height; } - layer.contentsGravity = kCAGravityResize; - layer.contentsCenter = contentsCenter; + obj.contentsGravity = kCAGravityResize; + obj.contentsCenter = contentsCenter; } else { - layer.contents = nil; + obj.contents = nil; } } diff --git a/Source/Private/_ASHierarchyChangeSet.h b/Source/Private/_ASHierarchyChangeSet.h index ef3bf17c38..7eb263d794 100644 --- a/Source/Private/_ASHierarchyChangeSet.h +++ b/Source/Private/_ASHierarchyChangeSet.h @@ -1,18 +1,25 @@ // // _ASHierarchyChangeSet.h -// AsyncDisplayKit -// -// Created by Adlai Holler on 9/29/15. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import #import #import +#import +#import NS_ASSUME_NONNULL_BEGIN @@ -85,7 +92,7 @@ NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType); @property (nonatomic, readonly) _ASHierarchyChangeType changeType; -+ (NSDictionary *)sectionToIndexSetMapFromChanges:(NSArray<_ASHierarchyItemChange *> *)changes; ++ (NSDictionary *)sectionToIndexSetMapFromChanges:(NSArray<_ASHierarchyItemChange *> *)changes; /** * If this is a .OriginalInsert or .OriginalDelete change, this returns a copied change @@ -114,6 +121,12 @@ NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType); /// Indicates whether the change set is empty, that is it includes neither reload data nor per item or section changes. @property (nonatomic, readonly) BOOL isEmpty; +/// The top-level activity for this update. +@property (nonatomic, OS_ACTIVITY_NULLABLE) os_activity_t rootActivity; + +/// The activity for submitting this update i.e. between -beginUpdates and -endUpdates. +@property (nonatomic, OS_ACTIVITY_NULLABLE) os_activity_t submitActivity; + - (instancetype)initWithOldData:(std::vector)oldItemCounts NS_DESIGNATED_INITIALIZER; /** @@ -142,6 +155,44 @@ NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType); */ - (NSUInteger)newSectionForOldSection:(NSUInteger)oldSection; +/** + * A table that maps old section indexes to new section indexes. + */ +@property (nonatomic, readonly, strong) ASIntegerMap *sectionMapping; + +/** + * A table that maps new section indexes to old section indexes. + */ +@property (nonatomic, readonly, strong) ASIntegerMap *reverseSectionMapping; + +/** + * A table that provides the item mapping for the old section. If the section was deleted + * or is out of bounds, returns the empty table. + */ +- (ASIntegerMap *)itemMappingInSection:(NSInteger)oldSection; + +/** + * A table that provides the reverse item mapping for the new section. If the section was inserted + * or is out of bounds, returns the empty table. + */ +- (ASIntegerMap *)reverseItemMappingInSection:(NSInteger)newSection; + +/** + * Get the old item index path for the given new index path. + * + * @precondition The change set must be completed. + * @return The old index path, or nil if the given item was inserted. + */ +- (nullable NSIndexPath *)oldIndexPathForNewIndexPath:(NSIndexPath *)indexPath; + +/** + * Get the new item index path for the given old index path. + * + * @precondition The change set must be completed. + * @return The new index path, or nil if the given item was deleted. + */ +- (nullable NSIndexPath *)newIndexPathForOldIndexPath:(NSIndexPath *)indexPath; + /// 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. - (void)markCompletedWithNewItemCounts:(std::vector)newItemCounts; diff --git a/Source/Private/_ASHierarchyChangeSet.mm b/Source/Private/_ASHierarchyChangeSet.mm index 2184e8aaac..d6e4324b0b 100644 --- a/Source/Private/_ASHierarchyChangeSet.mm +++ b/Source/Private/_ASHierarchyChangeSet.mm @@ -1,13 +1,18 @@ // -// _ASHierarchyChangeSet.m -// AsyncDisplayKit -// -// Created by Adlai Holler on 9/29/15. +// _ASHierarchyChangeSet.mm +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -94,7 +99,13 @@ NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType) + (void)ensureItemChanges:(NSArray<_ASHierarchyItemChange *> *)changes ofSameType:(_ASHierarchyChangeType)changeType; @end -@interface _ASHierarchyChangeSet () +@interface _ASHierarchyChangeSet () + +// array index is old section index, map goes oldItem -> newItem +@property (nonatomic, strong, readonly) NSMutableArray *itemMappings; + +// array index is new section index, map goes newItem -> oldItem +@property (nonatomic, strong, readonly) NSMutableArray *reverseItemMappings; @property (nonatomic, strong, readonly) NSMutableArray<_ASHierarchyItemChange *> *insertItemChanges; @property (nonatomic, strong, readonly) NSMutableArray<_ASHierarchyItemChange *> *originalInsertItemChanges; @@ -119,6 +130,10 @@ NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType) std::vector _newItemCounts; void (^_completionHandler)(BOOL finished); } +@synthesize sectionMapping = _sectionMapping; +@synthesize reverseSectionMapping = _reverseSectionMapping; +@synthesize itemMappings = _itemMappings; +@synthesize reverseItemMappings = _reverseItemMappings; - (instancetype)init { @@ -239,16 +254,121 @@ NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType) - (NSUInteger)newSectionForOldSection:(NSUInteger)oldSection { - ASDisplayNodeAssertNotNil(_deletedSections, @"Cannot call %@ before `markCompleted` returns.", NSStringFromSelector(_cmd)); - ASDisplayNodeAssertNotNil(_insertedSections, @"Cannot call %@ before `markCompleted` returns.", NSStringFromSelector(_cmd)); - [self _ensureCompleted]; - if ([_deletedSections containsIndex:oldSection]) { - return NSNotFound; - } + return [self.sectionMapping integerForKey:oldSection]; +} - NSUInteger newIndex = oldSection - [_deletedSections countOfIndexesInRange:NSMakeRange(0, oldSection)]; - newIndex += [_insertedSections as_indexChangeByInsertingItemsBelowIndex:newIndex]; - return newIndex; +- (NSUInteger)oldSectionForNewSection:(NSUInteger)newSection +{ + return [self.reverseSectionMapping integerForKey:newSection]; +} + +- (ASIntegerMap *)sectionMapping +{ + ASDisplayNodeAssertNotNil(_deletedSections, @"Cannot call %s before `markCompleted` returns.", sel_getName(_cmd)); + ASDisplayNodeAssertNotNil(_insertedSections, @"Cannot call %s before `markCompleted` returns.", sel_getName(_cmd)); + [self _ensureCompleted]; + if (_sectionMapping == nil) { + _sectionMapping = [ASIntegerMap mapForUpdateWithOldCount:_oldItemCounts.size() deleted:_deletedSections inserted:_insertedSections]; + } + return _sectionMapping; +} + +- (ASIntegerMap *)reverseSectionMapping +{ + if (_reverseSectionMapping == nil) { + _reverseSectionMapping = [self.sectionMapping inverseMap]; + } + return _reverseSectionMapping; +} + +- (NSMutableArray *)itemMappings +{ + [self _ensureCompleted]; + + if (_itemMappings == nil) { + _itemMappings = [NSMutableArray array]; + auto insertMap = [_ASHierarchyItemChange sectionToIndexSetMapFromChanges:_originalInsertItemChanges]; + auto deleteMap = [_ASHierarchyItemChange sectionToIndexSetMapFromChanges:_originalDeleteItemChanges]; + NSInteger oldSection = 0; + for (auto oldCount : _oldItemCounts) { + NSInteger newSection = [self newSectionForOldSection:oldSection]; + ASIntegerMap *table; + if (newSection == NSNotFound) { + table = ASIntegerMap.emptyMap; + } else { + table = [ASIntegerMap mapForUpdateWithOldCount:oldCount deleted:deleteMap[@(oldSection)] inserted:insertMap[@(newSection)]]; + } + _itemMappings[oldSection] = table; + oldSection++; + } + } + return _itemMappings; +} + +- (NSMutableArray *)reverseItemMappings +{ + [self _ensureCompleted]; + + if (_reverseItemMappings == nil) { + _reverseItemMappings = [NSMutableArray array]; + for (NSInteger newSection = 0; newSection < _newItemCounts.size(); newSection++) { + NSInteger oldSection = [self oldSectionForNewSection:newSection]; + ASIntegerMap *table; + if (oldSection == NSNotFound) { + table = ASIntegerMap.emptyMap; + } else { + table = [[self itemMappingInSection:oldSection] inverseMap]; + } + _reverseItemMappings[newSection] = table; + } + } + return _reverseItemMappings; +} + +- (ASIntegerMap *)itemMappingInSection:(NSInteger)oldSection +{ + if (self.includesReloadData || oldSection >= _oldItemCounts.size()) { + return ASIntegerMap.emptyMap; + } + return self.itemMappings[oldSection]; +} + +- (ASIntegerMap *)reverseItemMappingInSection:(NSInteger)newSection +{ + if (self.includesReloadData || newSection >= _newItemCounts.size()) { + return ASIntegerMap.emptyMap; + } + return self.reverseItemMappings[newSection]; +} + +- (NSIndexPath *)oldIndexPathForNewIndexPath:(NSIndexPath *)indexPath +{ + [self _ensureCompleted]; + NSInteger newSection = indexPath.section; + NSInteger oldSection = [self oldSectionForNewSection:newSection]; + if (oldSection == NSNotFound) { + return nil; + } + NSInteger oldItem = [[self reverseItemMappingInSection:newSection] integerForKey:indexPath.item]; + if (oldItem == NSNotFound) { + return nil; + } + return [NSIndexPath indexPathForItem:oldItem inSection:oldSection]; +} + +- (NSIndexPath *)newIndexPathForOldIndexPath:(NSIndexPath *)indexPath +{ + [self _ensureCompleted]; + NSInteger oldSection = indexPath.section; + NSInteger newSection = [self newSectionForOldSection:oldSection]; + if (newSection == NSNotFound) { + return nil; + } + NSInteger newItem = [[self itemMappingInSection:oldSection] integerForKey:indexPath.item]; + if (newItem == NSNotFound) { + return nil; + } + return [NSIndexPath indexPathForItem:newItem inSection:newSection]; } - (void)reloadData @@ -380,34 +500,12 @@ NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType) } [_ASHierarchyItemChange ensureItemChanges:_insertItemChanges ofSameType:_ASHierarchyChangeTypeInsert]; - NSDictionary *insertedIndexPathsMap = [_ASHierarchyItemChange sectionToIndexSetMapFromChanges:_insertItemChanges]; - [_ASHierarchyItemChange ensureItemChanges:_deleteItemChanges ofSameType:_ASHierarchyChangeTypeDelete]; - NSDictionary *deletedIndexPathsMap = [_ASHierarchyItemChange sectionToIndexSetMapFromChanges:_deleteItemChanges]; for (_ASHierarchyItemChange *change in _reloadItemChanges) { NSAssert(change.changeType == _ASHierarchyChangeTypeReload, @"It must be a reload change to be in here"); - NSMutableArray *newIndexPaths = [NSMutableArray arrayWithCapacity:change.indexPaths.count]; - - // Every indexPaths in the change need to update its section and/or row - // depending on all the deletions and insertions - // For reference, when batching reloads/deletes/inserts: - // - delete/reload indexPaths that are passed in should all be their current indexPaths - // - insert indexPaths that are passed in should all be their future indexPaths after deletions - for (NSIndexPath *indexPath in change.indexPaths) { - NSUInteger section = [self newSectionForOldSection:indexPath.section]; - NSUInteger item = indexPath.item; - - // Update row number based on deletions that are above the current row in the current section - NSIndexSet *indicesDeletedInSection = deletedIndexPathsMap[@(indexPath.section)]; - item -= [indicesDeletedInSection countOfIndexesInRange:NSMakeRange(0, item)]; - // Update row number based on insertions that are above the current row in the future section - NSIndexSet *indicesInsertedInSection = insertedIndexPathsMap[@(section)]; - item += [indicesInsertedInSection as_indexChangeByInsertingItemsBelowIndex:item]; - - NSIndexPath *newIndexPath = [NSIndexPath indexPathForItem:item inSection:section]; - [newIndexPaths addObject:newIndexPath]; - } + + auto newIndexPaths = ASArrayByFlatMapping(change.indexPaths, NSIndexPath *indexPath, [self newIndexPathForOldIndexPath:indexPath]); // All reload changes are translated into deletes and inserts // We delete the items that needs reload together with other deleted items, at their original index @@ -557,7 +655,7 @@ NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType) - (NSString *)description { - return ASObjectDescriptionMake(self, [self propertiesForDescription]); + return ASObjectDescriptionMakeWithoutObject([self propertiesForDescription]); } - (NSString *)debugDescription @@ -568,7 +666,9 @@ NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType) - (NSMutableArray *)propertiesForDescription { NSMutableArray *result = [NSMutableArray array]; - [result addObject:@{ @"includesReloadData" : @(_includesReloadData) }]; + if (_includesReloadData) { + [result addObject:@{ @"reloadData" : @"YES" }]; + } if (_reloadSectionChanges.count > 0) { [result addObject:@{ @"reloadSections" : [_ASHierarchySectionChange smallDescriptionForSectionChanges:_reloadSectionChanges] }]; } diff --git a/Source/Private/_ASPendingState.h b/Source/Private/_ASPendingState.h index 4cdcfb61f5..246e892a7e 100644 --- a/Source/Private/_ASPendingState.h +++ b/Source/Private/_ASPendingState.h @@ -1,11 +1,18 @@ // // _ASPendingState.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/Private/_ASPendingState.mm b/Source/Private/_ASPendingState.mm index 6f0a5426f2..411a1f9f8d 100644 --- a/Source/Private/_ASPendingState.mm +++ b/Source/Private/_ASPendingState.mm @@ -1,11 +1,18 @@ // // _ASPendingState.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -24,7 +31,8 @@ typedef struct { // Properties int needsDisplay:1; int needsLayout:1; - + int layoutIfNeeded:1; + // Flags indicating that a given property should be applied to the view at creation int setClipsToBounds:1; int setOpaque:1; @@ -35,7 +43,6 @@ typedef struct { int setBounds:1; int setBackgroundColor:1; int setTintColor:1; - int setContents:1; int setHidden:1; int setAlpha:1; int setCornerRadius:1; @@ -44,9 +51,14 @@ typedef struct { int setAnchorPoint:1; int setPosition:1; int setZPosition:1; - int setContentsScale:1; int setTransform:1; int setSublayerTransform:1; + int setContents:1; + int setContentsGravity:1; + int setContentsRect:1; + int setContentsCenter:1; + int setContentsScale:1; + int setRasterizationScale:1; int setUserInteractionEnabled:1; int setExclusiveTouch:1; int setShadowColor:1; @@ -74,6 +86,7 @@ typedef struct { int setAccessibilityHeaderElements:1; int setAccessibilityActivationPoint:1; int setAccessibilityPath:1; + int setSemanticContentAttribute:1; } ASPendingStateFlags; @implementation _ASPendingState @@ -85,16 +98,20 @@ typedef struct { CGRect frame; // Frame is only to be used for synchronous views wrapped by nodes (see setFrame:) CGRect bounds; CGColorRef backgroundColor; - id contents; CGFloat alpha; CGFloat cornerRadius; UIViewContentMode contentMode; CGPoint anchorPoint; CGPoint position; CGFloat zPosition; - CGFloat contentsScale; CATransform3D transform; CATransform3D sublayerTransform; + id contents; + NSString *contentsGravity; + CGRect contentsRect; + CGRect contentsCenter; + CGFloat contentsScale; + CGFloat rasterizationScale; CGColorRef shadowColor; CGFloat shadowOpacity; CGSize shadowOffset; @@ -117,6 +134,7 @@ typedef struct { NSArray *accessibilityHeaderElements; CGPoint accessibilityActivationPoint; UIBezierPath *accessibilityPath; + UISemanticContentAttribute semanticContentAttribute; ASPendingStateFlags _flags; } @@ -151,7 +169,6 @@ ASDISPLAYNODE_INLINE void ASPendingStateApplyMetricsToLayer(_ASPendingState *sta @synthesize frame=frame; @synthesize bounds=bounds; @synthesize backgroundColor=backgroundColor; -@synthesize contents=contents; @synthesize hidden=isHidden; @synthesize needsDisplayOnBoundsChange=needsDisplayOnBoundsChange; @synthesize allowsGroupOpacity=allowsGroupOpacity; @@ -166,9 +183,14 @@ ASDISPLAYNODE_INLINE void ASPendingStateApplyMetricsToLayer(_ASPendingState *sta @synthesize anchorPoint=anchorPoint; @synthesize position=position; @synthesize zPosition=zPosition; -@synthesize contentsScale=contentsScale; @synthesize transform=transform; @synthesize sublayerTransform=sublayerTransform; +@synthesize contents=contents; +@synthesize contentsGravity=contentsGravity; +@synthesize contentsRect=contentsRect; +@synthesize contentsCenter=contentsCenter; +@synthesize contentsScale=contentsScale; +@synthesize rasterizationScale=rasterizationScale; @synthesize userInteractionEnabled=userInteractionEnabled; @synthesize exclusiveTouch=exclusiveTouch; @synthesize shadowColor=shadowColor; @@ -178,6 +200,7 @@ ASDISPLAYNODE_INLINE void ASPendingStateApplyMetricsToLayer(_ASPendingState *sta @synthesize borderWidth=borderWidth; @synthesize borderColor=borderColor; @synthesize asyncdisplaykit_asyncTransactionContainer=asyncTransactionContainer; +@synthesize semanticContentAttribute=semanticContentAttribute; static CGColorRef blackColorRef = NULL; @@ -219,7 +242,6 @@ static BOOL defaultAllowsEdgeAntialiasing = NO; bounds = CGRectZero; backgroundColor = nil; tintColor = defaultTintColor; - contents = nil; isHidden = NO; needsDisplayOnBoundsChange = NO; allowsGroupOpacity = defaultAllowsGroupOpacity; @@ -232,9 +254,14 @@ static BOOL defaultAllowsEdgeAntialiasing = NO; anchorPoint = CGPointMake(0.5, 0.5); position = CGPointZero; zPosition = 0.0; - contentsScale = 1.0f; transform = CATransform3DIdentity; sublayerTransform = CATransform3DIdentity; + contents = nil; + contentsGravity = kCAGravityResize; + contentsRect = CGRectMake(0.0f, 0.0f, 1.0f, 1.0f); + contentsCenter = CGRectMake(0.0f, 0.0f, 1.0f, 1.0f); + contentsScale = 1.0f; + rasterizationScale = 1.0f; userInteractionEnabled = YES; shadowColor = blackColorRef; shadowOpacity = 0.0; @@ -258,6 +285,7 @@ static BOOL defaultAllowsEdgeAntialiasing = NO; accessibilityActivationPoint = CGPointZero; accessibilityPath = nil; edgeAntialiasingMask = (kCALayerLeftEdge | kCALayerRightEdge | kCALayerTopEdge | kCALayerBottomEdge); + semanticContentAttribute = UISemanticContentAttributeUnspecified; return self; } @@ -272,6 +300,11 @@ static BOOL defaultAllowsEdgeAntialiasing = NO; _flags.needsLayout = YES; } +- (void)layoutIfNeeded +{ + _flags.layoutIfNeeded = YES; +} + - (void)setClipsToBounds:(BOOL)flag { clipsToBounds = flag; @@ -359,16 +392,6 @@ static BOOL defaultAllowsEdgeAntialiasing = NO; _flags.setTintColor = YES; } -- (void)setContents:(id)newContents -{ - if (contents == newContents) { - return; - } - - contents = newContents; - _flags.setContents = YES; -} - - (void)setHidden:(BOOL)flag { isHidden = flag; @@ -416,12 +439,6 @@ static BOOL defaultAllowsEdgeAntialiasing = NO; _flags.setZPosition = YES; } -- (void)setContentsScale:(CGFloat)newContentsScale -{ - contentsScale = newContentsScale; - _flags.setContentsScale = YES; -} - - (void)setTransform:(CATransform3D)newTransform { transform = newTransform; @@ -434,6 +451,46 @@ static BOOL defaultAllowsEdgeAntialiasing = NO; _flags.setSublayerTransform = YES; } +- (void)setContents:(id)newContents +{ + if (contents == newContents) { + return; + } + + contents = newContents; + _flags.setContents = YES; +} + +- (void)setContentsGravity:(NSString *)newContentsGravity +{ + contentsGravity = newContentsGravity; + _flags.setContentsGravity = YES; +} + +- (void)setContentsRect:(CGRect)newContentsRect +{ + contentsRect = newContentsRect; + _flags.setContentsRect = YES; +} + +- (void)setContentsCenter:(CGRect)newContentsCenter +{ + contentsCenter = newContentsCenter; + _flags.setContentsCenter = YES; +} + +- (void)setContentsScale:(CGFloat)newContentsScale +{ + contentsScale = newContentsScale; + _flags.setContentsScale = YES; +} + +- (void)setRasterizationScale:(CGFloat)newRasterizationScale +{ + rasterizationScale = newRasterizationScale; + _flags.setRasterizationScale = YES; +} + - (void)setUserInteractionEnabled:(BOOL)flag { userInteractionEnabled = flag; @@ -506,6 +563,11 @@ static BOOL defaultAllowsEdgeAntialiasing = NO; _flags.setAsyncTransactionContainer = YES; } +- (void)setSemanticContentAttribute:(UISemanticContentAttribute)attribute { + semanticContentAttribute = attribute; + _flags.setSemanticContentAttribute = YES; +} + - (BOOL)isAccessibilityElement { return isAccessibilityElement; @@ -698,9 +760,6 @@ static BOOL defaultAllowsEdgeAntialiasing = NO; if (flags.setZPosition) layer.zPosition = zPosition; - if (flags.setContentsScale) - layer.contentsScale = contentsScale; - if (flags.setTransform) layer.transform = transform; @@ -710,6 +769,21 @@ static BOOL defaultAllowsEdgeAntialiasing = NO; if (flags.setContents) layer.contents = contents; + if (flags.setContentsGravity) + layer.contentsGravity = contentsGravity; + + if (flags.setContentsRect) + layer.contentsRect = contentsRect; + + if (flags.setContentsCenter) + layer.contentsCenter = contentsCenter; + + if (flags.setContentsScale) + layer.contentsScale = contentsScale; + + if (flags.setRasterizationScale) + layer.rasterizationScale = rasterizationScale; + if (flags.setClipsToBounds) layer.masksToBounds = clipsToBounds; @@ -761,9 +835,6 @@ static BOOL defaultAllowsEdgeAntialiasing = NO; if (flags.setEdgeAntialiasingMask) layer.edgeAntialiasingMask = edgeAntialiasingMask; - if (flags.needsLayout) - [layer setNeedsLayout]; - if (flags.setAsyncTransactionContainer) layer.asyncdisplaykit_asyncTransactionContainer = asyncTransactionContainer; @@ -771,6 +842,12 @@ static BOOL defaultAllowsEdgeAntialiasing = NO; ASDisplayNodeAssert(layer.opaque == opaque, @"Didn't set opaque as desired"); ASPendingStateApplyMetricsToLayer(self, layer); + + if (flags.needsLayout) + [layer setNeedsLayout]; + + if (flags.layoutIfNeeded) + [layer layoutIfNeeded]; } - (void)applyToView:(UIView *)view withSpecialPropertiesHandling:(BOOL)specialPropertiesHandling @@ -802,9 +879,6 @@ static BOOL defaultAllowsEdgeAntialiasing = NO; if (flags.setBounds) view.bounds = bounds; - if (flags.setContentsScale) - layer.contentsScale = contentsScale; - if (flags.setTransform) layer.transform = transform; @@ -814,6 +888,21 @@ static BOOL defaultAllowsEdgeAntialiasing = NO; if (flags.setContents) layer.contents = contents; + if (flags.setContentsGravity) + layer.contentsGravity = contentsGravity; + + if (flags.setContentsRect) + layer.contentsRect = contentsRect; + + if (flags.setContentsCenter) + layer.contentsCenter = contentsCenter; + + if (flags.setContentsScale) + layer.contentsScale = contentsScale; + + if (flags.setRasterizationScale) + layer.rasterizationScale = rasterizationScale; + if (flags.setClipsToBounds) view.clipsToBounds = clipsToBounds; @@ -831,7 +920,7 @@ static BOOL defaultAllowsEdgeAntialiasing = NO; view.tintColor = self.tintColor; if (flags.setOpaque) - view.layer.opaque = opaque; + layer.opaque = opaque; if (flags.setHidden) view.hidden = isHidden; @@ -889,14 +978,15 @@ static BOOL defaultAllowsEdgeAntialiasing = NO; if (flags.setEdgeAntialiasingMask) layer.edgeAntialiasingMask = edgeAntialiasingMask; - if (flags.needsLayout) - [view setNeedsLayout]; - if (flags.setAsyncTransactionContainer) view.asyncdisplaykit_asyncTransactionContainer = asyncTransactionContainer; if (flags.setOpaque) - ASDisplayNodeAssert(view.layer.opaque == opaque, @"Didn't set opaque as desired"); + ASDisplayNodeAssert(layer.opaque == opaque, @"Didn't set opaque as desired"); + + if (flags.setSemanticContentAttribute) { + view.semanticContentAttribute = semanticContentAttribute; + } if (flags.setIsAccessibilityElement) view.isAccessibilityElement = isAccessibilityElement; @@ -955,6 +1045,12 @@ static BOOL defaultAllowsEdgeAntialiasing = NO; } else { ASPendingStateApplyMetricsToLayer(self, layer); } + + if (flags.needsLayout) + [view setNeedsLayout]; + + if (flags.layoutIfNeeded) + [view layoutIfNeeded]; } // FIXME: Make this more efficient by tracking which properties are set rather than reading everything. @@ -968,10 +1064,14 @@ static BOOL defaultAllowsEdgeAntialiasing = NO; pendingState.position = layer.position; pendingState.zPosition = layer.zPosition; pendingState.bounds = layer.bounds; - pendingState.contentsScale = layer.contentsScale; pendingState.transform = layer.transform; pendingState.sublayerTransform = layer.sublayerTransform; pendingState.contents = layer.contents; + pendingState.contentsGravity = layer.contentsGravity; + pendingState.contentsRect = layer.contentsRect; + pendingState.contentsCenter = layer.contentsCenter; + pendingState.contentsScale = layer.contentsScale; + pendingState.rasterizationScale = layer.rasterizationScale; pendingState.clipsToBounds = layer.masksToBounds; pendingState.backgroundColor = layer.backgroundColor; pendingState.opaque = layer.opaque; @@ -1005,10 +1105,14 @@ static BOOL defaultAllowsEdgeAntialiasing = NO; pendingState.position = layer.position; pendingState.zPosition = layer.zPosition; pendingState.bounds = view.bounds; - pendingState.contentsScale = layer.contentsScale; pendingState.transform = layer.transform; pendingState.sublayerTransform = layer.sublayerTransform; pendingState.contents = layer.contents; + pendingState.contentsGravity = layer.contentsGravity; + pendingState.contentsRect = layer.contentsRect; + pendingState.contentsCenter = layer.contentsCenter; + pendingState.contentsScale = layer.contentsScale; + pendingState.rasterizationScale = layer.rasterizationScale; pendingState.clipsToBounds = view.clipsToBounds; pendingState.backgroundColor = layer.backgroundColor; pendingState.tintColor = view.tintColor; @@ -1033,6 +1137,7 @@ static BOOL defaultAllowsEdgeAntialiasing = NO; pendingState.allowsGroupOpacity = layer.allowsGroupOpacity; pendingState.allowsEdgeAntialiasing = layer.allowsEdgeAntialiasing; pendingState.edgeAntialiasingMask = layer.edgeAntialiasingMask; + pendingState.semanticContentAttribute = view.semanticContentAttribute; pendingState.isAccessibilityElement = view.isAccessibilityElement; pendingState.accessibilityLabel = view.accessibilityLabel; pendingState.accessibilityHint = view.accessibilityHint; @@ -1078,10 +1183,14 @@ static BOOL defaultAllowsEdgeAntialiasing = NO; || flags.setFrame || flags.setBounds || flags.setPosition - || flags.setContentsScale || flags.setTransform || flags.setSublayerTransform || flags.setContents + || flags.setContentsGravity + || flags.setContentsRect + || flags.setContentsCenter + || flags.setContentsScale + || flags.setRasterizationScale || flags.setClipsToBounds || flags.setBackgroundColor || flags.setTintColor @@ -1107,6 +1216,7 @@ static BOOL defaultAllowsEdgeAntialiasing = NO; || flags.needsLayout || flags.setAsyncTransactionContainer || flags.setOpaque + || flags.setSemanticContentAttribute || flags.setIsAccessibilityElement || flags.setAccessibilityLabel || flags.setAccessibilityHint diff --git a/Source/Private/_ASScopeTimer.h b/Source/Private/_ASScopeTimer.h index 5d9aa9cec6..9e4af4c7b7 100644 --- a/Source/Private/_ASScopeTimer.h +++ b/Source/Private/_ASScopeTimer.h @@ -1,11 +1,18 @@ // // _ASScopeTimer.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #pragma once diff --git a/Source/TextKit/ASEqualityHashHelpers.h b/Source/TextKit/ASEqualityHashHelpers.h deleted file mode 100644 index 20255c13ae..0000000000 --- a/Source/TextKit/ASEqualityHashHelpers.h +++ /dev/null @@ -1,171 +0,0 @@ -// -// ASEqualityHashHelpers.h -// 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 - -// From folly: -// This is the Hash128to64 function from Google's cityhash (available -// under the MIT License). We use it to reduce multiple 64 bit hashes -// into a single hash. -inline uint64_t ASHashCombine(const uint64_t upper, const uint64_t lower) { - // Murmur-inspired hashing. - const uint64_t kMul = 0x9ddfea08eb382d69ULL; - uint64_t a = (lower ^ upper) * kMul; - a ^= (a >> 47); - uint64_t b = (upper ^ a) * kMul; - b ^= (b >> 47); - b *= kMul; - return b; -} - -#if __LP64__ -inline size_t ASHash64ToNative(uint64_t key) { - return key; -} -#else - -// Thomas Wang downscaling hash function -inline size_t ASHash64ToNative(uint64_t key) { - key = (~key) + (key << 18); - key = key ^ (key >> 31); - key = key * 21; - key = key ^ (key >> 11); - key = key + (key << 6); - key = key ^ (key >> 22); - return (uint32_t) key; -} -#endif - -NSUInteger ASIntegerArrayHash(const NSUInteger *subhashes, NSUInteger count); - -namespace AS { - // Default is not an ObjC class - template - struct is_objc_class : std::false_type { }; - - // Conditionally enable this template specialization on whether T is convertible to id, makes the is_objc_class a true_type - template - struct is_objc_class::value, bool>::type> : std::true_type { }; - - // ASUtils::hash()(value) -> either std::hash if c++ or [o hash] if ObjC object. - template struct hash; - - // For non-objc types, defer to std::hash - template struct hash::value>::type> { - size_t operator ()(const T& a) { - return std::hash()(a); - } - }; - - // For objc types, call [o hash] - template struct hash::value>::type> { - size_t operator ()(id o) { - return [o hash]; - } - }; - - template struct is_equal; - - // For non-objc types use == operator - template struct is_equal::value>::type> { - bool operator ()(const T& a, const T& b) { - return a == b; - } - }; - - // For objc types, check pointer equality, then use -isEqual: - template struct is_equal::value>::type> { - bool operator ()(id a, id b) { - return a == b || [a isEqual:b]; - } - }; - -}; - -namespace ASTupleOperations -{ - // Recursive case (hash up to Index) - template ::value - 1> - struct _hash_helper - { - static size_t hash(Tuple const& tuple) - { - size_t prev = _hash_helper::hash(tuple); - using TypeForIndex = typename std::tuple_element::type; - size_t thisHash = AS::hash()(std::get(tuple)); - return ASHashCombine(prev, thisHash); - } - }; - - // Base case (hash 0th element) - template - struct _hash_helper - { - static size_t hash(Tuple const& tuple) - { - using TypeForIndex = typename std::tuple_element<0,Tuple>::type; - return AS::hash()(std::get<0>(tuple)); - } - }; - - // Recursive case (elements equal up to Index) - template ::value - 1> - struct _eq_helper - { - static bool equal(Tuple const& a, Tuple const& b) - { - bool prev = _eq_helper::equal(a, b); - using TypeForIndex = typename std::tuple_element::type; - auto aValue = std::get(a); - auto bValue = std::get(b); - return prev && AS::is_equal()(aValue, bValue); - } - }; - - // Base case (0th elements equal) - template - struct _eq_helper - { - static bool equal(Tuple const& a, Tuple const& b) - { - using TypeForIndex = typename std::tuple_element<0,Tuple>::type; - auto& aValue = std::get<0>(a); - auto& bValue = std::get<0>(b); - return AS::is_equal()(aValue, bValue); - } - }; - - - template struct hash; - - template - struct hash> - { - size_t operator()(std::tuple const& tt) const - { - return _hash_helper>::hash(tt); - } - }; - - - template struct equal_to; - - template - struct equal_to> - { - bool operator()(std::tuple const& a, std::tuple const& b) const - { - return _eq_helper>::equal(a, b); - } - }; - -} diff --git a/Source/TextKit/ASEqualityHashHelpers.mm b/Source/TextKit/ASEqualityHashHelpers.mm deleted file mode 100644 index 3fd02471ff..0000000000 --- a/Source/TextKit/ASEqualityHashHelpers.mm +++ /dev/null @@ -1,21 +0,0 @@ -// -// ASEqualityHashHelpers.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 - -NSUInteger ASIntegerArrayHash(const NSUInteger *subhashes, NSUInteger count) -{ - uint64_t result = subhashes[0]; - for (int ii = 1; ii < count; ++ii) { - result = ASHashCombine(result, subhashes[ii]); - } - return ASHash64ToNative(result); -} - diff --git a/Source/TextKit/ASLayoutManager.h b/Source/TextKit/ASLayoutManager.h index c7664818bf..6d0b21b118 100644 --- a/Source/TextKit/ASLayoutManager.h +++ b/Source/TextKit/ASLayoutManager.h @@ -1,11 +1,18 @@ // // ASLayoutManager.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/TextKit/ASLayoutManager.m b/Source/TextKit/ASLayoutManager.m index bc18e7142e..b603f654e7 100644 --- a/Source/TextKit/ASLayoutManager.m +++ b/Source/TextKit/ASLayoutManager.m @@ -1,11 +1,18 @@ // // ASLayoutManager.m -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/TextKit/ASTextKitAttributes.h b/Source/TextKit/ASTextKitAttributes.h index e4258a2be4..ee11fddea6 100755 --- a/Source/TextKit/ASTextKitAttributes.h +++ b/Source/TextKit/ASTextKitAttributes.h @@ -1,11 +1,18 @@ // // ASTextKitAttributes.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #pragma once diff --git a/Source/TextKit/ASTextKitAttributes.mm b/Source/TextKit/ASTextKitAttributes.mm index e78ad258a2..abe55c81d7 100755 --- a/Source/TextKit/ASTextKitAttributes.mm +++ b/Source/TextKit/ASTextKitAttributes.mm @@ -1,36 +1,54 @@ // // ASTextKitAttributes.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import -#import - -#include +#import NSString *const ASTextKitTruncationAttributeName = @"ck_truncation"; NSString *const ASTextKitEntityAttributeName = @"ck_entity"; size_t ASTextKitAttributes::hash() const { - NSUInteger subhashes[] = { +#pragma clang diagnostic push +#pragma clang diagnostic warning "-Wpadded" + struct { + NSUInteger attrStringHash; + NSUInteger truncationStringHash; + NSUInteger avoidTrunactionSetHash; + NSLineBreakMode lineBreakMode; + NSUInteger maximumNumberOfLines; + NSUInteger exclusionPathsHash; + CGSize shadowOffset; + NSUInteger shadowColorHash; + CGFloat shadowOpacity; + CGFloat shadowRadius; +#pragma clang diagnostic pop + } data = { [attributedString hash], [truncationAttributedString hash], [avoidTailTruncationSet hash], - std::hash()(lineBreakMode), - std::hash()(maximumNumberOfLines), + lineBreakMode, + maximumNumberOfLines, [exclusionPaths hash], - std::hash()(shadowOffset.width), - std::hash()(shadowOffset.height), + shadowOffset, [shadowColor hash], - std::hash()(shadowOpacity), - std::hash()(shadowRadius), + shadowOpacity, + shadowRadius, }; - return ASIntegerArrayHash(subhashes, sizeof(subhashes) / sizeof(subhashes[0])); + return ASHashBytes(&data, sizeof(data)); } diff --git a/Source/TextKit/ASTextKitComponents.h b/Source/TextKit/ASTextKitComponents.h index 6cf94ba361..9fe5201c82 100644 --- a/Source/TextKit/ASTextKitComponents.h +++ b/Source/TextKit/ASTextKitComponents.h @@ -1,11 +1,18 @@ // // ASTextKitComponents.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/TextKit/ASTextKitComponents.mm b/Source/TextKit/ASTextKitComponents.mm index f7810b63f2..2854ad6997 100644 --- a/Source/TextKit/ASTextKitComponents.mm +++ b/Source/TextKit/ASTextKitComponents.mm @@ -1,11 +1,18 @@ // // ASTextKitComponents.m -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/TextKit/ASTextKitContext.h b/Source/TextKit/ASTextKitContext.h index 3373e08546..8de63a1bf1 100755 --- a/Source/TextKit/ASTextKitContext.h +++ b/Source/TextKit/ASTextKitContext.h @@ -1,11 +1,18 @@ // // ASTextKitContext.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/TextKit/ASTextKitContext.mm b/Source/TextKit/ASTextKitContext.mm index 79f40c5813..64fcf65092 100755 --- a/Source/TextKit/ASTextKitContext.mm +++ b/Source/TextKit/ASTextKitContext.mm @@ -1,11 +1,18 @@ // // ASTextKitContext.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/TextKit/ASTextKitCoreTextAdditions.h b/Source/TextKit/ASTextKitCoreTextAdditions.h index e43508c92e..94108961cb 100644 --- a/Source/TextKit/ASTextKitCoreTextAdditions.h +++ b/Source/TextKit/ASTextKitCoreTextAdditions.h @@ -1,11 +1,18 @@ // // ASTextKitCoreTextAdditions.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/TextKit/ASTextKitCoreTextAdditions.m b/Source/TextKit/ASTextKitCoreTextAdditions.m index 4e90fefd44..05935db5ca 100644 --- a/Source/TextKit/ASTextKitCoreTextAdditions.m +++ b/Source/TextKit/ASTextKitCoreTextAdditions.m @@ -1,11 +1,18 @@ // // ASTextKitCoreTextAdditions.m -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/TextKit/ASTextKitEntityAttribute.h b/Source/TextKit/ASTextKitEntityAttribute.h index 6b28b5540e..dfb9348159 100755 --- a/Source/TextKit/ASTextKitEntityAttribute.h +++ b/Source/TextKit/ASTextKitEntityAttribute.h @@ -1,11 +1,18 @@ // // ASTextKitEntityAttribute.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/TextKit/ASTextKitEntityAttribute.m b/Source/TextKit/ASTextKitEntityAttribute.m index 0b2aaa2d96..4664b24980 100755 --- a/Source/TextKit/ASTextKitEntityAttribute.m +++ b/Source/TextKit/ASTextKitEntityAttribute.m @@ -1,11 +1,18 @@ // // ASTextKitEntityAttribute.m -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/TextKit/ASTextKitFontSizeAdjuster.h b/Source/TextKit/ASTextKitFontSizeAdjuster.h index 218c355231..5f979b4d67 100644 --- a/Source/TextKit/ASTextKitFontSizeAdjuster.h +++ b/Source/TextKit/ASTextKitFontSizeAdjuster.h @@ -1,11 +1,18 @@ // // ASTextKitFontSizeAdjuster.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/TextKit/ASTextKitFontSizeAdjuster.mm b/Source/TextKit/ASTextKitFontSizeAdjuster.mm index 171ae32566..3858a9441c 100644 --- a/Source/TextKit/ASTextKitFontSizeAdjuster.mm +++ b/Source/TextKit/ASTextKitFontSizeAdjuster.mm @@ -1,11 +1,18 @@ // // ASTextKitFontSizeAdjuster.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // @@ -14,23 +21,30 @@ #import #import -#import #import +#import +#import //#define LOG(...) NSLog(__VA_ARGS__) #define LOG(...) +@interface ASTextKitFontSizeAdjuster() +@property (nonatomic, strong, readonly) NSLayoutManager *sizingLayoutManager; +@property (nonatomic, strong, readonly) NSTextContainer *sizingTextContainer; +@end + @implementation ASTextKitFontSizeAdjuster { __weak ASTextKitContext *_context; ASTextKitAttributes _attributes; - std::mutex _textKitMutex; BOOL _measured; CGFloat _scaleFactor; - NSLayoutManager *_sizingLayoutManager; - NSTextContainer *_sizingTextContainer; + ASDN::Mutex __instanceLock__; } +@synthesize sizingLayoutManager = _sizingLayoutManager; +@synthesize sizingTextContainer = _sizingTextContainer; + - (instancetype)initWithContext:(ASTextKitContext *)context constrainedSize:(CGSize)constrainedSize textKitAttributes:(const ASTextKitAttributes &)textComponentAttributes; @@ -87,38 +101,61 @@ - (NSUInteger)lineCountForString:(NSAttributedString *)attributedString { - NSUInteger lineCount = 0; + NSUInteger lineCount = 0; + + NSLayoutManager *sizingLayoutManager = [self sizingLayoutManager]; + NSTextContainer *sizingTextContainer = [self sizingTextContainer]; + + NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:attributedString]; + [textStorage addLayoutManager:sizingLayoutManager]; + + [sizingLayoutManager ensureLayoutForTextContainer:sizingTextContainer]; + for (NSRange lineRange = { 0, 0 }; NSMaxRange(lineRange) < [sizingLayoutManager numberOfGlyphs] && lineCount <= _attributes.maximumNumberOfLines; lineCount++) { + [sizingLayoutManager lineFragmentRectForGlyphAtIndex:NSMaxRange(lineRange) effectiveRange:&lineRange]; + } + + [textStorage removeLayoutManager:sizingLayoutManager]; + return lineCount; +} + +- (CGSize)boundingBoxForString:(NSAttributedString *)attributedString +{ + NSLayoutManager *sizingLayoutManager = [self sizingLayoutManager]; + NSTextContainer *sizingTextContainer = [self sizingTextContainer]; + + NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:attributedString]; + [textStorage addLayoutManager:sizingLayoutManager]; + + [sizingLayoutManager ensureLayoutForTextContainer:sizingTextContainer]; + CGRect textRect = [sizingLayoutManager boundingRectForGlyphRange:NSMakeRange(0, [textStorage length]) + inTextContainer:sizingTextContainer]; + [textStorage removeLayoutManager:sizingLayoutManager]; + return textRect.size; +} + +- (NSLayoutManager *)sizingLayoutManager +{ + ASDN::MutexLocker l(__instanceLock__); + if (_sizingLayoutManager == nil) { + _sizingLayoutManager = [[ASLayoutManager alloc] init]; + _sizingLayoutManager.usesFontLeading = NO; - static std::mutex __static_mutex; - std::lock_guard l(__static_mutex); - - NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:attributedString]; - if (_sizingLayoutManager == nil) { - _sizingLayoutManager = [[ASLayoutManager alloc] init]; - _sizingLayoutManager.usesFontLeading = NO; - } - [textStorage addLayoutManager:_sizingLayoutManager]; if (_sizingTextContainer == nil) { - // make this text container unbounded in height so that the layout manager will compute the total - // number of lines and not stop counting when height runs out. - _sizingTextContainer = [[NSTextContainer alloc] initWithSize:CGSizeMake(_constrainedSize.width, CGFLOAT_MAX)]; - _sizingTextContainer.lineFragmentPadding = 0; - - // use 0 regardless of what is in the attributes so that we get an accurate line count - _sizingTextContainer.maximumNumberOfLines = 0; - [_sizingLayoutManager addTextContainer:_sizingTextContainer]; + // make this text container unbounded in height so that the layout manager will compute the total + // number of lines and not stop counting when height runs out. + _sizingTextContainer = [[NSTextContainer alloc] initWithSize:CGSizeMake(_constrainedSize.width, CGFLOAT_MAX)]; + _sizingTextContainer.lineFragmentPadding = 0; + + // use 0 regardless of what is in the attributes so that we get an accurate line count + _sizingTextContainer.maximumNumberOfLines = 0; + + _sizingTextContainer.lineBreakMode = _attributes.lineBreakMode; + _sizingTextContainer.exclusionPaths = _attributes.exclusionPaths; } - - _sizingTextContainer.lineBreakMode = _attributes.lineBreakMode; - _sizingTextContainer.exclusionPaths = _attributes.exclusionPaths; - - - for (NSRange lineRange = { 0, 0 }; NSMaxRange(lineRange) < [_sizingLayoutManager numberOfGlyphs] && lineCount <= _attributes.maximumNumberOfLines; lineCount++) { - [_sizingLayoutManager lineFragmentRectForGlyphAtIndex:NSMaxRange(lineRange) effectiveRange:&lineRange]; - } - - [textStorage removeLayoutManager:_sizingLayoutManager]; - return lineCount; + [_sizingLayoutManager addTextContainer:_sizingTextContainer]; + } + + return _sizingLayoutManager; } - (CGFloat)scaleFactor @@ -195,7 +232,7 @@ // if max lines still doesn't fit, continue without checking that we fit in the constrained height if (maxLinesFits == YES && heightFits == NO) { // max lines fit so make sure that we fit in the constrained height. - CGSize stringSize = [scaledString boundingRectWithSize:CGSizeMake(_constrainedSize.width, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin context:nil].size; + CGSize stringSize = [self boundingBoxForString:scaledString]; heightFits = (stringSize.height <= _constrainedSize.height); } } diff --git a/Source/TextKit/ASTextKitRenderer+Positioning.h b/Source/TextKit/ASTextKitRenderer+Positioning.h index e298de9452..3ee402f224 100755 --- a/Source/TextKit/ASTextKitRenderer+Positioning.h +++ b/Source/TextKit/ASTextKitRenderer+Positioning.h @@ -1,11 +1,18 @@ // // ASTextKitRenderer+Positioning.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/TextKit/ASTextKitRenderer+Positioning.mm b/Source/TextKit/ASTextKitRenderer+Positioning.mm index 1b03282781..b0f6c39b16 100755 --- a/Source/TextKit/ASTextKitRenderer+Positioning.mm +++ b/Source/TextKit/ASTextKitRenderer+Positioning.mm @@ -1,11 +1,18 @@ // // ASTextKitRenderer+Positioning.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/TextKit/ASTextKitRenderer+TextChecking.h b/Source/TextKit/ASTextKitRenderer+TextChecking.h index 32dc02f3d5..6c779c9800 100755 --- a/Source/TextKit/ASTextKitRenderer+TextChecking.h +++ b/Source/TextKit/ASTextKitRenderer+TextChecking.h @@ -1,11 +1,18 @@ // // ASTextKitRenderer+TextChecking.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/TextKit/ASTextKitRenderer+TextChecking.mm b/Source/TextKit/ASTextKitRenderer+TextChecking.mm index 5de148cc22..ff3d57a935 100755 --- a/Source/TextKit/ASTextKitRenderer+TextChecking.mm +++ b/Source/TextKit/ASTextKitRenderer+TextChecking.mm @@ -1,11 +1,18 @@ // // ASTextKitRenderer+TextChecking.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/TextKit/ASTextKitRenderer.h b/Source/TextKit/ASTextKitRenderer.h index 61b0b231ac..1475dde432 100755 --- a/Source/TextKit/ASTextKitRenderer.h +++ b/Source/TextKit/ASTextKitRenderer.h @@ -1,11 +1,18 @@ // // ASTextKitRenderer.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/TextKit/ASTextKitRenderer.mm b/Source/TextKit/ASTextKitRenderer.mm index 9a1de9a2aa..0f5e8ac99e 100755 --- a/Source/TextKit/ASTextKitRenderer.mm +++ b/Source/TextKit/ASTextKitRenderer.mm @@ -1,11 +1,18 @@ // // ASTextKitRenderer.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/TextKit/ASTextKitShadower.h b/Source/TextKit/ASTextKitShadower.h index c1f6ebe174..d08c94efd2 100755 --- a/Source/TextKit/ASTextKitShadower.h +++ b/Source/TextKit/ASTextKitShadower.h @@ -1,11 +1,18 @@ // // ASTextKitShadower.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/TextKit/ASTextKitShadower.mm b/Source/TextKit/ASTextKitShadower.mm index 4186312a59..2715438252 100755 --- a/Source/TextKit/ASTextKitShadower.mm +++ b/Source/TextKit/ASTextKitShadower.mm @@ -1,11 +1,18 @@ // // ASTextKitShadower.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/TextKit/ASTextKitTailTruncater.h b/Source/TextKit/ASTextKitTailTruncater.h index 3588c1eff9..26f2fb127a 100755 --- a/Source/TextKit/ASTextKitTailTruncater.h +++ b/Source/TextKit/ASTextKitTailTruncater.h @@ -1,11 +1,18 @@ // // ASTextKitTailTruncater.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/TextKit/ASTextKitTailTruncater.mm b/Source/TextKit/ASTextKitTailTruncater.mm index c7aabe8e7a..b160fbb1e2 100755 --- a/Source/TextKit/ASTextKitTailTruncater.mm +++ b/Source/TextKit/ASTextKitTailTruncater.mm @@ -1,11 +1,18 @@ // // ASTextKitTailTruncater.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/TextKit/ASTextKitTruncating.h b/Source/TextKit/ASTextKitTruncating.h index 05cabfda25..b8af53d809 100755 --- a/Source/TextKit/ASTextKitTruncating.h +++ b/Source/TextKit/ASTextKitTruncating.h @@ -1,11 +1,18 @@ // // ASTextKitTruncating.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/TextKit/ASTextNodeTypes.h b/Source/TextKit/ASTextNodeTypes.h index 866824730e..2e70b0c1e7 100644 --- a/Source/TextKit/ASTextNodeTypes.h +++ b/Source/TextKit/ASTextNodeTypes.h @@ -1,11 +1,18 @@ // // ASTextNodeTypes.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #pragma once diff --git a/Source/TextKit/ASTextNodeWordKerner.h b/Source/TextKit/ASTextNodeWordKerner.h index af76500567..b5c1923882 100644 --- a/Source/TextKit/ASTextNodeWordKerner.h +++ b/Source/TextKit/ASTextNodeWordKerner.h @@ -1,11 +1,18 @@ // // ASTextNodeWordKerner.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/TextKit/ASTextNodeWordKerner.m b/Source/TextKit/ASTextNodeWordKerner.m index 7fae924da2..d9f6fba02c 100644 --- a/Source/TextKit/ASTextNodeWordKerner.m +++ b/Source/TextKit/ASTextNodeWordKerner.m @@ -1,11 +1,18 @@ // // ASTextNodeWordKerner.m -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/UIImage+ASConvenience.h b/Source/UIImage+ASConvenience.h index 1c89ccf4cd..e9612c8b7b 100644 --- a/Source/UIImage+ASConvenience.h +++ b/Source/UIImage+ASConvenience.h @@ -1,13 +1,18 @@ // // UIImage+ASConvenience.h -// AsyncDisplayKit -// -// Created by Hannah Troisi on 6/24/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/UIImage+ASConvenience.m b/Source/UIImage+ASConvenience.m index 4d1b5985c9..efc1e35360 100644 --- a/Source/UIImage+ASConvenience.m +++ b/Source/UIImage+ASConvenience.m @@ -1,13 +1,18 @@ // // UIImage+ASConvenience.m -// AsyncDisplayKit -// -// Created by Hannah Troisi on 6/24/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/UIResponder+AsyncDisplayKit.h b/Source/UIResponder+AsyncDisplayKit.h index c39b5275a9..42dbe49402 100644 --- a/Source/UIResponder+AsyncDisplayKit.h +++ b/Source/UIResponder+AsyncDisplayKit.h @@ -1,9 +1,18 @@ // // UIResponder+AsyncDisplayKit.h -// AsyncDisplayKit +// Texture // -// Created by Adlai Holler on 2/13/17. -// Copyright © 2017 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 /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/UIResponder+AsyncDisplayKit.m b/Source/UIResponder+AsyncDisplayKit.m index 55e4c9fbfa..a80544d686 100644 --- a/Source/UIResponder+AsyncDisplayKit.m +++ b/Source/UIResponder+AsyncDisplayKit.m @@ -1,9 +1,18 @@ // // UIResponder+AsyncDisplayKit.m -// AsyncDisplayKit +// Texture // -// Created by Adlai Holler on 2/13/17. -// Copyright © 2017 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 /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "UIResponder+AsyncDisplayKit.h" diff --git a/Source/_ASTransitionContext.h b/Source/_ASTransitionContext.h index 0b59fe1a84..2a8a6f14fe 100644 --- a/Source/_ASTransitionContext.h +++ b/Source/_ASTransitionContext.h @@ -1,13 +1,18 @@ // // _ASTransitionContext.h -// AsyncDisplayKit -// -// Created by Levi McCallum on 2/4/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/_ASTransitionContext.m b/Source/_ASTransitionContext.m index fac52d28e0..da6350a4ab 100644 --- a/Source/_ASTransitionContext.m +++ b/Source/_ASTransitionContext.m @@ -1,13 +1,18 @@ // // _ASTransitionContext.m -// AsyncDisplayKit -// -// Created by Levi McCallum on 2/4/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/tvOS/ASControlNode+tvOS.h b/Source/tvOS/ASControlNode+tvOS.h index 7f56cf5ddd..cd05078a72 100644 --- a/Source/tvOS/ASControlNode+tvOS.h +++ b/Source/tvOS/ASControlNode+tvOS.h @@ -1,13 +1,18 @@ // // ASControlNode+tvOS.h -// AsyncDisplayKit -// -// Created by Aaron Schubert on 21/04/2016. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #if TARGET_OS_TV diff --git a/Source/tvOS/ASControlNode+tvOS.m b/Source/tvOS/ASControlNode+tvOS.m index b1da932137..35acbc0457 100644 --- a/Source/tvOS/ASControlNode+tvOS.m +++ b/Source/tvOS/ASControlNode+tvOS.m @@ -1,14 +1,22 @@ // // ASControlNode+tvOS.m -// AsyncDisplayKit -// -// Created by Aaron Schubert on 21/04/2016. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. // +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + #if TARGET_OS_TV #import diff --git a/Source/tvOS/ASImageNode+tvOS.h b/Source/tvOS/ASImageNode+tvOS.h index 0eaf1c5ee5..d808db10ab 100644 --- a/Source/tvOS/ASImageNode+tvOS.h +++ b/Source/tvOS/ASImageNode+tvOS.h @@ -1,13 +1,18 @@ // // ASImageNode+tvOS.h -// AsyncDisplayKit -// -// Created by Aaron Schubert on 21/04/2016. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #if TARGET_OS_TV diff --git a/Source/tvOS/ASImageNode+tvOS.m b/Source/tvOS/ASImageNode+tvOS.m index 535210a385..d3da889c52 100644 --- a/Source/tvOS/ASImageNode+tvOS.m +++ b/Source/tvOS/ASImageNode+tvOS.m @@ -1,15 +1,21 @@ // // ASImageNode+tvOS.m -// AsyncDisplayKit -// -// Created by Aaron Schubert on 21/04/2016. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // +#import #if TARGET_OS_TV #import diff --git a/ASDKListKit/ASDKListKit.xcodeproj/project.pbxproj b/SubspecWorkspaces/ASDKListKit/ASDKListKit.xcodeproj/project.pbxproj similarity index 83% rename from ASDKListKit/ASDKListKit.xcodeproj/project.pbxproj rename to SubspecWorkspaces/ASDKListKit/ASDKListKit.xcodeproj/project.pbxproj index d90f13803b..814ad945a4 100644 --- a/ASDKListKit/ASDKListKit.xcodeproj/project.pbxproj +++ b/SubspecWorkspaces/ASDKListKit/ASDKListKit.xcodeproj/project.pbxproj @@ -15,6 +15,10 @@ CC55323D1E16F2A90011C01F /* ASListTestCellNode.m in Sources */ = {isa = PBXBuildFile; fileRef = CC5532351E16F2A90011C01F /* ASListTestCellNode.m */; }; CC55323E1E16F2A90011C01F /* ASListTestObject.m in Sources */ = {isa = PBXBuildFile; fileRef = CC5532371E16F2A90011C01F /* ASListTestObject.m */; }; CC55323F1E16F2A90011C01F /* ASListKitTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC5532381E16F2A90011C01F /* ASListKitTests.m */; }; + CCC31FAD1EF9B96600E41731 /* ASDisplayNode+OCMock.m in Sources */ = {isa = PBXBuildFile; fileRef = CCC31FA51EF9B96600E41731 /* ASDisplayNode+OCMock.m */; }; + CCC31FAE1EF9B96600E41731 /* ASTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = CCC31FA71EF9B96600E41731 /* ASTestCase.m */; }; + CCC31FAF1EF9B96600E41731 /* NSInvocation+ASTestHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = CCC31FAA1EF9B96600E41731 /* NSInvocation+ASTestHelpers.m */; }; + CCC31FB01EF9B96600E41731 /* OCMockObject+ASAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = CCC31FAC1EF9B96600E41731 /* OCMockObject+ASAdditions.m */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -35,7 +39,14 @@ CC5532361E16F2A90011C01F /* ASListTestObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASListTestObject.h; sourceTree = ""; }; CC5532371E16F2A90011C01F /* ASListTestObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASListTestObject.m; sourceTree = ""; }; CC5532381E16F2A90011C01F /* ASListKitTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASListKitTests.m; sourceTree = ""; }; - CC55326C1E16F67A0011C01F /* ASXCTExtensions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ASXCTExtensions.h; sourceTree = ""; }; + CCC31FA51EF9B96600E41731 /* ASDisplayNode+OCMock.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "ASDisplayNode+OCMock.m"; sourceTree = ""; }; + CCC31FA61EF9B96600E41731 /* ASTestCase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASTestCase.h; sourceTree = ""; }; + CCC31FA71EF9B96600E41731 /* ASTestCase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASTestCase.m; sourceTree = ""; }; + CCC31FA81EF9B96600E41731 /* ASXCTExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASXCTExtensions.h; sourceTree = ""; }; + CCC31FA91EF9B96600E41731 /* NSInvocation+ASTestHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSInvocation+ASTestHelpers.h"; sourceTree = ""; }; + CCC31FAA1EF9B96600E41731 /* NSInvocation+ASTestHelpers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSInvocation+ASTestHelpers.m"; sourceTree = ""; }; + CCC31FAB1EF9B96600E41731 /* OCMockObject+ASAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "OCMockObject+ASAdditions.h"; sourceTree = ""; }; + CCC31FAC1EF9B96600E41731 /* OCMockObject+ASAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "OCMockObject+ASAdditions.m"; sourceTree = ""; }; D6BDED6F23A72F40F571EEF0 /* Pods-ASDKListKitTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ASDKListKitTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-ASDKListKitTests/Pods-ASDKListKitTests.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -92,7 +103,7 @@ CC5532251E16EB9D0011C01F /* ASDKListKitTests */ = { isa = PBXGroup; children = ( - CC55326D1E16F67D0011C01F /* Common */, + CCC31FA41EF9B96600E41731 /* Common */, CC55326E1E170A740011C01F /* ListKit Fixtures */, CC5532381E16F2A90011C01F /* ASListKitTests.m */, CC5532281E16EB9D0011C01F /* Info.plist */, @@ -100,14 +111,6 @@ path = ASDKListKitTests; sourceTree = ""; }; - CC55326D1E16F67D0011C01F /* Common */ = { - isa = PBXGroup; - children = ( - CC55326C1E16F67A0011C01F /* ASXCTExtensions.h */, - ); - name = Common; - sourceTree = ""; - }; CC55326E1E170A740011C01F /* ListKit Fixtures */ = { isa = PBXGroup; children = ( @@ -127,6 +130,22 @@ name = "ListKit Fixtures"; sourceTree = ""; }; + CCC31FA41EF9B96600E41731 /* Common */ = { + isa = PBXGroup; + children = ( + CCC31FA51EF9B96600E41731 /* ASDisplayNode+OCMock.m */, + CCC31FA61EF9B96600E41731 /* ASTestCase.h */, + CCC31FA71EF9B96600E41731 /* ASTestCase.m */, + CCC31FA81EF9B96600E41731 /* ASXCTExtensions.h */, + CCC31FA91EF9B96600E41731 /* NSInvocation+ASTestHelpers.h */, + CCC31FAA1EF9B96600E41731 /* NSInvocation+ASTestHelpers.m */, + CCC31FAB1EF9B96600E41731 /* OCMockObject+ASAdditions.h */, + CCC31FAC1EF9B96600E41731 /* OCMockObject+ASAdditions.m */, + ); + name = Common; + path = ../../Tests/Common; + sourceTree = SOURCE_ROOT; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -204,7 +223,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - 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"; + 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; }; 876CE14CAF6A87E34577E157 /* [CP] Copy Pods Resources */ = { @@ -244,10 +263,14 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + CCC31FAD1EF9B96600E41731 /* ASDisplayNode+OCMock.m in Sources */, CC55323E1E16F2A90011C01F /* ASListTestObject.m in Sources */, + CCC31FAF1EF9B96600E41731 /* NSInvocation+ASTestHelpers.m in Sources */, CC5532391E16F2A90011C01F /* ASListTestSupplementarySource.m in Sources */, + CCC31FB01EF9B96600E41731 /* OCMockObject+ASAdditions.m in Sources */, CC55323D1E16F2A90011C01F /* ASListTestCellNode.m in Sources */, CC55323B1E16F2A90011C01F /* ASListKitTestAdapterDataSource.m in Sources */, + CCC31FAE1EF9B96600E41731 /* ASTestCase.m in Sources */, CC55323C1E16F2A90011C01F /* ASListTestSection.m in Sources */, CC55323F1E16F2A90011C01F /* ASListKitTests.m in Sources */, CC55323A1E16F2A90011C01F /* ASListTestSupplementaryNode.m in Sources */, @@ -260,12 +283,14 @@ CC55321D1E16EB7A0011C01F /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + IPHONEOS_DEPLOYMENT_TARGET = 8.0; }; name = Debug; }; CC55321E1E16EB7A0011C01F /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + IPHONEOS_DEPLOYMENT_TARGET = 8.0; }; name = Release; }; @@ -311,7 +336,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; INFOPLIST_FILE = ASDKListKitTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 10.2; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; @@ -357,7 +382,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; INFOPLIST_FILE = ASDKListKitTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 10.2; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = asyncdisplaykit.ASDKListKitTests; diff --git a/ASDKListKit/ASDKListKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/SubspecWorkspaces/ASDKListKit/ASDKListKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from ASDKListKit/ASDKListKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to SubspecWorkspaces/ASDKListKit/ASDKListKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/ASDKListKit/ASDKListKit.xcworkspace/contents.xcworkspacedata b/SubspecWorkspaces/ASDKListKit/ASDKListKit.xcworkspace/contents.xcworkspacedata similarity index 100% rename from ASDKListKit/ASDKListKit.xcworkspace/contents.xcworkspacedata rename to SubspecWorkspaces/ASDKListKit/ASDKListKit.xcworkspace/contents.xcworkspacedata diff --git a/SubspecWorkspaces/ASDKListKit/ASDKListKitTests/ASListKitTestAdapterDataSource.h b/SubspecWorkspaces/ASDKListKit/ASDKListKitTests/ASListKitTestAdapterDataSource.h new file mode 100644 index 0000000000..d14c9eef90 --- /dev/null +++ b/SubspecWorkspaces/ASDKListKit/ASDKListKitTests/ASListKitTestAdapterDataSource.h @@ -0,0 +1,25 @@ +// +// ASListKitTestAdapterDataSource.h +// Texture +// +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +@interface ASListKitTestAdapterDataSource : NSObject + +// array of numbers which is then passed to -[IGListTestSection setItems:] +@property (nonatomic, strong) NSArray *objects; + +@end diff --git a/SubspecWorkspaces/ASDKListKit/ASDKListKitTests/ASListKitTestAdapterDataSource.m b/SubspecWorkspaces/ASDKListKit/ASDKListKitTests/ASListKitTestAdapterDataSource.m new file mode 100644 index 0000000000..b9a86160a4 --- /dev/null +++ b/SubspecWorkspaces/ASDKListKit/ASDKListKitTests/ASListKitTestAdapterDataSource.m @@ -0,0 +1,39 @@ +// +// ASListKitTestAdapterDataSource.m +// Texture +// +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import "ASListKitTestAdapterDataSource.h" +#import "ASListTestSection.h" + +@implementation ASListKitTestAdapterDataSource + +- (NSArray *)objectsForListAdapter:(IGListAdapter *)listAdapter +{ + return self.objects; +} + +- (IGListSectionController *)listAdapter:(IGListAdapter *)listAdapter sectionControllerForObject:(id)object +{ + ASListTestSection *section = [[ASListTestSection alloc] init]; + return section; +} + +- (nullable UIView *)emptyViewForListAdapter:(IGListAdapter *)listAdapter +{ + return nil; +} + +@end diff --git a/ASDKListKit/ASDKListKitTests/ASListKitTests.m b/SubspecWorkspaces/ASDKListKit/ASDKListKitTests/ASListKitTests.m similarity index 83% rename from ASDKListKit/ASDKListKitTests/ASListKitTests.m rename to SubspecWorkspaces/ASDKListKit/ASDKListKitTests/ASListKitTests.m index 5312b10874..28b6205d7f 100644 --- a/ASDKListKit/ASDKListKitTests/ASListKitTests.m +++ b/SubspecWorkspaces/ASDKListKit/ASDKListKitTests/ASListKitTests.m @@ -1,9 +1,18 @@ // // ASListKitTests.m -// AsyncDisplayKit +// Texture // -// Created by Adlai Holler on 12/25/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 /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -11,8 +20,9 @@ #import "ASListKitTestAdapterDataSource.h" #import "ASXCTExtensions.h" #import +#import "ASTestCase.h" -@interface ASListKitTests : XCTestCase +@interface ASListKitTests : ASTestCase @property (nonatomic, strong) ASCollectionNode *collectionNode; @property (nonatomic, strong) UICollectionView *collectionView; diff --git a/SubspecWorkspaces/ASDKListKit/ASDKListKitTests/ASListTestCellNode.h b/SubspecWorkspaces/ASDKListKit/ASDKListKitTests/ASListTestCellNode.h new file mode 100644 index 0000000000..d9d4c44ac4 --- /dev/null +++ b/SubspecWorkspaces/ASDKListKit/ASDKListKitTests/ASListTestCellNode.h @@ -0,0 +1,22 @@ +// +// ASListTestCellNode.h +// Texture +// +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +@interface ASListTestCellNode : ASCellNode + +@end diff --git a/SubspecWorkspaces/ASDKListKit/ASDKListKitTests/ASListTestCellNode.m b/SubspecWorkspaces/ASDKListKit/ASDKListKitTests/ASListTestCellNode.m new file mode 100644 index 0000000000..4963f36280 --- /dev/null +++ b/SubspecWorkspaces/ASDKListKit/ASDKListKitTests/ASListTestCellNode.m @@ -0,0 +1,22 @@ +// +// ASListTestCellNode.m +// Texture +// +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import "ASListTestCellNode.h" + +@implementation ASListTestCellNode + +@end diff --git a/SubspecWorkspaces/ASDKListKit/ASDKListKitTests/ASListTestObject.h b/SubspecWorkspaces/ASDKListKit/ASDKListKitTests/ASListTestObject.h new file mode 100644 index 0000000000..a4dbf7c047 --- /dev/null +++ b/SubspecWorkspaces/ASDKListKit/ASDKListKitTests/ASListTestObject.h @@ -0,0 +1,31 @@ +// +// ASListTestObject.h +// Texture +// +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface ASListTestObject : NSObject + +- (instancetype)initWithKey:(id )key value:(id)value; + +@property (nonatomic, strong, readonly) id key; +@property (nonatomic, strong) id value; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ASDKListKit/ASDKListKitTests/ASListTestObject.m b/SubspecWorkspaces/ASDKListKit/ASDKListKitTests/ASListTestObject.m similarity index 55% rename from ASDKListKit/ASDKListKitTests/ASListTestObject.m rename to SubspecWorkspaces/ASDKListKit/ASDKListKitTests/ASListTestObject.m index ffd1216dd4..4b815361b9 100644 --- a/ASDKListKit/ASDKListKitTests/ASListTestObject.m +++ b/SubspecWorkspaces/ASDKListKit/ASDKListKitTests/ASListTestObject.m @@ -1,9 +1,18 @@ // // ASListTestObject.m -// AsyncDisplayKit +// Texture // -// Created by Adlai Holler on 12/25/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 /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "ASListTestObject.h" diff --git a/SubspecWorkspaces/ASDKListKit/ASDKListKitTests/ASListTestSection.h b/SubspecWorkspaces/ASDKListKit/ASDKListKitTests/ASListTestSection.h new file mode 100644 index 0000000000..6e21457f90 --- /dev/null +++ b/SubspecWorkspaces/ASDKListKit/ASDKListKitTests/ASListTestSection.h @@ -0,0 +1,27 @@ +// +// ASListTestSection.h +// Texture +// +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +@interface ASListTestSection : IGListSectionController + +@property (nonatomic) NSInteger itemCount; + +@property (nonatomic) NSInteger selectedItemIndex; + +@end diff --git a/ASDKListKit/ASDKListKitTests/ASListTestSection.m b/SubspecWorkspaces/ASDKListKit/ASDKListKitTests/ASListTestSection.m similarity index 60% rename from ASDKListKit/ASDKListKitTests/ASListTestSection.m rename to SubspecWorkspaces/ASDKListKit/ASDKListKitTests/ASListTestSection.m index eac14aec0f..1d17605de4 100644 --- a/ASDKListKit/ASDKListKitTests/ASListTestSection.m +++ b/SubspecWorkspaces/ASDKListKit/ASDKListKitTests/ASListTestSection.m @@ -1,9 +1,18 @@ // // ASListTestSection.m -// AsyncDisplayKit +// Texture // -// Created by Adlai Holler on 12/25/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 /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "ASListTestSection.h" diff --git a/SubspecWorkspaces/ASDKListKit/ASDKListKitTests/ASListTestSupplementaryNode.h b/SubspecWorkspaces/ASDKListKit/ASDKListKitTests/ASListTestSupplementaryNode.h new file mode 100644 index 0000000000..7ec2b83609 --- /dev/null +++ b/SubspecWorkspaces/ASDKListKit/ASDKListKitTests/ASListTestSupplementaryNode.h @@ -0,0 +1,22 @@ +// +// ASListTestSupplementaryNode.h +// Texture +// +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +@interface ASListTestSupplementaryNode : ASCellNode + +@end diff --git a/SubspecWorkspaces/ASDKListKit/ASDKListKitTests/ASListTestSupplementaryNode.m b/SubspecWorkspaces/ASDKListKit/ASDKListKitTests/ASListTestSupplementaryNode.m new file mode 100644 index 0000000000..367200b77e --- /dev/null +++ b/SubspecWorkspaces/ASDKListKit/ASDKListKitTests/ASListTestSupplementaryNode.m @@ -0,0 +1,22 @@ +// +// ASListTestSupplementaryNode.m +// Texture +// +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import "ASListTestSupplementaryNode.h" + +@implementation ASListTestSupplementaryNode + +@end diff --git a/SubspecWorkspaces/ASDKListKit/ASDKListKitTests/ASListTestSupplementarySource.h b/SubspecWorkspaces/ASDKListKit/ASDKListKitTests/ASListTestSupplementarySource.h new file mode 100644 index 0000000000..37cb4816f4 --- /dev/null +++ b/SubspecWorkspaces/ASDKListKit/ASDKListKitTests/ASListTestSupplementarySource.h @@ -0,0 +1,29 @@ +// +// ASListTestSupplementarySource.h +// Texture +// +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +@interface ASListTestSupplementarySource : NSObject + +@property (nonatomic, strong, readwrite) NSArray *supportedElementKinds; + +@property (nonatomic, weak) id collectionContext; + +@property (nonatomic, weak) IGListSectionController *sectionController; + +@end diff --git a/ASDKListKit/ASDKListKitTests/ASListTestSupplementarySource.m b/SubspecWorkspaces/ASDKListKit/ASDKListKitTests/ASListTestSupplementarySource.m similarity index 58% rename from ASDKListKit/ASDKListKitTests/ASListTestSupplementarySource.m rename to SubspecWorkspaces/ASDKListKit/ASDKListKitTests/ASListTestSupplementarySource.m index 2184258a53..30e44749a1 100644 --- a/ASDKListKit/ASDKListKitTests/ASListTestSupplementarySource.m +++ b/SubspecWorkspaces/ASDKListKit/ASDKListKitTests/ASListTestSupplementarySource.m @@ -1,9 +1,18 @@ // // ASListTestSupplementarySource.m -// AsyncDisplayKit +// Texture // -// Created by Adlai Holler on 12/25/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 /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "ASListTestSupplementarySource.h" diff --git a/ASDKListKit/ASDKListKitTests/Info.plist b/SubspecWorkspaces/ASDKListKit/ASDKListKitTests/Info.plist similarity index 100% rename from ASDKListKit/ASDKListKitTests/Info.plist rename to SubspecWorkspaces/ASDKListKit/ASDKListKitTests/Info.plist diff --git a/SubspecWorkspaces/ASDKListKit/Podfile b/SubspecWorkspaces/ASDKListKit/Podfile new file mode 100644 index 0000000000..d5b84556fb --- /dev/null +++ b/SubspecWorkspaces/ASDKListKit/Podfile @@ -0,0 +1,9 @@ +source 'https://github.com/CocoaPods/Specs.git' + +platform :ios, '8.0' +target 'ASDKListKitTests' do + pod 'Texture/IGListKit', :path => '../..' + pod 'OCMock', '~> 3.4' + pod 'JGMethodSwizzler', :git => 'https://github.com/JonasGessner/JGMethodSwizzler', :branch => 'master' +end + diff --git a/Tests/ASAbsoluteLayoutSpecSnapshotTests.m b/Tests/ASAbsoluteLayoutSpecSnapshotTests.m index f4c238040a..c78dca2a49 100644 --- a/Tests/ASAbsoluteLayoutSpecSnapshotTests.m +++ b/Tests/ASAbsoluteLayoutSpecSnapshotTests.m @@ -1,13 +1,20 @@ // // ASAbsoluteLayoutSpecSnapshotTests.m -// AsyncDisplayKit +// Texture // // Created by Huy Nguyen on 18/10/15. // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "ASLayoutSpecSnapshotTestsHelper.h" diff --git a/Tests/ASBackgroundLayoutSpecSnapshotTests.mm b/Tests/ASBackgroundLayoutSpecSnapshotTests.mm index 31b5522d11..6429447331 100644 --- a/Tests/ASBackgroundLayoutSpecSnapshotTests.mm +++ b/Tests/ASBackgroundLayoutSpecSnapshotTests.mm @@ -1,11 +1,18 @@ // // ASOverlayLayoutSpecSnapshotTests.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "ASLayoutSpecSnapshotTestsHelper.h" diff --git a/Tests/ASBasicImageDownloaderContextTests.m b/Tests/ASBasicImageDownloaderContextTests.m index 8d5338817e..c92149234b 100644 --- a/Tests/ASBasicImageDownloaderContextTests.m +++ b/Tests/ASBasicImageDownloaderContextTests.m @@ -1,11 +1,18 @@ // // ASBasicImageDownloaderContextTests.m -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Tests/ASBasicImageDownloaderTests.m b/Tests/ASBasicImageDownloaderTests.m index c4d05244ca..2dd7d3375e 100644 --- a/Tests/ASBasicImageDownloaderTests.m +++ b/Tests/ASBasicImageDownloaderTests.m @@ -1,13 +1,20 @@ // // ASBasicImageDownloaderTests.m -// AsyncDisplayKit +// Texture // // Created by Victor Mayorov on 10/06/15. // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Tests/ASBatchFetchingTests.m b/Tests/ASBatchFetchingTests.m index b9fab3f480..97eab3576c 100644 --- a/Tests/ASBatchFetchingTests.m +++ b/Tests/ASBatchFetchingTests.m @@ -1,11 +1,18 @@ // // ASBatchFetchingTests.m -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -31,26 +38,26 @@ - (void)testBatchNullState { ASBatchContext *context = [[ASBatchContext alloc] init]; - BOOL shouldFetch = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionDown, ASScrollDirectionVerticalDirections, CGRectZero, CGSizeZero, CGPointZero, 0.0, YES); + BOOL shouldFetch = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionDown, ASScrollDirectionVerticalDirections, CGRectZero, CGSizeZero, CGPointZero, 0.0, YES, CGPointZero, nil); XCTAssert(shouldFetch == NO, @"Should not fetch in the null state"); } - (void)testBatchAlreadyFetching { ASBatchContext *context = [[ASBatchContext alloc] init]; [context beginBatchFetching]; - BOOL shouldFetch = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionDown, ASScrollDirectionVerticalDirections, PASSING_RECT, PASSING_SIZE, PASSING_POINT, 1.0, YES); + BOOL shouldFetch = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionDown, ASScrollDirectionVerticalDirections, PASSING_RECT, PASSING_SIZE, PASSING_POINT, 1.0, YES, CGPointZero, nil); XCTAssert(shouldFetch == NO, @"Should not fetch when context is already fetching"); } - (void)testUnsupportedScrollDirections { ASBatchContext *context = [[ASBatchContext alloc] init]; - BOOL fetchRight = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionRight, ASScrollDirectionHorizontalDirections, PASSING_RECT, PASSING_SIZE, PASSING_POINT, 1.0, YES); + BOOL fetchRight = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionRight, ASScrollDirectionHorizontalDirections, PASSING_RECT, PASSING_SIZE, PASSING_POINT, 1.0, YES, CGPointZero, nil); XCTAssert(fetchRight == YES, @"Should fetch for scrolling right"); - BOOL fetchDown = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionDown, ASScrollDirectionVerticalDirections, PASSING_RECT, PASSING_SIZE, PASSING_POINT, 1.0, YES); + BOOL fetchDown = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionDown, ASScrollDirectionVerticalDirections, PASSING_RECT, PASSING_SIZE, PASSING_POINT, 1.0, YES, CGPointZero, nil); XCTAssert(fetchDown == YES, @"Should fetch for scrolling down"); - BOOL fetchUp = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionUp, ASScrollDirectionVerticalDirections, PASSING_RECT, PASSING_SIZE, PASSING_POINT, 1.0, YES); + BOOL fetchUp = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionUp, ASScrollDirectionVerticalDirections, PASSING_RECT, PASSING_SIZE, PASSING_POINT, 1.0, YES, CGPointZero, nil); XCTAssert(fetchUp == NO, @"Should not fetch for scrolling up"); - BOOL fetchLeft = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionLeft, ASScrollDirectionHorizontalDirections, PASSING_RECT, PASSING_SIZE, PASSING_POINT, 1.0, YES); + BOOL fetchLeft = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionLeft, ASScrollDirectionHorizontalDirections, PASSING_RECT, PASSING_SIZE, PASSING_POINT, 1.0, YES, CGPointZero, nil); XCTAssert(fetchLeft == NO, @"Should not fetch for scrolling left"); } @@ -58,7 +65,7 @@ CGFloat screen = 1.0; ASBatchContext *context = [[ASBatchContext alloc] init]; // scroll to 1-screen top offset, height is 1 screen, so bottom is 1 screen away from end of content - BOOL shouldFetch = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionDown, ASScrollDirectionVerticalDirections, VERTICAL_RECT(screen), VERTICAL_SIZE(screen * 3.0), VERTICAL_OFFSET(screen * 1.0), 1.0, YES); + BOOL shouldFetch = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionDown, ASScrollDirectionVerticalDirections, VERTICAL_RECT(screen), VERTICAL_SIZE(screen * 3.0), VERTICAL_OFFSET(screen * 1.0), 1.0, YES, CGPointZero, nil); XCTAssert(shouldFetch == YES, @"Fetch should begin when vertically scrolling to exactly 1 leading screen away"); } @@ -66,7 +73,7 @@ CGFloat screen = 1.0; ASBatchContext *context = [[ASBatchContext alloc] init]; // 3 screens of content, scroll only 1/2 of one screen - BOOL shouldFetch = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionDown, ASScrollDirectionVerticalDirections, VERTICAL_RECT(screen), VERTICAL_SIZE(screen * 3.0), VERTICAL_OFFSET(screen * 0.5), 1.0, YES); + BOOL shouldFetch = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionDown, ASScrollDirectionVerticalDirections, VERTICAL_RECT(screen), VERTICAL_SIZE(screen * 3.0), VERTICAL_OFFSET(screen * 0.5), 1.0, YES, CGPointZero, nil); XCTAssert(shouldFetch == NO, @"Fetch should not begin when vertically scrolling less than the leading distance away"); } @@ -74,7 +81,7 @@ CGFloat screen = 1.0; ASBatchContext *context = [[ASBatchContext alloc] init]; // 3 screens of content, top offset to 3-screens, height 1 screen, so its 1 screen past the leading - BOOL shouldFetch = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionDown, ASScrollDirectionVerticalDirections, VERTICAL_RECT(screen), VERTICAL_SIZE(screen * 3.0), VERTICAL_OFFSET(screen * 3.0), 1.0, YES); + BOOL shouldFetch = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionDown, ASScrollDirectionVerticalDirections, VERTICAL_RECT(screen), VERTICAL_SIZE(screen * 3.0), VERTICAL_OFFSET(screen * 3.0), 1.0, YES, CGPointZero, nil); XCTAssert(shouldFetch == YES, @"Fetch should begin when vertically scrolling past the content size"); } @@ -82,7 +89,7 @@ CGFloat screen = 1.0; ASBatchContext *context = [[ASBatchContext alloc] init]; // scroll to 1-screen left offset, width is 1 screen, so right is 1 screen away from end of content - BOOL shouldFetch = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionRight, ASScrollDirectionVerticalDirections, HORIZONTAL_RECT(screen), HORIZONTAL_SIZE(screen * 3.0), HORIZONTAL_OFFSET(screen * 1.0), 1.0, YES); + BOOL shouldFetch = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionRight, ASScrollDirectionVerticalDirections, HORIZONTAL_RECT(screen), HORIZONTAL_SIZE(screen * 3.0), HORIZONTAL_OFFSET(screen * 1.0), 1.0, YES, CGPointZero, nil); XCTAssert(shouldFetch == YES, @"Fetch should begin when horizontally scrolling to exactly 1 leading screen away"); } @@ -90,7 +97,7 @@ CGFloat screen = 1.0; ASBatchContext *context = [[ASBatchContext alloc] init]; // 3 screens of content, scroll only 1/2 of one screen - BOOL shouldFetch = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionLeft, ASScrollDirectionHorizontalDirections, HORIZONTAL_RECT(screen), HORIZONTAL_SIZE(screen * 3.0), HORIZONTAL_OFFSET(screen * 0.5), 1.0, YES); + BOOL shouldFetch = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionLeft, ASScrollDirectionHorizontalDirections, HORIZONTAL_RECT(screen), HORIZONTAL_SIZE(screen * 3.0), HORIZONTAL_OFFSET(screen * 0.5), 1.0, YES, CGPointZero, nil); XCTAssert(shouldFetch == NO, @"Fetch should not begin when horizontally scrolling less than the leading distance away"); } @@ -98,7 +105,7 @@ CGFloat screen = 1.0; ASBatchContext *context = [[ASBatchContext alloc] init]; // 3 screens of content, left offset to 3-screens, width 1 screen, so its 1 screen past the leading - BOOL shouldFetch = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionDown, ASScrollDirectionHorizontalDirections, HORIZONTAL_RECT(screen), HORIZONTAL_SIZE(screen * 3.0), HORIZONTAL_OFFSET(screen * 3.0), 1.0, YES); + BOOL shouldFetch = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionDown, ASScrollDirectionHorizontalDirections, HORIZONTAL_RECT(screen), HORIZONTAL_SIZE(screen * 3.0), HORIZONTAL_OFFSET(screen * 3.0), 1.0, YES, CGPointZero, nil); XCTAssert(shouldFetch == YES, @"Fetch should begin when vertically scrolling past the content size"); } @@ -106,7 +113,7 @@ CGFloat screen = 1.0; ASBatchContext *context = [[ASBatchContext alloc] init]; // when the content size is < screen size, the target offset will always be 0 - BOOL shouldFetch = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionDown, ASScrollDirectionVerticalDirections, VERTICAL_RECT(screen), VERTICAL_SIZE(screen * 0.5), VERTICAL_OFFSET(0.0), 1.0, YES); + BOOL shouldFetch = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionDown, ASScrollDirectionVerticalDirections, VERTICAL_RECT(screen), VERTICAL_SIZE(screen * 0.5), VERTICAL_OFFSET(0.0), 1.0, YES, CGPointZero, nil); XCTAssert(shouldFetch == YES, @"Fetch should begin when the target is 0 and the content size is smaller than the scree"); } @@ -114,7 +121,7 @@ CGFloat screen = 1.0; ASBatchContext *context = [[ASBatchContext alloc] init]; // when the content size is < screen size, the target offset will always be 0 - BOOL shouldFetch = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionRight, ASScrollDirectionHorizontalDirections, HORIZONTAL_RECT(screen), HORIZONTAL_SIZE(screen * 0.5), HORIZONTAL_OFFSET(0.0), 1.0, YES); + BOOL shouldFetch = ASDisplayShouldFetchBatchForContext(context, ASScrollDirectionRight, ASScrollDirectionHorizontalDirections, HORIZONTAL_RECT(screen), HORIZONTAL_SIZE(screen * 0.5), HORIZONTAL_OFFSET(0.0), 1.0, YES, CGPointZero, nil); XCTAssert(shouldFetch == YES, @"Fetch should begin when the target is 0 and the content size is smaller than the scree"); } diff --git a/Tests/ASBridgedPropertiesTests.mm b/Tests/ASBridgedPropertiesTests.mm index b0770a9113..82b5d3350c 100644 --- a/Tests/ASBridgedPropertiesTests.mm +++ b/Tests/ASBridgedPropertiesTests.mm @@ -1,13 +1,20 @@ // // ASBridgedPropertiesTests.mm -// AsyncDisplayKit +// Texture // // Created by Adlai Holler on 1/7/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. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Tests/ASCALayerTests.m b/Tests/ASCALayerTests.m index d52d6ec30a..1ff20d396c 100644 --- a/Tests/ASCALayerTests.m +++ b/Tests/ASCALayerTests.m @@ -1,15 +1,23 @@ // // ASCALayerTests.m -// AsyncDisplayKit +// Texture // -// Created by Adlai Holler on 9/2/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 /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import #import -#import /** * Tests that confirm what we know about Core Animation behavior. diff --git a/Tests/ASCenterLayoutSpecSnapshotTests.mm b/Tests/ASCenterLayoutSpecSnapshotTests.mm index 98370e8002..2275b14ce1 100644 --- a/Tests/ASCenterLayoutSpecSnapshotTests.mm +++ b/Tests/ASCenterLayoutSpecSnapshotTests.mm @@ -1,11 +1,18 @@ // // ASCenterLayoutSpecSnapshotTests.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "ASLayoutSpecSnapshotTestsHelper.h" diff --git a/Tests/ASCollectionModernDataSourceTests.m b/Tests/ASCollectionModernDataSourceTests.m new file mode 100644 index 0000000000..ec97e8c644 --- /dev/null +++ b/Tests/ASCollectionModernDataSourceTests.m @@ -0,0 +1,368 @@ +// +// ASCollectionModernDataSourceTests.m +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import +#import +#import "OCMockObject+ASAdditions.h" +#import "ASTestCase.h" + +@interface ASCollectionModernDataSourceTests : ASTestCase +@end + +@interface ASTestCellNode : ASCellNode +@end + +@interface ASTestSection : NSObject +@property (nonatomic, readonly) NSMutableArray *viewModels; +@end + +@implementation ASCollectionModernDataSourceTests { +@private + id mockDataSource; + UIWindow *window; + UIViewController *viewController; + ASCollectionNode *collectionNode; + NSMutableArray *sections; +} + +- (void)setUp { + [super setUp]; + // Default is 2 sections: 2 items in first, 1 item in second. + sections = [NSMutableArray array]; + [sections addObject:[ASTestSection new]]; + [sections[0].viewModels addObject:[NSObject new]]; + [sections[0].viewModels addObject:[NSObject new]]; + [sections addObject:[ASTestSection new]]; + [sections[1].viewModels addObject:[NSObject new]]; + window = [[UIWindow alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]; + viewController = [[UIViewController alloc] init]; + + window.rootViewController = viewController; + [window makeKeyAndVisible]; + collectionNode = [[ASCollectionNode alloc] initWithCollectionViewLayout:[UICollectionViewFlowLayout new]]; + collectionNode.frame = viewController.view.bounds; + collectionNode.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + [viewController.view addSubnode:collectionNode]; + + mockDataSource = OCMStrictProtocolMock(@protocol(ASCollectionDataSource)); + [mockDataSource addImplementedOptionalProtocolMethods: + @selector(numberOfSectionsInCollectionNode:), + @selector(collectionNode:numberOfItemsInSection:), + @selector(collectionNode:nodeBlockForItemAtIndexPath:), + @selector(collectionNode:viewModelForItemAtIndexPath:), + @selector(collectionNode:contextForSection:), + nil]; + [mockDataSource setExpectationOrderMatters:YES]; + + // NOTE: Adding optionally-implemented methods after this point won't work due to ASCollectionNode selector caching. + collectionNode.dataSource = mockDataSource; +} + +- (void)tearDown +{ + [collectionNode waitUntilAllUpdatesAreCommitted]; + [super tearDown]; +} + +#pragma mark - Test Methods + +- (void)testInitialDataLoading +{ + [self loadInitialData]; +} + +- (void)testReloadingAnItem +{ + [self loadInitialData]; + + // Reload at (0, 0) + NSIndexPath *reloadedPath = [NSIndexPath indexPathForItem:0 inSection:0]; + + [self performUpdateReloadingSections:nil + reloadingItems:@{ reloadedPath: [NSObject new] } + reloadMappings:@{ reloadedPath: reloadedPath } + insertingItems:nil + deletingItems:nil + skippedReloadIndexPaths:nil]; +} + +- (void)testInsertingAnItem +{ + [self loadInitialData]; + + // Insert at (1, 0) + NSIndexPath *insertedPath = [NSIndexPath indexPathForItem:0 inSection:1]; + + [self performUpdateReloadingSections:nil + reloadingItems:nil + reloadMappings:nil + insertingItems:@{ insertedPath: [NSObject new] } + deletingItems:nil + skippedReloadIndexPaths:nil]; +} + +- (void)testReloadingAnItemWithACompatibleViewModel +{ + [self loadInitialData]; + + // Reload and delete together, for good measure. + NSIndexPath *reloadedPath = [NSIndexPath indexPathForItem:1 inSection:0]; + NSIndexPath *deletedPath = [NSIndexPath indexPathForItem:0 inSection:0]; + + id viewModel = [NSObject new]; + + // Cell node should get -canUpdateToViewModel: + id mockCellNode = [collectionNode nodeForItemAtIndexPath:reloadedPath]; + OCMExpect([mockCellNode canUpdateToViewModel:viewModel]) + .andReturn(YES); + + [self performUpdateReloadingSections:nil + reloadingItems:@{ reloadedPath: viewModel } + reloadMappings:@{ reloadedPath: [NSIndexPath indexPathForItem:0 inSection:0] } + insertingItems:nil + deletingItems:@[ deletedPath ] + skippedReloadIndexPaths:@[ reloadedPath ]]; +} + +- (void)testReloadingASection +{ + [self loadInitialData]; + + [self performUpdateReloadingSections:@{ @0: [ASTestSection new] } + reloadingItems:nil + reloadMappings:nil + insertingItems:nil + deletingItems:nil + skippedReloadIndexPaths:nil]; +} + +#pragma mark - Helpers + +- (void)loadInitialData +{ + // Count methods are called twice in a row for first data load. + // Since -reloadData is routed through our batch update system, + // the batch update latches the "old data source counts" if needed at -beginUpdates time + // and then verifies them against the "new data source counts" after the updates. + // This isn't ideal, but the cost is very small and the system works well. + for (int i = 0; i < 2; i++) { + // It reads all the counts + [self expectDataSourceCountMethods]; + } + + // It reads each section object. + for (NSInteger section = 0; section < sections.count; section++) { + [self expectContextMethodForSection:section]; + } + + // It reads the contents for each item. + for (NSInteger section = 0; section < sections.count; section++) { + NSArray *viewModels = sections[section].viewModels; + + // For each item: + for (NSInteger i = 0; i < viewModels.count; i++) { + NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:section]; + [self expectViewModelMethodForItemAtIndexPath:indexPath viewModel:viewModels[i]]; + [self expectNodeBlockMethodForItemAtIndexPath:indexPath]; + } + } + + [window layoutIfNeeded]; + + // Assert item counts & content: + [self assertCollectionNodeContent]; +} + +/** + * Adds expectations for the sequence: + * + * numberOfSectionsInCollectionNode: + * for section in countsArray + * numberOfItemsInSection: + */ +- (void)expectDataSourceCountMethods +{ + // -numberOfSectionsInCollectionNode + OCMExpect([mockDataSource numberOfSectionsInCollectionNode:collectionNode]) + .andReturn(sections.count); + + // For each section: + // Note: Skip fast enumeration for readability. + for (NSInteger section = 0; section < sections.count; section++) { + OCMExpect([mockDataSource collectionNode:collectionNode numberOfItemsInSection:section]) + .andReturn(sections[section].viewModels.count); + } +} + +- (void)expectViewModelMethodForItemAtIndexPath:(NSIndexPath *)indexPath viewModel:(id)viewModel +{ + OCMExpect([mockDataSource collectionNode:collectionNode viewModelForItemAtIndexPath:indexPath]) + .andReturn(viewModel); +} + +- (void)expectContextMethodForSection:(NSInteger)section +{ + OCMExpect([mockDataSource collectionNode:collectionNode contextForSection:section]) + .andReturn(sections[section]); +} + +- (void)expectNodeBlockMethodForItemAtIndexPath:(NSIndexPath *)indexPath +{ + OCMExpect([mockDataSource collectionNode:collectionNode nodeBlockForItemAtIndexPath:indexPath]) + .andReturn((ASCellNodeBlock)^{ + ASCellNode *node = [ASTestCellNode new]; + // Generating multiple partial mocks of the same class is not thread-safe. + id mockNode; + @synchronized (NSNull.null) { + mockNode = OCMPartialMock(node); + } + [mockNode setExpectationOrderMatters:YES]; + return mockNode; + }); +} + +/// Asserts that counts match and all view-models are up-to-date between us and collectionNode. +- (void)assertCollectionNodeContent +{ + // Assert section count + XCTAssertEqual(collectionNode.numberOfSections, sections.count); + + for (NSInteger section = 0; section < sections.count; section++) { + ASTestSection *sectionObject = sections[section]; + NSArray *viewModels = sectionObject.viewModels; + + // Assert section object + XCTAssertEqualObjects([collectionNode contextForSection:section], sectionObject); + + // Assert item count + XCTAssertEqual([collectionNode numberOfItemsInSection:section], viewModels.count); + for (NSInteger item = 0; item < viewModels.count; item++) { + // Assert view model + // Could use pointer equality but the error message is less readable. + NSIndexPath *indexPath = [NSIndexPath indexPathForItem:item inSection:section]; + id viewModel = viewModels[indexPath.item]; + XCTAssertEqualObjects(viewModel, [collectionNode viewModelForItemAtIndexPath:indexPath]); + ASCellNode *node = [collectionNode nodeForItemAtIndexPath:indexPath]; + XCTAssertEqualObjects(node.viewModel, viewModel); + } + } +} + +/** + * Updates the collection node, with expectations and assertions about the call-order and the correctness of the + * new data. You should update the data source _before_ calling this method. + * + * skippedReloadIndexPaths are the old index paths for nodes that should use -canUpdateToViewModel: instead of being refetched. + */ +- (void)performUpdateReloadingSections:(NSDictionary *)reloadedSections + reloadingItems:(NSDictionary *)reloadedItems + reloadMappings:(NSDictionary *)reloadMappings + insertingItems:(NSDictionary *)insertedItems + deletingItems:(NSArray *)deletedItems + skippedReloadIndexPaths:(NSArray *)skippedReloadIndexPaths +{ + [collectionNode performBatchUpdates:^{ + // First update our data source. + [reloadedItems enumerateKeysAndObjectsUsingBlock:^(NSIndexPath * _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) { + sections[key.section].viewModels[key.item] = obj; + }]; + [reloadedSections enumerateKeysAndObjectsUsingBlock:^(NSNumber * _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) { + sections[key.integerValue] = obj; + }]; + + // Deletion paths, sorted descending + for (NSIndexPath *indexPath in [deletedItems sortedArrayUsingSelector:@selector(compare:)].reverseObjectEnumerator) { + [sections[indexPath.section].viewModels removeObjectAtIndex:indexPath.item]; + } + + // Insertion paths, sorted ascending. + NSArray *insertionsSortedAcending = [insertedItems.allKeys sortedArrayUsingSelector:@selector(compare:)]; + for (NSIndexPath *indexPath in insertionsSortedAcending) { + [sections[indexPath.section].viewModels insertObject:insertedItems[indexPath] atIndex:indexPath.item]; + } + + // Then update the collection node. + NSMutableIndexSet *reloadedSectionIndexes = [NSMutableIndexSet indexSet]; + for (NSNumber *i in reloadedSections) { + [reloadedSectionIndexes addIndex:i.integerValue]; + } + [collectionNode reloadSections:reloadedSectionIndexes]; + [collectionNode reloadItemsAtIndexPaths:reloadedItems.allKeys]; + [collectionNode deleteItemsAtIndexPaths:deletedItems]; + [collectionNode insertItemsAtIndexPaths:insertedItems.allKeys]; + + // Before the commit, lay out our expectations. + + // Expect it to load the new counts. + [self expectDataSourceCountMethods]; + + // Combine reloads + inserts and expect them to load content for all of them, in ascending order. + NSMutableDictionary *insertsPlusReloads = [NSMutableDictionary dictionary]; + [insertsPlusReloads addEntriesFromDictionary:insertedItems]; + + // Go through reloaded sections and add all their items into `insertsPlusReloads` + [reloadedSectionIndexes enumerateIndexesUsingBlock:^(NSUInteger section, BOOL * _Nonnull stop) { + [self expectContextMethodForSection:section]; + NSArray *viewModels = sections[section].viewModels; + for (NSInteger i = 0; i < viewModels.count; i++) { + NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:section]; + insertsPlusReloads[indexPath] = viewModels[i]; + } + }]; + + [reloadedItems enumerateKeysAndObjectsUsingBlock:^(NSIndexPath * _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) { + insertsPlusReloads[reloadMappings[key]] = obj; + }]; + + for (NSIndexPath *indexPath in [insertsPlusReloads.allKeys sortedArrayUsingSelector:@selector(compare:)]) { + [self expectViewModelMethodForItemAtIndexPath:indexPath viewModel:insertsPlusReloads[indexPath]]; + NSIndexPath *oldIndexPath = [reloadMappings allKeysForObject:indexPath].firstObject; + BOOL isSkippedReload = oldIndexPath && [skippedReloadIndexPaths containsObject:oldIndexPath]; + if (!isSkippedReload) { + [self expectNodeBlockMethodForItemAtIndexPath:indexPath]; + } + } + } completion:nil]; + + // Assert that the counts and view models are all correct now. + [self assertCollectionNodeContent]; +} + +@end + +#pragma mark - Other Objects + +@implementation ASTestCellNode + +- (BOOL)canUpdateToViewModel:(id)viewModel +{ + // Our tests default to NO for migrating view models. We use OCMExpect to return YES when we specifically want to. + return NO; +} + +@end + +@implementation ASTestSection +@synthesize collectionView; +@synthesize sectionName; + +- (instancetype)init +{ + if (self = [super init]) { + _viewModels = [NSMutableArray array]; + } + return self; +} + +@end diff --git a/Tests/ASCollectionViewFlowLayoutInspectorTests.m b/Tests/ASCollectionViewFlowLayoutInspectorTests.m index 3dd8d76885..f24a1b0f54 100644 --- a/Tests/ASCollectionViewFlowLayoutInspectorTests.m +++ b/Tests/ASCollectionViewFlowLayoutInspectorTests.m @@ -1,11 +1,18 @@ // // ASCollectionViewFlowLayoutInspectorTests.m -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Tests/ASCollectionViewTests.mm b/Tests/ASCollectionViewTests.mm index 7244aaa5a6..00cf58d2b5 100644 --- a/Tests/ASCollectionViewTests.mm +++ b/Tests/ASCollectionViewTests.mm @@ -1,11 +1,18 @@ // -// ASCollectionViewTests.m -// AsyncDisplayKit +// ASCollectionViewTests.mm +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -114,7 +121,7 @@ - (ASCellNode *)collectionView:(ASCollectionView *)collectionView nodeForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { - return [[ASCellNode alloc] init]; + return [[ASTextCellNodeWithSetSelectedCounter alloc] init]; } - (void)collectionNode:(ASCollectionNode *)collectionNode willBeginBatchFetchWithContext:(ASBatchContext *)context @@ -252,7 +259,8 @@ [window setRootViewController:testController]; [window makeKeyAndVisible]; - [testController.collectionView reloadDataImmediately]; + [testController.collectionNode reloadData]; + [testController.collectionNode waitUntilAllUpdatesAreCommitted]; [testController.collectionView layoutIfNeeded]; NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:0]; @@ -383,12 +391,13 @@ ASCollectionViewTestController *testController = [[ASCollectionViewTestController alloc] initWithNibName:nil bundle:nil];\ __unused ASCollectionViewTestDelegate *del = testController.asyncDelegate;\ __unused ASCollectionView *cv = testController.collectionView;\ - __unused ASCollectionNode *cn = testController.collectionNode;\ + ASCollectionNode *cn = testController.collectionNode;\ UIWindow *window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];\ [window makeKeyAndVisible]; \ window.rootViewController = testController;\ \ - [testController.collectionView reloadDataImmediately];\ + [cn reloadData];\ + [cn waitUntilAllUpdatesAreCommitted]; \ [testController.collectionView layoutIfNeeded]; - (void)testThatSubmittingAValidInsertDoesNotThrowAnException @@ -487,12 +496,24 @@ updateValidationTestPrologue NSSet *nodeBatch1 = [NSSet setWithArray:[cn visibleNodes]]; XCTAssertGreaterThan(nodeBatch1.count, 0); + + NSArray *visibleLayoutAttributesBatch1 = [cv.collectionViewLayout layoutAttributesForElementsInRect:cv.bounds]; + XCTAssertGreaterThan(visibleLayoutAttributesBatch1.count, 0); // Expect all visible nodes get 1 applyLayoutAttributes and have a non-nil value. for (ASTextCellNodeWithSetSelectedCounter *node in nodeBatch1) { XCTAssertEqual(node.applyLayoutAttributesCount, 1, @"Expected applyLayoutAttributes to be called exactly once for visible nodes."); XCTAssertNotNil(node.layoutAttributes, @"Expected layoutAttributes to be non-nil for visible cell node."); } + + for (UICollectionViewLayoutAttributes *layoutAttributes in visibleLayoutAttributesBatch1) { + if (layoutAttributes.representedElementCategory != UICollectionElementCategorySupplementaryView) { + continue; + } + ASTextCellNodeWithSetSelectedCounter *node = (ASTextCellNodeWithSetSelectedCounter *)[cv supplementaryNodeForElementKind:layoutAttributes.representedElementKind atIndexPath:layoutAttributes.indexPath]; + XCTAssertEqual(node.applyLayoutAttributesCount, 1, @"Expected applyLayoutAttributes to be called exactly once for visible supplementary nodes."); + XCTAssertNotNil(node.layoutAttributes, @"Expected layoutAttributes to be non-nil for visible supplementary node."); + } // Scroll to next batch of items. NSIndexPath *nextIP = [NSIndexPath indexPathForItem:nodeBatch1.count inSection:0]; @@ -508,6 +529,15 @@ XCTAssertEqual(node.applyLayoutAttributesCount, 1, @"Expected applyLayoutAttributes to be called exactly once for visible nodes, even after node is removed."); XCTAssertNil(node.layoutAttributes, @"Expected layoutAttributes to be nil for removed cell node."); } + + for (UICollectionViewLayoutAttributes *layoutAttributes in visibleLayoutAttributesBatch1) { + if (layoutAttributes.representedElementCategory != UICollectionElementCategorySupplementaryView) { + continue; + } + ASTextCellNodeWithSetSelectedCounter *node = (ASTextCellNodeWithSetSelectedCounter *)[cv supplementaryNodeForElementKind:layoutAttributes.representedElementKind atIndexPath:layoutAttributes.indexPath]; + XCTAssertEqual(node.applyLayoutAttributesCount, 1, @"Expected applyLayoutAttributes to be called exactly once for visible supplementary nodes, even after node is removed."); + XCTAssertNil(node.layoutAttributes, @"Expected layoutAttributes to be nil for removed supplementary node."); + } } - (void)testCellNodeIndexPathConsistency @@ -592,7 +622,7 @@ for (NSInteger i = 0; i < 2; i++) { // NOTE: waitUntilAllUpdatesAreCommitted or reloadDataImmediately is not sufficient here!! XCTestExpectation *done = [self expectationWithDescription:[NSString stringWithFormat:@"Reload #%td complete", i]]; - [cv reloadDataWithCompletion:^{ + [cn reloadDataWithCompletion:^{ [done fulfill]; }]; [self waitForExpectationsWithTimeout:1 handler:nil]; @@ -724,7 +754,8 @@ updateValidationTestPrologue del.sectionGeneration++; - [cv reloadDataImmediately]; + [cn reloadData]; + [cn waitUntilAllUpdatesAreCommitted]; NSInteger sectionCount = del->_itemCounts.size(); for (NSInteger section = 0; section < sectionCount; section++) { diff --git a/Tests/ASControlNodeTests.m b/Tests/ASControlNodeTests.m index 7988b2c5bb..1961037e2f 100644 --- a/Tests/ASControlNodeTests.m +++ b/Tests/ASControlNodeTests.m @@ -1,11 +1,18 @@ // // ASControlNodeTests.m -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Tests/ASDimensionTests.mm b/Tests/ASDimensionTests.mm index 5c37dead3a..6832dc5e88 100644 --- a/Tests/ASDimensionTests.mm +++ b/Tests/ASDimensionTests.mm @@ -1,11 +1,18 @@ // // ASDimensionTests.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -22,7 +29,7 @@ { XCTAssertNoThrow(ASDimensionMake(ASDimensionUnitAuto, 0)); XCTAssertThrows(ASDimensionMake(ASDimensionUnitAuto, 100)); - XCTAssertThrows(ASDimensionMake(@"")); + ASXCTAssertEqualDimensions(ASDimensionAuto, ASDimensionMake(@"")); ASXCTAssertEqualDimensions(ASDimensionAuto, ASDimensionMake(@"auto")); } diff --git a/Tests/ASDispatchTests.m b/Tests/ASDispatchTests.m index 8b35b5a0d7..f159f5e9da 100644 --- a/Tests/ASDispatchTests.m +++ b/Tests/ASDispatchTests.m @@ -1,9 +1,18 @@ // // ASDispatchTests.m -// AsyncDisplayKit +// Texture // -// Created by Adlai Holler on 8/25/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 /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -35,4 +44,29 @@ XCTAssertEqualObjects(indices, [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, iterations)]); } +- (void)testDispatchAsync +{ + dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + NSInteger expectedThreadCount = [NSProcessInfo processInfo].activeProcessorCount * 2; + NSLock *lock = [NSLock new]; + NSMutableSet *threads = [NSMutableSet set]; + NSMutableIndexSet *indices = [NSMutableIndexSet indexSet]; + XCTestExpectation *expectation = [self expectationWithDescription:@"Executed all blocks"]; + + size_t const iterations = 1E5; + ASDispatchAsync(iterations, q, 0, ^(size_t i) { + [lock lock]; + [threads addObject:[NSThread currentThread]]; + XCTAssertFalse([indices containsIndex:i]); + [indices addIndex:i]; + if (indices.count == iterations) { + [expectation fulfill]; + } + [lock unlock]; + }); + [self waitForExpectationsWithTimeout:10 handler:nil]; + XCTAssertLessThanOrEqual(threads.count, expectedThreadCount); + XCTAssertEqualObjects(indices, [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, iterations)]); +} + @end diff --git a/Tests/ASDisplayLayerTests.m b/Tests/ASDisplayLayerTests.m index c0a1cb52fa..1abcc71541 100644 --- a/Tests/ASDisplayLayerTests.m +++ b/Tests/ASDisplayLayerTests.m @@ -1,11 +1,18 @@ // // ASDisplayLayerTests.m -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Tests/ASDisplayNodeAppearanceTests.m b/Tests/ASDisplayNodeAppearanceTests.m index 37fa20488d..f636e7830e 100644 --- a/Tests/ASDisplayNodeAppearanceTests.m +++ b/Tests/ASDisplayNodeAppearanceTests.m @@ -1,11 +1,18 @@ // // ASDisplayNodeAppearanceTests.m -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Tests/ASDisplayNodeExtrasTests.m b/Tests/ASDisplayNodeExtrasTests.m index 6f1731d211..bd8e2556f9 100644 --- a/Tests/ASDisplayNodeExtrasTests.m +++ b/Tests/ASDisplayNodeExtrasTests.m @@ -1,6 +1,6 @@ // // ASDisplayNodeExtrasTests.m -// AsyncDisplayKit +// Texture // // Created by Kiel Gillard on 27/06/2016. // Copyright © 2016 Facebook. All rights reserved. diff --git a/Tests/ASDisplayNodeImplicitHierarchyTests.m b/Tests/ASDisplayNodeImplicitHierarchyTests.m index 5d99275b1b..a3a7e160f3 100644 --- a/Tests/ASDisplayNodeImplicitHierarchyTests.m +++ b/Tests/ASDisplayNodeImplicitHierarchyTests.m @@ -1,13 +1,20 @@ // // ASDisplayNodeImplicitHierarchyTests.m -// AsyncDisplayKit +// Texture // // Created by Levi McCallum on 2/1/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. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Tests/ASDisplayNodeLayoutTests.mm b/Tests/ASDisplayNodeLayoutTests.mm index 435e891cde..a998e98285 100644 --- a/Tests/ASDisplayNodeLayoutTests.mm +++ b/Tests/ASDisplayNodeLayoutTests.mm @@ -1,17 +1,25 @@ // -// ASDisplayNodeLayoutTests.m -// AsyncDisplayKit +// ASDisplayNodeLayoutTests.mm +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "ASXCTExtensions.h" #import #import "ASLayoutSpecSnapshotTestsHelper.h" #import +#import @interface ASDisplayNodeLayoutTests : XCTestCase @end @@ -86,9 +94,9 @@ ASButtonNode *buttonNode = [ASButtonNode new]; [displayNode addSubnode:buttonNode]; - __block size_t numberOfLayoutSpecThatFitsCalls = 0; + __block atomic_int numberOfLayoutSpecThatFitsCalls = ATOMIC_VAR_INIT(0); displayNode.layoutSpecBlock = ^(ASDisplayNode * _Nonnull node, ASSizeRange constrainedSize) { - __sync_fetch_and_add(&numberOfLayoutSpecThatFitsCalls, 1); + atomic_fetch_add(&numberOfLayoutSpecThatFitsCalls, 1); return [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsZero child:buttonNode]; }; diff --git a/Tests/ASDisplayNodeSnapshotTests.m b/Tests/ASDisplayNodeSnapshotTests.m index 203a46b94d..4062be015e 100644 --- a/Tests/ASDisplayNodeSnapshotTests.m +++ b/Tests/ASDisplayNodeSnapshotTests.m @@ -1,6 +1,6 @@ // // ASDisplayNodeSnapshotTests.m -// AsyncDisplayKit +// Texture // // Created by Adlai Holler on 8/16/16. // Copyright © 2016 Facebook. All rights reserved. diff --git a/Tests/ASDisplayNodeTests.mm b/Tests/ASDisplayNodeTests.mm index 39c2d68486..12801b3a33 100644 --- a/Tests/ASDisplayNodeTests.mm +++ b/Tests/ASDisplayNodeTests.mm @@ -1,11 +1,18 @@ // -// ASDisplayNodeTests.m -// AsyncDisplayKit +// ASDisplayNodeTests.mm +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -230,13 +237,6 @@ for (ASDisplayNode *n in @[ nodes ]) {\ XCTAssertFalse([node becomeFirstResponder]); } -- (void)testLayerBackedFirstResponderBehavior { - ASTestDisplayNode *node = [[ASTestResponderNode alloc] init]; - node.layerBacked = YES; - XCTAssertTrue([node canBecomeFirstResponder]); - XCTAssertFalse([node becomeFirstResponder]); -} - - (void)setUp { [super setUp]; @@ -1885,10 +1885,8 @@ static bool stringContainsPointer(NSString *description, id p) { node.debugName = @"big troll eater name"; XCTAssertTrue([node.description containsString:node.debugName], @"debugName didn't end up in description"); - XCTAssertTrue([node.description containsString:@"debugName"], @"Node description should contain `debugName`."); [node layer]; XCTAssertTrue([node.description containsString:node.debugName], @"debugName didn't end up in description"); - XCTAssertTrue([node.description containsString:@"debugName"], @"Node description should contain `debugName`."); } - (void)testNameInDescriptionLayer @@ -1982,7 +1980,7 @@ static bool stringContainsPointer(NSString *description, id p) { - (void)testThatRasterizedNodesGetInterfaceStateUpdatesWhenContainerEntersHierarchy { ASDisplayNode *supernode = [[ASDisplayNode alloc] init]; - supernode.shouldRasterizeDescendants = YES; + [supernode enableSubtreeRasterization]; ASDisplayNode *subnode = [[ASDisplayNode alloc] init]; ASSetDebugNames(supernode, subnode); UIWindow *window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; @@ -2000,7 +1998,7 @@ static bool stringContainsPointer(NSString *description, id p) { - (void)testThatRasterizedNodesGetInterfaceStateUpdatesWhenAddedToContainerThatIsInHierarchy { ASDisplayNode *supernode = [[ASDisplayNode alloc] init]; - supernode.shouldRasterizeDescendants = YES; + [supernode enableSubtreeRasterization]; ASDisplayNode *subnode = [[ASDisplayNode alloc] init]; ASSetDebugNames(supernode, subnode); @@ -2015,52 +2013,10 @@ static bool stringContainsPointer(NSString *description, id p) { XCTAssertFalse(subnode.isVisible); } -- (void)testThatLoadedNodeGetsUnloadedIfSubtreeBecomesRasterized -{ - ASDisplayNode *supernode = [[ASDisplayNode alloc] init]; - [supernode view]; - ASDisplayNode *subnode = [[ASDisplayNode alloc] init]; - ASSetDebugNames(supernode, subnode); - [supernode addSubnode:subnode]; - XCTAssertTrue(subnode.nodeLoaded); - supernode.shouldRasterizeDescendants = YES; - XCTAssertFalse(subnode.nodeLoaded); -} - -- (void)testThatLoadedNodeGetsUnloadedIfAddedToRasterizedSubtree -{ - ASDisplayNode *supernode = [[ASDisplayNode alloc] init]; - supernode.shouldRasterizeDescendants = YES; - ASDisplayNode *subnode = [[ASDisplayNode alloc] init]; - ASSetDebugNames(supernode, subnode); - [subnode view]; - XCTAssertTrue(subnode.nodeLoaded); - [supernode addSubnode:subnode]; - XCTAssertFalse(subnode.nodeLoaded); - XCTAssertTrue(ASHierarchyStateIncludesRasterized(subnode.hierarchyState)); -} - -- (void)testThatClearingRasterizationBitMidwayDownTheTreeWorksRight -{ - ASDisplayNode *topNode = [[ASDisplayNode alloc] init]; - topNode.shouldRasterizeDescendants = YES; - ASDisplayNode *middleNode = [[ASDisplayNode alloc] init]; - middleNode.shouldRasterizeDescendants = YES; - ASDisplayNode *bottomNode = [[ASDisplayNode alloc] init]; - ASSetDebugNames(topNode, middleNode, bottomNode); - [middleNode addSubnode:bottomNode]; - [topNode addSubnode:middleNode]; - XCTAssertTrue(ASHierarchyStateIncludesRasterized(bottomNode.hierarchyState)); - XCTAssertTrue(ASHierarchyStateIncludesRasterized(middleNode.hierarchyState)); - middleNode.shouldRasterizeDescendants = NO; - XCTAssertTrue(ASHierarchyStateIncludesRasterized(bottomNode.hierarchyState)); - XCTAssertTrue(ASHierarchyStateIncludesRasterized(middleNode.hierarchyState)); -} - - (void)testThatRasterizingWrapperNodesIsNotAllowed { ASDisplayNode *rasterizedSupernode = [[ASDisplayNode alloc] init]; - rasterizedSupernode.shouldRasterizeDescendants = YES; + [rasterizedSupernode enableSubtreeRasterization]; ASDisplayNode *subnode = [[ASDisplayNode alloc] initWithViewBlock:^UIView * _Nonnull{ return [[UIView alloc] init]; }]; @@ -2115,7 +2071,7 @@ static bool stringContainsPointer(NSString *description, id p) { { ASTestDisplayNode *node = [[ASTestDisplayNode alloc] init]; node.debugName = @"Node"; - node.shouldRasterizeDescendants = YES; + [node enableSubtreeRasterization]; ASTestDisplayNode *subnode = [[ASTestDisplayNode alloc] init]; subnode.debugName = @"Subnode"; @@ -2307,4 +2263,21 @@ static bool stringContainsPointer(NSString *description, id p) { ASXCTAssertEqualPoints([node convertPoint:node.bounds.origin toNode:nil], expectedOrigin); } +- (void)testThatItIsAllowedToRetrieveDebugDescriptionIncludingVCOffMainThread +{ + ASDisplayNode *node = [[ASDisplayNode alloc] init]; + UIViewController *vc = [[UIViewController alloc] init]; + [vc.view addSubnode:node]; + dispatch_group_t g = dispatch_group_create(); + __block NSString *debugDescription; + dispatch_group_async(g, dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0), ^{ + debugDescription = [node debugDescription]; + }); + dispatch_group_wait(g, DISPATCH_TIME_FOREVER); + // Ensure the debug description contains the VC string. + // Have to split into two lines because XCTAssert macro can't handle the stringWithFormat:. + BOOL hasVC = [debugDescription containsString:[NSString stringWithFormat:@"%p", vc]]; + XCTAssert(hasVC); +} + @end diff --git a/Tests/ASDisplayNodeTestsHelper.h b/Tests/ASDisplayNodeTestsHelper.h index 21a149e4b2..4473500048 100644 --- a/Tests/ASDisplayNodeTestsHelper.h +++ b/Tests/ASDisplayNodeTestsHelper.h @@ -1,11 +1,18 @@ // // ASDisplayNodeTestsHelper.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Tests/ASDisplayNodeTestsHelper.m b/Tests/ASDisplayNodeTestsHelper.m index 473a17a37c..05397b83b1 100644 --- a/Tests/ASDisplayNodeTestsHelper.m +++ b/Tests/ASDisplayNodeTestsHelper.m @@ -1,11 +1,18 @@ // // ASDisplayNodeTestsHelper.m -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "ASDisplayNodeTestsHelper.h" diff --git a/Tests/ASEditableTextNodeTests.m b/Tests/ASEditableTextNodeTests.m index 7ac4ad5013..8e9b3b5074 100644 --- a/Tests/ASEditableTextNodeTests.m +++ b/Tests/ASEditableTextNodeTests.m @@ -1,13 +1,20 @@ // // ASEditableTextNodeTests.m -// AsyncDisplayKit +// Texture // // Created by Michael Schneider on 5/31/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. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Tests/ASImageNodeSnapshotTests.m b/Tests/ASImageNodeSnapshotTests.m index bb9c730d3d..c3bdb8cd36 100644 --- a/Tests/ASImageNodeSnapshotTests.m +++ b/Tests/ASImageNodeSnapshotTests.m @@ -1,11 +1,18 @@ // // ASImageNodeSnapshotTests.m -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "ASSnapshotTestCase.h" diff --git a/Tests/ASInsetLayoutSpecSnapshotTests.mm b/Tests/ASInsetLayoutSpecSnapshotTests.mm index 52cfab331c..275b7cde03 100644 --- a/Tests/ASInsetLayoutSpecSnapshotTests.mm +++ b/Tests/ASInsetLayoutSpecSnapshotTests.mm @@ -1,11 +1,18 @@ // // ASInsetLayoutSpecSnapshotTests.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "ASLayoutSpecSnapshotTestsHelper.h" diff --git a/Tests/ASIntegerMapTests.m b/Tests/ASIntegerMapTests.m new file mode 100644 index 0000000000..7afdccf1c3 --- /dev/null +++ b/Tests/ASIntegerMapTests.m @@ -0,0 +1,117 @@ +// +// ASIntegerMapTests.m +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import "ASTestCase.h" +#import "ASIntegerMap.h" + +@interface ASIntegerMapTests : ASTestCase + +@end + +@implementation ASIntegerMapTests + +- (void)testIsEqual +{ + ASIntegerMap *map = [[ASIntegerMap alloc] init]; + [map setInteger:1 forKey:0]; + ASIntegerMap *alsoMap = [[ASIntegerMap alloc] init]; + [alsoMap setInteger:1 forKey:0]; + ASIntegerMap *notMap = [[ASIntegerMap alloc] init]; + [notMap setInteger:2 forKey:0]; + XCTAssertEqualObjects(map, alsoMap); + XCTAssertNotEqualObjects(map, notMap); +} + +#pragma mark - Changeset mapping + +/// 1 item, no changes -> identity map +- (void)testEmptyChange +{ + ASIntegerMap *map = [ASIntegerMap mapForUpdateWithOldCount:1 deleted:nil inserted:nil]; + XCTAssertEqual(map, ASIntegerMap.identityMap); +} + +/// 0 items -> empty map +- (void)testChangeOnNoData +{ + ASIntegerMap *map = [ASIntegerMap mapForUpdateWithOldCount:0 deleted:nil inserted:nil]; + XCTAssertEqual(map, ASIntegerMap.emptyMap); +} + +/// 2 items, delete 0 +- (void)testBasicChange1 +{ + ASIntegerMap *map = [ASIntegerMap mapForUpdateWithOldCount:2 deleted:[NSIndexSet indexSetWithIndex:0] inserted:nil]; + XCTAssertEqual([map integerForKey:0], NSNotFound); + XCTAssertEqual([map integerForKey:1], 0); + XCTAssertEqual([map integerForKey:2], NSNotFound); +} + +/// 2 items, insert 0 +- (void)testBasicChange2 +{ + ASIntegerMap *map = [ASIntegerMap mapForUpdateWithOldCount:2 deleted:nil inserted:[NSIndexSet indexSetWithIndex:0]]; + XCTAssertEqual([map integerForKey:0], 1); + XCTAssertEqual([map integerForKey:1], 2); + XCTAssertEqual([map integerForKey:2], NSNotFound); +} + +/// 2 items, insert 0, delete 0 +- (void)testChange1 +{ + ASIntegerMap *map = [ASIntegerMap mapForUpdateWithOldCount:2 deleted:[NSIndexSet indexSetWithIndex:0] inserted:[NSIndexSet indexSetWithIndex:0]]; + XCTAssertEqual([map integerForKey:0], NSNotFound); + XCTAssertEqual([map integerForKey:1], 1); + XCTAssertEqual([map integerForKey:2], NSNotFound); +} + +/// 4 items, insert {0-1, 3} +- (void)testChange2 +{ + NSMutableIndexSet *inserts = [NSMutableIndexSet indexSetWithIndexesInRange:NSMakeRange(0, 2)]; + [inserts addIndex:3]; + ASIntegerMap *map = [ASIntegerMap mapForUpdateWithOldCount:4 deleted:nil inserted:inserts]; + XCTAssertEqual([map integerForKey:0], 2); + XCTAssertEqual([map integerForKey:1], 4); + XCTAssertEqual([map integerForKey:2], 5); + XCTAssertEqual([map integerForKey:3], 6); +} + +/// 4 items, delete {0-1, 3} +- (void)testChange3 +{ + NSMutableIndexSet *deletes = [NSMutableIndexSet indexSetWithIndexesInRange:NSMakeRange(0, 2)]; + [deletes addIndex:3]; + ASIntegerMap *map = [ASIntegerMap mapForUpdateWithOldCount:4 deleted:deletes inserted:nil]; + XCTAssertEqual([map integerForKey:0], NSNotFound); + XCTAssertEqual([map integerForKey:1], NSNotFound); + XCTAssertEqual([map integerForKey:2], 0); + XCTAssertEqual([map integerForKey:3], NSNotFound); +} + +/// 5 items, delete {0-1, 3} insert {1-2, 4} +- (void)testChange4 +{ + NSMutableIndexSet *deletes = [NSMutableIndexSet indexSetWithIndexesInRange:NSMakeRange(0, 2)]; + [deletes addIndex:3]; + NSMutableIndexSet *inserts = [NSMutableIndexSet indexSetWithIndexesInRange:NSMakeRange(1, 2)]; + [inserts addIndex:4]; + ASIntegerMap *map = [ASIntegerMap mapForUpdateWithOldCount:5 deleted:deletes inserted:inserts]; + XCTAssertEqual([map integerForKey:0], NSNotFound); + XCTAssertEqual([map integerForKey:1], NSNotFound); + XCTAssertEqual([map integerForKey:2], 0); + XCTAssertEqual([map integerForKey:3], NSNotFound); + XCTAssertEqual([map integerForKey:4], 3); + XCTAssertEqual([map integerForKey:5], NSNotFound); +} + +@end diff --git a/Tests/ASLayoutElementStyleTests.m b/Tests/ASLayoutElementStyleTests.m index 36a8d6a84e..ebec4bbd53 100644 --- a/Tests/ASLayoutElementStyleTests.m +++ b/Tests/ASLayoutElementStyleTests.m @@ -1,11 +1,18 @@ // // ASLayoutElementStyleTests.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Tests/ASLayoutFlatteningTests.m b/Tests/ASLayoutFlatteningTests.m new file mode 100644 index 0000000000..56f76c5d4a --- /dev/null +++ b/Tests/ASLayoutFlatteningTests.m @@ -0,0 +1,210 @@ +// +// ASLayoutFlatteningTests.m +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import +#import + +@interface ASLayoutFlatteningTests : XCTestCase +@end + +@implementation ASLayoutFlatteningTests + +static ASLayout *layoutWithCustomPosition(CGPoint position, id element, NSArray *sublayouts) +{ + return [ASLayout layoutWithLayoutElement:element + size:CGSizeMake(100, 100) + position:position + sublayouts:sublayouts]; +} + +static ASLayout *layout(id element, NSArray *sublayouts) +{ + return layoutWithCustomPosition(CGPointZero, element, sublayouts); +} + +- (void)testThatFlattenedLayoutContainsOnlyDirectSubnodesInValidOrder +{ + ASLayout *flattenedLayout; + + @autoreleasepool { + NSMutableArray *subnodes = [NSMutableArray array]; + NSMutableArray *layoutSpecs = [NSMutableArray array]; + NSMutableArray *indirectSubnodes = [NSMutableArray array]; + + ASDisplayNode *(^subnode)() = ^ASDisplayNode *() { [subnodes addObject:[[ASDisplayNode alloc] init]]; return [subnodes lastObject]; }; + ASLayoutSpec *(^layoutSpec)() = ^ASLayoutSpec *() { [layoutSpecs addObject:[[ASLayoutSpec alloc] init]]; return [layoutSpecs lastObject]; }; + ASDisplayNode *(^indirectSubnode)() = ^ASDisplayNode *() { [indirectSubnodes addObject:[[ASDisplayNode alloc] init]]; return [indirectSubnodes lastObject]; }; + + NSArray *sublayouts = @[ + layout(subnode(), @[ + layout(indirectSubnode(), @[]), + ]), + layout(layoutSpec(), @[ + layout(subnode(), @[]), + layout(layoutSpec(), @[ + layout(layoutSpec(), @[]), + layout(subnode(), @[]), + ]), + layout(layoutSpec(), @[]), + ]), + layout(layoutSpec(), @[ + layout(subnode(), @[ + layout(indirectSubnode(), @[]), + layout(indirectSubnode(), @[ + layout(indirectSubnode(), @[]) + ]), + ]) + ]), + layout(subnode(), @[]), + ]; + + ASDisplayNode *rootNode = [[ASDisplayNode alloc] init]; + ASLayout *originalLayout = [ASLayout layoutWithLayoutElement:rootNode + size:CGSizeMake(1000, 1000) + sublayouts:sublayouts]; + flattenedLayout = [originalLayout filteredNodeLayoutTree]; + NSArray *flattenedSublayouts = flattenedLayout.sublayouts; + NSUInteger sublayoutsCount = flattenedSublayouts.count; + + XCTAssertEqualObjects(originalLayout.layoutElement, flattenedLayout.layoutElement, @"The root node should be reserved"); + XCTAssertTrue(ASPointIsNull(flattenedLayout.position), @"Position of the root layout should be null"); + XCTAssertEqual(subnodes.count, sublayoutsCount, @"Flattened layout should only contain direct subnodes"); + for (int i = 0; i < sublayoutsCount; i++) { + XCTAssertEqualObjects(subnodes[i], flattenedSublayouts[i].layoutElement, @"Sublayouts should be in correct order (flattened in DFS fashion)"); + } + } + + for (ASLayout *sublayout in flattenedLayout.sublayouts) { + XCTAssertNotNil(sublayout.layoutElement, @"Sublayout elements should be retained"); + XCTAssertEqual(0, sublayout.sublayouts.count, @"Sublayouts should not have their own sublayouts"); + } +} + +#pragma mark - Test reusing ASLayouts while flattening + +- (void)testThatLayoutWithNonNullPositionIsNotReused +{ + ASDisplayNode *rootNode = [[ASDisplayNode alloc] init]; + ASLayout *originalLayout = layoutWithCustomPosition(CGPointMake(10, 10), rootNode, @[]); + ASLayout *flattenedLayout = [originalLayout filteredNodeLayoutTree]; + XCTAssertNotEqualObjects(originalLayout, flattenedLayout, "@Layout should be reused"); + XCTAssertTrue(ASPointIsNull(flattenedLayout.position), @"Position of a root layout should be null"); +} + +- (void)testThatLayoutWithNullPositionAndNoSublayoutIsReused +{ + ASDisplayNode *rootNode = [[ASDisplayNode alloc] init]; + ASLayout *originalLayout = layoutWithCustomPosition(ASPointNull, rootNode, @[]); + ASLayout *flattenedLayout = [originalLayout filteredNodeLayoutTree]; + XCTAssertEqualObjects(originalLayout, flattenedLayout, "@Layout should be reused"); + XCTAssertTrue(ASPointIsNull(flattenedLayout.position), @"Position of a root layout should be null"); +} + +- (void)testThatLayoutWithNullPositionAndFlattenedNodeSublayoutsIsReused +{ + ASLayout *flattenedLayout; + + @autoreleasepool { + ASDisplayNode *rootNode = [[ASDisplayNode alloc] init]; + NSMutableArray *subnodes = [NSMutableArray array]; + ASDisplayNode *(^subnode)() = ^ASDisplayNode *() { [subnodes addObject:[[ASDisplayNode alloc] init]]; return [subnodes lastObject]; }; + ASLayout *originalLayout = layoutWithCustomPosition(ASPointNull, + rootNode, + @[ + layoutWithCustomPosition(CGPointMake(10, 10), subnode(), @[]), + layoutWithCustomPosition(CGPointMake(20, 20), subnode(), @[]), + layoutWithCustomPosition(CGPointMake(30, 30), subnode(), @[]), + ]); + flattenedLayout = [originalLayout filteredNodeLayoutTree]; + XCTAssertEqualObjects(originalLayout, flattenedLayout, "@Layout should be reused"); + XCTAssertTrue(ASPointIsNull(flattenedLayout.position), @"Position of the root layout should be null"); + } + + for (ASLayout *sublayout in flattenedLayout.sublayouts) { + XCTAssertNotNil(sublayout.layoutElement, @"Sublayout elements should be retained"); + XCTAssertEqual(0, sublayout.sublayouts.count, @"Sublayouts should not have their own sublayouts"); + } +} + +- (void)testThatLayoutWithNullPositionAndUnflattenedSublayoutsIsNotReused +{ + ASLayout *flattenedLayout; + + @autoreleasepool { + ASDisplayNode *rootNode = [[ASDisplayNode alloc] init]; + NSMutableArray *subnodes = [NSMutableArray array]; + NSMutableArray *layoutSpecs = [NSMutableArray array]; + NSMutableArray *indirectSubnodes = [NSMutableArray array]; + NSMutableArray *reusedLayouts = [NSMutableArray array]; + + ASDisplayNode *(^subnode)() = ^ASDisplayNode *() { [subnodes addObject:[[ASDisplayNode alloc] init]]; return [subnodes lastObject]; }; + ASLayoutSpec *(^layoutSpec)() = ^ASLayoutSpec *() { [layoutSpecs addObject:[[ASLayoutSpec alloc] init]]; return [layoutSpecs lastObject]; }; + ASDisplayNode *(^indirectSubnode)() = ^ASDisplayNode *() { [indirectSubnodes addObject:[[ASDisplayNode alloc] init]]; return [indirectSubnodes lastObject]; }; + ASLayout *(^reusedLayout)(ASDisplayNode *) = ^ASLayout *(ASDisplayNode *subnode) { [reusedLayouts addObject:layout(subnode, @[])]; return [reusedLayouts lastObject]; }; + + /* + * Layouts with sublayouts of both nodes and layout specs should not be reused. + * However, all flattened node sublayouts with valid position should be reused. + */ + ASLayout *originalLayout = layoutWithCustomPosition(ASPointNull, + rootNode, + @[ + reusedLayout(subnode()), + // The 2 node sublayouts below should be reused although they are in a layout spec sublayout. + // That is because each of them have an absolute position of zero. + // This case can happen, for example, as the result of a background/overlay layout spec. + layout(layoutSpec(), @[ + reusedLayout(subnode()), + reusedLayout(subnode()) + ]), + layout(subnode(), @[ + layout(layoutSpec(), @[]) + ]), + layout(subnode(), @[ + layout(indirectSubnode(), @[]) + ]), + layoutWithCustomPosition(CGPointMake(10, 10), subnode(), @[]), + // The 2 node sublayouts below shouldn't be reused because they have non-zero absolute positions. + layoutWithCustomPosition(CGPointMake(20, 20), layoutSpec(), @[ + layout(subnode(), @[]), + layout(subnode(), @[]) + ]), + ]); + flattenedLayout = [originalLayout filteredNodeLayoutTree]; + NSArray *flattenedSublayouts = flattenedLayout.sublayouts; + NSUInteger sublayoutsCount = flattenedSublayouts.count; + + XCTAssertNotEqualObjects(originalLayout, flattenedLayout, @"Original layout should not be reused"); + XCTAssertEqualObjects(originalLayout.layoutElement, flattenedLayout.layoutElement, @"The root node should be reserved"); + XCTAssertTrue(ASPointIsNull(flattenedLayout.position), @"Position of the root layout should be null"); + XCTAssertTrue(reusedLayouts.count <= sublayoutsCount, @"Some sublayouts can't be reused"); + XCTAssertEqual(subnodes.count, sublayoutsCount, @"Flattened layout should only contain direct subnodes"); + int numOfActualReusedLayouts = 0; + for (int i = 0; i < sublayoutsCount; i++) { + ASLayout *sublayout = flattenedSublayouts[i]; + XCTAssertEqualObjects(subnodes[i], sublayout.layoutElement, @"Sublayouts should be in correct order (flattened in DFS fashion)"); + if ([reusedLayouts containsObject:sublayout]) { + numOfActualReusedLayouts++; + } + } + XCTAssertEqual(numOfActualReusedLayouts, reusedLayouts.count, @"Should reuse all layouts that can be reused"); + } + + for (ASLayout *sublayout in flattenedLayout.sublayouts) { + XCTAssertNotNil(sublayout.layoutElement, @"Sublayout elements should be retained"); + XCTAssertEqual(0, sublayout.sublayouts.count, @"Sublayouts should not have their own sublayouts"); + } +} + +@end diff --git a/Tests/ASLayoutSpecSnapshotTestsHelper.h b/Tests/ASLayoutSpecSnapshotTestsHelper.h index bec489a4a5..cfea40af36 100644 --- a/Tests/ASLayoutSpecSnapshotTestsHelper.h +++ b/Tests/ASLayoutSpecSnapshotTestsHelper.h @@ -1,11 +1,18 @@ // // ASLayoutSpecSnapshotTestsHelper.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "ASSnapshotTestCase.h" diff --git a/Tests/ASLayoutSpecSnapshotTestsHelper.m b/Tests/ASLayoutSpecSnapshotTestsHelper.m index 418d2dea12..b70f2e5944 100644 --- a/Tests/ASLayoutSpecSnapshotTestsHelper.m +++ b/Tests/ASLayoutSpecSnapshotTestsHelper.m @@ -1,11 +1,18 @@ // // ASLayoutSpecSnapshotTestsHelper.m -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "ASLayoutSpecSnapshotTestsHelper.h" diff --git a/Tests/ASLayoutSpecTests.m b/Tests/ASLayoutSpecTests.m index fc21efb346..6300924648 100644 --- a/Tests/ASLayoutSpecTests.m +++ b/Tests/ASLayoutSpecTests.m @@ -1,6 +1,6 @@ // // ASLayoutSpecTests.m -// AsyncDisplayKit +// Texture // // Created by Michael Schneider on 1/27/17. // Copyright © 2017 Facebook. All rights reserved. diff --git a/Tests/ASMultiplexImageNodeTests.m b/Tests/ASMultiplexImageNodeTests.m index 49a424b5c9..9eb41874b8 100644 --- a/Tests/ASMultiplexImageNodeTests.m +++ b/Tests/ASMultiplexImageNodeTests.m @@ -1,38 +1,44 @@ // // ASMultiplexImageNodeTests.m -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import -#import +#import "NSInvocation+ASTestHelpers.h" #import #import #import -#import - #import @interface ASMultiplexImageNodeTests : XCTestCase { @private - id _mockCache; - id _mockDownloader; + id mockCache; + id mockDownloader; + id mockDataSource; + id mockDelegate; + ASMultiplexImageNode *imageNode; } @end - @implementation ASMultiplexImageNodeTests -#pragma mark - -#pragma mark Helpers. +#pragma mark - Helpers. - (NSURL *)_testImageURL { @@ -46,8 +52,7 @@ return [UIImage imageWithContentsOfFile:[self _testImageURL].path]; } -#pragma mark - -#pragma mark Unit tests. +#pragma mark - Unit tests. // TODO: add tests for delegate display notifications @@ -55,83 +60,73 @@ { [super setUp]; - _mockCache = [OCMockObject mockForProtocol:@protocol(ASImageCacheProtocol)]; - _mockDownloader = [OCMockObject mockForProtocol:@protocol(ASImageDownloaderProtocol)]; + mockCache = OCMStrictProtocolMock(@protocol(ASImageCacheProtocol)); + [mockCache setExpectationOrderMatters:YES]; + mockDownloader = OCMStrictProtocolMock(@protocol(ASImageDownloaderProtocol)); + [mockDownloader setExpectationOrderMatters:YES]; + imageNode = [[ASMultiplexImageNode alloc] initWithCache:mockCache downloader:mockDownloader]; + + mockDataSource = OCMStrictProtocolMock(@protocol(ASMultiplexImageNodeDataSource)); + [mockDataSource setExpectationOrderMatters:YES]; + imageNode.dataSource = mockDataSource; + + mockDelegate = OCMProtocolMock(@protocol(ASMultiplexImageNodeDelegate)); + [mockDelegate setExpectationOrderMatters:YES]; + imageNode.delegate = mockDelegate; } - (void)tearDown { - _mockCache = nil; - _mockDownloader = nil; + OCMVerifyAll(mockDelegate); + OCMVerifyAll(mockDataSource); + OCMVerifyAll(mockDownloader); + OCMVerifyAll(mockCache); [super tearDown]; } - (void)testDataSourceImageMethod { - ASMultiplexImageNode *imageNode = [[ASMultiplexImageNode alloc] initWithCache:_mockCache downloader:_mockDownloader]; - - // Mock the data source. - // Note that we're not using a niceMock because we want to assert if the URL data-source method gets hit, as the image - // method should be hit first and exclusively if it successfully returns an image. - id mockDataSource = [OCMockObject mockForProtocol:@protocol(ASMultiplexImageNodeDataSource)]; - imageNode.dataSource = mockDataSource; - NSNumber *imageIdentifier = @1; - // Expect the image method to be hit, and have it return our test image. - UIImage *testImage = [self _testImage]; - [[[mockDataSource expect] andReturn:testImage] multiplexImageNode:imageNode imageForImageIdentifier:imageIdentifier]; + OCMExpect([mockDataSource multiplexImageNode:imageNode imageForImageIdentifier:imageIdentifier]) + .andReturn([self _testImage]); imageNode.imageIdentifiers = @[imageIdentifier]; [imageNode reloadImageIdentifierSources]; - [mockDataSource verify]; - // Also expect it to be loaded immediately. XCTAssertEqualObjects(imageNode.loadedImageIdentifier, imageIdentifier, @"imageIdentifier was not loaded"); // And for the image to be equivalent to the image we provided. XCTAssertEqualObjects(UIImagePNGRepresentation(imageNode.image), - UIImagePNGRepresentation(testImage), + UIImagePNGRepresentation([self _testImage]), @"Loaded image isn't the one we provided"); - - imageNode.delegate = nil; - imageNode.dataSource = nil; } - (void)testDataSourceURLMethod { - ASMultiplexImageNode *imageNode = [[ASMultiplexImageNode alloc] initWithCache:_mockCache downloader:_mockDownloader]; - NSNumber *imageIdentifier = @1; - // Mock the data source such that we... - id mockDataSource = [OCMockObject niceMockForProtocol:@protocol(ASMultiplexImageNodeDataSource)]; - imageNode.dataSource = mockDataSource; - // (a) first expect to be hit for the image directly, and fail to return it. - [mockDataSource setExpectationOrderMatters:YES]; - [[[mockDataSource expect] andReturn:nil] multiplexImageNode:imageNode imageForImageIdentifier:imageIdentifier]; - // (b) and then expect to be hit for the URL, which we'll return. - [[[mockDataSource expect] andReturn:[self _testImageURL]] multiplexImageNode:imageNode URLForImageIdentifier:imageIdentifier]; + // First expect to be hit for the image directly, and fail to return it. + OCMExpect([mockDataSource multiplexImageNode:imageNode imageForImageIdentifier:imageIdentifier]) + .andReturn(nil); + // BUG: -imageForImageIdentifier is called twice in this case (where we return nil). + OCMExpect([mockDataSource multiplexImageNode:imageNode imageForImageIdentifier:imageIdentifier]) + .andReturn(nil); + // Then expect to be hit for the URL, which we'll return. + OCMExpect([mockDataSource multiplexImageNode:imageNode URLForImageIdentifier:imageIdentifier]) + .andReturn([self _testImageURL]); // Mock the cache to do a cache-hit for the test image URL. - [[[_mockCache stub] andDo:^(NSInvocation *inv) { - // Params are URL, callbackQueue, completion - NSArray *URL = [inv getArgumentAtIndexAsObject:2]; - - ASImageCacherCompletion completionBlock = [inv getArgumentAtIndexAsObject:4]; - - // Call the completion block with our test image and URL. - NSURL *testImageURL = [self _testImageURL]; - XCTAssertEqualObjects(URL, testImageURL, @"Fetching URL other than test image"); + OCMExpect([mockCache cachedImageWithURL:[self _testImageURL] callbackQueue:OCMOCK_ANY completion:[OCMArg isNotNil]]) + .andDo(^(NSInvocation *inv) { + ASImageCacherCompletion completionBlock = [inv as_argumentAtIndexAsObject:4]; completionBlock([self _testImage]); - }] cachedImageWithURL:[OCMArg any] callbackQueue:[OCMArg any] completion:[OCMArg any]]; + }); imageNode.imageIdentifiers = @[imageIdentifier]; // Kick off loading. [imageNode reloadImageIdentifierSources]; - // Verify the data source. - [mockDataSource verify]; // Also expect it to be loaded immediately. XCTAssertEqualObjects(imageNode.loadedImageIdentifier, imageIdentifier, @"imageIdentifier was not loaded"); // And for the image to be equivalent to the image we provided. @@ -143,152 +138,123 @@ - (void)testAddLowerQualityImageIdentifier { // Adding a lower quality image identifier should not cause any loading. - ASMultiplexImageNode *imageNode = [[ASMultiplexImageNode alloc] initWithCache:_mockCache downloader:_mockDownloader]; - - NSNumber *highResIdentifier = @2; - - // Mock the data source such that we: (a) return the test image, and log whether we get hit for the lower-quality image. - id mockDataSource = [OCMockObject mockForProtocol:@protocol(ASMultiplexImageNodeDataSource)]; - imageNode.dataSource = mockDataSource; - __block int dataSourceHits = 0; - [[[mockDataSource stub] andDo:^(NSInvocation *inv) { - dataSourceHits++; - - // Return the test image. - [inv setReturnValue:(void *)[self _testImage]]; - }] multiplexImageNode:[OCMArg any] imageForImageIdentifier:[OCMArg any]]; + NSNumber *highResIdentifier = @2, *lowResIdentifier = @1; + OCMExpect([mockDataSource multiplexImageNode:imageNode imageForImageIdentifier:highResIdentifier]) + .andReturn([self _testImage]); imageNode.imageIdentifiers = @[highResIdentifier]; [imageNode reloadImageIdentifierSources]; // At this point, we should have the high-res identifier loaded and the DS should have been hit once. XCTAssertEqualObjects(imageNode.loadedImageIdentifier, highResIdentifier, @"High res identifier should be loaded."); - XCTAssertTrue(dataSourceHits == 1, @"Unexpected DS hit count"); - // Add the low res identifier. - NSNumber *lowResIdentifier = @1; + // BUG: We should not get another -imageForImageIdentifier:highResIdentifier. + OCMExpect([mockDataSource multiplexImageNode:imageNode imageForImageIdentifier:highResIdentifier]) + .andReturn([self _testImage]); + imageNode.imageIdentifiers = @[highResIdentifier, lowResIdentifier]; [imageNode reloadImageIdentifierSources]; - // At this point the high-res should still be loaded, and the data source should have been hit again + // At this point the high-res should still be loaded, and the data source should not have been hit again (see BUG above). XCTAssertEqualObjects(imageNode.loadedImageIdentifier, highResIdentifier, @"High res identifier should be loaded."); - XCTAssertTrue(dataSourceHits == 2, @"Unexpected DS hit count"); } - (void)testAddHigherQualityImageIdentifier { - // Adding a higher quality image identifier should cause loading. - ASMultiplexImageNode *imageNode = [[ASMultiplexImageNode alloc] initWithCache:_mockCache downloader:_mockDownloader]; + NSNumber *lowResIdentifier = @1, *highResIdentifier = @2; - NSNumber *lowResIdentifier = @1; - - // Mock the data source such that we: (a) return the test image, and log how many times the DS gets hit. - id mockDataSource = [OCMockObject mockForProtocol:@protocol(ASMultiplexImageNodeDataSource)]; - imageNode.dataSource = mockDataSource; - __block int dataSourceHits = 0; - [[[mockDataSource stub] andDo:^(NSInvocation *inv) { - dataSourceHits++; - - // Return the test image. - [inv setReturnValue:(void *)[self _testImage]]; - }] multiplexImageNode:[OCMArg any] imageForImageIdentifier:[OCMArg any]]; + OCMExpect([mockDataSource multiplexImageNode:imageNode imageForImageIdentifier:lowResIdentifier]) + .andReturn([self _testImage]); imageNode.imageIdentifiers = @[lowResIdentifier]; [imageNode reloadImageIdentifierSources]; // At this point, we should have the low-res identifier loaded and the DS should have been hit once. XCTAssertEqualObjects(imageNode.loadedImageIdentifier, lowResIdentifier, @"Low res identifier should be loaded."); - XCTAssertTrue(dataSourceHits == 1, @"Unexpected DS hit count"); - // Add the low res identifier. - NSNumber *highResIdentifier = @2; + OCMExpect([mockDataSource multiplexImageNode:imageNode imageForImageIdentifier:highResIdentifier]) + .andReturn([self _testImage]); + imageNode.imageIdentifiers = @[highResIdentifier, lowResIdentifier]; [imageNode reloadImageIdentifierSources]; // At this point the high-res should be loaded, and the data source should been hit twice. XCTAssertEqualObjects(imageNode.loadedImageIdentifier, highResIdentifier, @"High res identifier should be loaded."); - XCTAssertTrue(dataSourceHits == 2, @"Unexpected DS hit count"); } -- (void)testProgressiveDownloading +- (void)testIntermediateImageDownloading { - ASMultiplexImageNode *imageNode = [[ASMultiplexImageNode alloc] initWithCache:_mockCache downloader:_mockDownloader]; imageNode.downloadsIntermediateImages = YES; + // Let them call URLForImageIdentifier all they want. + OCMStub([mockDataSource multiplexImageNode:imageNode URLForImageIdentifier:[OCMArg isNotNil]]); + // Set up a few identifiers to load. NSInteger identifierCount = 5; NSMutableArray *imageIdentifiers = [NSMutableArray array]; - for (NSInteger identifierIndex = 0; identifierIndex < identifierCount; identifierIndex++) - [imageIdentifiers insertObject:@(identifierIndex + 1) atIndex:0]; + for (NSInteger identifier = identifierCount; identifier > 0; identifier--) { + [imageIdentifiers addObject:@(identifier)]; + } - // Mock the data source to only make the images available progressively. - // This is necessary because ASMultiplexImageNode will try to grab the best image immediately, regardless of - // `downloadsIntermediateImages`. - id mockDataSource = [OCMockObject niceMockForProtocol:@protocol(ASMultiplexImageNodeDataSource)]; - imageNode.dataSource = mockDataSource; - __block NSUInteger loadedImageCount = 0; - [[[mockDataSource stub] andDo:^(NSInvocation *inv) { - id requestedIdentifier = [inv getArgumentAtIndexAsObject:3]; + // Create the array of IDs in the order we expect them to get -imageForImageIdentifier: + // BUG: The second to last ID (the last one that returns nil) will get -imageForImageIdentifier: called + // again after the last ID (the one that returns non-nil). + id secondToLastID = imageIdentifiers[identifierCount - 2]; + NSArray *imageIdentifiersThatWillBeCalled = [imageIdentifiers arrayByAddingObject:secondToLastID]; - NSInteger requestedIdentifierValue = [requestedIdentifier intValue]; - - // If no images are loaded, bail on trying to load anything but the worst image. - if (!imageNode.loadedImageIdentifier && requestedIdentifierValue != [[imageIdentifiers lastObject] integerValue]) - return; - - // Bail if it's trying to load an identifier that's more than one step than what's loaded. - NSInteger nextImageIdentifier = [(NSNumber *)imageNode.loadedImageIdentifier integerValue] + 1; - if (requestedIdentifierValue != nextImageIdentifier) - return; - - // Return the test image. - loadedImageCount++; - [inv setReturnValue:(void *)[self _testImage]]; - }] multiplexImageNode:[OCMArg any] imageForImageIdentifier:[OCMArg any]]; + for (id imageID in imageIdentifiersThatWillBeCalled) { + // Return nil for everything except the worst ID. + OCMExpect([mockDataSource multiplexImageNode:imageNode imageForImageIdentifier:imageID]) + .andDo(^(NSInvocation *inv){ + id imageID = [inv as_argumentAtIndexAsObject:3]; + if ([imageID isEqual:imageIdentifiers.lastObject]) { + [inv as_setReturnValueWithObject:[self _testImage]]; + } else { + [inv as_setReturnValueWithObject:nil]; + } + }); + } imageNode.imageIdentifiers = imageIdentifiers; [imageNode reloadImageIdentifierSources]; - - XCTAssertTrue(loadedImageCount == identifierCount, @"Expected to load the same number of identifiers we supplied"); } - (void)testUncachedDownload { // Mock a cache miss. - id mockCache = [OCMockObject mockForProtocol:@protocol(ASImageCacheProtocol)]; - [[[mockCache stub] andDo:^(NSInvocation *inv) { - ASImageCacherCompletion completion = [inv getArgumentAtIndexAsObject:4]; + OCMExpect([mockCache cachedImageWithURL:[self _testImageURL] callbackQueue:OCMOCK_ANY completion:[OCMArg isNotNil]]) + .andDo(^(NSInvocation *inv){ + ASImageCacherCompletion completion = [inv as_argumentAtIndexAsObject:4]; completion(nil); - }] cachedImageWithURL:[OCMArg any] callbackQueue:[OCMArg any] completion:[OCMArg any]]; + }); // Mock a 50%-progress URL download. - id mockDownloader = [OCMockObject mockForProtocol:@protocol(ASImageDownloaderProtocol)]; const CGFloat mockedProgress = 0.5; - [[[mockDownloader stub] andDo:^(NSInvocation *inv) { + OCMExpect([mockDownloader downloadImageWithURL:[self _testImageURL] callbackQueue:OCMOCK_ANY downloadProgress:[OCMArg isNotNil] completion:[OCMArg isNotNil]]) + .andDo(^(NSInvocation *inv){ // Simulate progress. - ASImageDownloaderProgress progressBlock = [inv getArgumentAtIndexAsObject:4]; + ASImageDownloaderProgress progressBlock = [inv as_argumentAtIndexAsObject:4]; progressBlock(mockedProgress); // Simulate completion. - ASImageDownloaderCompletion completionBlock = [inv getArgumentAtIndexAsObject:5]; + ASImageDownloaderCompletion completionBlock = [inv as_argumentAtIndexAsObject:5]; completionBlock([self _testImage], nil, nil); - }] downloadImageWithURL:[OCMArg any] callbackQueue:[OCMArg any] downloadProgress:[OCMArg any] completion:[OCMArg any]]; + }); - ASMultiplexImageNode *imageNode = [[ASMultiplexImageNode alloc] initWithCache:mockCache downloader:mockDownloader]; NSNumber *imageIdentifier = @1; - // Mock the data source to return our test URL. - id mockDataSource = [OCMockObject niceMockForProtocol:@protocol(ASMultiplexImageNodeDataSource)]; - [[[mockDataSource stub] andReturn:[self _testImageURL]] multiplexImageNode:imageNode URLForImageIdentifier:imageIdentifier]; - imageNode.dataSource = mockDataSource; + // Mock the data source to return nil image, and our test URL. + OCMExpect([mockDataSource multiplexImageNode:imageNode imageForImageIdentifier:imageIdentifier]); + // BUG: Multiplex image node will call imageForImageIdentifier twice if we return nil. + OCMExpect([mockDataSource multiplexImageNode:imageNode imageForImageIdentifier:imageIdentifier]); + OCMExpect([mockDataSource multiplexImageNode:imageNode URLForImageIdentifier:imageIdentifier]) + .andReturn([self _testImageURL]); // Mock the delegate to expect start, 50% progress, and completion invocations. - id mockDelegate = [OCMockObject mockForProtocol:@protocol(ASMultiplexImageNodeDelegate)]; - [[mockDelegate expect] multiplexImageNode:imageNode didStartDownloadOfImageWithIdentifier:imageIdentifier]; - [[mockDelegate expect] multiplexImageNode:imageNode didUpdateDownloadProgress:mockedProgress forImageWithIdentifier:imageIdentifier]; - [[mockDelegate expect] multiplexImageNode:imageNode didFinishDownloadingImageWithIdentifier:imageIdentifier error:nil]; - [[mockDelegate expect] multiplexImageNode:imageNode didUpdateImage:[OCMArg any] withIdentifier:imageIdentifier fromImage:nil withIdentifier:nil]; - imageNode.delegate = mockDelegate; + OCMExpect([mockDelegate multiplexImageNode:imageNode didStartDownloadOfImageWithIdentifier:imageIdentifier]); + OCMExpect([mockDelegate multiplexImageNode:imageNode didUpdateDownloadProgress:mockedProgress forImageWithIdentifier:imageIdentifier]); + OCMExpect([mockDelegate multiplexImageNode:imageNode didUpdateImage:[OCMArg isNotNil] withIdentifier:imageIdentifier fromImage:[OCMArg isNil] withIdentifier:[OCMArg isNil]]); + OCMExpect([mockDelegate multiplexImageNode:imageNode didFinishDownloadingImageWithIdentifier:imageIdentifier error:[OCMArg isNil]]); imageNode.imageIdentifiers = @[imageIdentifier]; // Kick off loading. @@ -297,15 +263,11 @@ // Wait until the image is loaded. [self expectationForPredicate:[NSPredicate predicateWithFormat:@"loadedImageIdentifier = %@", imageIdentifier] evaluatedWithObject:imageNode handler:nil]; [self waitForExpectationsWithTimeout:30 handler:nil]; - - // Verify the delegation. - [mockDelegate verify]; } - (void)testThatSettingAnImageExternallyWillThrow { - ASMultiplexImageNode *multiplexImageNode = [[ASMultiplexImageNode alloc] init]; - XCTAssertThrows(multiplexImageNode.image = [UIImage imageNamed:@""]); + XCTAssertThrows(imageNode.image = [UIImage imageNamed:@""]); } @end diff --git a/Tests/ASMutableAttributedStringBuilderTests.m b/Tests/ASMutableAttributedStringBuilderTests.m index c638696945..dd98130fef 100644 --- a/Tests/ASMutableAttributedStringBuilderTests.m +++ b/Tests/ASMutableAttributedStringBuilderTests.m @@ -1,11 +1,18 @@ // // ASMutableAttributedStringBuilderTests.m -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Tests/ASNetworkImageNodeTests.m b/Tests/ASNetworkImageNodeTests.m index 67f9ee347f..94dc668d52 100644 --- a/Tests/ASNetworkImageNodeTests.m +++ b/Tests/ASNetworkImageNodeTests.m @@ -1,6 +1,6 @@ // // ASNetworkImageNodeTests.m -// AsyncDisplayKit +// Texture // // Created by Adlai Holler on 10/14/16. // Copyright © 2016 Facebook. All rights reserved. diff --git a/Tests/ASOverlayLayoutSpecSnapshotTests.mm b/Tests/ASOverlayLayoutSpecSnapshotTests.mm index a944ce76d4..9b9cc68277 100644 --- a/Tests/ASOverlayLayoutSpecSnapshotTests.mm +++ b/Tests/ASOverlayLayoutSpecSnapshotTests.mm @@ -1,11 +1,18 @@ // // ASOverlayLayoutSpecSnapshotTests.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "ASLayoutSpecSnapshotTestsHelper.h" diff --git a/Tests/ASPagerNodeTests.m b/Tests/ASPagerNodeTests.m index 9117cf372e..428247c534 100644 --- a/Tests/ASPagerNodeTests.m +++ b/Tests/ASPagerNodeTests.m @@ -1,6 +1,6 @@ // // ASPagerNodeTests.m -// AsyncDisplayKit +// Texture // // Created by Luke Parham on 11/6/16. // Copyright © 2016 Facebook. All rights reserved. diff --git a/Tests/ASPerformanceTestContext.h b/Tests/ASPerformanceTestContext.h index 3d80eb7d74..42075f5fc8 100644 --- a/Tests/ASPerformanceTestContext.h +++ b/Tests/ASPerformanceTestContext.h @@ -1,6 +1,6 @@ // // ASPerformanceTestContext.h -// AsyncDisplayKit +// Texture // // Created by Adlai Holler on 8/28/16. // Copyright © 2016 Facebook. All rights reserved. diff --git a/Tests/ASPerformanceTestContext.m b/Tests/ASPerformanceTestContext.m index f1c3914e5d..ee6027aff7 100644 --- a/Tests/ASPerformanceTestContext.m +++ b/Tests/ASPerformanceTestContext.m @@ -1,6 +1,6 @@ // // ASPerformanceTestContext.m -// AsyncDisplayKit +// Texture // // Created by Adlai Holler on 8/28/16. // Copyright © 2016 Facebook. All rights reserved. diff --git a/Tests/ASPhotosFrameworkImageRequestTests.m b/Tests/ASPhotosFrameworkImageRequestTests.m index 954d7bf87b..0a2c7870ec 100644 --- a/Tests/ASPhotosFrameworkImageRequestTests.m +++ b/Tests/ASPhotosFrameworkImageRequestTests.m @@ -1,13 +1,20 @@ // // ASPhotosFrameworkImageRequestTests.m -// AsyncDisplayKit +// Texture // // Created by Adlai Holler on 9/25/15. // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Tests/ASRatioLayoutSpecSnapshotTests.mm b/Tests/ASRatioLayoutSpecSnapshotTests.mm index fb50aaa75c..8aafa6610b 100644 --- a/Tests/ASRatioLayoutSpecSnapshotTests.mm +++ b/Tests/ASRatioLayoutSpecSnapshotTests.mm @@ -1,11 +1,18 @@ // // ASRatioLayoutSpecSnapshotTests.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "ASLayoutSpecSnapshotTestsHelper.h" diff --git a/Tests/ASRectTableTests.m b/Tests/ASRectTableTests.m index 55c1dbab15..ca897a3571 100644 --- a/Tests/ASRectTableTests.m +++ b/Tests/ASRectTableTests.m @@ -1,6 +1,6 @@ // // ASRectTableTests.m -// AsyncDisplayKit +// Texture // // Created by Adlai Holler on 2/24/17. // Copyright © 2017 Facebook. All rights reserved. diff --git a/Tests/ASRelativeLayoutSpecSnapshotTests.mm b/Tests/ASRelativeLayoutSpecSnapshotTests.mm index 33abb5d443..8a11564c3b 100644 --- a/Tests/ASRelativeLayoutSpecSnapshotTests.mm +++ b/Tests/ASRelativeLayoutSpecSnapshotTests.mm @@ -1,11 +1,18 @@ // // ASRelativeLayoutSpecSnapshotTests.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "ASLayoutSpecSnapshotTestsHelper.h" diff --git a/Tests/ASSnapshotTestCase.h b/Tests/ASSnapshotTestCase.h index 501b523a3d..580b487b9a 100644 --- a/Tests/ASSnapshotTestCase.h +++ b/Tests/ASSnapshotTestCase.h @@ -1,11 +1,18 @@ // // ASSnapshotTestCase.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Tests/ASSnapshotTestCase.m b/Tests/ASSnapshotTestCase.m index 81c5b74adf..a929bb1594 100644 --- a/Tests/ASSnapshotTestCase.m +++ b/Tests/ASSnapshotTestCase.m @@ -1,11 +1,18 @@ // // ASSnapshotTestCase.m -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "ASSnapshotTestCase.h" diff --git a/Tests/ASStackLayoutSpecSnapshotTests.mm b/Tests/ASStackLayoutSpecSnapshotTests.mm index e39bf2a081..042203fe1b 100644 --- a/Tests/ASStackLayoutSpecSnapshotTests.mm +++ b/Tests/ASStackLayoutSpecSnapshotTests.mm @@ -1,11 +1,18 @@ // // ASStackLayoutSpecSnapshotTests.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "ASLayoutSpecSnapshotTestsHelper.h" diff --git a/Tests/ASTableViewTests.mm b/Tests/ASTableViewTests.mm index 1190a9e780..6c53fc8498 100644 --- a/Tests/ASTableViewTests.mm +++ b/Tests/ASTableViewTests.mm @@ -1,11 +1,18 @@ // -// ASTableViewTests.m -// AsyncDisplayKit +// ASTableViewTests.mm +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -54,7 +61,7 @@ - (instancetype)__initWithFrame:(CGRect)frame style:(UITableViewStyle)style { - return [super _initWithFrame:frame style:style dataControllerClass:[ASTestDataController class] eventLog:nil]; + return [super _initWithFrame:frame style:style dataControllerClass:[ASTestDataController class] owningNode:nil eventLog:nil]; } - (ASTestDataController *)testDataController @@ -382,7 +389,7 @@ CGSize tableViewFinalSize = CGSizeMake(100, 500); // Width and height are swapped so that a later size change will simulate a rotation ASTestTableView *tableView = [[ASTestTableView alloc] __initWithFrame:CGRectMake(0, 0, tableViewFinalSize.height, tableViewFinalSize.width) - style:UITableViewStylePlain]; + style:UITableViewStylePlain]; ASTableViewFilledDataSource *dataSource = [ASTableViewFilledDataSource new]; @@ -475,7 +482,7 @@ { CGSize tableViewSize = CGSizeMake(100, 500); ASTestTableView *tableView = [[ASTestTableView alloc] __initWithFrame:CGRectMake(0, 0, tableViewSize.width, tableViewSize.height) - style:UITableViewStylePlain]; + style:UITableViewStylePlain]; ASTableViewFilledDataSource *dataSource = [ASTableViewFilledDataSource new]; tableView.asyncDelegate = dataSource; diff --git a/Tests/ASTableViewThrashTests.m b/Tests/ASTableViewThrashTests.m index ac91042b89..e547fe39cb 100644 --- a/Tests/ASTableViewThrashTests.m +++ b/Tests/ASTableViewThrashTests.m @@ -1,15 +1,25 @@ // // ASTableViewThrashTests.m -// AsyncDisplayKit +// Texture // -// Created by Adlai Holler on 6/21/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 /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import #import #import #import +#import // Set to 1 to use UITableView and see if the issue still exists. @@ -40,7 +50,7 @@ static NSString *ASThrashArrayDescription(NSArray *array) { return str; } -static volatile int32_t ASThrashTestItemNextID = 1; +static atomic_uint ASThrashTestItemNextID; @interface ASThrashTestItem: NSObject @property (nonatomic, readonly) NSInteger itemID; @@ -56,7 +66,7 @@ static volatile int32_t ASThrashTestItemNextID = 1; - (instancetype)init { self = [super init]; if (self != nil) { - _itemID = OSAtomicIncrement32(&ASThrashTestItemNextID); + _itemID = atomic_fetch_add(&ASThrashTestItemNextID, 1); } return self; } @@ -99,7 +109,7 @@ static volatile int32_t ASThrashTestItemNextID = 1; - (CGFloat)headerHeight; @end -static volatile int32_t ASThrashTestSectionNextID = 1; +static atomic_uint ASThrashTestSectionNextID = 1; @implementation ASThrashTestSection /// Create an array of sections with the given count @@ -114,7 +124,7 @@ static volatile int32_t ASThrashTestSectionNextID = 1; - (instancetype)initWithCount:(NSInteger)count { self = [super init]; if (self != nil) { - _sectionID = OSAtomicIncrement32(&ASThrashTestSectionNextID); + _sectionID = atomic_fetch_add(&ASThrashTestSectionNextID, 1); _items = [ASThrashTestItem itemsWithCount:count]; } return self; diff --git a/Tests/ASTextKitCoreTextAdditionsTests.m b/Tests/ASTextKitCoreTextAdditionsTests.m index 95ff3e1fa1..fbf4a5ded3 100644 --- a/Tests/ASTextKitCoreTextAdditionsTests.m +++ b/Tests/ASTextKitCoreTextAdditionsTests.m @@ -1,11 +1,18 @@ // // ASTextKitCoreTextAdditionsTests.m -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Tests/ASTextKitTests.mm b/Tests/ASTextKitTests.mm index 8e32340b69..a39a333b10 100644 --- a/Tests/ASTextKitTests.mm +++ b/Tests/ASTextKitTests.mm @@ -1,11 +1,18 @@ // // ASTextKitTests.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Tests/ASTextKitTruncationTests.mm b/Tests/ASTextKitTruncationTests.mm index 75ba4736e3..9b0a39f99c 100644 --- a/Tests/ASTextKitTruncationTests.mm +++ b/Tests/ASTextKitTruncationTests.mm @@ -1,11 +1,18 @@ // // ASTextKitTruncationTests.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Tests/ASTextNodePerformanceTests.m b/Tests/ASTextNodePerformanceTests.m index c6bce2d9fc..aecf6d2fdd 100644 --- a/Tests/ASTextNodePerformanceTests.m +++ b/Tests/ASTextNodePerformanceTests.m @@ -1,6 +1,6 @@ // // ASTextNodePerformanceTests.m -// AsyncDisplayKit +// Texture // // Created by Adlai Holler on 8/28/16. // Copyright © 2016 Facebook. All rights reserved. diff --git a/Tests/ASTextNodeSnapshotTests.m b/Tests/ASTextNodeSnapshotTests.m index c0acf7ad4c..0d86a7274f 100644 --- a/Tests/ASTextNodeSnapshotTests.m +++ b/Tests/ASTextNodeSnapshotTests.m @@ -1,12 +1,19 @@ // // ASTextNodeSnapshotTests.m -// AsyncDisplayKit +// Texture // // Created by Garrett Moon on 8/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. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "ASSnapshotTestCase.h" diff --git a/Tests/ASTextNodeTests.m b/Tests/ASTextNodeTests.m index 2baa932579..5b1f2cf4d2 100644 --- a/Tests/ASTextNodeTests.m +++ b/Tests/ASTextNodeTests.m @@ -1,11 +1,18 @@ // // ASTextNodeTests.m -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Tests/ASTextNodeWordKernerTests.mm b/Tests/ASTextNodeWordKernerTests.mm index 91e19b7b25..6b0f933cfc 100644 --- a/Tests/ASTextNodeWordKernerTests.mm +++ b/Tests/ASTextNodeWordKernerTests.mm @@ -1,11 +1,18 @@ // // ASTextNodeWordKernerTests.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Tests/ASUICollectionViewTests.m b/Tests/ASUICollectionViewTests.m index 89ba755863..ec7a51fa2d 100644 --- a/Tests/ASUICollectionViewTests.m +++ b/Tests/ASUICollectionViewTests.m @@ -1,14 +1,23 @@ // // ASUICollectionViewTests.m -// AsyncDisplayKit +// Texture // -// Created by Adlai Holler on 8/18/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 /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import #import -#import +#import "NSInvocation+ASTestHelpers.h" @interface ASUICollectionViewTests : XCTestCase @@ -86,7 +95,7 @@ id dataSource = [OCMockObject niceMockForProtocol:@protocol(UICollectionViewDataSource)]; __block id view = nil; [[[dataSource expect] andDo:^(NSInvocation *invocation) { - NSIndexPath *indexPath = [invocation getArgumentAtIndexAsObject:4]; + NSIndexPath *indexPath = [invocation as_argumentAtIndexAsObject:4]; view = [cv dequeueReusableSupplementaryViewOfKind:@"SuppKind" withReuseIdentifier:@"ReuseID" forIndexPath:indexPath]; [invocation setReturnValue:&view]; }] collectionView:cv viewForSupplementaryElementOfKind:@"SuppKind" atIndexPath:indexPath]; @@ -95,12 +104,13 @@ cv.dataSource = dataSource; if (shouldFail) { XCTAssertThrowsSpecificNamed([cv layoutIfNeeded], NSException, NSInternalInconsistencyException); - } else { - [cv layoutIfNeeded]; - XCTAssertEqualObjects(attr, [cv layoutAttributesForSupplementaryElementOfKind:@"SuppKind" atIndexPath:indexPath]); - XCTAssertEqual(view, [cv supplementaryViewForElementKind:@"SuppKind" atIndexPath:indexPath]); + // Early return because behavior after exception is thrown is undefined. + return; } + [cv layoutIfNeeded]; + XCTAssertEqualObjects(attr, [cv layoutAttributesForSupplementaryElementOfKind:@"SuppKind" atIndexPath:indexPath]); + XCTAssertEqual(view, [cv supplementaryViewForElementKind:@"SuppKind" atIndexPath:indexPath]); [dataSource verify]; [layoutMock verify]; } @@ -113,7 +123,7 @@ // Setup empty data source – 0 sections, 0 items [[[dataSource stub] andDo:^(NSInvocation *invocation) { - NSIndexPath *indexPath = [invocation getArgumentAtIndexAsObject:3]; + NSIndexPath *indexPath = [invocation as_argumentAtIndexAsObject:3]; __autoreleasing UICollectionViewCell *view = [cv dequeueReusableCellWithReuseIdentifier:@"CellID" forIndexPath:indexPath]; [invocation setReturnValue:&view]; }] collectionView:cv cellForItemAtIndexPath:OCMOCK_ANY]; diff --git a/Tests/ASVideoNodeTests.m b/Tests/ASVideoNodeTests.m index c8944bd5ef..278509efe0 100644 --- a/Tests/ASVideoNodeTests.m +++ b/Tests/ASVideoNodeTests.m @@ -1,11 +1,18 @@ // // ASVideoNodeTests.m -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Tests/ASViewControllerTests.m b/Tests/ASViewControllerTests.m index 24a24a3fce..3fa91d87f5 100644 --- a/Tests/ASViewControllerTests.m +++ b/Tests/ASViewControllerTests.m @@ -1,15 +1,24 @@ // // ASViewControllerTests.m -// AsyncDisplayKit +// Texture // -// Created by Adlai Holler on 8/25/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 /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import #import #import -#import +#import "NSInvocation+ASTestHelpers.h" @interface ASViewControllerTests : XCTestCase @@ -51,7 +60,7 @@ [[[animator expect] andReturnValue:@0.3] transitionDuration:[OCMArg any]]; XCTestExpectation *e = [self expectationWithDescription:@"Transition completed"]; [[[animator expect] andDo:^(NSInvocation *invocation) { - id ctx = [invocation getArgumentAtIndexAsObject:2]; + id ctx = [invocation as_argumentAtIndexAsObject:2]; UIView *container = [ctx containerView]; [container addSubview:vc.view]; vc.view.alpha = 0; diff --git a/Tests/ASWeakMapTests.m b/Tests/ASWeakMapTests.m index 6934d9207a..21e2dde8a7 100644 --- a/Tests/ASWeakMapTests.m +++ b/Tests/ASWeakMapTests.m @@ -1,13 +1,20 @@ // // ASWeakMapTests.m -// AsyncDisplayKit +// Texture // // Created by Chris Danford on 7/23/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. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Tests/ASWeakSetTests.m b/Tests/ASWeakSetTests.m index b0ef35b05e..59b3f577d9 100644 --- a/Tests/ASWeakSetTests.m +++ b/Tests/ASWeakSetTests.m @@ -1,13 +1,20 @@ // // ASWeakSetTests.m -// AsyncDisplayKit +// Texture // // Created by Adlai Holler on 1/7/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. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Tests/ASWrapperSpecSnapshotTests.mm b/Tests/ASWrapperSpecSnapshotTests.mm index 21619348af..5f8e0e88b9 100644 --- a/Tests/ASWrapperSpecSnapshotTests.mm +++ b/Tests/ASWrapperSpecSnapshotTests.mm @@ -1,11 +1,18 @@ // // ASWrapperSpecSnapshotTests.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // diff --git a/Tests/ASXCTExtensions.h b/Tests/ASXCTExtensions.h deleted file mode 100644 index 574b7a7175..0000000000 --- a/Tests/ASXCTExtensions.h +++ /dev/null @@ -1,36 +0,0 @@ -/** - * XCTest extensions for CGGeometry. - * - * Prefer these to XCTAssert(CGRectEqualToRect(...)) because you get output - * that tells you what went wrong. - * Could use NSValue, but using strings makes the description messages shorter. - */ - -#import - -#define ASXCTAssertEqualSizes(s0, s1, ...) \ - _XCTPrimitiveAssertEqualObjects(self, NSStringFromCGSize(s0), @#s0, NSStringFromCGSize(s1), @#s1, __VA_ARGS__) - -#define ASXCTAssertNotEqualSizes(s0, s1, ...) \ - _XCTPrimitiveAssertNotEqualObjects(self, NSStringFromCGSize(s0), @#s0, NSStringFromCGSize(s1), @#s1, __VA_ARGS__) - -#define ASXCTAssertEqualPoints(p0, p1, ...) \ - _XCTPrimitiveAssertEqualObjects(self, NSStringFromCGPoint(p0), @#p0, NSStringFromCGPoint(p1), @#p1, __VA_ARGS__) - -#define ASXCTAssertNotEqualPoints(p0, p1, ...) \ - _XCTPrimitiveAssertNotEqualObjects(self, NSStringFromCGPoint(p0), @#p0, NSStringFromCGPoint(p1), @#p1, __VA_ARGS__) - -#define ASXCTAssertEqualRects(r0, r1, ...) \ - _XCTPrimitiveAssertEqualObjects(self, NSStringFromCGRect(r0), @#r0, NSStringFromCGRect(r1), @#r1, __VA_ARGS__) - -#define ASXCTAssertNotEqualRects(r0, r1, ...) \ - _XCTPrimitiveAssertNotEqualObjects(self, NSStringFromCGRect(r0), @#r0, NSStringFromCGRect(r1), @#r1, __VA_ARGS__) - -#define ASXCTAssertEqualDimensions(r0, r1, ...) \ - _XCTPrimitiveAssertEqualObjects(self, NSStringFromASDimension(r0), @#r0, NSStringFromASDimension(r1), @#r1, __VA_ARGS__) - -#define ASXCTAssertNotEqualDimensions(r0, r1, ...) \ - _XCTPrimitiveAssertNotEqualObjects(self, NSStringFromASDimension(r0), @#r0, NSStringFromASDimension(r1), @#r1, __VA_ARGS__) - -#define ASXCTAssertEqualSizeRanges(r0, r1, ...) \ - _XCTPrimitiveAssertEqualObjects(self, NSStringFromASSizeRange(r0), @#r0, NSStringFromASSizeRange(r1), @#r1, __VA_ARGS__) diff --git a/Tests/ArrayDiffingTests.m b/Tests/ArrayDiffingTests.m index a7d869bbb3..854b32a2ea 100644 --- a/Tests/ArrayDiffingTests.m +++ b/Tests/ArrayDiffingTests.m @@ -1,13 +1,20 @@ // // ArrayDiffingTests.m -// AsyncDisplayKit +// Texture // // Created by Levi McCallum on 1/29/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. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Tests/Common/ASDisplayNode+OCMock.m b/Tests/Common/ASDisplayNode+OCMock.m new file mode 100644 index 0000000000..4fd0db028b --- /dev/null +++ b/Tests/Common/ASDisplayNode+OCMock.m @@ -0,0 +1,31 @@ +// +// ASDisplayNode+OCMock.m +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +/** + * For some reason, when creating partial mocks of nodes, OCMock fails to find + * these class methods that it swizzled! + */ +@implementation ASDisplayNode (OCMock) + ++ (Class)ocmock_replaced_viewClass +{ + return [_ASDisplayView class]; +} + ++ (Class)ocmock_replaced_layerClass +{ + return [_ASDisplayLayer class]; +} + +@end diff --git a/Tests/Common/ASTestCase.h b/Tests/Common/ASTestCase.h new file mode 100644 index 0000000000..4868f64d05 --- /dev/null +++ b/Tests/Common/ASTestCase.h @@ -0,0 +1,23 @@ +// +// ASTestCase.h +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface ASTestCase : XCTestCase + +@property (class, nonatomic, nullable, readonly) ASTestCase *currentTestCase; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Tests/Common/ASTestCase.m b/Tests/Common/ASTestCase.m new file mode 100644 index 0000000000..5bc720253f --- /dev/null +++ b/Tests/Common/ASTestCase.m @@ -0,0 +1,110 @@ +// +// ASTestCase.m +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import "ASTestCase.h" +#import +#import +#import +#import "OCMockObject+ASAdditions.h" + +static __weak ASTestCase *currentTestCase; + +@implementation ASTestCase { + ASWeakSet *registeredMockObjects; +} + +- (void)setUp +{ + [super setUp]; + currentTestCase = self; + registeredMockObjects = [ASWeakSet new]; +} + +- (void)tearDown +{ + // Clear out all application windows. Note: the system will retain these sometimes on its + // own but we'll do our best. + for (UIWindow *window in [UIApplication sharedApplication].windows) { + [window resignKeyWindow]; + window.hidden = YES; + window.rootViewController = nil; + for (UIView *view in window.subviews) { + [view removeFromSuperview]; + } + } + + // Set nil for all our subclasses' ivars. Use setValue:forKey: so memory is managed correctly. + // This is important to do _inside_ the test-perform, so that we catch any issues caused by the + // deallocation, and so that we're inside the @autoreleasepool for the test invocation. + Class c = [self class]; + while (c != [ASTestCase class]) { + unsigned int ivarCount; + Ivar *ivars = class_copyIvarList(c, &ivarCount); + for (unsigned int i = 0; i < ivarCount; i++) { + Ivar ivar = ivars[i]; + NSString *key = [NSString stringWithCString:ivar_getName(ivar) encoding:NSUTF8StringEncoding]; + if (OCMIsObjectType(ivar_getTypeEncoding(ivar))) { + [self setValue:nil forKey:key]; + } + } + if (ivars) { + free(ivars); + } + + c = [c superclass]; + } + + for (OCMockObject *mockObject in registeredMockObjects) { + OCMVerifyAll(mockObject); + [mockObject stopMocking]; + + // Invocations retain arguments, which may cause retain cycles. + // Manually clear them all out. + NSMutableArray *invocations = object_getIvar(mockObject, class_getInstanceVariable(OCMockObject.class, "invocations")); + [invocations removeAllObjects]; + } + + // Go ahead and spin the run loop before finishing, so the system + // unregisters/cleans up whatever possible. + [NSRunLoop.mainRunLoop runMode:NSDefaultRunLoopMode beforeDate:NSDate.distantPast]; + + [super tearDown]; +} + +- (void)invokeTest +{ + // This will call setup, run, then teardown. + @autoreleasepool { + [super invokeTest]; + } + + // Now that the autorelease pool is drained, drain the dealloc queue also. + [[ASDeallocQueue sharedDeallocationQueue] test_drain]; +} + ++ (ASTestCase *)currentTestCase +{ + return currentTestCase; +} + +@end + +@implementation ASTestCase (OCMockObjectRegistering) + +- (void)registerMockObject:(id)mockObject +{ + @synchronized (registeredMockObjects) { + [registeredMockObjects addObject:mockObject]; + } +} + +@end diff --git a/ASDKListKit/ASDKListKitTests/ASXCTExtensions.h b/Tests/Common/ASXCTExtensions.h similarity index 100% rename from ASDKListKit/ASDKListKitTests/ASXCTExtensions.h rename to Tests/Common/ASXCTExtensions.h diff --git a/Tests/Common/NSInvocation+ASTestHelpers.h b/Tests/Common/NSInvocation+ASTestHelpers.h new file mode 100644 index 0000000000..da64096e32 --- /dev/null +++ b/Tests/Common/NSInvocation+ASTestHelpers.h @@ -0,0 +1,36 @@ +// +// NSInvocation+ASTestHelpers.h +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface NSInvocation (ASTestHelpers) + +/** + * Formats the argument at the given index as an object and returns it. + * + * Currently only supports arguments that are themselves objects, but handles + * getting the argument into ARC safely. + */ +- (nullable id)as_argumentAtIndexAsObject:(NSInteger)index; + +/** + * Sets the return value, simulating ARC behavior. + * + * Currently only supports invocations whose return values are already object types. + */ +- (void)as_setReturnValueWithObject:(nullable id)object; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Tests/Common/NSInvocation+ASTestHelpers.m b/Tests/Common/NSInvocation+ASTestHelpers.m new file mode 100644 index 0000000000..d442a2011e --- /dev/null +++ b/Tests/Common/NSInvocation+ASTestHelpers.m @@ -0,0 +1,36 @@ +// +// NSInvocation+ASTestHelpers.m +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import "NSInvocation+ASTestHelpers.h" + +@implementation NSInvocation (ASTestHelpers) + +- (id)as_argumentAtIndexAsObject:(NSInteger)index +{ + void *buf; + [self getArgument:&buf atIndex:index]; + return (__bridge id)buf; +} + +- (void)as_setReturnValueWithObject:(id)object +{ + if (object == nil) { + const void *fixedBuf = NULL; + [self setReturnValue:&fixedBuf]; + } else { + // Retain, then autorelease. + const void *fixedBuf = CFAutorelease((__bridge_retained void *)object); + [self setReturnValue:&fixedBuf]; + } +} + +@end diff --git a/Tests/Common/OCMockObject+ASAdditions.h b/Tests/Common/OCMockObject+ASAdditions.h new file mode 100644 index 0000000000..a8e7004915 --- /dev/null +++ b/Tests/Common/OCMockObject+ASAdditions.h @@ -0,0 +1,33 @@ +// +// OCMockObject+ASAdditions.h +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +@interface OCMockObject (ASAdditions) + +/** + * NOTE: All OCMockObjects created during an ASTestCase call OCMVerifyAll during -tearDown. + */ + +/** + * A method to manually specify which optional protocol methods should return YES + * from -respondsToSelector:. + * + * If you don't call this method, the default OCMock behavior is to + * "implement" all optional protocol methods, which makes it impossible to + * test scenarios where only a subset of optional protocol methods are implemented. + * + * You should only call this on protocol mocks. + */ +- (void)addImplementedOptionalProtocolMethods:(SEL)aSelector, ... NS_REQUIRES_NIL_TERMINATION; + +@end diff --git a/Tests/Common/OCMockObject+ASAdditions.m b/Tests/Common/OCMockObject+ASAdditions.m new file mode 100644 index 0000000000..86dcdbf9d8 --- /dev/null +++ b/Tests/Common/OCMockObject+ASAdditions.m @@ -0,0 +1,145 @@ +// +// OCMockObject+ASAdditions.m +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import "OCMockObject+ASAdditions.h" + +#import +#import +#import "ASTestCase.h" + +@interface ASTestCase (OCMockObjectRegistering) + +- (void)registerMockObject:(id)mockObject; + +@end + +@implementation OCMockObject (ASAdditions) + ++ (void)load +{ + // [OCProtocolMockObject respondsToSelector:] <-> [(self) swizzled_protocolMockRespondsToSelector:] + Method orig = class_getInstanceMethod(OCMockObject.protocolMockObjectClass, @selector(respondsToSelector:)); + Method new = class_getInstanceMethod(self, @selector(swizzled_protocolMockRespondsToSelector:)); + method_exchangeImplementations(orig, new); + + // init <-> swizzled_init + Method origInit = class_getInstanceMethod([OCMockObject class], @selector(init)); + Method newInit = class_getInstanceMethod(self, @selector(swizzled_init)); + method_exchangeImplementations(origInit, newInit); +} + +/// Since OCProtocolMockObject is private, use this method to get the class. ++ (Class)protocolMockObjectClass +{ + static Class c; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + c = NSClassFromString(@"OCProtocolMockObject"); + NSAssert(c != Nil, nil); + }); + return c; +} + +/// Whether the user has opted-in to specify which optional methods are implemented for this object. +- (BOOL)hasSpecifiedOptionalProtocolMethods +{ + return objc_getAssociatedObject(self, @selector(optionalImplementedMethods)) != nil; +} + +/// The optional protocol selectors the user has added via -addImplementedOptionalProtocolMethods: +- (NSMutableSet *)optionalImplementedMethods +{ + NSMutableSet *result = objc_getAssociatedObject(self, _cmd); + if (result == nil) { + result = [NSMutableSet set]; + objc_setAssociatedObject(self, _cmd, result, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + } + return result; +} + +- (void)addImplementedOptionalProtocolMethods:(SEL)aSelector, ... +{ + // Can't use isKindOfClass: since we're a proxy. + NSAssert(object_getClass(self) == OCMockObject.protocolMockObjectClass, @"Cannot call this method on non-protocol mocks."); + NSMutableSet *methods = self.optionalImplementedMethods; + + // First arg is not returned by va_arg, needs to be handled separately. + if (aSelector != NULL) { + [methods addObject:NSStringFromSelector(aSelector)]; + } + + va_list args; + va_start(args, aSelector); + SEL s; + while((s = va_arg(args, SEL))) + { + [methods addObject:NSStringFromSelector(s)]; + } + va_end(args); +} + +- (BOOL)implementsOptionalProtocolMethod:(SEL)aSelector +{ + NSAssert(self.hasSpecifiedOptionalProtocolMethods, @"Shouldn't call this method if the user hasn't opted-in to specifying optional protocol methods."); + + // Check our collection first. It'll be in here if they explicitly marked the method as implemented. + for (NSString *str in self.optionalImplementedMethods) { + if (sel_isEqual(NSSelectorFromString(str), aSelector)) { + return YES; + } + } + + // If they didn't explicitly mark it implemented, check if they stubbed/expected it. That counts too, but + // we still want them to have the option to declare that the method exists without + // stubbing it or making an expectation, so the rest of OCMock's mechanisms work as expected. + return [self handleSelector:aSelector]; +} + +- (BOOL)swizzled_protocolMockRespondsToSelector:(SEL)aSelector +{ + // Can't use isKindOfClass: since we're a proxy. + NSAssert(object_getClass(self) == OCMockObject.protocolMockObjectClass, @"Swizzled method should only ever be called for protocol mocks."); + + // If they haven't called our public method to opt-in, use the default behavior. + if (!self.hasSpecifiedOptionalProtocolMethods) { + return [self swizzled_protocolMockRespondsToSelector:aSelector]; + } + + Ivar i = class_getInstanceVariable([self class], "mockedProtocol"); + NSAssert(i != NULL, nil); + Protocol *mockedProtocol = object_getIvar(self, i); + NSAssert(mockedProtocol != NULL, nil); + + // Check if it's an optional protocol method. If not, just return the default implementation (which has now swapped). + struct objc_method_description methodDescription; + methodDescription = protocol_getMethodDescription(mockedProtocol, aSelector, NO, YES); + if (methodDescription.name == NULL) { + methodDescription = protocol_getMethodDescription(mockedProtocol, aSelector, NO, NO); + if (methodDescription.name == NULL) { + return [self swizzled_protocolMockRespondsToSelector:aSelector]; + } + } + + // It's an optional instance or class method. Override the return value. + return [self implementsOptionalProtocolMethod:aSelector]; +} + +// Whenever a mock object is initted, register it with the current test case +// so that it gets verified and its invocations are cleared during -tearDown. +- (instancetype)swizzled_init +{ + [self swizzled_init]; + [ASTestCase.currentTestCase registerMockObject:self]; + return self; +} + +@end diff --git a/Tests/TestHost/AppDelegate.h b/Tests/TestHost/AppDelegate.h index 90be689aea..d9bc76ef51 100644 --- a/Tests/TestHost/AppDelegate.h +++ b/Tests/TestHost/AppDelegate.h @@ -1,11 +1,18 @@ // // AppDelegate.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Tests/TestHost/AppDelegate.m b/Tests/TestHost/AppDelegate.m index f348d3a943..51bf905c2f 100644 --- a/Tests/TestHost/AppDelegate.m +++ b/Tests/TestHost/AppDelegate.m @@ -1,11 +1,18 @@ // // AppDelegate.mm -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "AppDelegate.h" diff --git a/Tests/TestHost/main.m b/Tests/TestHost/main.m index d99695b526..27c659b765 100644 --- a/Tests/TestHost/main.m +++ b/Tests/TestHost/main.m @@ -1,11 +1,18 @@ // // main.m -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/AsyncDisplayKit.podspec b/Texture.podspec similarity index 55% rename from AsyncDisplayKit.podspec rename to Texture.podspec index ff026649f1..5cb0b9366a 100644 --- a/AsyncDisplayKit.podspec +++ b/Texture.podspec @@ -1,13 +1,15 @@ Pod::Spec.new do |spec| - spec.name = 'AsyncDisplayKit' - spec.version = '2.2' - spec.license = { :type => 'BSD' } - spec.homepage = 'http://asyncdisplaykit.org' - spec.authors = { 'Scott Goodson' => 'scottgoodson@gmail.com' } + spec.name = 'Texture' + spec.version = '2.3.4' + spec.license = { :type => 'BSD and Apache 2', } + spec.homepage = 'http://texturegroup.org' + spec.authors = { 'Huy Nguyen' => 'huy@pinterest.com', 'Garrett Moon' => 'garrett@excitedpixel.com', 'Scott Goodson' => 'scottgoodson@gmail.com', 'Michael Schneider' => 'schneider@pinterest.com', 'Adlai Holler' => 'adlai@pinterest.com' } spec.summary = 'Smooth asynchronous user interfaces for iOS apps.' - spec.source = { :git => 'https://github.com/facebook/AsyncDisplayKit.git', :tag => spec.version.to_s } + spec.source = { :git => 'https://github.com/TextureGroup/Texture.git', :tag => spec.version.to_s } + spec.module_name = 'AsyncDisplayKit' + spec.header_dir = 'AsyncDisplayKit' - spec.documentation_url = 'http://asyncdisplaykit.org/appledoc/' + spec.documentation_url = 'http://texturegroup.org/appledoc/' spec.weak_frameworks = 'Photos','MapKit','AssetsLibrary' spec.requires_arc = true @@ -19,13 +21,12 @@ Pod::Spec.new do |spec| # Subspecs spec.subspec 'Core' do |core| - core.prefix_header_file = 'Source/AsyncDisplayKit-Prefix.pch' core.public_header_files = [ 'Source/*.h', 'Source/Details/**/*.h', 'Source/Layout/**/*.h', 'Source/Base/*.h', - 'Source/Debug/AsyncDisplayKit+Debug.h', + 'Source/Debug/**/*.h', 'Source/TextKit/ASTextNodeTypes.h', 'Source/TextKit/ASTextKitComponents.h' ] @@ -44,30 +45,26 @@ Pod::Spec.new do |spec| end 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.8' + pin.dependency 'PINRemoteImage/iOS', '= 3.0.0-beta.11' pin.dependency 'PINRemoteImage/PINCache' - pin.dependency 'AsyncDisplayKit/Core' + pin.dependency 'Texture/Core' end spec.subspec 'IGListKit' do |igl| - # Note: The core.prefix_header_file includes setup of IG_LIST_KIT, so the line below could be removed. - igl.xcconfig = { 'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) IG_LIST_KIT=1' } - igl.dependency 'IGListKit', '2.1.0' - igl.dependency 'AsyncDisplayKit/Core' + igl.dependency 'IGListKit', '3.0.0' + igl.dependency 'Texture/Core' end - + spec.subspec 'Yoga' do |yoga| yoga.xcconfig = { 'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) YOGA=1' } - yoga.dependency 'Yoga', '1.0.2' - yoga.dependency 'AsyncDisplayKit/Core' + yoga.dependency 'Yoga', '1.5.0' + yoga.dependency 'Texture/Core' end # Include optional PINRemoteImage module spec.default_subspec = 'PINRemoteImage' - spec.social_media_url = 'https://twitter.com/AsyncDisplayKit' + spec.social_media_url = 'https://twitter.com/TextureiOS' spec.library = 'c++' spec.pod_target_xcconfig = { 'CLANG_CXX_LANGUAGE_STANDARD' => 'c++11', diff --git a/build.sh b/build.sh index d451bc9ad7..1684c2497d 100755 --- a/build.sh +++ b/build.sh @@ -69,7 +69,7 @@ if type xcpretty-travis-formatter &> /dev/null; then FORMATTER="-s" fi -if [ "$MODE" = "tests" ]; then +if [ "$MODE" = "tests" -o "$MODE" = "all" ]; then echo "Building & testing AsyncDisplayKit." pod install set -o pipefail && xcodebuild \ @@ -78,24 +78,22 @@ if [ "$MODE" = "tests" ]; then -sdk "$SDK" \ -destination "$PLATFORM" \ build-for-testing test | xcpretty $FORMATTER - trap - EXIT - exit 0 + success="1" fi if [ "$MODE" = "tests_listkit" ]; then echo "Building & testing AsyncDisplayKit+IGListKit." - pod install --project-directory=ASDKListKit + pod install --project-directory=SubspecWorkspaces/ASDKListKit set -o pipefail && xcodebuild \ - -workspace ASDKListKit/ASDKListKit.xcworkspace \ + -workspace SubspecWorkspaces/ASDKListKit/ASDKListKit.xcworkspace \ -scheme ASDKListKitTests \ -sdk "$SDK" \ -destination "$PLATFORM" \ build-for-testing test | xcpretty $FORMATTER - trap - EXIT - exit 0 + success="1" fi -if [ "$MODE" = "examples" ]; then +if [ "$MODE" = "examples" -o "$MODE" = "all" ]; then echo "Verifying that all AsyncDisplayKit examples compile." #Update cocoapods repo pod repo update master @@ -105,8 +103,7 @@ if [ "$MODE" = "examples" ]; then build_example $example done - trap - EXIT - exit 0 + success="1" fi if [ "$MODE" = "examples-pt1" ]; then @@ -119,8 +116,7 @@ if [ "$MODE" = "examples-pt1" ]; then build_example $example done - trap - EXIT - exit 0 + success="1" fi if [ "$MODE" = "examples-pt2" ]; then @@ -133,8 +129,7 @@ if [ "$MODE" = "examples-pt2" ]; then build_example $example done - trap - EXIT - exit 0 + success="1" fi if [ "$MODE" = "examples-pt3" ]; then @@ -147,8 +142,7 @@ if [ "$MODE" = "examples-pt3" ]; then build_example $example done - trap - EXIT - exit 0 + success="1" fi if [ "$MODE" = "examples-extra" ]; then @@ -161,8 +155,7 @@ if [ "$MODE" = "examples-extra" ]; then build_example $example done - trap - EXIT - exit 0 + success="1" fi # Support building a specific example: sh build.sh example examples/ASDKLayoutTransition @@ -172,11 +165,10 @@ if [ "$MODE" = "example" ]; then pod repo update master build_example $2 - trap - EXIT - exit 0 + success="1" fi -if [ "$MODE" = "life-without-cocoapods" ]; then +if [ "$MODE" = "life-without-cocoapods" -o "$MODE" = "all" ]; then echo "Verifying that AsyncDisplayKit functions as a static library." set -o pipefail && xcodebuild \ @@ -185,11 +177,10 @@ if [ "$MODE" = "life-without-cocoapods" ]; then -sdk "$SDK" \ -destination "$PLATFORM" \ build | xcpretty $FORMATTER - trap - EXIT - exit 0 + success="1" fi -if [ "$MODE" = "framework" ]; then +if [ "$MODE" = "framework" -o "$MODE" = "all" ]; then echo "Verifying that AsyncDisplayKit functions as a dynamic framework (for Swift/Carthage users)." set -o pipefail && xcodebuild \ @@ -198,22 +189,25 @@ if [ "$MODE" = "framework" ]; then -sdk "$SDK" \ -destination "$PLATFORM" \ build | xcpretty $FORMATTER - trap - EXIT - exit 0 + success="1" fi -if [ "$MODE" = "cocoapods-lint" ]; then +if [ "$MODE" = "cocoapods-lint" -o "$MODE" = "all" ]; then echo "Verifying that podspec lints." set -o pipefail && pod env && pod lib lint - trap - EXIT - exit 0 + success="1" fi -if [ "$MODE" = "carthage" ]; then +if [ "$MODE" = "carthage" -o "$MODE" = "all" ]; then echo "Verifying carthage works." set -o pipefail && carthage update && carthage build --no-skip-current fi +if [ "$success" = "1" ]; then + trap - EXIT + exit 0 +fi + echo "Unrecognised mode '$MODE'." diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100755 index 0000000000..bd5d18888f --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,2 @@ +_site/ + diff --git a/docs/404.md b/docs/404.md new file mode 100755 index 0000000000..5c0791ac92 --- /dev/null +++ b/docs/404.md @@ -0,0 +1,11 @@ +--- +id: 4oh4 +title: Page Not Found +layout: default +--- + +# Page Not Found + +Crikey! There doesn't seem to be anything here. + +If you find a broken link, feel free to send a pull request. You can also let us know at [Github](https://github.com/texturegroup/texture/issues) so that we can fix it. diff --git a/docs/CNAME b/docs/CNAME new file mode 100755 index 0000000000..80ea8a240d --- /dev/null +++ b/docs/CNAME @@ -0,0 +1 @@ +texturegroup.org \ No newline at end of file diff --git a/docs/Gemfile b/docs/Gemfile new file mode 100755 index 0000000000..99e72d8322 --- /dev/null +++ b/docs/Gemfile @@ -0,0 +1,5 @@ +source 'https://rubygems.org' + +gem 'github-pages' +gem 'rouge', '~>1.7' +gem 'jekyll', '~>3.2.0' diff --git a/docs/README.md b/docs/README.md new file mode 100755 index 0000000000..8c1c2ee544 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,31 @@ +# Texture Documentation + +We use [Jekyll](http://jekyllrb.com/) to build the site using Markdown and host it on [Github Pages](https://pages.github.com/). + +### Dependencies + +Github Pages uses Jekyll to host a site and Jekyll has the following dependencies. + + - [Ruby](http://www.ruby-lang.org/) (version >= 2.0.0) + - [RubyGems](http://rubygems.org/) (version >= 1.3.7) + - [Bundler](http://gembundler.com/) + +Mac OS X comes pre-installed with Ruby, but you may need to update RubyGems (via `gem update --system`). +Once you have RubyGems, use it to install bundler. + +```sh +$ gem install bundler +$ cd gh-pages # Go to folder +$ bundle install # Might need sudo. +``` + +### Run Jekyll Locally + +Use Jekyll to serve the website locally (by default, at `http://localhost:4000`): + +```sh +$ bundle exec jekyll serve [--incremental] +$ open http://localhost:4000/ +``` + +For more, see https://help.github.com/articles/setting-up-your-github-pages-site-locally-with-jekyll/ diff --git a/docs/_config.yml b/docs/_config.yml new file mode 100755 index 0000000000..ab347c39b8 --- /dev/null +++ b/docs/_config.yml @@ -0,0 +1,24 @@ +--- +url: http://texturegroup.org +name: Texture +relative_permalinks: false +markdown: kramdown +timezone: America/Los_Angeles +google_analytics: UA-87502907-1 + +safe: true +lsi: false +highlighter: rouge + +defaults: + - + scope: + path: "" + type: "posts" + values: + layout: post + is_post: true + +collections: + docs: + output: true diff --git a/docs/_data/nav_docs.yml b/docs/_data/nav_docs.yml new file mode 100755 index 0000000000..91e76d3434 --- /dev/null +++ b/docs/_data/nav_docs.yml @@ -0,0 +1,72 @@ +- title: Quick Start + items: + - getting-started + - resources + - installation + - adoption-guide-2-0-beta1 +- title: Core Concepts + items: + - intelligent-preloading + - containers-overview + - node-overview + - subclassing + - node-lifecycle + - faq +- title: Layout + items: + - layout2-quickstart + - automatic-layout-examples-2 + - layout2-layoutspec-types + - layout2-layout-element-properties + - layout2-api-sizing + - layout-transition-api +- title: Conveniences + items: + - hit-test-slop + - batch-fetching-api + - automatic-subnode-mgmt + - inversion + - image-modification-block + - placeholder-fade-duration + - accessibility + - uicollectionviewinterop +- title: Optimizations + items: + - layer-backing + - subtree-rasterization + - synchronous-concurrency + - corner-rounding +- title: Tools + items: + - debug-tool-hit-test-visualization + - debug-tool-pixel-scaling + - debug-tool-ASRangeController +- title: Advanced Technologies + items: + - asvisibility + - asenvironment + - asrunloopqueue +- title: Node Containers + items: + - containers-asviewcontroller + - containers-asnodecontroller + - containers-astablenode + - containers-ascollectionnode + - containers-aspagernode +- title: Nodes + items: + - display-node + - cell-node + - button-node + - text-node + - image-node + - network-image-node + - video-node + - map-node + - control-node + - scroll-node + - editable-text-node + - multiplex-image-node + + + diff --git a/docs/_docs/accessibility.md b/docs/_docs/accessibility.md new file mode 100755 index 0000000000..45261003d9 --- /dev/null +++ b/docs/_docs/accessibility.md @@ -0,0 +1,11 @@ +--- +title: Accessibility +layout: docs +permalink: /docs/accessibility.html +prevPage: placeholder-fade-duration.html +nextPage: layer-backing.html +--- + +Accessibility works seamlessly in ways that even UIKit doesn’t provide. When using the powerful optimization features of Layer Backing (`.layerBacked`) and Subtree Rasterization (`.shouldRasterizeDescendants`), VoiceOver can access fine-grained metadata about each element. This is pretty amazing: `CALayer` doesn’t support accessibility, and rasterization reduces everything to a single flat image. + +The Texture team fundamentally believes in Accessibility, and invested the time to create an innovative system to make this possible with zero developer effort. As a bonus, this also allows Automated UI Testing greater access to the interface. \ No newline at end of file diff --git a/docs/_docs/adoption-guide-2-0-beta1.md b/docs/_docs/adoption-guide-2-0-beta1.md new file mode 100755 index 0000000000..18591a1ca6 --- /dev/null +++ b/docs/_docs/adoption-guide-2-0-beta1.md @@ -0,0 +1,146 @@ +--- +title: "Upgrading to 2.0" +layout: docs +permalink: /docs/adoption-guide-2-0-beta1.html +prevPage: adoption-guide-2-0-beta1.html +--- + +
    +
  1. GitHub Release Notes
  2. +
  3. Getting the 2.0 Release Candidate
  4. +
  5. Testing your app with 2.0
  6. +
  7. Migrating to 2.0
  8. +
  9. Migrating to 2.0 (Layout)
  10. +
+ +## Release Notes + +Please read the official release notes on GitHub. + + +## Getting the Release Candidate + +Add the following to your podfile + +
+
+
+pod 'Texture', '>= 2.0'
+
+
+
+ +then run + +
+
+
+pod repo update
+pod update Texture
+
+
+
+ +in the terminal. + +## Testing 2.0 + +Once you have updated to 2.0, you will see many deprecation warnings. Don't worry! + +These warnings are quite safe, because we have bridged all of the old APIs for you, so that you can test out the 2.0, before migrating to the new API. + +If your app fails to build instead of just showing the warnings, you might have Warnings as Errors enabled for your project. You have a few options: + +1. Disable deprecation warnings in the Xcode project settings +2. Disable warnings as errors in the project's build settings. +3. Disable deprecation warnings in Texture. To do this, change `line 74` in `ASBaseDefines.h` to `# define ASDISPLAYNODE_WARN_DEPRECATED 0` + +Once your app builds and runs, test it to make sure everything is working normally. If you find any problems, try adopting the new API in that area and re-test. + +One key behavior change you may notice: + +- ASStackLayoutSpec's `.alignItems` property default changed to `ASStackLayoutAlignItemsStretch` instead of `ASStackLayoutAlignItemsStart`. This may cause distortion in your UI. + +If you still have issues, please file a GitHub issue and we'd be happy to help you out! + +## Migrating to 2.0 + +Once your app is working, it's time to start converting! + +A full API changelog from `1.9.92` to `2.0-beta.1` is available here. + +#### ASDisplayNode Changes + +- ASDisplayNode's `.usesImplicitHierarchyManagement` has been renamed to `.automaticallyManagesSubnodes`. The Automatic Subnode Management API has been moved out of Beta, but has a few documented [limitations](). + +- ASDisplayNode's `-cancelLayoutTransitionsInProgress` has been renamed to `-cancelLayoutTransition`. The Layout Transition API has been moved out of Beta. Significant new functionality is planed for future dot releases. + + +#### Updated Interface State Callback Methods + +The new method names are meant to unify the range update methods to show how they relate to each other and be a bit more self-explanatory: + +- `didEnterPreloadState / didExitPreloadState` +- `didEnterDisplayState / didExitDisplayState` +- `didEnterVisibleState / didExitVisibleState` + +These new methods replace the following: + +- `loadStateDidChange:(BOOL)inLoadState` +- `displayStateDidChange:(BOOL)inDisplayState` +- `visibleStateDidChange:(BOOL)isVisible` + +#### Collection / Table API Updates + +Texture's collection and table APIs have been moved from the view space (`collectionView`, `tableView`) to the node space (`collectionNode`, `tableNode`). + +- Search your project for `tableView` and `collectionView`. Most, if not all, of the data source / delegate methods have new node versions. + +It is important that developers using Texture understand that an ASCollectionNode is backed by an ASCollectionView (a subclass of UICollectionView). ASCollectionNode runs asynchronously, so calling number -numberOfRowsInSection on the collectionNode is different than calling it on the collectionView. + +For example, let's say you have an empty table. You insert `100` rows and then immediately call -tableView:numberOfRowsInSection. This will return `0` rows. If you call -waitUntilAllUpdatesAreCommitted after insertion (waits until the collectionNode synchronizes with the collectionView), you will get 100, _but_ you might block the main thread. A good developer should rarely (or never) need to use -waitUntilAllUpdatesAreCommitted. If you update the collectionNode and then need to read back immediately, you should use the collectionNode API. You shouldn't need to talk to the collectionView. + +As a rule of thumb, use the collection / table node API for everything, unless the API is not available on the collectionNode. + +To summarize, any `indexPath` that is passed to the `collectionView` space references data that has been synced with `ASCollectionNode`'s underlying `UICollectionView`. Conversly, any `indexPath` that is passed to the `collectionNode` space references asynchronous data that *might not yet* have been synced with ASCollectionNode's underlying `UICollectionView`. The same concepts apply to `ASTableNode`. + +An exception to this is `ASTableNode`'s `-didSelectRowAtIndexPath:`, which is called in UIKit space to make sure that `indexPath` indicies reference the data in the onscreen (data that has been synced to the underlying `UICollectionView` `dataSource`). + +While previous versions of the framework required the developer to be aware of the asynchronous interplay between `ASCollectionNode` and its underlying `UICollectionView`, this new API should provide better safegaurds against developer-introduced data source inconsistencies. + +Other updates include: + +- Deprecate `ASTableView`'s -init method. Please use `ASTableNode` instead of `ASTableView`. While this makes adopting the framework marginally more difficult to, the benefits of using ASTableNode / ASCollectionNode over their ASTableView / ASCollectionView counterparts are signficant. + +- Deprecate `-beginUpdates` and `-endUpdatesAnimated:`. Please use the `-performBatchUpdates:` methods instead. + +- Deprecate `-reloadDataImmediately`. Please see the header file comments for the deprecation solution. + +- Moved range tuning to the `tableNode` / `collectionNode` (from the `tableView` / `collectionView`) + +- `constrainedSizeForNodeAtIndexPath:` moved from the `.dataSource` to the `.delegate` to be consistent with UIKit definitions of the roles. **Note:** Make sure that you provide a delegate for any `ASTableNode`, `ASCollectionNode` or `ASPagerNodes` that use this method. Your code will silently not call your delegate method, if you do not have a delegate assigned. + +- Renamed `pagerNode:constrainedSizeForNodeAtIndexPath:` to `pagerNode:constrainedSizeForNodeAtIndex:` + +- collection view update validation assertions are now enabled. If you see something like `"Invalid number of items in section 2. The number of items after the update (7) must be equal to the number of items before the update (4) plus or minus the number of items inserted or removed from the section (4 inserted, 0 removed)”`, please check the data source logic. If you have any questions, reach out to us on GitHub. + +Best Practices: + +- Use node blocks if possible. These are run in parallel on a background thread, resulting in 10x performance gains. +- Use nodes to store things about your rows. +- Make sure to batch updates that need to be batched. + +Resources: + +- [Video](https://youtu.be/yuDqvE5n_1g) of the ASCollectionNode Behind-the-Scenes talk at Pinterest. The diagrams seen in the talk. + +- PR [#2390](https://github.com/facebook/AsyncDisplayKit/pull/2390) and PR [#2381](https://github.com/facebook/AsyncDisplayKit/pull/2381) show how we converted AsyncDisplayKit's [example projects](https://github.com/texturegroup/texture/tree/master/examples) to conform to this new API. + + +#### Layout API Updates + +Please read the separate Layout 2.0 Conversion Guide for an overview of the upgrades and to see how to convert your existing layout code. + +#### Help us out + +If we're missing something from this list, please let us know or edit this doc for us (GitHub edit link at the top of page)! diff --git a/docs/_docs/apidiff-1992-to-20beta1.md b/docs/_docs/apidiff-1992-to-20beta1.md new file mode 100755 index 0000000000..bc681276fd --- /dev/null +++ b/docs/_docs/apidiff-1992-to-20beta1.md @@ -0,0 +1,7 @@ +--- +title: API Diff +layout: apidiff +permalink: /docs/apidiff-1992-to-20beta1.html +--- + + diff --git a/docs/_docs/appledocs.md b/docs/_docs/appledocs.md new file mode 100755 index 0000000000..4b466e9928 --- /dev/null +++ b/docs/_docs/appledocs.md @@ -0,0 +1,9 @@ + +--- +title: api +layout: docs +permalink: /docs/appledocs.html +--- + +

hi

+ \ No newline at end of file diff --git a/docs/_docs/asenvironment.md b/docs/_docs/asenvironment.md new file mode 100755 index 0000000000..c749cd0802 --- /dev/null +++ b/docs/_docs/asenvironment.md @@ -0,0 +1,19 @@ +--- +title: ASEnvironment +layout: docs +permalink: /docs/asenvironment.html +prevPage: asvisibility.html +nextPage: asrunloopqueue.html +--- + +`ASEnvironment` is a performant and scalable way to enable upward and downward propagation of information throughout the node hierarchy. It stores a variety of critical “environmental” metadata, like the trait collection, interface state, hierarchy state, and more. + +Any object that conforms to the `` protocol can propagate specific states defined in an `ASEnvironmentState` up and/or down the ASEnvironment tree. To define how merges of States should happen, specific merge functions can be provided. + +Compared to UIKit, this system is very efficient and one of the reasons why nodes are much lighter weight than UIViews. This is achieved by using simple structures to store data rather than creating objects. For example, `UITraitCollection` is an object, but `ASEnvironmentTraitCollection` is just a struct. + +This means that whenever a node needs to query something about its environment, for example to check its [interface state](http://texturegroup.org/docs/intelligent-preloading.html#interface-state-ranges), instead of climbing the entire tree or checking all of its children, it can go to one spot and read the value that was propogated to it. + +A key operating principle of ASEnvironment is to update values when new subnodes are added or removed. + +ASEnvironment powers many of the most valuable features of Texture. **There is no public API available at this time.** diff --git a/docs/_docs/asrunloopqueue.md b/docs/_docs/asrunloopqueue.md new file mode 100755 index 0000000000..cd2e978c22 --- /dev/null +++ b/docs/_docs/asrunloopqueue.md @@ -0,0 +1,14 @@ +--- +title: ASRunLoopQueue +layout: docs +permalink: /docs/asrunloopqueue.html +prevPage: asenvironment.html +--- + +Even with main thread work, Texture is able to dramatically reduce its impact on the user experience by way of the rather amazing ASRunLoopQueue. + +`ASRunloopQueue` breaks up operations that must be performed on the main thread into far smaller chunks, easily 1/10th of the size that they otherwise would be, so that operation such as allocating UIViews or even destroying objects can be spread out and allow the run loops to more frequently turn. This more periodic turning allows the device to much more frequently check if a user touch has started or if an animation timer requires a new frame to be drawn, allowing far greater responsiveness even when the device is very busy and processing a large queue of main thread work. + +It's a longer discussion why this kind of technique is extremely challenging to implement with `UIKit`, but it has to do with the fact that `Texture` prepares content in advance, giving it a buffer of time where it can spread out the creation of these objects in tiny chunks. If it doesn't finish by the time it needs to be on screen, then it finishes the rest of what needs to be created in a single chunk. `UIKit` has no similar mechanisms to create things in advance, and there is always just one huge chunk as a view controller or cell needs to come on screen. + +**ASRunLoopQueue is enabled by default when running Texture.** A developer does not need to be aware of it's existence except to know that it helps reduce main thread blockage. diff --git a/docs/_docs/asviewcontroller.md b/docs/_docs/asviewcontroller.md new file mode 100755 index 0000000000..9cba5be9e5 --- /dev/null +++ b/docs/_docs/asviewcontroller.md @@ -0,0 +1,55 @@ +--- +title: ASViewController +layout: docs +permalink: /docs/asviewcontroller.html +prevPage: +nextPage: aspagernode.html +--- + +`ASViewController` is a direct subclass of `UIViewController`. For the most part, it can be used in place of any `UIViewController` relatively easily. + +The main difference is that you construct and return the node you'd like managed as opposed to the way `UIViewController` provides a view of its own. + +Consider the following `ASViewController` subclass that would like to use a custom table node as its managed node. + +
+SwiftObjective-C +
+
+- (instancetype)initWithModel:(NSArray *)models
+{
+    ASTableNode *tableNode = [[ASTableNode alloc] initWithStyle:UITableViewStylePlain];
+
+    if (!(self = [super initWithNode:tableNode])) { return nil; }
+
+    self.models = models;
+    
+    self.tableNode = tableNode;
+    self.tableNode.dataSource = self;
+    
+    return self;
+}
+
+ + +
+
+ +The most important line is: + +`if (!(self = [super initWithNode:tableNode])) { return nil; }` + +As you can see, `ASViewController`'s are initialized with a node of your choosing. diff --git a/docs/_docs/asvisibility.md b/docs/_docs/asvisibility.md new file mode 100755 index 0000000000..6ca8ef2de8 --- /dev/null +++ b/docs/_docs/asvisibility.md @@ -0,0 +1,15 @@ +--- +title: ASVisibility +layout: docs +permalink: /docs/asvisibility.html +prevPage: debug-tool-ASRangeController.html +nextPage: asenvironment.html +--- + +`ASNavigationController` and `ASTabBarController` both implement the `ASVisibility` protocol. These classes can be used even without `ASDisplayNodes`, making them suitable base classes for your inheritance hierarchy. For any child view controllers that are `ASViewControllers`, these classes know the exact number of user taps it would take to make the view controller visible (0 if currently visible). + +Knowing a view controller’s visibility depth allows view controllers to automatically take appropriate actions as a user approaches or leaves them. Non-default tabs in an app might preload some of their data; a controller 3 levels deep in a navigation stack might progressively free memory for images, text, and fetched data as it gets deeper. + +Any container view controller can implement a simple protocol to integrate with the system. For example, `ASNavigationController` will return a visibility depth of it's own `visibilityDepth` + 1 for a view controller that would be revealed by tapping the back button once. + +You can opt into some of this behavior automatically by enabling `automaticallyAdjustRangeModeBasedOnViewEvents` on `ASViewController`s. With this enabled, if either the view controller or its node conform to `ASRangeControllerUpdateRangeProtocol` (`ASCollectionNode` and `ASTableNode` do by default), the ranges will automatically be decreased as the visibility depth increases to save memory. diff --git a/docs/_docs/automatic-layout-basics.md b/docs/_docs/automatic-layout-basics.md new file mode 100755 index 0000000000..8a830b1cd4 --- /dev/null +++ b/docs/_docs/automatic-layout-basics.md @@ -0,0 +1,36 @@ +--- +title: Layout Basics +layout: docs +permalink: /docs/automatic-layout-basics.html +prevPage: scroll-node.html +nextPage: automatic-layout-containers.html +--- + +##Box Model Layout + +ASLayout is an automatic, asynchronous, purely Objective-C box model layout feature. It is a simplified version of CSS flex box, loosely inspired by ComponentKit’s Layout. It is designed to make your layouts extensible and reusable. + +`UIView` instances store position and size in their `center` and `bounds` properties. As constraints change, Core Animation performs a layout pass to call `layoutSubviews`, asking views to update these properties on their subviews. + +`` instances (all ASDisplayNodes and subclasses) do not have any size or position information. Instead, Texture calls the `layoutSpecThatFits:` method with a given size constraint and the component must return a structure describing both its size, and the position and sizes of its children. + +##Terminology + +The terminology is a bit confusing, so here is a brief description of all of the Texture automatic layout players: + +Items that conform to the **\ protocol** declares a method for measuring the layout of an object. A layout is defined by an ASLayout return value, and must specify 1) the size (but not position) of the layoutable object, and 2) the size and position of all of its immediate child objects. The tree recursion is driven by parents requesting layouts from their children in order to determine their size, followed by the parents setting the position of the children once the size is known. + +This protocol also implements a "family" of layoutable protocols - the `AS{*}LayoutSpec` protocols. These protocols contain layout options that can be used for specific layout specs. For example, `ASStackLayoutSpec` has options defining how a layoutable should shrink or grow based upon available space. These layout options are all stored in an `ASLayoutOptions` class (that is defined in `ASLayoutablePrivate`). Generally you needn't worry about the layout options class, as the layoutable protocols allow all direct access to the options via convenience properties. If you are creating custom layout spec, then you can extend the backing layout options class to accommodate any new layout options. + +All ASDisplayNodes and subclasses as well as the `ASLayoutSpecs` conform to this protocol. + +An **`ASLayoutSpec`** is an immutable object that describes a layout. Creation of a layout spec should only happen by a user in layoutSpecThatFits:. During that method, a layout spec can be created and mutated. Once it is passed back to Texture, the isMutable flag will be set to NO and any further mutations will cause an assert. + +Every ASLayoutSpec must act on at least one child. The ASLayoutSpec has the responsibility of holding on to the spec children. Some layout specs, like ASInsetLayoutSpec, only require a single child. Others, have multiple. + +You don’t need to be aware of **`ASLayout`** except to know that it represents a computed immutable layout tree and is returned by objects conforming to the `` protocol. + +##Layout for UIKit Components: +- for UIViews that are added directly, you will still need to manually lay it out in `didLoad:` +- for UIViews that are added via `[ASDisplayNode initWithViewBlock:]` or its variants, you can then include it in `layoutSpecThatFits:` + diff --git a/docs/_docs/automatic-layout-containers.md b/docs/_docs/automatic-layout-containers.md new file mode 100755 index 0000000000..436d9c6efb --- /dev/null +++ b/docs/_docs/automatic-layout-containers.md @@ -0,0 +1,192 @@ +--- +title: LayoutSpecs +layout: docs +permalink: /docs/automatic-layout-containers.html +prevPage: scroll-node.html +nextPage: layout-api-debugging.html +--- + +Texture includes a library of `layoutSpec` components that can be composed to declaratively specify a layout. + +The **child(ren) of a layoutSpec may be a node, a layoutSpec or a combination of the two types.** In the below image, an `ASStackLayoutSpec` (vertical) containing a text node and an image node, is wrapped in another `ASStackLayoutSpec` (horizontal) with another text node. + + + +Both nodes and layoutSpecs conform to the `` protocol. Any `ASLayoutable` object may be the child of a layoutSpec. ASLayoutable properties may be applied to `ASLayoutable` objects to create complex UI designs. + +### Single Child layoutSpecs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
LayoutSpecDescription
ASInsetLayoutSpec

Applies an inset margin around a component.

The object that is being inset must have an intrinsic size.

ASOverlayLayoutSpec

Lays out a component, stretching another component on top of it as an overlay.

The underlay object must have an intrinsic size. Additionally, the order in which subnodes are added matters for this layoutSpec; the overlay object must be added as a subnode to the parent node after the underlay object.

ASBackgroundLayoutSpec

Lays out a component, stretching another component behind it as a backdrop.

The foreground object must have an intrinsic size. The order in which subnodes are added matters for this layoutSpec; the background object must be added as a subnode to the parent node before the foreground object.

ASCenterLayoutSpec

Centers a component in the available space.

The ASCenterLayoutSpec must have an intrinisic size.

ASRatioLayoutSpec

Lays out a component at a fixed aspect ratio (which can be scaled).

This spec is great for objects that do not have an intrinisic size, such as ASNetworkImageNodes and ASVideoNodes.

ASRelativeLayoutSpec

Lays out a component and positions it within the layout bounds according to vertical and horizontal positional specifiers. Similar to the “9-part” image areas, a child can be positioned at any of the 4 corners, or the middle of any of the 4 edges, as well as the center.

ASLayoutSpec

Can be used as a spacer in a stack spec with other children, when .flexGrow and/or .flexShrink is applied.

This class can also be subclassed to create custom layout specs - advanced Texture only!

+ +### Multiple Child(ren) layoutSpecs + +The following layoutSpecs may contain one or more children. + + + + + + + + + + + + + + +
LayoutSpecDescription
ASStackLayoutSpec

Allows you to stack components vertically or horizontally and specify how they should be flexed and aligned to fit in the available space.

This is the most common layoutSpec.

ASStaticLayoutSpecAllows positioning children at fixed offsets using the .sizeRange and .layoutPosition ASLayoutable properties.
+ +# ASLayoutable Properties + +The following properties can be applied to both nodes _and_ `layoutSpec`s; both conform to the `ASLayoutable` protocol. + +### ASStackLayoutable Properties + +The following properties may be set on any node or `layoutSpec`s, but will only apply to those who are a **child of a stack** `layoutSpec`. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PropertyDescription
CGFloat .spacingBeforeAdditional space to place before this object in the stacking direction.
CGFloat .spacingAfterAdditional space to place after this object in the stacking direction.
BOOL .flexGrowIf the sum of childrens' stack dimensions is less than the minimum size, should this object grow? Used when attached to a stack layout.
BOOL .flexShrinkIf the sum of childrens' stack dimensions is greater than the maximum size, should this object shrink? Used when attached to a stack layout.
ASRelativeDimension .flexBasisSpecifies the initial size for this object, in the stack dimension (horizontal or vertical), before the flexGrow or flexShrink properties are applied and the remaining space is distributed.
ASStackLayoutAlignSelf alignSelfOrientation of the object along cross axis, overriding alignItems. Used when attached to a stack layout.
CGFloat .ascenderUsed for baseline alignment. The distance from the top of the object to its baseline.
CGFloat .descenderUsed for baseline alignment. The distance from the baseline of the object to its bottom.
+ +### ASStaticLayoutable Properties + +The following properties may be set on any node or `layoutSpec`s, but will only apply to those who are a **child of a static** `layoutSpec`. + + + + + + + + + + + + + + +
PropertyDescription
.sizeRangeIf specified, the child's size is restricted according to this ASRelativeSizeRange. Percentages are resolved relative to the static layout spec.
.layoutPositionThe CGPoint position of this object within its parent spec.
+ +### Providing Intrinsic Sizes for Leaf Nodes + +Texture's layout is recursive, starting at the layoutSpec returned from `layoutSpecThatFits:` and proceeding down until it reaches the leaf nodes included in any nested `layoutSpec`s. + +Some leaf nodes provide their own intrinsic size, such as `ASTextNode` or `ASImageNode`. An attributed string or an image have their own sizes. Other leaf nodes require an intrinsic size to be set. + +**Nodes that require the developer to provide an intrinsic size:** + + - `ASDisplayNode` custom subclasses may provide their intrinisc size by implementing `calculateSizeThatFits:`. + - `ASNetworkImageNode` or `ASMultiplexImageNode` have no intrinsic size until the image is downloaded. + - `ASVideoNode` or `ASVideoNodePlayer` have no intrinsic size until the video is downloaded. + + +To provide an intrinisc size for these nodes, you can set one of the following: + + 1. implement `calculateSizeThatFits:` for **custom ASDisplayNode subclasses** only. + 2. set `.preferredFrameSize` + 3. set `.sizeRange` for children of **static** nodes only. + + +Note that `.preferredFrameSize` is not considered by `ASTextNodes`. Also, setting .sizeRange on a node will override the node's intrinisic size provided by `calculateSizeThatFits:`. + +### Common Confusions + +There are two main confusions that developers have when using layoutSpecs + + 1. Certain ASLayoutable properties only apply to children of stack nodes, while other properties only apply to children of static nodes. All ASLayoutable properties can be applied to any node or layoutSpec, however certain properties will only take effect depending on the type of the parent layoutSpec they are wrapped in. These differences are highlighted above in the ASStackLayoutable Properties and ASStaticLayoutable Properties sections. + 2. Have I set an intrinsic size for all of my leaf nodes? + + +#### I set `.flexGrow` on my node, but it doesn't grow? + +Upward propogation of `ASLayoutable` properties is currently disabled. Thus, in certain situations, the `.flexGrow` property must be manually applied to the containers. Two common examples of this that we see include: + +- a node (with `flexGrow` enabled) is wrapped in a static layoutSpec, wrapped in a stack layoutSpec. **solution**: enable `flexGrow` on the static layoutSpec as well. +- a node (with `flexGrow` enabled) is wrapped in an inset spec. **solution**: enable `flexGrow` on the inset spec as well. + + +#### I want to provide a size for my image, but I don't want to hard code the size. + +#### Why won't my stack spec span the full width? + +#### Difference between `ASInsetLayoutSpec` and `ASOverlayLayoutSpec` + +An overlay spec requires the underlay object (object to which the overlay item will be applied) to have an intrinsic size. It will center the overlay object in the middle of this area. + +An inset spec requires its object to have an intrinsic size. It adds the inset padding to this size to calculate the final size of the inset spec. + + + +### Best Practices + - Texture layout is called on a background thread. Do not access the device screen bounds, or any other UIKit methods in `layoutSpecThatFits:`. + - don't wrap everything in a staticLayoutSpec? + - avoid using preferred frame size for everything - won't respond nicely to device rotation or device sizing differences? diff --git a/docs/_docs/automatic-layout-examples-2.md b/docs/_docs/automatic-layout-examples-2.md new file mode 100755 index 0000000000..07da8df131 --- /dev/null +++ b/docs/_docs/automatic-layout-examples-2.md @@ -0,0 +1,264 @@ +--- +title: Layout Examples +layout: docs +permalink: /docs/automatic-layout-examples-2.html +prevPage: layout2-quickstart.html +nextPage: layout2-layoutspec-types.html +--- + +Check out the layout specs example project to play around with the code below. + +## Simple Header with Left and Right Justified Text + + + +To create this layout, we will use a: + +- a vertical `ASStackLayoutSpec` +- a horizontal `ASStackLayoutSpec` +- `ASInsetLayoutSpec` to inset the entire header + +The diagram below shows the composition of the layout elements (nodes + layout specs). + + + +
+ + Swift + Objective-C + +
+
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
+{
+  // when the username / location text is too long, 
+  // shrink the stack to fit onscreen rather than push content to the right, offscreen
+  ASStackLayoutSpec *nameLocationStack = [ASStackLayoutSpec verticalStackLayoutSpec];
+  nameLocationStack.style.flexShrink = 1.0;
+  nameLocationStack.style.flexGrow = 1.0;
+  
+  // if fetching post location data from server, 
+  // check if it is available yet and include it if so
+  if (_postLocationNode.attributedText) {
+    nameLocationStack.children = @[_usernameNode, _postLocationNode];
+  } else {
+    nameLocationStack.children = @[_usernameNode];
+  }
+  
+  // horizontal stack
+  ASStackLayoutSpec *headerStackSpec = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal
+                                                                               spacing:40
+                                                                        justifyContent:ASStackLayoutJustifyContentStart
+                                                                            alignItems:ASStackLayoutAlignItemsCenter
+                                                                              children:@[nameLocationStack, _postTimeNode]];
+  
+  // inset the horizontal stack
+  return [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsMake(0, 10, 0, 10) child:headerStackSpec];
+}
+  
+ +
+
+ +Rotate the example project from portrait to landscape to see how the spacer grows and shrinks. + +## Photo with Inset Text Overlay + + + +To create this layout, we will use a: + +- `ASInsetLayoutSpec` to inset the text +- `ASOverlayLayoutSpec` to overlay the inset text spec on top of the photo + +
+ + Swift + Objective-C + +
+
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
+{
+  _photoNode.style.preferredSize = CGSizeMake(USER_IMAGE_HEIGHT*2, USER_IMAGE_HEIGHT*2);
+
+  // INIFINITY is used to make the inset unbounded
+  UIEdgeInsets insets = UIEdgeInsetsMake(INFINITY, 12, 12, 12);
+  ASInsetLayoutSpec *textInsetSpec = [ASInsetLayoutSpec insetLayoutSpecWithInsets:insets child:_titleNode];
+  
+  return [ASOverlayLayoutSpec overlayLayoutSpecWithChild:_photoNode overlay:textInsetSpec];
+}
+  
+ +
+
+ +## Photo with Outset Icon Overlay + + + +To create this layout, we will use a: + +- `ASAbsoluteLayoutSpec` to place the photo and icon which have been individually sized and positioned using their `ASLayoutable` properties + +
+ + Swift + Objective-C + +
+
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
+{
+  _iconNode.style.preferredSize = CGSizeMake(40, 40);
+  _iconNode.style.layoutPosition = CGPointMake(150, 0);
+  
+  _photoNode.style.preferredSize = CGSizeMake(150, 150);
+  _photoNode.style.layoutPosition = CGPointMake(40 / 2.0, 40 / 2.0);
+  
+  return [ASAbsoluteLayoutSpec absoluteLayoutSpecWithSizing:ASAbsoluteLayoutSpecSizingSizeToFit
+                                                   children:@[_photoNode, _iconNode]];
+}
+  
+ +
+
+ + + +## Simple Inset Text Cell + + + +To recreate the layout of a single cell as is used in Pinterest's search view above, we will use a: + +- `ASInsetLayoutSpec` to inset the text +- `ASCenterLayoutSpec` to center the text according to the specified properties + +
+ + Swift + Objective-C + +
+
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
+{
+    UIEdgeInsets insets = UIEdgeInsetsMake(0, 12, 4, 4);
+    ASInsetLayoutSpec *inset = [ASInsetLayoutSpec insetLayoutSpecWithInsets:insets
+                                                                      child:_titleNode];
+
+    return [ASCenterLayoutSpec centerLayoutSpecWithCenteringOptions:ASCenterLayoutSpecCenteringY
+                                                      sizingOptions:ASCenterLayoutSpecSizingOptionMinimumX
+                                                              child:inset];
+}
+  
+ +
+
+ +## Top and Bottom Separator Lines + + + +To create the layout above, we will use a: + +- a `ASInsetLayoutSpec` to inset the text +- a vertical `ASStackLayoutSpec` to stack the two separator lines on the top and bottom of the text + +The diagram below shows the composition of the layoutables (layout specs + nodes). + + + +The following code can also be found in the `ASLayoutSpecPlayground` [example project](). + +
+ + Swift + Objective-C + +
+
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
+{
+  _topSeparator.style.flexGrow = 1.0;
+  _bottomSeparator.style.flexGrow = 1.0;
+
+  ASInsetLayoutSpec *insetContentSpec = [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsMake(20, 20, 20, 20) child:_textNode];
+
+  return [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical
+                                                 spacing:0
+                                          justifyContent:ASStackLayoutJustifyContentCenter
+                                              alignItems:ASStackLayoutAlignItemsStretch
+                                                children:@[_topSeparator, insetContentSpec, _bottomSeparator]];
+}
+  
+ +
+
diff --git a/docs/_docs/automatic-layout-examples.md b/docs/_docs/automatic-layout-examples.md new file mode 100755 index 0000000000..1523720935 --- /dev/null +++ b/docs/_docs/automatic-layout-examples.md @@ -0,0 +1,204 @@ +--- +title: Layout Examples +layout: docs +permalink: /docs/automatic-layout-examples.html +prevPage: automatic-layout-containers.html +nextPage: automatic-layout-debugging.html +--- + +Three examples in increasing order of complexity. +#NSSpain Talk Example + + + +
+SwiftObjective-C + +
+
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constraint
+{
+  ASStackLayoutSpec *vStack = [[ASStackLayoutSpec alloc] init];
+  
+  [vStack setChildren:@[titleNode, bodyNode];
+
+  ASStackLayoutSpec *hstack = [[ASStackLayoutSpec alloc] init];
+  hStack.direction          = ASStackLayoutDirectionHorizontal;
+  hStack.spacing            = 5.0;
+
+  [hStack setChildren:@[imageNode, vStack]];
+  
+  ASInsetLayoutSpec *insetSpec = [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsMake(5,5,5,5) child:hStack];
+
+  return insetSpec;
+}
+
+ +
+
+ +###Discussion + +#Social App Layout + + + +
+SwiftObjective-C + +
+
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
+{
+  // header stack
+  _userAvatarImageView.preferredFrameSize = CGSizeMake(USER_IMAGE_HEIGHT, USER_IMAGE_HEIGHT);  // constrain avatar image frame size
+  
+  ASLayoutSpec *spacer = [[ASLayoutSpec alloc] init];
+  spacer.flexGrow      = YES;
+
+  ASStackLayoutSpec *headerStack = [ASStackLayoutSpec horizontalStackLayoutSpec];
+  headerStack.alignItems         = ASStackLayoutAlignItemsCenter;       // center items vertically in horizontal stack
+  headerStack.justifyContent     = ASStackLayoutJustifyContentStart;    // justify content to left side of header stack
+  headerStack.spacing            = HORIZONTAL_BUFFER;
+
+  [headerStack setChildren:@[_userAvatarImageView, _userNameLabel, spacer, _photoTimeIntervalSincePostLabel]];
+  
+  // header inset stack
+  
+  UIEdgeInsets insets                = UIEdgeInsetsMake(0, HORIZONTAL_BUFFER, 0, HORIZONTAL_BUFFER);
+  ASInsetLayoutSpec *headerWithInset = [ASInsetLayoutSpec insetLayoutSpecWithInsets:insets child:headerStack];
+  headerWithInset.flexShrink = YES;
+  
+  // vertical stack
+  
+  CGFloat cellWidth                  = constrainedSize.max.width;
+  _photoImageView.preferredFrameSize = CGSizeMake(cellWidth, cellWidth);  // constrain photo frame size
+  
+  ASStackLayoutSpec *verticalStack   = [ASStackLayoutSpec verticalStackLayoutSpec];
+  verticalStack.alignItems           = ASStackLayoutAlignItemsStretch;    // stretch headerStack to fill horizontal space
+  
+  [verticalStack setChildren:@[headerWithInset, _photoImageView, footerWithInset]];
+
+  return verticalStack;
+}
+
+ +
+
+ +###Discussion + +Get the full Texture project at examples/ASDKgram. + +#Social App Layout 2 + + + +
+SwiftObjective-C + +
+
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize {
+
+  ASLayoutSpec *textSpec  = [self textSpec];
+  ASLayoutSpec *imageSpec = [self imageSpecWithSize:constrainedSize];
+  ASOverlayLayoutSpec *soldOutOverImage = [ASOverlayLayoutSpec overlayLayoutSpecWithChild:imageSpec 
+                                                                                  overlay:[self soldOutLabelSpec]];
+  
+  NSArray *stackChildren = @[soldOutOverImage, textSpec];
+  
+  ASStackLayoutSpec *mainStack = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical 
+                                                                         spacing:0.0
+                                                                  justifyContent:ASStackLayoutJustifyContentStart
+                                                                      alignItems:ASStackLayoutAlignItemsStretch          
+                                                                        children:stackChildren];
+  
+  ASOverlayLayoutSpec *soldOutOverlay = [ASOverlayLayoutSpec overlayLayoutSpecWithChild:mainStack 
+                                                                                overlay:self.soldOutOverlay];
+  
+  return soldOutOverlay;
+}
+
+- (ASLayoutSpec *)textSpec {
+  CGFloat kInsetHorizontal        = 16.0;
+  CGFloat kInsetTop               = 6.0;
+  CGFloat kInsetBottom            = 0.0;
+  UIEdgeInsets textInsets         = UIEdgeInsetsMake(kInsetTop, kInsetHorizontal, kInsetBottom, kInsetHorizontal);
+  
+  ASLayoutSpec *verticalSpacer    = [[ASLayoutSpec alloc] init];
+  verticalSpacer.flexGrow         = YES;
+  
+  ASLayoutSpec *horizontalSpacer1 = [[ASLayoutSpec alloc] init];
+  horizontalSpacer1.flexGrow      = YES;
+  
+  ASLayoutSpec *horizontalSpacer2 = [[ASLayoutSpec alloc] init];
+  horizontalSpacer2.flexGrow      = YES;
+  
+  NSArray *info1Children = @[self.firstInfoLabel, self.distanceLabel, horizontalSpacer1, self.originalPriceLabel];
+  NSArray *info2Children = @[self.secondInfoLabel, horizontalSpacer2, self.finalPriceLabel];
+  if ([ItemNode isRTL]) {
+    info1Children = [[info1Children reverseObjectEnumerator] allObjects];
+    info2Children = [[info2Children reverseObjectEnumerator] allObjects];
+  }
+  
+  ASStackLayoutSpec *info1Stack = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal 
+                                                                          spacing:1.0
+                                                                   justifyContent:ASStackLayoutJustifyContentStart 
+                                                                       alignItems:ASStackLayoutAlignItemsBaselineLast children:info1Children];
+  
+  ASStackLayoutSpec *info2Stack = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal 
+                                                                          spacing:0.0
+                                                                   justifyContent:ASStackLayoutJustifyContentCenter 
+                                                                       alignItems:ASStackLayoutAlignItemsBaselineLast children:info2Children];
+  
+  ASStackLayoutSpec *textStack = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical 
+                                                                         spacing:0.0
+                                                                  justifyContent:ASStackLayoutJustifyContentEnd
+                                                                      alignItems:ASStackLayoutAlignItemsStretch
+                                                                        children:@[self.titleLabel, verticalSpacer, info1Stack, info2Stack]];
+  
+  ASInsetLayoutSpec *textWrapper = [ASInsetLayoutSpec insetLayoutSpecWithInsets:textInsets 
+                                                                          child:textStack];
+  textWrapper.flexGrow = YES;
+  
+  return textWrapper;
+}
+
+- (ASLayoutSpec *)imageSpecWithSize:(ASSizeRange)constrainedSize {
+  CGFloat imageRatio = [self imageRatioFromSize:constrainedSize.max];
+  
+  ASRatioLayoutSpec *imagePlace = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:imageRatio child:self.dealImageView];
+  
+  self.badge.layoutPosition = CGPointMake(0, constrainedSize.max.height - kFixedLabelsAreaHeight - kBadgeHeight);
+  self.badge.sizeRange = ASRelativeSizeRangeMake(ASRelativeSizeMake(ASRelativeDimensionMakeWithPercent(0), ASRelativeDimensionMakeWithPoints(kBadgeHeight)), ASRelativeSizeMake(ASRelativeDimensionMakeWithPercent(1), ASRelativeDimensionMakeWithPoints(kBadgeHeight)));
+  ASStaticLayoutSpec *badgePosition = [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[self.badge]];
+  
+  ASOverlayLayoutSpec *badgeOverImage = [ASOverlayLayoutSpec overlayLayoutSpecWithChild:imagePlace overlay:badgePosition];
+  badgeOverImage.flexGrow = YES;
+  
+  return badgeOverImage;
+}
+
+- (ASLayoutSpec *)soldOutLabelSpec {
+  ASCenterLayoutSpec *centerSoldOutLabel = [ASCenterLayoutSpec centerLayoutSpecWithCenteringOptions:ASCenterLayoutSpecCenteringXY 
+  sizingOptions:ASCenterLayoutSpecSizingOptionMinimumXY child:self.soldOutLabelFlat];
+  ASStaticLayoutSpec *soldOutBG = [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[self.soldOutLabelBackground]];
+  ASCenterLayoutSpec *centerSoldOut = [ASCenterLayoutSpec centerLayoutSpecWithCenteringOptions:ASCenterLayoutSpecCenteringXY   sizingOptions:ASCenterLayoutSpecSizingOptionDefault child:soldOutBG];
+  ASBackgroundLayoutSpec *soldOutLabelOverBackground = [ASBackgroundLayoutSpec backgroundLayoutSpecWithChild:centerSoldOutLabel background:centerSoldOut];
+  return soldOutLabelOverBackground;
+}
+
+ +
+
+ +###Discussion + +Get the full Texture project at examples/CatDealsCollectionView. diff --git a/docs/_docs/automatic-subnode-mgmt.md b/docs/_docs/automatic-subnode-mgmt.md new file mode 100755 index 0000000000..e4ff534206 --- /dev/null +++ b/docs/_docs/automatic-subnode-mgmt.md @@ -0,0 +1,296 @@ +--- +title: Automatic Subnode Management +layout: docs +permalink: /docs/automatic-subnode-mgmt.html +prevPage: batch-fetching-api.html +nextPage: inversion.html +--- + +Enabling Automatic Subnode Management (ASM) is required to use the Layout Transition API. However, apps that don't require animations can still benefit from the reduction in code size that this feature enables. + +When enabled, ASM means that your nodes no longer require `addSubnode:` or `removeFromSupernode` method calls. The presence or absence of the ASM node _and_ its subnodes is completely determined in its `layoutSpecThatFits:` method. + +### Example ### +
+Consider the following intialization method from the PhotoCellNode class in ASDKgram sample app. This ASCellNode subclass produces a simple social media photo feed cell. + +In the "Original Code" we see the familiar `addSubnode:` calls in bold. In the "Code with ASM" these have been removed and replaced with a single line that enables ASM. + +By setting `.automaticallyManagesSubnodes` to `YES` on the `ASCellNode`, we _no longer_ need to call `addSubnode:` for each of the `ASCellNode`'s subnodes. These `subNodes` will be present in the node hierarchy as long as this class' `layoutSpecThatFits:` method includes them. + + +Original code +
+ + Objective-C + Swift + +
+
+- (instancetype)initWithPhotoObject:(PhotoModel *)photo;
+{
+  self = [super init];
+  
+  if (self) {
+    _photoModel = photo;
+    
+    _userAvatarImageNode = [[ASNetworkImageNode alloc] init];
+    _userAvatarImageNode.URL = photo.ownerUserProfile.userPicURL;
+    [self addSubnode:_userAvatarImageNode];
+
+    _photoImageNode = [[ASNetworkImageNode alloc] init];
+    _photoImageNode.URL = photo.URL;
+    [self addSubnode:_photoImageNode];
+
+    _userNameTextNode = [[ASTextNode alloc] init];
+    _userNameTextNode.attributedString = [photo.ownerUserProfile usernameAttributedStringWithFontSize:FONT_SIZE];
+    [self addSubnode:_userNameTextNode];
+    
+    _photoLocationTextNode = [[ASTextNode alloc] init];
+    [photo.location reverseGeocodedLocationWithCompletionBlock:^(LocationModel *locationModel) {
+      if (locationModel == _photoModel.location) {
+        _photoLocationTextNode.attributedString = [photo locationAttributedStringWithFontSize:FONT_SIZE];
+        [self setNeedsLayout];
+      }
+    }];
+    [self addSubnode:_photoLocationTextNode];
+  }
+  
+  return self;
+}
+
+ +
+
+ +Code with ASM +
+ + Objective-C + Swift + +
+
+- (instancetype)initWithPhotoObject:(PhotoModel *)photo;
+{
+  self = [super init];
+  
+  if (self) {
+    self.automaticallyManagesSubnodes = YES;
+    
+    _photoModel = photo;
+    
+    _userAvatarImageNode = [[ASNetworkImageNode alloc] init];
+    _userAvatarImageNode.URL = photo.ownerUserProfile.userPicURL;
+
+    _photoImageNode = [[ASNetworkImageNode alloc] init];
+    _photoImageNode.URL = photo.URL;
+
+    _userNameTextNode = [[ASTextNode alloc] init];
+    _userNameTextNode.attributedString = [photo.ownerUserProfile usernameAttributedStringWithFontSize:FONT_SIZE];
+    
+    _photoLocationTextNode = [[ASTextNode alloc] init];
+    [photo.location reverseGeocodedLocationWithCompletionBlock:^(LocationModel *locationModel) {
+      if (locationModel == _photoModel.location) {
+        _photoLocationTextNode.attributedString = [photo locationAttributedStringWithFontSize:FONT_SIZE];
+        [self setNeedsLayout];
+      }
+    }];
+  }
+  
+  return self;
+}
+
+ +
+
+ +Several of the elements in this cell - `_userAvatarImageNode`, `_photoImageNode`, and `_photoLocationLabel` depend on seperate data fetches from the network that could return at any time. When should they be added to the UI? + +ASM knows whether or not to include these elements in the UI based on the information provided in the cell's `ASLayoutSpec`. + +
+An ASLayoutSpec completely describes the UI of a view in your app by specifying the hierarchy state of a node and its subnodes. An ASLayoutSpec is returned by a node from its layoutSpecThatFits: method. +
+ +**It is your job to construct a `layoutSpecThatFits:` that handles how the UI should look with and without these elements.** + +Consider the abreviated `layoutSpecThatFits:` method for the `ASCellNode` subclass above. + +
+ +Objective-C +Swift + + +
+
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
+{  
+  ASStackLayoutSpec *headerSubStack = [ASStackLayoutSpec verticalStackLayoutSpec];
+  headerSubStack.flexShrink         = YES;
+  if (_photoLocationLabel.attributedString) {
+    [headerSubStack setChildren:@[_userNameLabel, _photoLocationLabel]];
+  } else {
+    [headerSubStack setChildren:@[_userNameLabel]];
+  }
+  
+  _userAvatarImageNode.preferredFrameSize = CGSizeMake(USER_IMAGE_HEIGHT, USER_IMAGE_HEIGHT);     // constrain avatar image frame size
+
+  ASLayoutSpec *spacer           = [[ASLayoutSpec alloc] init]; 
+  spacer.flexGrow                = YES;
+  
+  UIEdgeInsets avatarInsets      = UIEdgeInsetsMake(HORIZONTAL_BUFFER, 0, HORIZONTAL_BUFFER, HORIZONTAL_BUFFER);
+  ASInsetLayoutSpec *avatarInset = [ASInsetLayoutSpec insetLayoutSpecWithInsets:avatarInsets child:_userAvatarImageNode];
+
+  ASStackLayoutSpec *headerStack = [ASStackLayoutSpec horizontalStackLayoutSpec];
+  headerStack.alignItems         = ASStackLayoutAlignItemsCenter;                     // center items vertically in horizontal stack
+  headerStack.justifyContent     = ASStackLayoutJustifyContentStart;                  // justify content to the left side of the header stack
+  [headerStack setChildren:@[avatarInset, headerSubStack, spacer]];
+  
+  // header inset stack
+  UIEdgeInsets insets                = UIEdgeInsetsMake(0, HORIZONTAL_BUFFER, 0, HORIZONTAL_BUFFER);
+  ASInsetLayoutSpec *headerWithInset = [ASInsetLayoutSpec insetLayoutSpecWithInsets:insets child:headerStack];
+  
+  // footer inset stack
+  UIEdgeInsets footerInsets          = UIEdgeInsetsMake(VERTICAL_BUFFER, HORIZONTAL_BUFFER, VERTICAL_BUFFER, HORIZONTAL_BUFFER);
+  ASInsetLayoutSpec *footerWithInset = [ASInsetLayoutSpec insetLayoutSpecWithInsets:footerInsets child:_photoCommentsNode];
+  
+  // vertical stack
+  CGFloat cellWidth                  = constrainedSize.max.width;
+  _photoImageNode.preferredFrameSize = CGSizeMake(cellWidth, cellWidth);              // constrain photo frame size
+  
+  ASStackLayoutSpec *verticalStack   = [ASStackLayoutSpec verticalStackLayoutSpec];
+  verticalStack.alignItems           = ASStackLayoutAlignItemsStretch;                // stretch headerStack to fill horizontal space
+  [verticalStack setChildren:@[headerWithInset, _photoImageNode, footerWithInset]];
+
+  return verticalStack;
+}
+
+ + +
+
+ + +Here you can see that the children of the `headerSubStack` depend on whether or not the `_photoLocationLabel` attributed string has returned from the reverseGeocode process yet. + +The `_userAvatarImageNode`, `_photoImageNode`, and `_photoCommentsNode` are added into the ASLayoutSpec, but will not show up until their data fetches return. + +### Updating an ASLayoutSpec ### +
+**If something happens that you know will change your `ASLayoutSpec`, it is your job to call `setNeedsLayout`**. This is equivalent to `transitionLayout:duration:0` in the Transition Layout API. You can see this call in the completion block of the `photo.location reverseGeocodedLocationWithCompletionBlock:` call in the first code block. + +An appropriately constructed ASLayoutSpec will know which subnodes need to be added, removed or animated. + +Try out the ASDKgram sample app after looking at the code above, and you will see how simple it is to code an `ASCellNode` whose layout is responsive to numerous, individual data fetches and returns. While the `ASLayoutSpec` is coded in a way that leaves holes for the avatar and photo to populate, you can see how the cell's height will automatically adjust to accomodate the comments node at the bottom of the photo. + +This is just a simple example, but this feature has many more powerful uses. + +
+Warning: addSubnode: and removeFromSupernode should never be called on a node that has ASM enabled. Doing so could cause the following exception - "A flattened layout must consist exclusively of node sublayouts". +
diff --git a/docs/_docs/batch-fetching-api.md b/docs/_docs/batch-fetching-api.md new file mode 100755 index 0000000000..5fd9044697 --- /dev/null +++ b/docs/_docs/batch-fetching-api.md @@ -0,0 +1,151 @@ +--- +title: Batch Fetching API +layout: docs +permalink: /docs/batch-fetching-api.html +prevPage: hit-test-slop.html +nextPage: automatic-subnode-mgmt.html +--- + +Texture's Batch Fetching API makes it easy to add fetching chunks of new data. Usually this would be done in a `-scrollViewDidScroll:` method, but Texture provides a more structured mechanism. + +By default, as a user is scrolling, when they approach the point in the table or collection where they are 2 "screens" away from the end of the current content, the table will try to fetch more data. + +If you'd like to configure how far away from the end you should be, just change the `leadingScreensForBatching` property on an `ASTableView` or `ASCollectionView` to something else. + +
+SwiftObjective-C + +
+
+tableNode.view.leadingScreensForBatching = 3.0;  // overriding default of 2.0
+
+ +
+
+ +### Batch Fetching Delegate Methods + +The first thing you have to do in order to support batch fetching, is implement a method that decides if it's an appropriate time to load new content or not. + +For tables it would look something like: + +
+SwiftObjective-C + +
+
+- (BOOL)shouldBatchFetchForTableNode:(ASTableNode *)tableNode
+{
+  if (_weNeedMoreContent) {
+    return YES;
+  }
+
+  return NO;
+}
+
+ +
+
+ +and for collections: + +
+SwiftObjective-C + +
+
+
+- (BOOL)shouldBatchFetchForCollectionNode:(ASCollectionNode *)collectionNode
+{
+  if (_weNeedMoreContent) {
+    return YES;
+  }
+
+  return NO;
+}
+
+ +
+
+ +These methods will be called when the user has scrolled into the batch fetching range, and their answer will determine if another request actually needs to be made or not. Usually this decision is based on if there is still data to fetch. + +If you return NO, then no new batch fetching process will happen. If you return YES, the batch fetching mechanism will start and the following method will be called next. + +`-tableNode:willBeginBatchFetchWithContext:` + +or + +`-collectionNode:willBeginBatchFetchWithContext:` + +This is where you should actually fetch data, be it from a web API or some local database. + +
+Note: This method will always be called on a background thread. This means, if you need to do any work on the main thread, you should dispatch it to the main thread and then proceed with the work needed in order to finish the batch fetch operation. +
+ +
+SwiftObjective-C + +
+
+- (void)tableNode:(ASTableNode *)tableNode willBeginBatchFetchWithContext:(ASBatchContext *)context 
+{
+  // Fetch data most of the time asynchronoulsy from an API or local database
+  NSArray *newPhotos = [SomeSource getNewPhotos];
+
+  // Insert data into table or collection node
+  [self insertNewRowsInTableNode:newPhotos];
+
+  // Decide if it's still necessary to trigger more batch fetches in the future
+  _stillDataToFetch = ...;
+
+  // Properly finish the batch fetch
+  [context completeBatchFetching:YES];
+}
+
+ +
+
+ +Once you've finished fetching your data, it is very important to let Texture know that you have finished the process. To do that, you need to call `-completeBatchFetching:` on the `context` object that was passed in with a parameter value of `YES`. This assures that the whole batch fetching mechanism stays in sync and the next batch fetching cycle can happen. Only by passing `YES` will the context know to attempt another batch update when necessary. + +Check out the following sample apps to see the batch fetching API in action: + diff --git a/docs/_docs/button-node.md b/docs/_docs/button-node.md new file mode 100755 index 0000000000..11eb6ca955 --- /dev/null +++ b/docs/_docs/button-node.md @@ -0,0 +1,98 @@ +--- +title: ASButtonNode +layout: docs +permalink: /docs/button-node.html +prevPage: cell-node.html +nextPage: text-node.html +--- + +### Basic Usage + +`ASButtonNode` subclasses `ASControlNode` in the same way `UIButton` subclasses `UIControl`. In contrast, being able to layer back the subnodes of every button can significantly lighten main thread impact relative to `UIButton`. + +### Control State + +If you've used `-setTitle:forControlState:` then you already know how to set up an ASButtonNode. The `ASButtonNode` version adds in a few parameters for conveniently setting attributes. + +
+SwiftObjective-C + +
+
+[buttonNode setTitle:@"Button Title Normal" withFont:nil withColor:[UIColor blueColor] forState:ASControlStateNormal];
+
+ +
+
+ +If you need even more control, you can also opt to use the attributed string version directly: + +
+SwiftObjective-C + +
+
+[self.buttonNode setAttributedTitle:attributedTitle forState:ASControlStateNormal];
+
+ +
+
+ +### Target-Action Pairs + +Again, analagous to UIKit, you can add sets of target-action pairs to respond to various events. + +
+SwiftObjective-C + +
+
+[buttonNode addTarget:self action:@selector(buttonPressed:) forControlEvents:ASControlNodeEventTouchUpInside];
+
+ +
+
+ +### Content Alignment + +`ASButtonNode` offers both `contentVerticalAlignment` and `contentHorizontalAlignment` properties. This allows you to easily set the alignment of the titleLabel or image you're using for your button. + +
+SwiftObjective-C + +
+
+self.buttonNode.contentVerticalAlignment = ASVerticalAlignmentTop;
+self.buttonNode.contentHorizontalAlignment = ASHorizontalAlignmentMiddle;
+
+ +
+
+ +
Note: At the moment, this property will not work if you aren't using -layoutSpecThatFits:. +
+ +### Gotchas + +There are a few things that might trip up someone new to the framework. + +##### View Hierarchies +Let's say you want to add an `ASButtonNode` to the view of one of your existing view controllers. The first thing you'll notice is that setting a title for a control state doesn't seem to make your title appear. You can fix this by calling `-measure:` on the button which will cause its title label to be measured and laid out. + +The next thing you'll notice is that, if you set titles of various lengths for different control states, the button will dynamically grow and shrink as the title changes. This is because changing the title causes `-setNeedsLayout` to be called on the button. Within a node hierarchy, this makes sense, and will work as expected. + +Long story short, use an `ASViewController`. + +##### Selected State + +If you want your button to change to a "selected" state after being tapped, you'll need to do that manually. + diff --git a/docs/_docs/cell-node.md b/docs/_docs/cell-node.md new file mode 100755 index 0000000000..10fa271cfe --- /dev/null +++ b/docs/_docs/cell-node.md @@ -0,0 +1,142 @@ +--- +title: ASCellNode +layout: docs +permalink: /docs/cell-node.html +prevPage: display-node.html +nextPage: button-node.html +--- + +`ASCellNode`, as you may have guessed, is the cell class of Texture. Unlike the various cells in UIKit, `ASCellNode` can be used with `ASTableNodes`, `ASCollectionNodes` and `ASPagerNodes`, making it incredibly flexible. + +### 3 Ways to Party + +There are three ways in which you can implement the cells you'll use in your Texture app: subclassing `ASCellNode`, initializing with an existing `ASViewController` or using an existing UIView or `CALayer`. + +#### Subclassing + +Subclassing an `ASCellNode` is pretty much the same as subclassing a regular `ASDisplayNode`. + +Most likely, you'll write a few of the following: + +- `-init` -- Thread safe initialization. +- `-layoutSpecThatFits:` -- Return a layout spec that defines the layout of your cell. +- `-didLoad` -- Called on the main thread. Good place to add gesture recognizers, etc. +- `-layout` -- Also called on the main thread. Layout is complete after the call to super which means you can do any extra tweaking you need to do. + + +#### Initializing with an `ASViewController` + +Say you already have some type of view controller written to display a view in your app. If you want to take that view controller and drop its view in as a cell in one of the scrolling nodes or a pager node its no problem. + +For example, say you already have a view controller written that manages an `ASTableNode`. To use that table as a page in an `ASPagerNode` you can use `-initWithViewControllerBlock`. + +
+SwiftObjective-C +
+
+- (ASCellNode *)pagerNode:(ASPagerNode *)pagerNode nodeAtIndex:(NSInteger)index
+{
+    NSArray *animals = self.allAnimals[index];
+    
+    ASCellNode *node = [[ASCellNode alloc] initWithViewControllerBlock:^UIViewController * _Nonnull{
+        return [[AnimalTableNodeController alloc] initWithAnimals:animals];
+    } didLoadBlock:nil];
+    
+    node.style.preferredSize = pagerNode.bounds.size;
+    
+    return node;
+}
+
+ +
+
+ +And this works for any combo of scrolling container node and `UIViewController` subclass. You want to embed random view controllers in your collection node? Go for it. + +
+Notice that you need to set the .style.preferredSize of a node created this way. Normally your nodes will implement -layoutSpecThatFits: but since these don't you'll need give the cell a size. +
+ + +#### Initializing with a `UIView` or `CALayer` + +Alternatively, if you already have a `UIView` or `CALayer` subclass that you'd like to drop in as cell you can do that instead. + +
+SwiftObjective-C +
+
+- (ASCellNode *)pagerNode:(ASPagerNode *)pagerNode nodeAtIndex:(NSInteger)index
+{
+    NSArray *animal = self.animals[index];
+    
+    ASCellNode *node = [[ASCellNode alloc] initWithViewBlock:^UIView * _Nonnull{
+        return [[SomeAnimalView alloc] initWithAnimal:animal];
+    }];
+
+    node.style.preferredSize = pagerNode.bounds.size;
+    
+    return node;
+}
+
+ +
+
+ +As you can see, its roughly the same idea. That being said, if you're doing this, you may consider converting the existing `UIView` subclass to be an `ASCellNode` subclass in order to gain the advantage of asynchronous display. + +### Never Show Placeholders + +Usually, if a cell hasn't finished its display pass before it has reached the screen it will show placeholders until it has completed drawing its content. + +If placeholders are unacceptable, you can set an `ASCellNode`'s `neverShowPlaceholders` property to `YES`. + +
+SwiftObjective-C +
+
+node.neverShowPlaceholders = YES;
+
+ +
+
+ +With this property set to `YES`, the main thread will be blocked until display has completed for the cell. This is more similar to UIKit, and in fact makes Texture scrolling visually indistinguishable from UIKit's, except being faster. + +
+Using this option does not eliminate all of the performance advantages of Texture. Normally, a cell has been preloading and is almost done when it reaches the screen, so the blocking time is very short. Even if the rangeTuningParameters are set to 0 this option outperforms UIKit. While the main thread is waiting, subnode display executes concurrently. +
+ +### `UITableViewCell` specific propertys + +UITableViewCell has properties like selectionStyle, accessoryType and seperatorInset that many of us use sometimes to give the Cell more detail. For this case ASCellNode has the same (passthrough) properties that can be used. + +
+UIKits UITableViewCell contains ASCellNode as a subview. Depending how your ASLayoutSpec is defined it may occure that your Layout overlays the UITableViewCell.accessoryView and therefore not visible. Make sure that your Layout doesn't overlays any UITableViewCell's specific properties. +
diff --git a/docs/_docs/containers-ascollectionnode.md b/docs/_docs/containers-ascollectionnode.md new file mode 100755 index 0000000000..e59771e61a --- /dev/null +++ b/docs/_docs/containers-ascollectionnode.md @@ -0,0 +1,228 @@ +--- +title: ASCollectionNode +layout: docs +permalink: /docs/containers-ascollectionnode.html +prevPage: containers-astablenode.html +nextPage: containers-aspagernode.html +--- + +`ASCollectionNode` is equivalent to UIKit's `UICollectionView` and can be used in place of any `UICollectionView`. + +`ASCollectionNode` replaces `UICollectionView`'s required method + +
+ + Swift + Objective-C + + +
+
+- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath;
+  
+ + +
+
+ +with your choice of **_one_** of the following methods + +
+SwiftObjective-C + +
+
+- (ASCellNode *)collectionNode:(ASCollectionNode *)collectionNode nodeForItemAtIndexPath:(NSIndexPath *)indexPath
+
+ +
+
+ +

+or +

+ +
+SwiftObjective-C + +
+
+- (ASCellNodeBlock)collectionNode:(ASCollectionNode *)collectionNode nodeBlockForItemAtIndexPath:(NSIndexPath *)indexPath
+
+ +
+
+ +It is recommended that you use the node block version of the method so that your collection node will be able to prepare and display all of its cells concurrently. + +As noted in the previous section: + +
    +
  • ASCollectionNodes do not utilize cell reuse.
  • +
  • Using the "nodeBlock" method is preferred.
  • +
  • It is very important that the returned node blocks are thread-safe.
  • +
  • ASCellNodes can be used by ASTableNode, ASCollectionNode and ASPagerNode.
  • +
+ +### Node Block Thread Safety Warning + +It is very important that node blocks be thread-safe. One aspect of that is ensuring that the data model is accessed _outside_ of the node block. Therefore, it is unlikely that you should need to use the index inside of the block. + +Consider the following `-collectionNode:nodeBlockForItemAtIndexPath:` method. + +
+SwiftObjective-C +
+
+- (ASCellNodeBlock)collectionNode:(ASCollectionNode *)collectionNode nodeBlockForItemAtIndexPath:(NSIndexPath *)indexPath
+{
+    PhotoModel *photoModel = [_photoFeed objectAtIndex:indexPath.row];
+    
+    // this may be executed on a background thread - it is important to make sure it is thread safe
+    ASCellNode *(^cellNodeBlock)() = ^ASCellNode *() {
+        PhotoCellNode *cellNode = [[PhotoCellNode alloc] initWithPhoto:photoModel];
+        cellNode.delegate = self;
+        return cellNode;
+    };
+    
+    return cellNodeBlock;
+}
+  
+ + +
+
+ +In the example above, you can see how the index is used to access the photo model before creating the node block. + +### Replacing a UICollectionViewController with an ASViewController + +Texture does not offer an equivalent to UICollectionViewController. Instead, you can use the flexibility of ASViewController to recreate any type of UI...ViewController. + +Consider, the following ASViewController subclass. + +An ASCollectionNode is assigned to be managed by an `ASViewController` in its `-initWithNode:` designated initializer method, thus making it a sort of ASCollectionNodeController. + +
+SwiftObjective-C +
+
+- (instancetype)init
+{
+  _flowLayout = [[UICollectionViewFlowLayout alloc] init];
+  _collectionNode = [[ASCollectionNode alloc] initWithCollectionViewLayout:_flowLayout];
+  
+  self = [super initWithNode:_collectionNode];
+  if (self) {
+    _flowLayout.minimumInteritemSpacing = 1;
+    _flowLayout.minimumLineSpacing = 1;
+  }
+  
+  return self;
+}
+
+ + +
+
+ +This works just as well with any node including as an ASTableNode, ASPagerNode, etc. + +### Accessing the ASCollectionView +If you've used previous versions of Texture, you'll notice that `ASCollectionView` has been removed in favor of `ASCollectionNode`. + +
+`ASCollectionView`, an actual `UICollectionView` subclass, is still used internally by `ASCollectionNode`. While it should not be created directly, it can still be used directly by accessing the `.view` property of an `ASCollectionNode`. +

+Don't forget that a node's `view` or `layer` property should only be accessed after viewDidLoad or didLoad, respectively, have been called. +
+ +The `LocationCollectionNodeController` above accesses the `ASCollectionView` directly in `-viewDidLoad`. + +
+SwiftObjective-C +
+
+- (void)viewDidLoad
+{
+  [super viewDidLoad];
+  
+  _collectionNode.delegate = self;
+  _collectionNode.dataSource = self;
+  _collectionNode.view.allowsSelection = NO;
+  _collectionNode.view.backgroundColor = [UIColor whiteColor];
+}
+
+ + +
+
+ +### Cell Sizing and Layout + +As discussed in the previous section, `ASCollectionNode` and `ASTableNode` do not need to keep track of the height of their `ASCellNode`s. + +Right now, cells will grow to fit their constrained size and will be laid out by whatever `UICollectionViewLayout` you provide. + +Soon, there will be a method such as `ASTableNode`'s `-constrainedSizeForRow:` but at the moment, if you'd like to constrain the size of a cell used in a collection node, you need to wrap your layoutSpec object in an `ASStaticLayoutSpec` and provide it with a + +### Examples + +The most detailed example of laying out the cells of an `ASCollectionNode` is the CustomCollectionView app. It includes a Pinterest style cell layout using an `ASCollectionNode` and a custom `UICollectionViewLayout`. + +#### More Sample Apps with ASCollectionNodes + + + +### Interoperability with UICollectionViewCells + +`ASCollectionNode` supports using UICollectionViewCells alongside native ASCellNodes. + +Note that these UIKit cells will **not** have the performance benefits of `ASCellNodes` (like preloading, async layout, and async drawing), even when mixed within the same `ASCollectionNode`. + +However, this interoperability allows developers the flexibility to test out the framework without needing to convert all of their cells at once. Read more here. \ No newline at end of file diff --git a/docs/_docs/containers-asnodecontroller.md b/docs/_docs/containers-asnodecontroller.md new file mode 100755 index 0000000000..7dd5034dab --- /dev/null +++ b/docs/_docs/containers-asnodecontroller.md @@ -0,0 +1,238 @@ +--- +title: "ASNodeController (Beta)" +layout: docs +permalink: /docs/containers-asnodecontroller.html +prevPage: containers-asviewcontroller.html +nextPage: containers-astablenode.html +--- + +
+To use this feature, you will need to import "ASNodeController+Beta.h" +
+ +The Texture team has many exciting ideas for expanding `ASNodeController`. Follow along [here](https://github.com/facebook/AsyncDisplayKit/issues/2964) if you'd like to participate in shaping the future of node controllers. + +For now, `ASNodeController` remains a simple, but powerful class. + +### Example + +The [example project](https://github.com/texturegroup/texture/pull/2945) attached in the initial PR modifies the normal [ASDKgram](https://github.com/texturegroup/texture/tree/master/examples/ASDKgram) project to use an `ASNodeController`. +This `PhotoCellNodeController` is used to manage the fetching of the comments data for a photo in a photo feed, once the photo enters the preload range. This node controller allows us to separate the preloading logic from where it previously existed in the `PhotoCellNode` "view" class. + +To convert ASDKgram to use an `ASNodeController`, we first create a `PhotoCellNodeController` class. + +This node controller overrides `ASNodeController`'s' `-loadNode` method to create a `PhotoCellNode` once required. It is not neccessary to call super in this method. + +This node controller also observes its node's interface state in order to intelligently preload the photo's comment feed model data when the `PhotoCellNode` enters the preload state (which indicates that the photo cell is likely to scroll onscreen soon). + +All of this logic can be removed from where it previously existed in the "view" (our `PhotoCellNode` class), leading to a more concise and MVC-friendly view class. + +
+ + Swift + Objective-C + + +
+
+@implementation PhotoCellNodeController
+
+- (void)loadNode
+{
+  self.node = [[PhotoCellNode alloc] initWithPhotoObject:self.photoModel];
+}
+
+- (void)didEnterPreloadState
+{
+  [super didEnterPreloadState];
+  
+  CommentFeedModel *commentFeedModel = _photoModel.commentFeed;
+  [commentFeedModel refreshFeedWithCompletionBlock:^(NSArray *newComments) {
+    // load comments for photo
+    if (commentFeedModel.numberOfItemsInFeed > 0) {
+      [self.node.photoCommentsNode updateWithCommentFeedModel:commentFeedModel];
+      [self.node setNeedsLayout];
+    }
+  }];
+}
+
+@end
+  
+ + +
+
+ +Next, we add a mutable array to the `PhotoFeedNodeController` to store our node controllers and instantiate it in the init method. + +
+ + Swift + Objective-C + + +
+
+@implementation PhotoFeedNodeController
+{
+  PhotoFeedModel          *_photoFeed;
+  ASTableNode             *_tableNode;
+  NSMutableArray *_photoCellNodeControllers;
+}
+
+- (instancetype)init
+{
+  _tableNode = [[ASTableNode alloc] init];
+  self = [super initWithNode:_tableNode];
+  
+  if (self) {
+    self.navigationItem.title = @"Texture";
+    [self.navigationController setNavigationBarHidden:YES];
+    
+    _tableNode.dataSource = self;
+    _tableNode.delegate = self;
+    
+    _photoCellNodeControllers = [NSMutableArray array];
+  }
+  
+  return self;
+}
+  
+ + +
+
+ +To use this node controller, we modify our table row insertion logic to create a `PhotoCellNodeController` rather than a `PhotoCellNode` directly and add it to our node controller array. + +
+ + Swift + Objective-C + + +
+
+- (void)insertNewRowsInTableNode:(NSArray *)newPhotos
+{
+  NSInteger section = 0;
+  NSMutableArray *indexPaths = [NSMutableArray array];
+  
+  NSUInteger newTotalNumberOfPhotos = [_photoFeed numberOfItemsInFeed];
+  for (NSUInteger row = newTotalNumberOfPhotos - newPhotos.count; row < newTotalNumberOfPhotos; row++) {
+  
+    // create photoCellNodeControllers for the new photos
+    PhotoCellNodeController *cellController = [[PhotoCellNodeController alloc] init];
+    cellController.photoModel = [_photoFeed objectAtIndex:row];
+    [_photoCellNodeControllers addObject:cellController];
+    
+    // include this index path in the insert rows call for the table
+    NSIndexPath *path = [NSIndexPath indexPathForRow:row inSection:section];
+    [indexPaths addObject:path];
+  }
+  
+  [_tableNode insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone];
+}
+  
+ + +
+
+ +Don't forget to modify the table data source method to return the node controller rather than the cell node. + +
+ + Swift + Objective-C + + +
+
+- (ASCellNodeBlock)tableNode:(ASTableNode *)tableNode nodeBlockForRowAtIndexPath:(NSIndexPath *)indexPath
+{
+  PhotoCellNodeController *cellController = [_photoCellNodeControllers objectAtIndex:indexPath.row];
+  // this will be executed on a background thread - important to make sure it's thread safe
+  ASCellNode *(^ASCellNodeBlock)() = ^ASCellNode *() {
+    PhotoCellNode *cellNode = [cellController node];
+    return cellNode;
+  };
+  
+  return ASCellNodeBlock;
+}
+  
+ + +
+
+ + + diff --git a/docs/_docs/containers-aspagernode.md b/docs/_docs/containers-aspagernode.md new file mode 100755 index 0000000000..f538983ee0 --- /dev/null +++ b/docs/_docs/containers-aspagernode.md @@ -0,0 +1,166 @@ +--- +title: ASPagerNode +layout: docs +permalink: /docs/containers-aspagernode.html +prevPage: containers-ascollectionnode.html +nextPage: display-node.html +--- + +`ASPagerNode` is a subclass of `ASCollectionNode` with a specific `UICollectionViewLayout` used under the hood. + +Using it allows you to produce a page style UI similar to what you'd create with UIKit's `UIPageViewController`. `ASPagerNode` currently supports staying on the correct page during rotation. It does _not_ currently support circular scrolling. + +The main dataSource methods are: + +
+SwiftObjective-C +
+
+- (NSInteger)numberOfPagesInPagerNode:(ASPagerNode *)pagerNode
+
+ + +
+
+ +and + +
+SwiftObjective-C +
+
+- (ASCellNode *)pagerNode:(ASPagerNode *)pagerNode nodeAtIndex:(NSInteger)index
+
+ + +
+
+ +or + +
+SwiftObjective-C +
+
+- (ASCellNodeBlock)pagerNode:(ASPagerNode *)pagerNode nodeBlockAtIndex:(NSInteger)index`
+
+ + +
+
+ +These two methods, just as with `ASCollectionNode` and `ASTableNode` need to return either an `ASCellNode` or an `ASCellNodeBlock` - a block that creates an `ASCellNode` and can be run on a background thread. + +Note that neither methods should rely on cell reuse (they will be called once per row). Also, unlike UIKit, these methods are not called when the row is just about to display. + +While `-pagerNode:nodeAtIndex:` will be called on the main thread, `-pagerNode:nodeBlockAtIndex:` is preferred because it concurrently allocates cell nodes, meaning that the `-init:` method of each of your subnodes will be run in the background. **It is very important that node blocks be thread-safe** as they can be called on the main thread or a background queue. + +### Node Block Thread Safety Warning + +It is imperative that the data model be accessed outside of the node block. This means that it is highly unlikely that you should need to use the index inside of the block. + +In the example below, you can see how the index is used to access the photo model before creating the node block. + +
+SwiftObjective-C +
+
+- (ASCellNodeBlock)pagerNode:(ASPagerNode *)pagerNode nodeBlockAtIndex:(NSInteger)index
+{
+  PhotoModel *photoModel = _photoFeed[index];
+  
+  // this part can be executed on a background thread - it is important to make sure it is thread safe!
+  ASCellNode *(^cellNodeBlock)() = ^ASCellNode *() {
+    PhotoCellNode *cellNode = [[PhotoCellNode alloc] initWithPhoto:photoModel];
+    return cellNode;
+  };
+  
+  return cellNodeBlock;
+}
+
+ + +
+
+ +### Using an ASViewController For Optimal Performance + +One especially useful pattern is to return an `ASCellNode` that is initialized with an existing `UIViewController` or `ASViewController`. For optimal performance, use an `ASViewController`. + +
+SwiftObjective-C +
+
+- (ASCellNode *)pagerNode:(ASPagerNode *)pagerNode nodeAtIndex:(NSInteger)index
+{
+    NSArray *animals = self.animals[index];
+    
+    ASCellNode *node = [[ASCellNode alloc] initWithViewControllerBlock:^{
+        return [[AnimalTableNodeController alloc] initWithAnimals:animals];;
+    } didLoadBlock:nil];
+    
+    node.style.preferredSize = pagerNode.bounds.size;
+    
+    return node;
+}
+
+ + +
+
+ +In this example, you can see that the node is constructed using the `-initWithViewControllerBlock:` method. It is usually necessary to provide a cell created this way with a `style.preferredSize` so that it can be laid out correctly. + +### Use ASPagerNode as root node of an ASViewController + +#### Log message while popping back in the view controller hierarchy +If you use an `ASPagerNode` embedded in an `ASViewController` in full screen. If you pop back from the view controller hierarchy you will see some error message in the console. + +To resolve the error message set `self.automaticallyAdjustsScrollViewInsets = NO;` in `viewDidLoad` in your `ASViewController` subclass. + +#### `navigationBar.translucent` is set to YES +If you have an `ASPagerNode` embedded in an `ASViewController` in full screen and set the `navigationBar.translucent` to `YES`, you will see an error message while pushing the view controller on the view controller stack. + +To resolve the error message add `[self.pagerNode waitUntilAllUpdatesAreCommitted];` within `- (void)viewWillAppear:(BOOL)animated` in your `ASViewController` subclass. +Unfortunately the disadvantage of this is that the first measurement pass will block the main thread until it finishes. + +#### Some more details about the error messages above +The reason for this error message is that due to the asynchronous nature of Texture, measurement of nodes will happen on a background thread as UIKit will resize the view of the `ASViewController` on on the main thread. The new layout pass has to wait until the old layout pass finishes with an old layout constrained size. Unfortunately while the measurement pass with the old constrained size is still in progress the `ASPagerFlowLayout` that is backing a `ASPagerNode` will print some errors in the console as it expects sizes for nodes already measured with the new constrained size. + +### Sample Apps + +Check out the following sample apps to see an `ASPagerNode` in action: + diff --git a/docs/_docs/containers-astablenode.md b/docs/_docs/containers-astablenode.md new file mode 100755 index 0000000000..7742e23c3b --- /dev/null +++ b/docs/_docs/containers-astablenode.md @@ -0,0 +1,228 @@ +--- +title: ASTableNode +layout: docs +permalink: /docs/containers-astablenode.html +prevPage: containers-asnodecontroller.html +nextPage: containers-ascollectionnode.html +--- + +`ASTableNode` is equivalent to UIKit's `UITableView` and can be used in place of any `UITableView`. + +`ASTableNode` replaces `UITableView`'s required method + +
+ + Swift + Objective-C + + +
+
+- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
+  
+ + +
+
+ +with your choice of **_one_** of the following methods + +
+ + Swift + Objective-C + + +
+
+- (ASCellNode *)tableNode:(ASTableNode *)tableNode nodeForRowAtIndexPath:(NSIndexPath *)indexPath
+  
+ + +
+
+ +or + +
+ + Swift + Objective-C + + +
+
+- (ASCellNodeBlock)tableNode:(ASTableNode *)tableNode nodeBlockForRowAtIndexPath:(NSIndexPath *)indexPath
+  
+ + +
+
+ +
+
+It is recommended that you use the node block version of these methods so that your table node will be able to prepare and display all of its cells concurrently. This means that all subnode initialization methods can be run in the background. Make sure to keep 'em thread safe. +
+ +These two methods, need to return either an `ASCellNode` or an `ASCellNodeBlock`. An `ASCellNodeBlock` is a block that creates a `ASCellNode` which can be run on a background thread. Note that `ASCellNodes` are used by `ASTableNode`, `ASCollectionNode` and `ASPagerNode`. + +Note that neither of these methods require a reuse mechanism. + +### Replacing UITableViewController with ASViewController + +Texture does not offer an equivalent to `UITableViewController`. Instead, use an `ASViewController` initialized with an `ASTableNode`. + +Consider, again, the `ASViewController` subclass - PhotoFeedNodeController - from the `ASDKgram sample app` that uses a table node as its managed node. + +An `ASTableNode` is assigned to be managed by an `ASViewController` in its `-initWithNode:` designated initializer method. + +
+SwiftObjective-C +
+
+- (instancetype)init
+{
+    _tableNode = [[ASTableNode alloc] initWithStyle:UITableViewStylePlain];
+    self = [super initWithNode:_tableNode];
+    
+    if (self) {
+      _tableNode.dataSource = self;
+      _tableNode.delegate = self;
+    }
+    
+    return self;
+}
+  
+ + +
+
+ +### Node Block Thread Safety Warning + +It is very important that node blocks be thread-safe. One aspect of that is ensuring that the data model is accessed _outside_ of the node block. Therefore, it is unlikely that you should need to use the index inside of the block. + +Consider the following `-tableNode:nodeBlockForRowAtIndexPath:` method from the `PhotoFeedNodeController.m` file in the ASDKgram sample app. + +
+SwiftObjective-C +
+
+- (ASCellNodeBlock)tableNode:(ASTableNode *)tableNode nodeBlockForRowAtIndexPath:(NSIndexPath *)indexPath
+{
+    PhotoModel *photoModel = [_photoFeed objectAtIndex:indexPath.row];
+    
+    // this may be executed on a background thread - it is important to make sure it is thread safe
+    ASCellNode *(^cellNodeBlock)() = ^ASCellNode *() {
+        PhotoCellNode *cellNode = [[PhotoCellNode alloc] initWithPhoto:photoModel];
+        cellNode.delegate = self;
+        return cellNode;
+    };
+    
+    return cellNodeBlock;
+}
+  
+ + +
+
+ +In the example above, you can see how the index is used to access the photo model before creating the node block. + +### Accessing the ASTableView + +If you've used previous versions of Texture, you'll notice that `ASTableView` has been removed in favor of `ASTableNode`. + +
+ASTableView, an actual UITableView subclass, is still used internally by ASTableNode. While it should not be created directly, it can still be used directly by accessing the .view property of an ASTableNode. + +Don't forget that a node's view or layer property should only be accessed after -viewDidLoad or -didLoad, respectively, have been called. +
+ +For example, you may want to set a table's separator style property. This can be done by accessing the table node's view in the `-viewDidLoad:` method as seen in the example below. + +
+SwiftObjective-C +
+
+- (void)viewDidLoad
+{
+  [super viewDidLoad];
+  
+  _tableNode.view.allowsSelection = NO;
+  _tableNode.view.separatorStyle = UITableViewCellSeparatorStyleNone;
+  _tableNode.view.leadingScreensForBatching = 3.0;  // default is 2.0
+}
+
+ + +
+
+ +### Table Row Height + +An important thing to notice is that `ASTableNode` does not provide an equivalent to `UITableView`'s `-tableView:heightForRowAtIndexPath:`. + +This is because nodes are responsible for determining their own height based on the provided constraints. This means you no longer have to write code to determine this detail at the view controller level. + +A node defines its height by way of the layoutSpec returned in the `-layoutSpecThatFits:` method. All nodes given a constrained size are able to calculate their desired size. + +
+By default, a ASTableNode provides its cells with a size range constraint where the minimum width is the tableNode's width and a minimum height is 0. The maximum width is also the tableNode's width but the maximum height is FLT_MAX. +

+This is all to say, a `tableNode`'s cells will always fill the full width of the `tableNode`, but their height is flexible making self-sizing cells something that happens automatically. +
+ +If you call `-setNeedsLayout` on an `ASCellNode`, it will automatically perform another layout pass and if its overall desired size has changed, the table will be informed and will update itself. + +This is different from `UIKit` where normally you would have to call reload row / item. This saves tons of code, check out the ASDKgram sample app to see side by side implementations of an `UITableView` and `ASTableNode` implemented social media feed. + +### Sample Apps using ASTableNode + diff --git a/docs/_docs/containers-asviewcontroller.md b/docs/_docs/containers-asviewcontroller.md new file mode 100755 index 0000000000..f3a5d8e5d3 --- /dev/null +++ b/docs/_docs/containers-asviewcontroller.md @@ -0,0 +1,67 @@ +--- +title: ASViewController +layout: docs +permalink: /docs/containers-asviewcontroller.html +prevPage: faq.html +nextPage: containers-asnodecontroller.html +--- + +`ASViewController` is a subclass of `UIViewController` that adds several useful features for hosting `ASDisplayNode` hierarchies. + +An `ASViewController` can be used in place of any `UIViewController` - including within a `UINavigationController`, `UITabBarController` and `UISplitViewController` or as a modal view controller. + +Benefits of using an `ASViewController`: +
    +
  1. Save Memory. An ASViewController that goes off screen will automatically reduce the size of the fetch data and display ranges of any of its children. This is key for memory management in large applications.
  2. +
  3. ASVisibility Feature. When used in an ASNavigationController or ASTabBarController, these classes know the exact number of user taps it would take to make the view controller visible.
  4. +
+ +More features will be added over time, so it is a good idea to base your view controllers off of this class. + +## Usage + +A `UIViewController` provides a view of its own. An `ASViewController` is assigned a node to manage in its designated initializer `-initWithNode:`. + +Consider the following `ASViewController` subclass, `PhotoFeedNodeController`, from the ASDKgram example project that would like to use a table node as its managed node. + +This table node is assigned to the `ASViewController` in its `-initWithNode:` designated initializer method. + +
+SwiftObjective-C +
+
+- (instancetype)init
+{
+  _tableNode = [[ASTableNode alloc] initWithStyle:UITableViewStylePlain];
+  self = [super initWithNode:_tableNode];
+  
+  if (self) {
+    _tableNode.dataSource = self;
+    _tableNode.delegate = self;
+  }
+  
+  return self;
+}
+  
+ + +
+
+ +
+
+Conversion Tip: If your app already has a complex view controller hierarchy, it is perfectly fine to have all of them subclass ASViewController. That is to say, even if you don't use ASViewController's designated initializer -initiWithNode:, and only use the ASViewController in the manner of a traditional UIViewController, this will give you the additional node support if you choose to adopt it in different areas your application. +
+ diff --git a/docs/_docs/containers-overview.md b/docs/_docs/containers-overview.md new file mode 100755 index 0000000000..335020541e --- /dev/null +++ b/docs/_docs/containers-overview.md @@ -0,0 +1,52 @@ +--- +title: Node Containers +layout: docs +permalink: /docs/containers-overview.html +prevPage: intelligent-preloading.html +nextPage: node-overview.html +--- + +### Use Nodes in Node Containers +It is highly recommended that you use Texture's nodes within a node container. Texture offers the following node containers. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Texture Node ContainerUIKit Equivalent
`ASCollectionNode`in place of UIKit's `UICollectionView`
`ASPagerNode`in place of UIKit's `UIPageViewController`
`ASTableNode`in place of UIKit's `UITableView`
`ASViewController`in place of UIKit's `UIViewController`
`ASNavigationController`in place of UIKit's `UINavigationController`. Implements the `ASVisibility` protocol.
`ASTabBarController`in place of UIKit's `UITabBarController`. Implements the `ASVisibility` protocol.
+ +
+Example code and specific sample projects are highlighted in the documentation for each node container. + + + +### What do I Gain by Using a Node Container? + +A node container automatically manages the intelligent preloading of its nodes. This means that all of the node's layout measurement, data fetching, decoding and rendering will be done asynchronously. Among other conveniences, this is why it is recommended to use nodes within a container node. + +Note that while it _is_ possible to use nodes directly (without an Texture node container), unless you add additional calls, they will only start displaying once they come onscreen (as UIKit does). This can lead to performance degredation and flashing of content. diff --git a/docs/_docs/control-node.md b/docs/_docs/control-node.md new file mode 100755 index 0000000000..30aef32914 --- /dev/null +++ b/docs/_docs/control-node.md @@ -0,0 +1,122 @@ +--- +title: ASControlNode +layout: docs +permalink: /docs/control-node.html +prevPage: map-node.html +nextPage: scroll-node.html +--- + +`ASControlNode` is the Texture equivalent to `UIControl`. You don't create instances of `ASControlNode` directly. Instead, you can use it as a subclassing point when creating controls of your own. In fact, ASTextNode, ASImageNode, ASVideoNode and ASMapNode are all subclasses of `ASControlNode`. + +This fact is especially useful when it comes to image and text nodes. Having the ability to add target-action pairs means that you can use any text or image node as a button without having to rely on creating gesture recognizers, as you would with text in UIKit, or creating extraneous views as you might when using `UIButton`. + +### Control State + +Like `UIControl`, `ASControlNode` has a state which defines its appearance and ability to support user interactions. Its state can be one of any state defined by `ASControlState`. + +
+SwiftObjective-C +
+
+typedef NS_OPTIONS(NSUInteger, ASControlState) {
+    ASControlStateNormal       = 0,
+    ASControlStateHighlighted  = 1 << 0,  // used when isHighlighted is set
+    ASControlStateDisabled     = 1 << 1,
+    ASControlStateSelected     = 1 << 2,  // used when isSelected is set
+    ...
+};
+
+ +
+
+ +### Target-Action Mechanism + +Also similarly to `UIControl`, `ASControlNode`'s have a set of events defined which you can react to by assigning a target-action pair. + +The available actions are: +
+SwiftObjective-C +
+
+typedef NS_OPTIONS(NSUInteger, ASControlNodeEvent)
+{
+  /** A touch-down event in the control node. */
+  ASControlNodeEventTouchDown         = 1 << 0,
+  /** A repeated touch-down event in the control node; for this event the value of the UITouch tapCount method is greater than one. */
+  ASControlNodeEventTouchDownRepeat   = 1 << 1,
+  /** An event where a finger is dragged inside the bounds of the control node. */
+  ASControlNodeEventTouchDragInside   = 1 << 2,
+  /** An event where a finger is dragged just outside the bounds of the control. */
+  ASControlNodeEventTouchDragOutside  = 1 << 3,
+  /** A touch-up event in the control node where the finger is inside the bounds of the node. */
+  ASControlNodeEventTouchUpInside     = 1 << 4,
+  /** A touch-up event in the control node where the finger is outside the bounds of the node. */
+  ASControlNodeEventTouchUpOutside    = 1 << 5,
+  /** A system event canceling the current touches for the control node. */
+  ASControlNodeEventTouchCancel       = 1 << 6,
+  /** All events, including system events. */
+  ASControlNodeEventAllEvents         = 0xFFFFFFFF
+};
+
+ +
+
+ +Assigning a target and action for these events is done with the same methods as a `UIControl`, namely using `–addTarget:action:forControlEvents:`. + +### Hit Test Slop + +While all node's have a `hitTestSlop` property, this is usually most useful when dealing with controls. Instead of needing to make your control bigger, or needing to override `-hitTest:withEvent:` you can just assign a `UIEdgeInsets` to your control and its boundaries will be expanded accordingly. + +
+SwiftObjective-C +
+
+CGFloat horizontalDiff = (bounds.size.width - _playButton.bounds.size.width)/2;
+CGFloat verticalDiff = (bounds.size.height - _playButton.bounds.size.height)/2;
+
+_playButton.hitTestSlop = UIEdgeInsetsMake(-verticalDiff, -horizontalDiff, -verticalDiff, -horizontalDiff);
+
+ +
+
+ +Remember that, since the property is an inset, you'll need to use negative values in order to expand the size of your tappable region. + +### Hit Test Visualization + +The hit test visualization tool is an option to enable highlighting of the tappable areas of your nodes. To enable it, include `[ASControlNode setEnableHitTestDebug:YES]` in your app delegate in `-application:didFinishLaunchingWithOptions:`. diff --git a/docs/_docs/corner-rounding.md b/docs/_docs/corner-rounding.md new file mode 100755 index 0000000000..d0fff0237a --- /dev/null +++ b/docs/_docs/corner-rounding.md @@ -0,0 +1,99 @@ +--- +title: Corner Rounding +layout: docs +permalink: /docs/corner-rounding.html +prevPage: synchronous-concurrency.html +nextPage: debug-tool-hit-test-visualization.html +--- + +When it comes to corner rounding, many developers stick with CALayer's `.cornerRadius` property. Unfortunately, this convenient property greatly taxes performance and should only be used when there is _no_ alternative. This post will cover: + + + +## CALayer's .cornerRadius is Expensive + +Why is `.cornerRadius` so expensive? Use of CALayer's `.cornerRadius` property triggers off-screen rendering to perform the clipping operation on every frame - 60 FPS during scrolling - even if the content in that area isn't changing! This means that the GPU has to switch contexts on every frame, between compositing the overall frame + additional passes for each use of `.cornerRadius`. + +Importantly, these costs don't show up in the Time Profiler, because they affect work done by the CoreAnimation Render Server on your app's behalf. This intensive thrash annihilates performance for a lot of devices. On the iPhone 4, 4S, and 5 / 5C (along with comparable iPads / iPods), expect to see notably degraded performance. On the iPhone 5S and newer, even if you can't see the impact directly, it will reduce headroom so that it takes less to cause a frame drop. + +## Performant Corner Rounding Strategies + +There are only three things to consider when picking a corner rounding strategy: + +
    +
  1. Is there movement underneath the corner?
  2. +
  3. Is there movement through the corner?
  4. +
  5. Are all 4 corners the same node *and* no other nodes intersect in the corner area?
  6. +
+ +Movement **underneath the corner** is any movement behind the corner. For example, as a rounded-corner collection cell scrolls over a background, the background will move underneath and out from under the corners. + +To describe movement **through the corner,** imagine a small rounded-corner scroll view containing a much larger photo. As you zoom and pan the photo inside of the scroll view, the photo will move through the corners of the scroll view. + + + +The above image shows movement underneath the corner highlighted in blue and movement through the corner highlighted in orange. + +
+Note: There can be movement inside of the rounded-corner object, without moving through the corner. The following image shows content, highlighted in green, inset from the edge with a margin equal to the size of the corner radius. When the content scrolls, it will not move through the corners. +
+ + + +Using the above method to adjust your design to eliminate one source of corner movement can make the difference between being able to use a fast rounding technique, or resorting to `.cornerRadius.`. + +The final consideration is to determine if all four corners cover the same node or if any subnodes interesect the corner area. + + + +### Precomposited Corners + +Precomposited corners refer to corners drawn using bezier paths to clip the content in a CGContext / UIGraphicsContext. In this scenario, the corners become part of the image itself — and are "baked in" to the single CALayer. There are two types of precomposited corners. + +The absolute best method is to use **precomposited opaque corners**. This is the most efficient method available, resulting in zero alpha blending (although this is much less critical than avoiding offscreen rendering). Unfortunately, this method is also the least flexible; the background behind the corners will need to be a solid color if the rounded image needs to move around on top of it. It's possible, but tricky to make precomposited corners with a textured or photo background - usually it's best to use precomposited alpha corners instead'.' + +The second method involves using bezier paths with **precomposited alpha corners** (`[path clip]`). This method is pretty flexible and should be one of the most frequently used. It does incur the cost of alpha blending across the full size of the content, and including an alpha channel increases memory impact by 25% over opaque precompositing - but these costs are tiny on modern devices, and a different order of magnitude than `.cornerRadius` offscreen rendering. + +A key limitation of precomposited corners is that the corners must only touch one node and not intersect with any subnodes. If either of these conditions exist, clip corners must be used. + +Note that Texture nodes have a special optimization of `.cornerRadius` that automatically implements precomposited corners **only when using** `.shouldRasterizeDescendants`. It's important to think carefully before you enable rasterization, so don't use this option without first reading all about the concept. + +
+If you're looking for a simple, flat-color rounded rectangle or circle, Texture offers a variety of conveniences to provide this. See `UIImage+ASConveniences.h` for methods to create flat-colored, rounded-corner resizable images using precomposited corners (both alpha and opaque are supported). These are great for use as placeholders for image nodes or backgrounds for ASButtonNode. +
+ +### Clip Corner + +This strategy involves placing **4 seperate opaque corners that sit on top of the content** that needs corner rounding. This method is flexible and has quite good performance. It has minor CPU overhead of 4 seperate layers, one layer for each corner. + + + +Clip corners applies to two main types of corner rounding situations: + +
    +
  • Rounded corners in situations in which the corners touch more than one node or intersect with any subnodes.
  • +
  • Rounded corners on top of a stationary texture or photo background. The photo clip corner method is tricky, but useful!
  • +
+ +## Is it ever okay to use CALayer's .cornerRadius property? + +There are a few, quite rare cases in which it is appropriate to use `.cornerRadius.` These include when there is dynamic content moving _both_ through the inside and underneath the corner. For certain animations, this is impossible to avoid. However, in many cases, it is easy to adjust your design to eliminate one of the sources of movement. One such case was discussed in the section on corner movement. + +It is much less bad, and okay as a shortcut, to use `.cornerRadius.` for screens in which nothing moves. However, *any* motion on the screen, even movement that doesn't involve the corners, will cause the `.cornerRadius.` perfromance tax. For example, having a rounded element in the navigation bar with a scrolling view beneath it will cause the impact even if they don't overlap. Animating anything onscreen, even if the user doesn't interact, will as well.' Additionally, any type of screen refresh will incur the cost of corner rounding. + +### Rasterization and Layerbacking + +Some people have suggested that using CALayer's `.shouldRasterize` can improve the performance of the `.cornerRadius` property. This is not well understood option that is generally perilous. As long as nothing causes it to re-rasterize (no movement, no tap to change color, not on a table view that moves, etc), it is okay to use. Generally we don't encourage this because it is very easy to cause much worse performance. For people who have not great app architecture and insist on using CALayer's `.cornerRadius` (e.g. their app is not very performant), this _can_ make a meaningful difference. However, if you are building your app from the ground up, we highly reccommend that you choose one of the better corner rounding strategies above. + +CALayer's `.shouldRasterize` is unrelated to Texture `node.shouldRasterizeDescendents`. When enabled, `.shouldRasterizeDescendents` will prevent the actual view and layer of the subnode children from being created. + +## Corner Rounding Strategy Flowchart + +Use this flowchart to select the most performant strategy to round a set of corners. + +corner rounding strategy flowchart diff --git a/docs/_docs/debug-tool-ASRangeController.md b/docs/_docs/debug-tool-ASRangeController.md new file mode 100755 index 0000000000..b1bbc33a80 --- /dev/null +++ b/docs/_docs/debug-tool-ASRangeController.md @@ -0,0 +1,42 @@ +--- +title: Range Visualization +layout: docs +permalink: /docs/debug-tool-ASRangeController.html +prevPage: debug-tool-pixel-scaling.html +nextPage: asvisibility.html +--- + +## Visualize ASRangeController tuning parameters (PR #1390) +
+This debug feature adds a semi-transparent subview in the bottom right hand corner of the sharedApplication keyWindow that visualizes the ASRangeTuningParameters per each ASLayoutRangeType for each visible (on-screen) instance of ASRangeController. + +- The instances of ASRangeController are represented as bars +- As you scroll around within ASTable/CollectionViews you can see the parameters (green = Visible, yellow = Display, and red = FetchData) move relative to each other. +- White arrows on the L and R sides of the individual RangeController bar views indicate the scrolling direction so that you can determine the leading / trailing tuning parameters (especially useful for vertically-oriented rangeControllers whose leading edge might be unclear within the horizontally-oriented bar view). +- The white debug label above the RangeController bar displays the RangeController dataSource’s class name to differentiate between nested views. +- The overlay can be moved with a panning gesture in order to see content under it. + +This debug feature is useful for highly optimized Texture apps that require tuning of any ASRangeController. Or for anyone who is curious about how ASRangeControllers work. + +The VerticalWithinHorizontal example app contains an ASPagerNode with embedded ASTableViews. In the screenshot with this feature enabled, you can see the two range controllers - ASTableView and ASCollectionView (ASPagerNode) - in the overlay. + +- The white arrows to the right of the rangeController bars indicate that the user is currently scrolling down through the table and right through the ASCollectionView/PagerNode. +- The ASTableView rangeController bar indicates that the range parameters are tuned to both fetch and decode more data in the downward table direction rather than in the reverse direction (which makes sense as the user is scrolling down). +- Since it’s less obvious whether or not the user will page to the right or left next, the ASCollectionView is tuned to fetch and decode equal amounts of data in each direction. +- In the video demo, you can see as the user scrolls between pages, that new ASTableView rangeControllers are created and removed in the overlay view. +![bc0b98f0-ebb8-11e5-8f50-421cb0f320c2](https://cloud.githubusercontent.com/assets/3419380/14057072/ef7f63a0-f2b2-11e5-92a5-f65b2d207e63.png) + +## Limitations +
    +
  • only shows onscreen ASRangeControllers
  • +
  • currently the ratio of red (fetch data), yellow (display) and green (visible) are relative to each other, but not between each bar view. So you cannot compare individual bars to eachother
  • +
+ +## Usage +In your `AppDelegate.m` file, +
    +
  • import AsyncDisplayKit+Debug.h
  • +
  • add [ASDisplayNode setShouldShowRangeDebugOverlay:YES] at the top of your AppDelegate's didFinishLaunchingWithOptions: method
  • +
+ +**Make sure to call this method before initializing any component that uses an ASRangeControllers (ASTableView, ASCollectionView).** diff --git a/docs/_docs/debug-tool-hit-test-visualization.md b/docs/_docs/debug-tool-hit-test-visualization.md new file mode 100755 index 0000000000..2b2f5f2a50 --- /dev/null +++ b/docs/_docs/debug-tool-hit-test-visualization.md @@ -0,0 +1,34 @@ +--- +title: Hit Test Visualization +layout: docs +permalink: /docs/debug-tool-hit-test-visualization.html +prevPage: corner-rounding.html +nextPage: debug-tool-pixel-scaling.html +--- + +## Visualize ASControlNode Tappable Areas +
+This debug feature adds a semi-transparent highlight overlay on any ASControlNodes containing a `target:action:` pair or gesture recognizer. The tappable range is defined as the ASControlNode’s frame + its `.hitTestSlop` `UIEdgeInsets`. Hit test slop is a unique feature of `ASControlNode` that allows it to extend its tappable range. + +In the screenshot below, you can quickly see that +
    +
  • The tappable area for the avatar image overlaps the username’s tappable area. In this case, the user avatar image is on top in the view hierarchy and is capturing some touches that should go to the username.
  • +
  • It would probably make sense to expand the `.hitTestSlop` for the username to allow the user to more easily hit it.
  • +
  • I’ve accidentally set the hitTestSlop’s UIEdgeInsets to be positive instead of negative for the photo likes count label. It’s going to be hard for a user to tap the smaller target.
  • +
+ +![screen shot 2016-03-25 at 4 39 23 pm](https://cloud.githubusercontent.com/assets/3419380/14057034/e1e71450-f2b1-11e5-8091-3e6f22862994.png) + +## Restrictions +
+A _green_ border on the edge(s) of the highlight overlay indicates that that edge of the tapable area is restricted by one of it's superview's tapable areas. An _orange_ border on the edge(s) of the highlight overlay indicates that that edge of the tapable area is clipped by .clipsToBounds of a parent in its hierarchy. + +## Usage +
+In your `AppDelegate.m` file, +
    +
  • import AsyncDisplayKit+Debug.h
  • +
  • add [ASControlNode setEnableHitTestDebug:YES] at the top of your AppDelegate's didFinishLaunchingWithOptions: method
  • +
+ +**Make sure to call this method before initializing any ASControlNodes - including ASButtonNodes, ASImageNodes, and ASTextNodes.** diff --git a/docs/_docs/debug-tool-pixel-scaling.md b/docs/_docs/debug-tool-pixel-scaling.md new file mode 100755 index 0000000000..2ff4e5e37b --- /dev/null +++ b/docs/_docs/debug-tool-pixel-scaling.md @@ -0,0 +1,60 @@ +--- +title: Image Scaling +layout: docs +permalink: /docs/debug-tool-pixel-scaling.html +prevPage: debug-tool-hit-test-visualization.html +nextPage: debug-tool-ASRangeController.html +--- + +## Visualize ASImageNode.image’s pixel scaling +
+This debug feature adds a red text overlay on the bottom right hand corner of an ASImageNode if (and only if) the image’s size in pixels does not match it’s bounds size in pixels, e.g. + +
+SwiftObjective-C + +
+
+CGFloat imageSizeInPixels = image.size.width * image.size.height;
+CGFloat boundsSizeInPixels = imageView.bounds.size.width * imageView.bounds.size.height;
+CGFloat scaleFactor = imageSizeInPixels / boundsSizeInPixels;
+
+if (scaleFactor != 1.0) {
+      NSString *scaleString = [NSString stringWithFormat:@"%.2fx", scaleFactor];
+      _debugLabelNode.hidden = NO;
+}
+
+ +
+
+ + +This debug feature is useful for quickly determining if you are + +
    +
  • downloading and rendering excessive amounts of image data
  • +
  • upscaling a low quality image
  • +
+ +In the screenshot below of an app with this debug feature enabled, you can see that the avatar image is unnecessarily large (9x too large) for it’s bounds size and that the center picture is more optimized, but not perfectly so. If you control your own endpoint, make sure to return an optimally sized image. + +![screen shot 2016-03-25 at 4 04 59 pm](https://cloud.githubusercontent.com/assets/3419380/14056994/15561daa-f2b1-11e5-9606-59d54d2b5354.png) + +## Usage +
+In your `AppDelegate.m` file, +
    +
  • import AsyncDisplayKit+Debug.h
  • +
  • add [ASImageNode setShouldShowImageScalingOverlay:YES] at the top of your AppDelegate's didFinishLaunchingWithOptions: method
  • +
+ +**Make sure to call this method before initializing any ASImageNodes.** diff --git a/docs/_docs/display-node.md b/docs/_docs/display-node.md new file mode 100755 index 0000000000..896c969dad --- /dev/null +++ b/docs/_docs/display-node.md @@ -0,0 +1,93 @@ +--- +title: ASDisplayNode +layout: docs +permalink: /docs/display-node.html +prevPage: containers-aspagernode.html +nextPage: cell-node.html +--- + +### Node Basics + +`ASDisplayNode` is the main view abstraction over `UIView` and `CALayer`. It initializes and owns a `UIView` in the same way `UIViews` create and own their own backing `CALayers`. + +
+SwiftObjective-C + +
+
+ASDisplayNode *node = [[ASDisplayNode alloc] init];
+node.backgroundColor = [UIColor orangeColor];
+node.bounds = CGRectMake(0, 0, 100, 100);
+
+NSLog(@"Underlying view: %@", node.view);
+	
+ + +
+
+ +A node has all the same properties as a `UIView`, so using them should feel very familiar to anyone familiar with UIKit. + +Properties of both views and layers are forwarded to nodes and can be easily accessed. + +
+SwiftObjective-C + +
+
+ASDisplayNode *node = [[ASDisplayNode alloc] init];
+node.clipsToBounds = YES;				  // not .masksToBounds
+node.borderColor = [UIColor blueColor];  //layer name when there is no UIView equivalent
+
+NSLog(@"Backing layer: %@", node.layer);
+	
+ + +
+
+ +As you can see, naming defaults to the `UIView` conventions*** unless there is no `UIView` equivalent. You also have access to your underlying `CALayer` just as you would when dealing with a plain `UIView`. + +When used with one of the node containers, a node’s properties will be set on a background thread, and its backing view/layer will be lazily constructed with the cached properties collected by the node. You rarely need to worry about jumping to a background thread as this will be taken care of by the framework, but it's important to know that this is happening under the hood. + +### View Wrapping + +In some cases, it is desirable to initialize a node and provide a view to be used as the backing view. These views are provided via a block that will return a view so that the actual construction of the view can be saved until later. These nodes’ display step happens synchronously. This is because a node can only be asynchronously displayed when it wraps an `_ASDisplayView` (the internal view subclass), not when it wraps a plain `UIView`. + +
+SwiftObjective-C + +
+
+ASDisplayNode *node = [ASDisplayNode alloc] initWithViewBlock:^{
+	SomeView *view  = [[SomeView alloc] init];
+	return view;
+}];
+	
+ + +
+
+ +Doing this allows you to wrap existing views if that is preferable to converting the `UIView` subclass to an `ASDisplayNode` subclass. + +
+ *** The only exception is that nodes use `position` instead of `center` for reasons beyond this intro. +
diff --git a/docs/_docs/editable-text-node.md b/docs/_docs/editable-text-node.md new file mode 100755 index 0000000000..a1fccd44a4 --- /dev/null +++ b/docs/_docs/editable-text-node.md @@ -0,0 +1,153 @@ +--- +title: ASEditableTextNode +layout: docs +permalink: /docs/editable-text-node.html +prevPage: scroll-node.html +nextPage: multiplex-image-node.html +--- + +`ASEditableTextNode` is available to be used anywhere you'd normally use a `UITextView` or `UITextField`. Under the hood, it uses a specialized `UITextView` as its backing view. You can access and configure this view directly any time after the node has loaded, as long as you do it on the main thread. + +It's also important to note that this node does not support layer backing due to the fact that it supports user interaction. + +### Basic Usage + +Using an editable text node as a text input is easy. If you want it to have text by default, you can assign an attributed string to the `attributedText` property. + +
+SwiftObjective-C + +
+
+ASEditableTextNode *editableTextNode = [[ASEditableTextNode alloc] init];
+
+editableTextNode.attributedText = [[NSAttributedString alloc] initWithString:@"Lorem ipsum dolor sit amet."];
+editableTextNode.textContainerInset = UIEdgeInsetsMake(8, 8, 8, 8);
+
+ + +
+
+ +### Placeholder Text + +If you want to display a text box with a placeholder that disappears after a user starts typing, just assign an attributed string to the `attributedPlaceholderText` property. + +
+SwiftObjective-C + +
+
+editableTextNode.attributedPlaceholderText = [[NSAttributedString alloc] initWithString:@"Type something here..."];
+
+ + +
+
+ +The property `isDisplayingPlaceholder` will initially return `YES`, but will toggle to `NO` any time the `attributedText` property is set to a non-empty string. + +### Typing Attributes + +To set up the style of the text your user will type into this text field, you can set the `typingAttributes`. + + +
+SwiftObjective-C + +
+
+editableTextNode.typingAttributes = @{NSForegroundColorAttributeName: [UIColor blueColor], 
+                                      NSBackgroundColorAttributeName: [UIColor redColor]};
+
+ + +
+
+ + +### ASEditableTextNode Delegate + +In order to respond to events associated with an editable text node, you can use any of the following delegate methods: + + +-- Indicates to the delegate that the text node began editing. + +
+SwiftObjective-C +
+
+- (void)editableTextNodeDidBeginEditing:(ASEditableTextNode *)editableTextNode;
+
+ +
+
+ +-- Asks the delegate whether the specified text should be replaced in the editable text node. + +
+SwiftObjective-C +
+
+- (BOOL)editableTextNode:(ASEditableTextNode *)editableTextNode shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text;
+
+ +
+
+ +-- Indicates to the delegate that the text node's selection has changed. + +
+SwiftObjective-C +
+
+- (void)editableTextNodeDidChangeSelection:(ASEditableTextNode *)editableTextNode fromSelectedRange:(NSRange)fromSelectedRange toSelectedRange:(NSRange)toSelectedRange dueToEditing:(BOOL)dueToEditing;
+
+ +
+
+ +-- Indicates to the delegate that the text node's text was updated. + +
+SwiftObjective-C +
+
+- (void)editableTextNodeDidUpdateText:(ASEditableTextNode *)editableTextNode;
+
+ +
+
+ +--  Indicates to the delegate that teh text node has finished editing. + +
+SwiftObjective-C +
+
+- (void)editableTextNodeDidFinishEditing:(ASEditableTextNode *)editableTextNode;
+
+ +
+
+ diff --git a/docs/_docs/faq.md b/docs/_docs/faq.md new file mode 100755 index 0000000000..fa7a7826f1 --- /dev/null +++ b/docs/_docs/faq.md @@ -0,0 +1,125 @@ +--- +title: FAQ +layout: docs +permalink: /docs/faq.html +prevPage: subclassing.html +nextPage: containers-asviewcontroller.html +--- + +### Common Developer Mistakes + + + +### Common Conceptual Misunderstandings + + + +### Common Questions + + + +### Accessing the node's view before it is loaded +
+Node `-init` methods are often called off the main thread, therefore it is imperative that no UIKit objects are accessed. Examples of common errors include accessing the node's view or creating a gesture recognizer. Instead, these operations are ideal to perform in `-didLoad`. + +Interacting with UIKit in `-init` can cause crashes and performance problems. +
+ +### Make sure you access your data source outside the node block +
+The `indexPath` parameter is only valid _outside_ the node block returned in `nodeBlockForItemAtIndexPath:` or `nodeBlockForRowAtIndexPath:`. Because these blocks are executed on a background thread, the `indexPath` may be invalid by execution time, due to additional changes in the data source. + +See an example of how to correctly code a node block in the ASTableNode page. Just as with UIKit, it will cause an exception if Nil is returned from the block for any `ASCellNode`. +
+ +### Take steps to avoid a retain cycle in viewBlocks +
+When using `initWithViewBlock:` it is important to prevent a retain cycle by capturing a strong reference to self. The two ways that a cycle can be created are by using any instance variable inside the block or directly referencing self without using a weak pointer. + +You can use properties instead of instance variables as long as they are accessed on a weak pointer to self. + +Because viewBlocks are always executed on the main thread, it is safe to preform UIKit operations (including gesture recognizer creation and addition). + +Although the block is destroyed after the view is created, in the event that the block is never run and the view is never created, then a cycle can persist preventing memory from being released. +
+ +### ASCellNode Reusability +
+Texture does not use cell reuse, for a number of specific reasons, one side effect of this is that it eliminates the large class of bugs associated with cell reuse. +
+ +### LayoutSpecs Are Regenerated +
+A node's layoutSpec gets regenerated every time its `layoutThatFits:` method is called. +
+ +### Layout API Sizing +
+If you're confused by `ASRelativeDimension`, `ASRelativeSize`, `ASRelativeSizeRange` and `ASSizeRange`, check out our Layout API Sizing guide. +
+ +### CALayer's .cornerRadius Property Kills Performance +
+CALayer's' .cornerRadius property is a disastrously expensive property that should only be used when there is no alternative. It is one of the least efficient, most render-intensive properties on CALayer (alongside shadowPath, masking, borders, etc). These properties trigger offscreen rendering to perform the clipping operation on every frame — 60FPS during scrolling! — even if the content in that area isn't changing. + +Using `.cornerRadius` will visually degraded performance on iPhone 4, 4S, and 5 / 5C (along with comparable iPads / iPods) and reduce head room and make frame drops more likely on 5S and newer devices. + +For a longer discussion and easy alternative corner rounding solutions, please read our comprehensive corner rounding guide. +
+ +### Texture does not support UIKit Auto Layout or InterfaceBuilder +
+UIKit Auto Layout and InterfaceBuilder are not supported by Texture. It is worth noting that both of these technologies are not permitted in established and disciplined iOS development teams, such as at Facebook, Instagram, and Pinterest. + +However, Texture's Layout API provides a variety of ASLayoutSpec objects that allow implementing automatic layout which is more efficient (multithreaded, off the main thread), easier to debug (can step into the code and see where all values come from, as it is open source), and reusable (you can build composable layouts that can be shared with different parts of the UI). +
+ +### ASDisplayNode keep alive reference + +
+
+
+ASTextNode *title=[[ASTextNode alloc]init];
+title.attributedString=Text;
+[self addSubnode:title];
+
+retain cycles
+(
+"-> _keepalive_node -> ASTextNode ",
+"-> _view -> _ASDisplayView "
+)
+
+
+
+ +
+This retain cycle is intentionally created because the node is in a "live" view hierarchy (it is inside the UIWindow that is onscreen). + +To see why this is necessary, consider that Apple also creates this retain cycle between UIView and CALayer. If you create a UIView and add its layer to a super layer, and then release the UIView, it will stay alive even though the CALayer delegate pointing to it is weak. + +For the same reason, if the node's view is a descendant of a window, but there is no reference to the node, we keep the node alive with a strong reference from the view. + +Good application design should not rely on this behavior, because a strong reference to the node should be maintained by the subnodes array or by an instance variable. However, this condition occasionally occurs, for example when using a UIView animation API. This cycle should never create a leak or even extend the lifecycle of a node any longer than it is absolutely necessary. +
+ +### UICollectionViewCell Compatibility + +Texture supports using UICollectionViewCells alongside native ASCellNodes. + +Note that these UIKit cells will **not** have the performance benefits of `ASCellNodes` (like preloading, async layout, and async drawing), even when mixed within the same `ASCollectionNode`. + +However, this interoperability allows developers the flexibility to test out the framework without needing to convert all of their cells at once. Read more here. diff --git a/docs/_docs/getting-started.md b/docs/_docs/getting-started.md new file mode 100755 index 0000000000..8b11e57cff --- /dev/null +++ b/docs/_docs/getting-started.md @@ -0,0 +1,53 @@ +--- +title: Getting Started +layout: docs +permalink: /docs/getting-started.html +nextPage: resources.html +--- + +Texture's basic unit is the `node`. `ASDisplayNode` is an abstraction +over `UIView`, which in turn is an abstraction over `CALayer`. Unlike views, which +can only be used on the main thread, nodes are thread-safe: you can +instantiate and configure entire hierarchies of them in parallel on background +threads. + +To keep its user interface smooth and responsive, your app should render at 60 +frames per second — the gold standard on iOS. This means the main thread +has one-sixtieth of a second to push each frame. That's 16 milliseconds to +execute all layout and drawing code! And because of system overhead, your code +usually has less than ten milliseconds to run before it causes a frame drop. + +Texture lets you move image decoding, text sizing and rendering, and +other expensive UI operations off the main thread, to keep the main thread available to +respond to user interaction. Texture has other tricks up its +sleeve too... but we'll get to that later. + +

Nodes

+ +If you're used to working with views, you already know how to use nodes. Most methods have a node equivalent and most `UIView` and `CALayer` properties are available as well. In any case where there is a naming discrepancy (such as `.clipsToBounds` vs `.masksToBounds`), nodes will default to the `UIView` name. The only exception is that nodes use position instead of center. + +Of course, you can always access the underlying view or layer directly via `node.view` or `node.layer`, just make sure to do it on the main thread! + +Texture offers a variety of nodes to replace the majority of the UIKit components that you are used to. Large scale apps have been able to completely write their UI using just Texture nodes. + +

Node Containers

+ +When converting an app to use Texture, a common mistake is to add nodes directly to an existing view hierarchy. Doing this will virtually guarantee that your nodes will flash as they are rendered. + +Instead, you should add nodes as subnodes of one of the many node container classes. These containers are in charge of telling contained nodes what state they're currently in so that data can be loaded and nodes can be rendered as efficiently as possible. You should think of these classes as the integration point between UIKit and Texture. + +

Layout Engine

+ +Texture's layout engine is both one of its most powerful and one of its most unique features. Based on the CSS FlexBox model, it provides a declarative way of specifying a custom node's size and layout of its subnodes. While all nodes are concurrently rendered by default, asynchronous measurement and layout are performed by providing an `ASLayoutSpec` for each node. + +

Advanced Developer Features

+ +Texture offers a variety of advanced developer features that cannot be found in UIKit or Foundation. Our developers have found that AsyncDisplyKit allows simplifications in their architecture and improves developer velocity. + +(Full list coming soon!) + +

Adding Texture to your App

+ +If you are new to Texture, we recommend that you check out our ASDKgram example app. We've created a handy guide (coming soon!) with step-by-step directions and a follow along example on how to add Texture to an app. + +If you run into any problems along the way, reach out to us GitHub or the Texture Slack community for help. diff --git a/docs/_docs/hit-test-slop.md b/docs/_docs/hit-test-slop.md new file mode 100755 index 0000000000..fa126ce880 --- /dev/null +++ b/docs/_docs/hit-test-slop.md @@ -0,0 +1,44 @@ +--- +title: Hit Test Slop +layout: docs +permalink: /docs/hit-test-slop.html +prevPage: layout-transition-api.html +nextPage: batch-fetching-api.html +--- + +`ASDisplayNode` has a `hitTestSlop` property of type `UIEdgeInsets` that when set to a non-zero inset, increase the bounds for hit testing to make it easier to tap or perform gestures on this node. + +ASDisplayNode is the base class for all nodes, so this property is available on any of Texture's nodes. + +
+Note: This affects the default implementation of -hitTest and -pointInside, so subclasses should call super if you override it and want hitTestSlop applied. +
+ +A node's ability to capture touch events is restricted by its parent's bounds + parent hitTestSlop UIEdgeInsets. Should you want to extend the hitTestSlop of a child outside its parent's bounds, simply extend the parent node's hitTestSlop to include the child's hitTestSlop needs. + +### Usage + +A common need for hit test slop, is when you have a text node (aka label) you'd like to use as a button. Often, the text node's height won't meet the 44 point minimum recommended for tappable areas. In that case, you can calculate the difference, and apply a negative inset to your label to increase the tappable area. + +
+SwiftObjective-C + +
+
+ASTextNode *textNode = [[ASTextNode alloc] init];
+
+CGFloat padding = (44.0 - button.bounds.size.height)/2.0;
+textNode.hitTestSlop = UIEdgeInsetsMake(-padding, 0, -padding, 0);
+
+ +
+
+ +
+To visualize hitTestSlop, check out the debug tool. +
diff --git a/docs/_docs/image-modification-block.md b/docs/_docs/image-modification-block.md new file mode 100755 index 0000000000..d4595831c0 --- /dev/null +++ b/docs/_docs/image-modification-block.md @@ -0,0 +1,152 @@ +--- +title: Image Modification Blocks +layout: docs +permalink: /docs/image-modification-block.html +prevPage: inversion.html +nextPage: placeholder-fade-duration.html +--- + +Many times, operations that would affect the appearance of an image you're displaying are big sources of main thread work. Naturally, you want to move these to a background thread. + +By assigning an `imageModificationBlock` to your imageNode, you can define a set of transformations that need to happen asynchronously to any image that gets set on the imageNode. + +
+SwiftObjective-C + +
+
+_backgroundImageNode.imageModificationBlock = ^(UIImage *image) {
+	UIImage *newImage = [image applyBlurWithRadius:30
+										 tintColor:[UIColor colorWithWhite:0.5 alpha:0.3]
+							 saturationDeltaFactor:1.8
+							 			 maskImage:nil];
+	return newImage ? newImage : image;
+};
+
+//some time later...
+
+_backgroundImageNode.image = someImage;
+
+ + +
+
+ +The image named "someImage" will now be blurred asynchronously before being assigned to the imageNode to be displayed. + +### Adding image effects + +The most efficient way to add image effects is by leveraging the `imageModificationBlock` block. If a block is provided it can perform drawing operations on the image during the display phase. As display is happening on a background thread it will not block the main thread. + +In the following example we assume we have an avatar node that will be setup in `init` of a supernode and the image of the node should be rounded. We provide the `imageModificationBlock` and in there we call a convenience method that transforms the image passed in into a circular image and return it. + +
+SwiftObjective-C + +
+
+- (instancetype)init
+{
+// ...
+  _userAvatarImageNode.imageModificationBlock = ^UIImage *(UIImage *image) {
+    CGSize profileImageSize = CGSizeMake(USER_IMAGE_HEIGHT, USER_IMAGE_HEIGHT);
+    return [image makeCircularImageWithSize:profileImageSize];
+  };
+  // ...
+}
+
+ + +
+ +
+ +The actual drawing code is nicely abstracted away in an UIImage category and looks like the following: + +
+SwiftObjective-C + +
+
+@implementation UIImage (Additions)
+- (UIImage *)makeCircularImageWithSize:(CGSize)size
+{
+  // make a CGRect with the image's size
+  CGRect circleRect = (CGRect) {CGPointZero, size};
+
+  // begin the image context since we're not in a drawRect:
+  UIGraphicsBeginImageContextWithOptions(circleRect.size, NO, 0);
+
+  // create a UIBezierPath circle
+  UIBezierPath *circle = [UIBezierPath bezierPathWithRoundedRect:circleRect cornerRadius:circleRect.size.width/2];
+
+  // clip to the circle
+  [circle addClip];
+
+  // draw the image in the circleRect *AFTER* the context is clipped
+  [self drawInRect:circleRect];
+
+  // get an image from the image context
+  UIImage *roundedImage = UIGraphicsGetImageFromCurrentImageContext();
+
+  // end the image context since we're not in a drawRect:
+  UIGraphicsEndImageContext();
+
+  return roundedImage;
+}
+@end
+
+ + +
+
+ +The imageModificationBlock is very handy and can be used to add all kind of image effects, such as rounding, adding borders, or other pattern overlays, without extraneous display calls. diff --git a/docs/_docs/image-node.md b/docs/_docs/image-node.md new file mode 100755 index 0000000000..d7b1057ed7 --- /dev/null +++ b/docs/_docs/image-node.md @@ -0,0 +1,90 @@ +--- +title: ASImageNode +layout: docs +permalink: /docs/image-node.html +prevPage: text-node.html +nextPage: network-image-node.html +--- + +`ASImageNode` is the Texture equivalent to `UIImageView`. The most basic difference is that images are decoded asynchronously by default. Of course, there are more advanced improvments as well such as GIF support and `imageModificationBlock`s. + +### Basic Usage + +Using an image node works exactly like using an image view. + +
+SwiftObjective-C + +
+
+ASImageNode *imageNode = [[ASImageNode alloc] init];
+
+imageNode.image = [UIImage imageNamed:@"someImage"];
+imageNode.contentMode = UIViewContentModeScaleAspectFill;
+
+ + +
+
+ + +### Image transformations and effects + +Many times, operations that would affect the appearance of an image you're displaying are big sources of main thread work. Naturally, you want to move these to a background thread. + +By assigning an `imageModificationBlock` to your `imageNode`, you can define a set of transformations that need to happen asynchronously to any image that gets set on the `imageNode`, including image effects such as rounding, adding borders, or other pattern overlays, without extraneous display calls. + +You can read more about it at Image Modification Blocks. + +### Image Cropping + +When an `imageNode`'s `contentMode` property is set to `UIViewContentModeScaleAspectFill`, it will automatically expand the image to fill the entire area of the imageNode, and crop any areas that go past the bounds due to scaling the image. + +By default, the expanded image will be centered within the bounds of the view. Take the following cat image. His face gets cut off by default. + + + +That's messed up. To fix it, you can set the `cropRect` property to move the image over. By default it is set to `CGRectMake(0.5, 0.5, 0.0, 0.0)`. + +The rectangle is specified as a "unit rectangle," using percentages of the source image's width and height. To show the image starting at the left side, you can set the `cropRect`'s `x` value to be `0.0`, meaning the image's origin should start at `{0, 0}` as opposed to the default. + +
+SwiftObjective-C + +
+
+self.animalImageNode.cropRect = CGRectMake(0, 0, 0.0, 0.0);
+
+ + +
+
+ +Leaving the width and height values at `0.0` means the image won't be stretched. + + + +Alternatively, you can set the `x` value of the origin to `1.0` to right align the image. + + + +### Forced Upscaling + +By default, an image won't be upscaled on the CPU when it is too small to fit into the bounds of the `imageNode` it has been set on. + +You can set `forceUpscaling` to `YES` if you'd like to change this fact. Doing so means your app will take up more memory any time you use an image that is smaller than its destination. + +### Detecting Image Scaling + +By using the pixel scaling tool, you can easily check each image in your app to see how much it has been scaled up or down. + +If images are too big, you risk rendering excessive amounts of image data, and when they're too small you spend time upscaling a low quality image. + +If you control your API, consider returning correctly scaled images so that this work can be avoided. diff --git a/docs/_docs/index.html b/docs/_docs/index.html new file mode 100755 index 0000000000..930d84ddd9 --- /dev/null +++ b/docs/_docs/index.html @@ -0,0 +1,4 @@ +--- +layout: redirect +destination: /docs/getting-started.html +--- diff --git a/docs/_docs/inset-layout-spec.md b/docs/_docs/inset-layout-spec.md new file mode 100755 index 0000000000..ad3bd3e5b1 --- /dev/null +++ b/docs/_docs/inset-layout-spec.md @@ -0,0 +1,7 @@ +--- +title: ASInsetLayoutSpec +layout: docs +permalink: /docs/inset-layout-spec.html +--- + +
😑 This page is coming soon...
\ No newline at end of file diff --git a/docs/_docs/installation.md b/docs/_docs/installation.md new file mode 100755 index 0000000000..81e6573a32 --- /dev/null +++ b/docs/_docs/installation.md @@ -0,0 +1,114 @@ +--- +title: Installation +layout: docs +permalink: /docs/installation.html +prevPage: resources.html +nextPage: adoption-guide-2-0-beta1.html +--- + +Texture may be added to your project via CocoaPods or Carthage. Do not forget to import the framework header: + +
+
+
+#import 
+
+
+
+ +or create a Objective-C bridging header (Swift). If you have any problems installing Texture, please contact us on Github or Slack! + +## CocoaPods + +Texture is available on CocoaPods. Add the following to your Podfile: + +
+
+
+target 'MyApp' do
+	pod "Texture"
+end
+
+
+
+ +Quit Xcode completely before running + +
+
+
+> pod install
+
+
+
+ +in the project directory in Terminal. + +To update your version of Texture, run + +
+
+
+> pod update Texture
+
+
+
+ +in the project directory in Terminal. + +Don't forget to use the workspace `.xcworkspace` file, _not_ the project `.xcodeproj` file. + +## Carthage (standard build) + +
+The standard way to use Carthage is to have a Cartfile list the dependencies, and then run `carthage update` to download the dependenices into the `Cathage/Checkouts` folder and build each of those into frameworks located in the `Carthage/Build` folder, and finally the developer has to manually integrate in the project. +
+ +Texture is also available through Carthage. + +Add the following to your Cartfile to get the **latest release** branch: + +
+
+
+github "texturegroup/texture"
+
+
+
+ +
+Or, to get the **master** branch: + +
+
+
+github "texturegroup/texture" "master"
+
+
+
+ +
+Texture has its own Cartfile which lists its dependencies, so this is the only line you will need to include in your Cartfile. + +Run + +
+
+
+> carthage update
+
+
+
+ +
+in Terminal. This will fetch dependencies into a `Carthage/Checkouts` folder, then build each one. + +Look for terminal output confirming `Texture`, `PINRemoteImage (3.0.0-beta.2)` and `PINCache` are all fetched and built. The Texture framework Cartfile should handle the dependencies correctly. + +In Xcode, on your application targets’ **“General”** settings tab, in the **“Linked Frameworks and Libraries”** section, drag and drop each framework you want to use from the `Carthage/Build` folder on disk. + +## Carthage (light) + +Texture does not yet support the lighter way of using Carthage, in which you manually add the project files. This is because one of its dependencies, `PINCache` (a nested dependency of `PINRemoteImage`) does not yet have a project file. + +Without including `PINRemoteImage` and `PINCache`, you will not get Texture's full image feature set. diff --git a/docs/_docs/intelligent-preloading.md b/docs/_docs/intelligent-preloading.md new file mode 100755 index 0000000000..24440f1acd --- /dev/null +++ b/docs/_docs/intelligent-preloading.md @@ -0,0 +1,95 @@ +--- +title: Intelligent Preloading +layout: docs +permalink: /docs/intelligent-preloading.html +prevPage: upgrading.html +nextPage: containers-overview.html +--- + +While a node's ability to be rendered and measured asynchronously and concurrently makes it quite powerful, another crucially important layer to Texture is the idea of intelligent preloading. + +As was pointed out in Getting Started, it is rarely advantageous to use a node outside of the context of one of the node containers. This is due to the fact that all nodes have a notion of their current interface state. + +This `interfaceState` property is constantly updated by an `ASRangeController` which all containers create and maintain internally. + +A node used outside of a container won't have its state updated by any range controller. This sometimes results in a flash as nodes are rendered after realizing they're already onscreen without any warning. + +## Interface State Ranges + +When nodes are added to a scrolling or paging interface they are typically in one of the following ranges. This means that as the scrolling view is scrolled, their interface states will be updated as they move through them. + + + +A node will be in one of following ranges: + + + + + + + + + + + + + + + + + + +
Interface StateDescription
PreloadThe furthest range out from being visible. This is where content is gathered from an external source, whether that’s some API or a local disk.
DisplayHere, display tasks such as text rasterization and image decoding take place.
VisibleThe node is onscreen by at least one pixel.
+ +## ASRangeTuningParameters + +The size of each of these ranges is measured in "screenfuls". While the default sizes will work well for many use cases, they can be tweaked quite easily by setting the tuning parameters for range type on your scrolling node. + + + +In the above visualization of a scrolling collection, the user is scrolling down. As you can see, the sizes of the ranges in the leading direction are quite a bit larger than the content the user is moving away from (the trailing direction). If the user were to change directions, the leading and trailing sides would dynamically swap in order to keep memory usage optimal. This allows you to worry about defining the leading and trailing sizes without having to worry about reacting to the changing scroll directions of your user. + +Intelligent preloading also works in multiple dimensions. + +## Interface State Callbacks + +As a user scrolls, nodes move through the ranges and react appropriately by loading data, rendering, etc. Your own node subclasses can easily tap into this mechanism by implementing the corresponding callback methods. + +#### Visible Range + +
+ +
+
+-didEnterVisibleState
+-didExitVisibleState
+
+
+
+ +#### Display Range + +
+ +
+
+-didEnterDisplayState
+-didExitDisplayState
+
+
+
+ +#### Preload Range + +
+ +
+
+-didEnterPreloadState
+-didExitPreloadState
+
+
+
+ +
+Just remember to call super ok? 😉 diff --git a/docs/_docs/inversion.md b/docs/_docs/inversion.md new file mode 100755 index 0000000000..2231c20b95 --- /dev/null +++ b/docs/_docs/inversion.md @@ -0,0 +1,32 @@ +--- +title: Inversion +layout: docs +permalink: /docs/inversion.html +prevPage: automatic-subnode-mgmt.html +nextPage: image-modification-block.html +--- + +`ASTableNode` and `ASCollectionNode` have a `inverted` property of type `BOOL` that when set to `YES`, will automatically invert the content so that it's layed out bottom to top, that is the 'first' (indexPath 0, 0) node is at the bottom rather than the top as usual. This is extremely covenient for chat/messaging apps, and with Texture it only takes one property. + +When this is enabled, developers only have to take one more step to have full inversion support and that is to adjust the `contentInset` of their `ASTableNode` or `ASCollectionNode` like so: + +
+ + Swift + Objective-C + + +
+
+ CGFloat inset = [self topBarsHeight];
+ self.tableNode.view.contentInset = UIEdgeInsetsMake(0, 0, inset, 0);
+ self.tableNode.view.scrollIndicatorInsets = UIEdgeInsetsMake(0, 0, inset, 0);
+  
+ + +
+
+ +See the SocialAppLayout-Inverted example project for more details. diff --git a/docs/_docs/layer-backing.md b/docs/_docs/layer-backing.md new file mode 100755 index 0000000000..3c1b1ca702 --- /dev/null +++ b/docs/_docs/layer-backing.md @@ -0,0 +1,29 @@ +--- +title: Layer Backing +layout: docs +permalink: /docs/layer-backing.html +prevPage: accessibility.html +nextPage: subtree-rasterization.html +--- + +In some cases, you can substantially improve your app's performance by using layers instead of views. **We recommend enabling layer-backing in any custom node that doesn't need touch handling**. + +With UIKit, manually converting view-based code to layers is laborious due to the difference in APIs. Worse, if at some point you need to enable touch handling or other view-specific functionality, you have to manually convert everything back (and risk regressions!). + +With all Texture nodes, converting an entire subtree from views to layers is as simple as: + +
+SwiftObjective-C +
+
+rootNode.layerBacked = YES;
+
+ +
+
+ +...and if you need to go back, it's as simple as deleting one line. + + diff --git a/docs/_docs/layout-api-debugging.md b/docs/_docs/layout-api-debugging.md new file mode 100755 index 0000000000..5b86012570 --- /dev/null +++ b/docs/_docs/layout-api-debugging.md @@ -0,0 +1,69 @@ +--- +title: Layout Debugging +layout: docs +permalink: /docs/layout-api-debugging.html +prevPage: automatic-layout-containers.html +nextPage: layout-api-sizing.html +--- + +Here are some helpful questions to ask yourself when you encounter any issues composing layoutSpecs. + +## Am I the child of an `ASStackLayoutSpec` or an `ASStaticLayoutSpec`? +
+Certain `ASLayoutable` properties will _only_ apply when the layoutable is a child of a _stack_ spec (the child is called an ASStackLayoutable), while other properties _only_ apply when the layoutable is a child of a _static_ spec (the child is called an ASStaticLayoutable). + +- table of [`ASStackLayoutables` properties](http://texturegroup.org/docs/automatic-layout-containers.html#asstacklayoutable-properties) +- table of [`ASStaticLayoutable` properties](http://texturegroup.org/docs/automatic-layout-containers.html#asstaticlayoutable-properties) + +All ASLayoutable properties can be applied to _any_ layoutable (e.g. any node or layout spec), however certain properties will only take effect depending on the type of the parent layout spec they are wrapped in. + +## Have I considered where to set `ASLayoutable` properties? +
+Let's say you have a node (`n1`) and you wrap it in a layout spec (`s1`). If you want to wrap the layout spec (`s1`) in a stack or static spec (`s2`), you will need to set all of the properties on the spec (`s1`) and not the node (`n1`). + +A common examples of this confusion involves flex grow and flex shrink. E.g. a node with `.flexGrow` enabled is wrapped in an inset spec. The inset spec will not grow as we expect. **Solution:** enable `.flexGrow` on the inset spec as well. + +## Have I provided sizes for any node that lacks an intrinsic size? +
+Texture's layout pass is recursive, starting at the layout spec returned from `-layoutSpecThatFits:` and proceeding down until it reaches the leaf nodes included in any nested layout specs. + +Some leaf nodes have a concept of their own intrinsic size, such as ASTextNode or ASImageNode. A text node knows the length of its formatted string and an ASImageNode knows the size of its static image. Other leaf nodes require an intrinsic size to be set. + +Nodes that require the developer to provide an intrinsic size: + +- `ASNetworkImageNode` or `ASMultiplexImageNode` have no intrinsic size until the image is downloaded. **A size must be provided for either node.** +- `ASVideoNode` or `ASVideoNodePlayer` have no intrinsic size until the video is downloaded. **A size must be provided for either node.** +- `ASDisplayNode` custom subclasses may provide their intrinisc size by implementing `-calculateSizeThatFits:`. + +To provide an intrinisc size for these nodes that lack intrinsic sizes (even if only momentarily), you can set one of the following: + +- set `.preferredFrameSize` on any node. +- set `.sizeRange` for children of **static** specs only. +- implement `-calculateSizeThatFits:` for **custom ASDisplayNode subclasses only**. + +*_Note that .preferredFrameSize is not considered by ASTextNodes. Also, setting .sizeRange on a node will override the node's intrinisic size provided by -calculateSizeThatFits:_ + +## When do I use `.preferredFrameSize` vs `.sizeRange`? +
+Set `.preferredFrameSize` to set a size for any node. Note that setting .preferredFrameSize on an `ASTextNode` will silently fail. We are working on fixing this, but in the meantime, you can wrap the ASTextNode in a static spec and provide it a .sizeRange. + +Set `.sizeRange` to set a size range for any node or layout spec that is the child of a *static* spec consisting. `.sizeRange` is the *only* way to set a size on a layout spec. Again, when using `.sizeRange`, you *must wrap the layoutable in a static layout spec for it to take effect.* + +A `.sizeRange` consists of a minimum and maximum constrained size (these sizes can be a specific point value or a relative value, like 70%). For details on the `.sizeRange` property's custom value type, check out our [Layout API Sizing guide](http://texturegroup.org/docs/layout-api-sizing.html). + +## `ASRelativeDimension` vs `ASRelativeSize` vs `ASRelativeSizeRange` vs `ASSizeRange` +
+Texture's Layout API supports configuring node and layout spec sizes with specific point values as well as relative values. Read the [Layout API Sizing guide](http://texturegroup.org/docs/layout-api-sizing.html) for a helpful chart and documentation on our custom layout value types. + +## Debugging layout specs with ASCII art +
+Calling `-asciiArtString` on any `ASDisplayNode` or `ASLayoutSpec` returns an ascii-art representation of the object and its children. An example of a simple layoutSpec ascii-art console output can be seen below. + +``` +-----------------------ASStackLayoutSpec---------------------- +| -----ASStackLayoutSpec----- -----ASStackLayoutSpec----- | +| | ASImageNode | | ASImageNode | | +| | ASImageNode | | ASImageNode | | +| --------------------------- --------------------------- | +-------------------------------------------------------------- + ``` diff --git a/docs/_docs/layout-api-sizing.md b/docs/_docs/layout-api-sizing.md new file mode 100755 index 0000000000..7937793c61 --- /dev/null +++ b/docs/_docs/layout-api-sizing.md @@ -0,0 +1,171 @@ +--- +title: Layout API Sizing +layout: docs +permalink: /docs/layout-api-sizing.html +prevPage: layout-api-debugging.html +nextPage: layout-transition-api.html +--- + +The easiest way to understand the compound dimension types in the Layout API is to see all the units in relation to one another. + + + +## Values (CGFloat, ASRelativeDimension) +
+`ASRelativeDimension` is essentially a normal **CGFloat with support for representing either a point value, or a % value**. It allows the same API to take in both fixed values, as well as relative ones. + +ASRelativeDimension is used to set the `flexBasis` property on a child of an `ASStackLayoutSpec`. The flexBasis property specifies the initial size in the stack dimension for this object, where the stack dimension is whether it is a horizontal or vertical stack. + +When a relative (%) value is used, it is resolved against the size of the parent. For example, an item with 50% flexBasis will ultimately have a point value set on it at the time that the stack achieves a concrete size. + +
+Note that .flexBasis can be set on any <ASLayoutable> (a node, or a layout spec), but will only take effect if that element is added as a child of a stack layout spec. This container-dependence of layoutable properties is a key area we’re working on clarifying. +
+ +#### Constructing ASRelativeDimensions +
+`ASDimension.h` contains 3 convenience functions to construct an `ASRelativeDimension`. It is easiest to use function that corresponds to the type (top 2 functions). + +
+SwiftObjective-C + +
+
+ASRelativeDimensionMakeWithPoints(CGFloat points);
+ASRelativeDimensionMakeWithPercent(CGFloat percent);
+ASRelativeDimensionMake(ASRelativeDimensionType type, CGFloat value);
+
+ +
+
+ +#### ASRelativeDimension Example +
+`PIPlaceSingleDetailNode` uses flexBasis to set 2 child nodes of a horizontal stack to share the width 40 / 60: + +
+SwiftObjective-C + +
+
+leftSideStack.flexBasis = ASRelativeDimensionMakeWithPercent(0.4f);
+self.detailLabel.flexBasis  = ASRelativeDimensionMakeWithPercent(0.6f);
+[horizontalStack setChildren:@[leftSideStack, self.detailLabel]];
+
+ +
+
+ + + +## Sizes (CGSize, ASRelativeSize) +
+`ASRelativeSize` is **similar to a CGSize, but its width and height may represent either a point or percent value.**  In fact, their unit type may even be different from one another. `ASRelativeSize` doesn't have a direct use in the Layout API, except to construct an `ASRelativeSizeRange`. + +- an `ASRelativeSize` consists of a `.width` and `.height` that are each `ASRelativeDimensions`. + +- the type of the width and height are independent; either one individually, or both, may be a point or percent value. (e.g. you could specify that an ASRelativeSize that has a height in points, but a variable % width) + +#### Constructing ASRelativeSizes +
+`ASRelativeSize.h` contains 2 convenience functions to construct an `ASRelativeSize`. **If you don't need to support relative (%) values, you can construct an `ASRelativeSize` with just a CGSize.** + +
+SwiftObjective-C + +
+
+ASRelativeSizeMake(ASRelativeDimension width, ASRelativeDimension height);
+ASRelativeSizeMakeWithCGSize(CGSize size);
+
+ +
+
+ +## Size Ranges (ASSizeRange, ASRelativeSizeRange) + +Because the layout spec system allows flexibility with elements growing and shrinking, we sometimes need to provide limits / boundaries to its flexibility. + +There are two size range types, but in essence, both contain a minimum and maximum size and that are used to influence the result of layout measurements. + +In the Pinterest code base, the **minimum size seems to be only necessary for stack specs in order to determine how much space to fill in between the children.** For example, with buttons in a nav bar, we don’t want them to stack as closely together as they can fit — rather a minimum width, as wide as the screen, is specified and causes the stack to add spacing to satisfy that constraint. + +**It’s much more common that the “max” constraint is what matters, though.** This is the case when text is wrapping or truncating - it’s encountering the maximum allowed width. Setting a minimum width for text doesn’t actually do anything—the text can’t be made longer—unless it’s in a stack, and spacing is added around it. + +#### ASSizeRange +
+UIKit doesn't provide a structure to bundle a minimum and maximum CGSize. So `ASSizeRange` was created to support **a minimum and maximum CGSize pair**. + +The `constrainedSize` that is passed as an input to `layoutSpecThatFits:` is an `ASSizeRange`. + +
+SwiftObjective-C + +
+
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize;
+
+ +
+
+ +#### ASRelativeSizeRange +
+`ASRelativeSizeRange` is essentially **a minimum and maximum size pair, that are used to constrain the size of a layout object.** The minimum and maximum sizes must **support both point and relative sizes**, which is where our friend the ASRelativeSize comes in. Hence, an ASRelativeSizeRange consists of a minimum and maximum `ASRelativeSize`. + +ASRelativeSizeRange is used to set the `sizeRange` property on a child of an `ASStaticLayoutSpec`. If specified, the child's size is restricted according to this size. + +
+Note that .sizeRange can be set on any <ASLayoutable> (a node, or a layout spec), but will only take effect if that element is added as a child of a static layout spec. This container-dependence of layoutable properties is a key area we’re working on clarifying. +
+ +#### ASSizeRange vs. ASRelativeSizeRange +
+Why do we pass a `ASSizeRange *constrainedSize` to a node's `layoutSpecThatFits:` function, but a `ASRelativeSizeRange` for the `.sizeRange` property on an element provided as a child of a layout spec? + + It’s pretty rare that you need the percent feature for a .sizeRange feature, but it’s there to make the API as flexible as possible. The input value of the constrainedSize that comes into the argument, has already been resolved by the parent’s size. It may have been influenced by a percent type, but has always be converted by that point into points. + +#### Constructing ASRelativeSizeRange +
+`ASRelativeSize.h` contains 4 convenience functions to construct an `ASRelativeSizeRange` from the various smaller units. + +- Percentage and point values can be combined. E.g. you could specify that an object is a certain height in points, but a variable percentage width. + +- If you only care to constrain the min / max or width / height, you can pass in `CGFLOAT_MIN`, `CGFLOAT_MAX`, `constrainedSize.max.width`, etc + +Most of the time, relative values are not needed for a size range _and_ the design requires an object to be forced to a particular size (min size = max size = no range). In this common case, you can use: + +
+SwiftObjective-C + +
+
+ASRelativeSizeRangeMakeWithExactCGSize(CGSize exact);
+
+ +
+
+ +### Sizing Conclusion +
+Here we have our original table, which has been annotated to show the uses of the various units in the Layout API. + + + +It’s worth noting that that there’s a certain flexibility to be able to use so many powerful options with a single API - flexBasis and sizeRange can be used to set points and percentages in different directions. However, since the majority of do not use the full set of options, we should adjust the API so that the powerful capabilities are a slightly more hidden. + diff --git a/docs/_docs/layout-engine.md b/docs/_docs/layout-engine.md new file mode 100755 index 0000000000..f73bed3225 --- /dev/null +++ b/docs/_docs/layout-engine.md @@ -0,0 +1,87 @@ +--- +title: Layout Engine +layout: docs +permalink: /docs/layout-engine.html +prevPage: subclassing.html +nextPage: containers-overview.html +--- + +Texture's layout engine is based on the CSS Box Model. While it is the feature of the framework that bears the weakest resemblance to the UIKit equivalent (AutoLayout), it is also among the most useful features once you've gotten used to it. With enough practice, you may just come to prefer creating declarative layouts to the constraint based approach. ;] + +The main way you participate in this system is by implementing `-layoutSpecThatFits:` in a node subclass. Here, you declaratively build up layout specs from the inside out, returning the final spec which will contain the rest. + +
+SwiftObjective-C +
+
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
+{
+  ASStackLayoutSpec *verticalStack = [ASStackLayoutSpec verticalStackLayoutSpec];
+  verticalStack.direction          = ASStackLayoutDirectionVertical;
+  verticalStack.spacing            = 4.0;
+  [verticalStack setChildren:_commentNodes];
+
+  return verticalStack;
+}
+  
+ + +
+
+ +Whle this example is extremely simple, it gives you an idea of how to use a layout spec. A stack layout spec, for instance, defines a layout of nodes in which the chlidren will be laid out adjacently, in the direction specified, with the spacing specified. It is very similar to `UIStackView` but with the added benefit of backwards compatibility. + +### ASLayoutable + +Layout spec's children can be any object whose class conforms to the `` protocol. All nodes, as well as all layout specs conform to the `` protocol. This means that your layout can be built up in composable chunks until you have what you want. + +Say you wanted to add 8 pts of padding to the stack you've already set up: + +
+SwiftObjective-C +
+ +
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
+{
+  ASStackLayoutSpec *verticalStack = [ASStackLayoutSpec verticalStackLayoutSpec];
+  verticalStack.direction          = ASStackLayoutDirectionVertical;
+  verticalStack.spacing            = 4.0;
+  [verticalStack setChildren:_commentNodes];
+  
+  UIEdgeInsets insets = UIEdgeInsetsMake(8, 8, 8, 8);
+  ASInsetLayoutSpec *insetSpec = [ASInsetLayoutSpec insetLayoutSpecWithInsets:insets 
+                                      child:verticalStack];
+  
+  return insetSpec;
+}
+  
+ + +
+
+ +You can easily do that by making that stack the child of an inset layout spec. + +Naturally, using layout specs takes a bit of practice so to learn more, check out the layout section. diff --git a/docs/_docs/layout-options.md b/docs/_docs/layout-options.md new file mode 100755 index 0000000000..709a7f8be3 --- /dev/null +++ b/docs/_docs/layout-options.md @@ -0,0 +1,52 @@ +--- +title: Layout Options +layout: docs +permalink: /docs/layout-options.html +prevPage: automatic-layout-debugging.html +nextPage: layer-backing.html +--- + +When using Texture, you have three options for layout. Note that UIKit Autolayout is **not** supported by Texture. +#Manual Sizing & Layout + +This original layout method shipped with Texture 1.0 and is analogous to UIKit's layout methods. Use this method for ASViewControllers (unless you subclass the node). + +`[ASDisplayNode calculateSizeThatFits:]` **vs.** `[UIView sizeThatFits:]` + +`[ASDisplayNode layout]` **vs.** `[UIView layoutSubviews]` + +###Advantages (over UIKit) +- Eliminates all main thread layout cost +- Results are cached + +###Shortcomings (same as UIKit): +- Code duplication between methods +- Logic is not reusable + +#Unified Sizing & Layout + +This layout method does not have a UIKit analog. It is implemented by calling + +`- (ASLayout *)calculateLayoutThatFits: (ASSizeRange)constraint` + +###Advantages +- zero duplication +- still async, still cached + +###Shortcomings +- logic is not reusable, and is still manual + +# Automatic, Extensible Layout + +This is the reccomended layout method. It does not have a UIKit analog and is implemented by calling + +`- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constraint` +###Advantages +- can reuse even complex, custom layouts +- built-in specs provide automatic layout +- combine to compose new layouts easily +- still async, cached, and zero duplication + +The diagram below shows how options #2 and #3 above both result in an ASLayout, except that in option #3, the ASLayout is produced automatically by the ASLayoutSpec. + + diff --git a/docs/_docs/layout-transition-api.md b/docs/_docs/layout-transition-api.md new file mode 100755 index 0000000000..e62eaadb3f --- /dev/null +++ b/docs/_docs/layout-transition-api.md @@ -0,0 +1,259 @@ +--- +title: Layout Transition API +layout: docs +permalink: /docs/layout-transition-api.html +prevPage: layout2-api-sizing.html +nextPage: hit-test-slop.html +--- + +The Layout Transition API was designed to make all animations with Texture easy - even transforming an entire set of views into a completely different set of views! + +With this system, you simply specify the desired layout and Texture will do the work to figure out differences from the current layout. It will automatically add new elements, remove unneeded elements after the transiton, and update the position of any existing elements. + +There are also easy to use APIs that allow you to fully customize the starting position of newly introduced elements, as well as the ending position of removed elements. + +
+Use of Automatic Subnode Management is required to use the Layout Transition API. +
+ +## Animating between Layouts +
+The layout Transition API makes it easy to animate between a node's generated layouts in response to some internal state change in a node. + +Imagine you wanted to implement this sign up form and animate in the new field when tapping the next button: + +![Imgur](http://i.imgur.com/Dsf1R72.gif) + +A standard way to implement this would be to create a container node called `SignupNode` that includes two editable text field nodes and a button node as subnodes. We'll include a property on the SignupNode called `fieldState` that will be used to select which editable text field node to show when the node calculates its layout. + +The internal layout spec of the `SignupNode` container would look something like this: + +
+ + Swift + Objective-C + +
+
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
+{
+  FieldNode *field;
+  if (self.fieldState == SignupNodeName) {
+    field = self.nameField;
+  } else {
+    field = self.ageField;
+  }
+
+  ASStackLayoutSpec *stack = [[ASStackLayoutSpec alloc] init];
+  [stack setChildren:@[field, self.buttonNode]];
+
+  UIEdgeInsets insets = UIEdgeInsetsMake(15.0, 15.0, 15.0, 15.0);
+  return [ASInsetLayoutSpec insetLayoutSpecWithInsets:insets child:stack];
+}
+
+ +
+
+ +To trigger a transition from the `nameField` to the `ageField` in this example, we'll update the SignupNode's `.fieldState` property and begin the transition with `transitionLayoutWithAnimation:`. + +This method will invalidate the current calculated layout and recompute a new layout with the `ageField` now in the stack. + +
+ + Swift + Objective-C + +
+
+self.signupNode.fieldState = SignupNodeAge;
+
+[self.signupNode transitionLayoutWithAnimation:YES];
+
+ +
+
+ +In the default implementation of this API, the layout will recalculate the new layout and use its sublayouts to size and position the SignupNode's subnodes without animation. Future versions of this API will likely include a default animation between layouts and we're open to feedback on what you'd like to see here. However, we'll need to implement a custom animation block to handle the signup form case. + +The example below represents an override of `animateLayoutTransition:` in the SignupNode. + +This method is called after the new layout has been calculated via `transitionLayoutWithAnimation:` and in the implementation we'll perform a specific animation based upon the fieldState property that was set before the animation was triggered. + +
+ + Swift + Objective-C + +
+
+- (void)animateLayoutTransition:(id<ASContextTransitioning>)context
+{
+  if (self.fieldState == SignupNodeName) {
+    CGRect initialNameFrame = [context initialFrameForNode:self.ageField];
+    initialNameFrame.origin.x += initialNameFrame.size.width;
+    self.nameField.frame = initialNameFrame;
+    self.nameField.alpha = 0.0;
+    CGRect finalAgeFrame = [context finalFrameForNode:self.nameField];
+    finalAgeFrame.origin.x -= finalAgeFrame.size.width;
+    [UIView animateWithDuration:0.4 animations:^{
+      self.nameField.frame = [context finalFrameForNode:self.nameField];
+      self.nameField.alpha = 1.0;
+      self.ageField.frame = finalAgeFrame;
+      self.ageField.alpha = 0.0;
+    } completion:^(BOOL finished) {
+      [context completeTransition:finished];
+    }];
+  } else {
+    CGRect initialAgeFrame = [context initialFrameForNode:self.nameField];
+    initialAgeFrame.origin.x += initialAgeFrame.size.width;
+    self.ageField.frame = initialAgeFrame;
+    self.ageField.alpha = 0.0;
+    CGRect finalNameFrame = [context finalFrameForNode:self.ageField];
+    finalNameFrame.origin.x -= finalNameFrame.size.width;
+    [UIView animateWithDuration:0.4 animations:^{
+      self.ageField.frame = [context finalFrameForNode:self.ageField];
+      self.ageField.alpha = 1.0;
+      self.nameField.frame = finalNameFrame;
+      self.nameField.alpha = 0.0;
+    } completion:^(BOOL finished) {
+      [context completeTransition:finished];
+    }];
+  }
+}
+
+ +
+
+ +The passed `ASContextTransitioning` context object in this method contains relevant information to help you determine the state of the nodes before and after the transition. It includes getters into old and new constrained sizes, inserted and removed nodes, and even the raw old and new `ASLayout` objects. In the `SignupNode` example, we're using it to determine the frame for each of the fields and animate them in an out of place. + +It is imperative to call `completeTransition:` on the context object once your animation has finished, as it will perform the necessary internal steps for the newly calculated layout to become the current `calculatedLayout`. + +Note that there hasn't been a use of `addSubnode:` or `removeFromSupernode` during the transition. Texture's layout transition API analyzes the differences in the node hierarchy between the old and new layout, implicitly performing node insertions and removals via Automatic Subnode Management. + +Nodes are inserted before your implementation of `animateLayoutTransition:` is called and this is a good place to manually manage the hierarchy before you begin the animation. Removals are performed in `didCompleteLayoutTransition:` after you call `completeTransition:` on the context object. If you need to manually perform deletions, override `didCompleteLayoutTransition:` and perform your custom operations. Note that this will override the default behavior and it is recommended to either call `super` or walk through the `removedSubnodes` getter in the context object to perform the cleanup. + +Passing `NO` to `transitionLayoutWithAnimation:` will still run through your `animateLayoutTransition:` and `didCompleteLayoutTransition:` implementations with the `[context isAnimated]` property set to `NO`. It is your choice on how to handle this case — if at all. An easy way to provide a default implementation this is to call super: + +
+ + Swift + Objective-C + +
+
+- (void)animateLayoutTransition:(id<ASContextTransitioning>)context
+{
+  if ([context isAnimated]) {
+    // perform animation
+  } else {
+    [super animateLayoutTransition:context];
+  }
+}
+
+ +
+
+ +## Animating constrainedSize Changes +
+There will be times you'll simply want to respond to bounds changes to your node and animate the recalculation of its layout. To handle this case, call `transitionLayoutWithSizeRange:animated:` on your node. + +This method is similar to `transitionLayoutWithAnimation:`, but will not trigger an animation if the passed `ASSizeRange` is equal to the current `constrainedSizeForCalculatedLayout` value. This is great for responding to rotation events and view controller size changes: + +
+ + Swift + Objective-C + +
+
+- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
+{
+  [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
+  [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext>  _Nonnull context) {
+    [self.node transitionLayoutWithSizeRange:ASSizeRangeMake(size, size) animated:YES];
+  } completion:nil];
+}
+
+ +
+
+ +## Examples that use the Layout Transition API + +- [ASDKLayoutTransition](https://github.com/texturegroup/texture/tree/master/examples/ASDKLayoutTransition) diff --git a/docs/_docs/layout2-api-sizing.md b/docs/_docs/layout2-api-sizing.md new file mode 100755 index 0000000000..1332dfe962 --- /dev/null +++ b/docs/_docs/layout2-api-sizing.md @@ -0,0 +1,191 @@ +--- +title: Layout API Sizing +layout: docs +permalink: /docs/layout2-api-sizing.html +nextPage: layout-transition-api.html +--- + +The easiest way to understand the compound dimension types in the Layout API is to see all the units in relation to one another. + + + +## Values (`CGFloat`, `ASDimension`) +
+`ASDimension` is essentially a **normal CGFloat with support for representing either a point value, a relative percentage value, or an auto value**. + +This unit allows the same API to take in both fixed values, as well as relative ones. + +
+ + Swift + Objective-C + +
+
+// dimension returned is relative (%)
+ASDimensionMake(@"50%");  
+ASDimensionMakeWithFraction(0.5);
+
+// dimension returned in points
+ASDimensionMake(@"70pt");
+ASDimensionMake(70);      
+ASDimensionMakeWithPoints(70);
+
+ + +### Example using `ASDimension` + +`ASDimension` is used to set the `flexBasis` property on a child of an `ASStackLayoutSpec`. The `flexBasis` property specifies an object's initial size in the stack dimension, where the stack dimension is whether it is a horizontal or vertical stack. + +In the following view, we want the left stack to occupy `40%` of the horizontal width and the right stack to occupy `60%` of the width. + + + +We do this by setting the `.flexBasis` property on the two childen of the horizontal stack: + +
+ + Swift + Objective-C + +
+
+self.leftStack.style.flexBasis = ASDimensionMake(@"40%");
+self.rightStack.style.flexBasis = ASDimensionMake(@"60%");
+
+[horizontalStack setChildren:@[self.leftStack, self.rightStack]];
+
+ + +## Sizes (`CGSize`, `ASLayoutSize`) + +`ASLayoutSize` is similar to a `CGSize`, but its **width and height values may represent either a point or percent value**. The type of the width and height are independent; either one may be a point or percent value. + +
+ + Swift + Objective-C + +
+
+ASLayoutSizeMake(ASDimension width, ASDimension height);
+
+ +
+
+ +
+`ASLayoutSize` is used for setting a layout element's `.preferredLayoutSize`, `.minLayoutSize` and `.maxLayoutSize` properties. It allows the same API to take in both fixed sizes, as well as relative ones. + +
+ + Swift + Objective-C + +
+
+// Dimension type "Auto" indicates that the layout element may 
+// be resolved in whatever way makes most sense given the circumstances
+ASDimension width = ASDimensionMake(ASDimensionUnitAuto, 0);  
+ASDimension height = ASDimensionMake(@"50%");
+
+layoutElement.style.preferredLayoutSize = ASLayoutSizeMake(width, height);
+
+ +
+
+ +
+If you do not need relative values, you can set the layout element's `.preferredSize`, `.minSize` and `.maxSize` properties. The properties take regular `CGSize` values. + +
+ + Swift + Objective-C + +
+
+layoutElement.style.preferredSize = CGSizeMake(30, 160);
+
+ +
+
+ +
+Most of the time, you won't want to constrain both width and height. In these cases, you can individually set a layout element's size properties using `ASDimension` values. + +
+ + Swift + Objective-C + +
+
+layoutElement.style.width     = ASDimensionMake(@"50%");
+layoutElement.style.minWidth  = ASDimensionMake(@"50%");
+layoutElement.style.maxWidth  = ASDimensionMake(@"50%");
+
+layoutElement.style.height    = ASDimensionMake(@"50%");
+layoutElement.style.minHeight = ASDimensionMake(@"50%");
+layoutElement.style.maxHeight = ASDimensionMake(@"50%");
+
+ +
+
+ +## Size Range (`ASSizeRange`) + +`UIKit` doesn't provide a structure to bundle a minimum and maximum `CGSize`. So, `ASSizeRange` was created to support **a minimum and maximum CGSize pair**. + +`ASSizeRange` is used mostly in the internals of the layout API. However, the `constrainedSize` value passed as an input to `layoutSpecThatFits:` is an `ASSizeRange`. + +
+ + Swift + Objective-C + +
+
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize;
+
+ +
+
+ +
+The `constrainedSize` passed to an `ASDisplayNode` subclass' `layoutSpecThatFits:` method is the minimum and maximum sizes that the node should fit in. The minimum and maximum `CGSize`s contained in `constrainedSize` can be used to size the node's layout elements. diff --git a/docs/_docs/layout2-conversion-guide.md b/docs/_docs/layout2-conversion-guide.md new file mode 100755 index 0000000000..6283cf1ec0 --- /dev/null +++ b/docs/_docs/layout2-conversion-guide.md @@ -0,0 +1,519 @@ +--- +title: Upgrading to Layout 2.0 (Beta) +layout: docs +permalink: /docs/layout2-conversion-guide.html +--- + +A list of the changes: + +- Introduction of true flex factors +- `ASStackLayoutSpec` `.alignItems` property default changed to `ASStackLayoutAlignItemsStretch` +- Rename `ASStaticLayoutSpec` to `ASAbsoluteLayoutSpec` +- Rename `ASLayoutable` to `ASLayoutElement` +- Set `ASLayoutElement` properties via `style` property +- Easier way to size of an `ASLayoutElement` +- Deprecation of `-[ASDisplayNode preferredFrameSize]` +- Deprecation of `-[ASLayoutElement measureWithSizeRange:]` +- Deprecation of `-[ASDisplayNode measure:]` +- Removal of `-[ASAbsoluteLayoutElement sizeRange]` +- Rename `ASRelativeDimension` to `ASDimension` +- Introduction of `ASDimensionUnitAuto` + +In addition to the inline examples comparing **1.x** layout code vs **2.0** layout code, the [example projects](https://github.com/texturegroup/texture/tree/master/examples) and layout documentation have been updated to use the new API. + +All other **2.0** changes not related to the Layout API are documented here. + +## Introduction of true flex factors + +With **1.x** the `flexGrow` and `flexShrink` properties were of type `BOOL`. + +With **2.0**, these properties are now type `CGFloat` with default values of `0.0`. + +This behavior is consistent with the Flexbox implementation for web. See [`flexGrow`](https://developer.mozilla.org/en-US/docs/Web/CSS/flex-grow) and [`flexShrink`](https://developer.mozilla.org/en-US/docs/Web/CSS/flex-shrink) for further information. + +
+SwiftObjective-C + +
+
+id<ASLayoutElement> layoutElement = ...;
+
+// 1.x:
+layoutElement.flexGrow = YES;
+layoutElement.flexShrink = YES;
+
+// 2.0:
+layoutElement.style.flexGrow = 1.0;
+layoutElement.style.flexShrink = 1.0;
+
+ + +
+
+ +## `ASStackLayoutSpec`'s `.alignItems` property default changed + +`ASStackLayoutSpec`'s `.alignItems` property default changed to `ASStackLayoutAlignItemsStretch` instead of `ASStackLayoutAlignItemsStart` to align with the CSS align-items property. + +## Rename `ASStaticLayoutSpec` to `ASAbsoluteLayoutSpec` & behavior change + +`ASStaticLayoutSpec` has been renamed to `ASAbsoluteLayoutSpec`, to be consistent with web terminology and better represent the intended behavior. + +
+SwiftObjective-C + +
+
+// 1.x:
+ASStaticLayoutSpec *layoutSpec = [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[...]];
+
+// 2.0:
+ASAbsoluteLayoutSpec *layoutSpec = [ASAbsoluteLayoutSpec absoluteLayoutSpecWithChildren:@[...]];
+
+ +
+
+ +
+**Please note** that there has also been a behavior change introduced. The following text overlay layout was previously created using a `ASStaticLayoutSpec`, `ASInsetLayoutSpec` and `ASOverlayLayoutSpec` as seen in the code below. + + + +
+Using `INFINITY` for the `top` value in the `UIEdgeInsets` property of the `ASInsetLayoutSpec` allowed the text inset to start at the bottom. This was possible because it would adopt the size of the static layout spec's `_photoNode`. + +
+ + Swift + Objective-C + +
+
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
+{
+  _photoNode.preferredFrameSize = CGSizeMake(USER_IMAGE_HEIGHT*2, USER_IMAGE_HEIGHT*2);
+  ASStaticLayoutSpec *backgroundImageStaticSpec = [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[_photoNode]];
+
+  UIEdgeInsets insets = UIEdgeInsetsMake(INFINITY, 12, 12, 12);
+  ASInsetLayoutSpec *textInsetSpec = [ASInsetLayoutSpec insetLayoutSpecWithInsets:insets child:_titleNode];
+
+  ASOverlayLayoutSpec *textOverlaySpec = [ASOverlayLayoutSpec overlayLayoutSpecWithChild:backgroundImageStaticSpec
+                                                                                 overlay:textInsetSpec];
+  
+  return textOverlaySpec;
+}
+  
+ +
+
+ +
+With the new `ASAbsoluteLayoutSpec` and same code above, the layout would now look like the picture below. The text is still there, but at ~900 pts (offscreen). + + + +## Rename `ASLayoutable` to `ASLayoutElement` + +Remember that an `ASLayoutSpec` contains children that conform to the `ASLayoutElement` protocol. Both `ASDisplayNodes` and `ASLayoutSpecs` conform to this protocol. + +The protocol has remained the same as **1.x**, but the name has been changed to be more descriptive. + +## Set `ASLayoutElement` properties via `ASLayoutElementStyle` + +An `ASLayoutElement`'s properties are are now set via it's `ASLayoutElementStyle` object. + +
+SwiftObjective-C + +
+
+id<ASLayoutElement> *layoutElement = ...;
+
+// 1.x:
+layoutElement.spacingBefore = 1.0;
+
+// 2.0:
+layoutElement.style.spacingBefore = 1.0;
+
+ +
+
+ +However, the properties specific to an `ASLayoutSpec` are still set directly on the layout spec. + +
+SwiftObjective-C + +
+
+// 1.x and 2.0
+ASStackLayoutSpec *stackLayoutSpec = ...;
+stackLayoutSpec.direction = ASStackLayoutDirectionVertical;
+stackLayoutSpec.justifyContent = ASStackLayoutJustifyContentStart;
+
+ +
+
+ +## Setting the size of an `ASLayoutElement` + +With **2.0** we introduce a new, easier, way to set the size of an `ASLayoutElement`. These methods replace the deprecated `-preferredFrameSize` and `-sizeRange` **1.x** methods. + +The following **optional** properties are provided via the layout element's `style` property: + +- `-[ASLayoutElementStyle width]`: specifies the width of an ASLayoutElement. The `minWidth` and `maxWidth` properties will override `width`. The height will be set to Auto unless provided. + +- `-[ASLayoutElementStyle minWidth]`: specifies the minimum width of an ASLayoutElement. This prevents the used value of the `width` property from becoming smaller than the specified for `minWidth`. + +- `-[ASLayoutElementStyle maxWidth]`: specifies the maximum width of an ASLayoutElement. It prevents the used value of the `width` property from becoming larger than the specified for `maxWidth`. + +- `-[ASLayoutElementStyle height]`: specifies the height of an ASLayoutElement. The `minHeight` and `maxHeight` properties will override `height`. The width will be set to Auto unless provided. + +- `-[ASLayoutElementStyle minHeight]`: specifies the minimum height of an ASLayoutElement. It prevents the used value of the `height` property from becoming smaller than the specified for `minHeight`. + +- `-[ASLayoutElementStyle maxHeight]`: specifies the maximum height of an ASLayoutElement. It prevents the used value of the `height` property from becoming larger than the specified for `maxHeight`. + +To set both the width and height with a `CGSize` value: + +- `-[ASLayoutElementStyle preferredSize]`: Provides a suggested size for a layout element. If the optional minSize or maxSize are provided, and the preferredSize exceeds these, the minSize or maxSize will be enforced. If this optional value is not provided, the layout element’s size will default to it’s intrinsic content size provided calculateSizeThatFits: + +- `-[ASLayoutElementStyle minSize]`: An optional property that provides a minimum size bound for a layout element. If provided, this restriction will always be enforced. If a parent layout element’s minimum size is smaller than its child’s minimum size, the child’s minimum size will be enforced and its size will extend out of the layout spec’s. + +- `-[ASLayoutElementStyle maxSize]`: An optional property that provides a maximum size bound for a layout element. If provided, this restriction will always be enforced. If a child layout element’s maximum size is smaller than its parent, the child’s maximum size will be enforced and its size will extend out of the layout spec’s. + +To set both the width and height with a relative (%) value (an `ASRelativeSize`): + +- `-[ASLayoutElementStyle preferredRelativeSize]`: Provides a suggested RELATIVE size for a layout element. An ASRelativeSize uses percentages rather than points to specify layout. E.g. width should be 50% of the parent’s width. If the optional minRelativeSize or maxRelativeSize are provided, and the preferredRelativeSize exceeds these, the minRelativeSize or maxRelativeSize will be enforced. If this optional value is not provided, the layout element’s size will default to its intrinsic content size provided calculateSizeThatFits: + +- `-[ASLayoutElementStyle minRelativeSize]`: An optional property that provides a minimum RELATIVE size bound for a layout element. If provided, this restriction will always be enforced. If a parent layout element’s minimum relative size is smaller than its child’s minimum relative size, the child’s minimum relative size will be enforced and its size will extend out of the layout spec’s. + +- `-[ASLayoutElementStyle maxRelativeSize]`: An optional property that provides a maximum RELATIVE size bound for a layout element. If provided, this restriction will always be enforced. If a parent layout element’s maximum relative size is smaller than its child’s maximum relative size, the child’s maximum relative size will be enforced and its size will extend out of the layout spec’s. + +For example, if you want to set a `width` of an `ASDisplayNode`: + +
+SwiftObjective-C + +
+
+// 1.x:
+// no good way to set an intrinsic size
+
+// 2.0:
+ASDisplayNode *ASDisplayNode = ...;
+
+// width 100 points, height: auto
+displayNode.style.width = ASDimensionMakeWithPoints(100);
+
+// width 50%, height: auto
+displayNode.style.width = ASDimensionMakeWithFraction(0.5);
+
+ASLayoutSpec *layoutSpec = ...;
+
+// width 100 points, height 100 points
+layoutSpec.style.preferredSize = CGSizeMake(100, 100);
+
+ +
+
+ +If you previously wrapped an `ASLayoutElement` with an `ASStaticLayoutSpec` just to give it a specific size (without setting the `layoutPosition` property on the element too), you don't have to do that anymore. + +
+SwiftObjective-C + +
+
+ASStackLayoutSpec *stackLayoutSpec = ...;
+id<ASLayoutElement> *layoutElement = ...;
+
+// 1.x:
+layoutElement.sizeRange = ASRelativeSizeRangeMakeWithExactCGSize(CGSizeMake(50, 50));
+ASStaticLayoutSpec *staticLayoutSpec = [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[layoutElement]];
+stackLayoutSpec.children = @[staticLayoutSpec];
+
+// 2.0:
+layoutElement.style.preferredSizeRange = ASRelativeSizeRangeMakeWithExactCGSize(CGSizeMake(50, 50));
+stackLayoutSpec.children = @[layoutElement];
+
+ +
+
+ +If you previously wrapped a `ASLayoutElement` within a `ASStaticLayoutSpec` just to return any layout spec from within `layoutSpecThatFits:` there is a new layout spec now that is called `ASWrapperLayoutSpec`. `ASWrapperLayoutSpec` is an `ASLayoutSpec` subclass that can wrap a `ASLayoutElement` and calculates the layout of the child based on the size given to the `ASLayoutElement`: + +
+SwiftObjective-C + +
+
+// 1.x - ASStaticLayoutSpec used as a "wrapper" to return subnode from layoutSpecThatFits: 
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
+{
+  return [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[subnode]];
+}
+
+// 2.0
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
+{
+  return [ASWrapperLayoutSpec wrapperWithLayoutElement:subnode];
+}
+
+// 1.x - ASStaticLayoutSpec used to set size (but not position) of subnode
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
+{
+  ASDisplayNode *subnode = ...;
+  subnode.preferredSize = ...;
+  return [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[subnode]];
+}
+
+// 2.0
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
+{
+  ASDisplayNode *subnode = ...;
+  subnode.style.preferredSize = CGSizeMake(constrainedSize.max.width, constrainedSize.max.height / 2.0);
+  return [ASWrapperLayoutSpec wrapperWithLayoutElement:subnode];
+}
+
+ +
+
+ +## Deprecation of `-[ASDisplayNode preferredFrameSize]` + +With the introduction of new sizing properties there is no need anymore for the `-[ASDisplayNode preferredFrameSize]` property. Therefore it is deprecated in **2.0**. Instead, use the size values on the `style` object of an `ASDisplayNode`: + +
+SwiftObjective-C + +
+
 
+ASDisplayNode *ASDisplayNode = ...;
+
+// 1.x:
+displayNode.preferredFrameSize = CGSize(100, 100);
+
+// 2.0
+displayNode.style.preferredSize = CGSize(100, 100);
+
+ +
+
+ +`-[ASDisplayNode preferredFrameSize]` was not supported properly and was often more confusing than helpful. The new sizing methods should be easier and more clear to implment. + +## Deprecation of `-[ASLayoutElement measureWithSizeRange:]` + +`-[ASLayoutElement measureWithSizeRange:]` is deprecated in **2.0**. + +#### Calling `measureWithSizeRange:` + +If you previously called `-[ASLayoutElement measureWithSizeRange:]` to receive an `ASLayout`, call `-[ASLayoutElement layoutThatFits:]` now instead. + +
+SwiftObjective-C + +
+
+// 1.x:
+ASLayout *layout = [layoutElement measureWithSizeRange:someSizeRange];
+
+// 2.0:
+ASLayout *layout = [layoutElement layoutThatFits:someSizeRange];
+
+ +
+
+ +#### Implementing `measureWithSizeRange:` + +If you are implementing a custom `class` that conforms to `ASLayoutElement` (e.g. creating a custom `ASLayoutSpec`) , replace `-measureWithSizeRange:` with `-calculateLayoutThatFits:` + +
+SwiftObjective-C + +
+
+// 1.x:
+- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize {}
+
+// 2.0:
+- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize {}
+
+ +
+
+ +`-calculateLayoutThatFits:` takes an `ASSizeRange` that specifies a min size and a max size of type `CGSize`. Choose any size in the given range, to calculate the children's size and position and return a `ASLayout` structure with the layout of child components. + +Besides `-calculateLayoutThatFits:` there are two additional methods on `ASLayoutElement` that you should know about if you are implementing classes that conform to `ASLayoutElement`: + +
+SwiftObjective-C + +
+
+- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
+                     restrictedToSize:(ASLayoutElementSize)size
+                 relativeToParentSize:(CGSize)parentSize;
+
+ +
+
+ +In certain advanced cases, you may want to override this method. Overriding this method allows you to receive the `layoutElement`'s size, parent size, and constrained size. With these values you could calculate the final constrained size and call `-calculateLayoutThatFits:` with the result. + +
+SwiftObjective-C + +
+
+- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize
+                  parentSize:(CGSize)parentSize;
+
+ +
+
+ +Call this on children`layoutElements` to compute their layouts within your implementation of `-calculateLayoutThatFits:`. + +For sample implementations of layout specs and the usage of the `calculateLayoutThatFits:` family of methods, check out the layout specs in Texture itself! + +## Deprecation of `-[ASDisplayNode measure:]` + +Use `-[ASDisplayNode layoutThatFits:]` instead to get an `ASLayout` and call `size` on the returned `ASLayout`: + +
+SwiftObjective-C + +
+
+// 1.x:
+CGSize size = [displayNode measure:CGSizeMake(100, 100)];
+
+// 2.0:
+// Creates an ASSizeRange with min and max sizes.
+ASLayout *layout = [displayNode layoutThatFits:ASSizeRangeMake(CGSizeZero, CGSizeMake(100, 100))];
+// Or an exact size
+// ASLayout *layout = [displayNode layoutThatFits:ASSizeRangeMake(CGSizeMake(100, 100))];
+CGSize size = layout.size;
+
+ +
+
+ +## Remove of `-[ASAbsoluteLayoutElement sizeRange]` + +The `sizeRange` property was removed from the `ASAbsoluteLayoutElement` protocol. Instead set the one of the following: + +- `-[ASLayoutElement width]` +- `-[ASLayoutElement height]` +- `-[ASLayoutElement minWidth]` +- `-[ASLayoutElement minHeight]` +- `-[ASLayoutElement maxWidth]` +- `-[ASLayoutElement maxHeight]` + +
+SwiftObjective-C + +
+
+id<ASLayoutElement> layoutElement = ...;
+
+// 1.x:
+layoutElement.sizeRange = ASRelativeSizeRangeMakeWithExactCGSize(CGSizeMake(50, 50));
+
+// 2.0:
+layoutElement.style.preferredSizeRange = ASRelativeSizeRangeMakeWithExactCGSize(CGSizeMake(50, 50));
+
+ +
+
+ +Due to the removal of `-[ASAbsoluteLayoutElement sizeRange]`, we also removed the `ASRelativeSizeRange`, as the type was no longer needed. + +## Rename `ASRelativeDimension` to `ASDimension` + +To simplify the naming and support the fact that dimensions are widely used in Texture now, `ASRelativeDimension` was renamed to `ASDimension`. Having a shorter name and handy functions to create it was an important goal for us. + +`ASRelativeDimensionTypePercent` and associated functions were renamed to use `Fraction` to be consistent with Apple terminology. + +
+SwiftObjective-C + +
+
+// 2.0:
+// Handy functions to create ASDimensions
+ASDimension dimensionInPoints;
+dimensionInPoints = ASDimensionMake(ASDimensionTypePoints, 5.0)
+dimensionInPoints = ASDimensionMake(5.0)
+dimensionInPoints = ASDimensionMakeWithPoints(5.0)
+dimensionInPoints = ASDimensionMake("5.0pt");
+
+ASDimension dimensionInFractions;
+dimensionInFractions = ASDimensionMake(ASDimensionTypeFraction, 0.5)
+dimensionInFractions = ASDimensionMakeWithFraction(0.5)
+dimensionInFractions = ASDimensionMake("50%");
+
+ +
+
+ +## Introduction of `ASDimensionUnitAuto` + +Previously `ASDimensionUnitPoints` and `ASDimensionUnitFraction` were the only two `ASDimensionUnit` enum values available. A new dimension type called `ASDimensionUnitAuto` now exists. All of the ``ASLayoutElementStyle` sizing properties are set to `ASDimensionAuto` by default. + +`ASDimensionUnitAuto` means more or less: *"I have no opinion" and may be resolved in whatever way makes most sense given the circumstances.* + +Most of the time this is the intrinsic content size of the `ASLayoutElement`. + +For example, if an `ASImageNode` has a `width` set to `ASDimensionUnitAuto`, the width of the linked image file will be used. For an `ASTextNode` the intrinsic content size will be calculated based on the text content. If an `ASLayoutElement` cannot provide any intrinsic content size like `ASVideoNode` for example the size needs to set explicitly. + +
+SwiftObjective-C + +
+
+// 2.0:
+// No specific size needs to be set as the imageNode's size 
+// will be calculated from the content (the image in this case)
+ASImageNode *imageNode = [ASImageNode new];
+imageNode.image = ...;
+
+// Specific size must be set for ASLayoutElement objects that
+// do not have an intrinsic content size (ASVideoNode does not
+// have a size until it's video downloads)
+ASVideoNode *videoNode = [ASVideoNode new];
+videoNode.style.preferredSize = CGSizeMake(200, 100);
+
+ +
+
+ diff --git a/docs/_docs/layout2-layout-element-properties.md b/docs/_docs/layout2-layout-element-properties.md new file mode 100755 index 0000000000..2efb71be6c --- /dev/null +++ b/docs/_docs/layout2-layout-element-properties.md @@ -0,0 +1,146 @@ +--- +title: Layout Element Properties +layout: docs +permalink: /docs/layout2-layout-element-properties.html +prevPage: layout2-layoutspec-types.html +nextPage: layout2-api-sizing.html +--- + +- ASStackLayoutElement Properties - will only take effect on a node or layout spec that is the child of a stack spec +- ASAbsoluteLayoutElement Properties - will only take effect on a node or layout spec that is the child of a absolute spec +- ASLayoutElement Properties - applies to all nodes & layout specs + +## ASStackLayoutElement Properties + +
+Please note that the following properties will only take effect if set on the child of an STACK layout spec. +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PropertyDescription
`CGFloat .style.spacingBefore`Additional space to place before this object in the stacking direction.
`CGFloat .style.spacingAfter`Additional space to place after this object in the stacking direction.
`BOOL .style.flexGrow`If the sum of childrens' stack dimensions is less than the minimum size, should this object grow?
`BOOL .style.flexShrink`If the sum of childrens' stack dimensions is greater than the maximum size, should this object shrink?
`ASDimension .style.flexBasis`Specifies the initial size for this object, in the stack dimension (horizontal or vertical), before the `flexGrow` / `flexShrink` properties are applied and the remaining space is distributed.
`ASStackLayoutAlignSelf .style.alignSelf`Orientation of the object along cross axis, overriding alignItems. Options include: +
    +
  • `ASStackLayoutAlignSelfAuto`
  • +
  • `ASStackLayoutAlignSelfStart`
  • +
  • `ASStackLayoutAlignSelfEnd`
  • +
  • `ASStackLayoutAlignSelfCenter`
  • +
  • `ASStackLayoutAlignSelfStretch`
  • +
`CGFloat .style.ascender`Used for baseline alignment. The distance from the top of the object to its baseline.
`CGFloat .style.descender`Used for baseline alignment. The distance from the baseline of the object to its bottom.
+ + +## ASAbsoluteLayoutElement Properties + +
+Please note that the following properties will only take effect if set on the child of an ABSOLUTE layout spec. +
+ + + + + + + + + + +
PropertyDescription
`CGPoint .style.layoutPosition`The `CGPoint` position of this object within its `ASAbsoluteLayoutSpec` parent spec.
+ +## ASLayoutElement Properties + +
+Please note that the following properties apply to ALL layout elements. +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PropertyDescription
`ASDimension .style.width`The `width` property specifies the width of the content area of an `ASLayoutElement`. The `minWidth` and `maxWidth` properties override `width`. Defaults to `ASDimensionAuto`.
`ASDimension .style.height`The `height` property specifies the height of the content area of an `ASLayoutElement`. The `minHeight` and `maxHeight` properties override `height`. Defaults to `ASDimensionAuto`.
`ASDimension .style.minWidth`The `minWidth` property is used to set the minimum width of a given element. It prevents the used value of the `width` property from becoming smaller than the value specified for `minWidth`. The value of `minWidth` overrides both `maxWidth` and `width`. Defaults to `ASDimensionAuto`.
`ASDimension .style.maxWidth`The `maxWidth` property is used to set the maximum width of a given element. It prevents the used value of the `width` property from becoming larger than the value specified for `maxWidth`. The value of `maxWidth` overrides `width`, but `minWidth` overrides `maxWidth`. Defaults to `ASDimensionAuto`.
`ASDimension .style.minHeight`The `minHeight` property is used to set the minimum height of a given element. It prevents the used value of the `height` property from becoming smaller than the value specified for `minHeight`. The value of `minHeight` overrides both `maxHeight` and `height`. Defaults to `ASDimensionAuto`.
`ASDimension .style.maxHeight`The `maxHeight` property is used to set the maximum height of a given element. It prevents the used value of the `height` property from becoming larger than the value specified for `maxHeight`. The value of `maxHeight` overrides `height`, but `minHeight` overrides `maxHeight`. Defaults to `ASDimensionAuto`
`CGSize .style.preferredSize`

Provides a suggested size for a layout element. If the optional minSize or maxSize are provided, and the preferredSize exceeds these, the minSize or maxSize will be enforced. If this optional value is not provided, the layout element’s size will default to it’s intrinsic content size provided calculateSizeThatFits:

+

This method is optional, but one of either preferredSize or preferredLayoutSize is required for nodes that either have no intrinsic content size or should be laid out at a different size than its intrinsic content size. For example, this property could be set on an ASImageNode to display at a size different from the underlying image size.

+

Warning: calling the getter when the size's width or height are relative will cause an assert.

`CGSize .style.minSize`

An optional property that provides a minimum size bound for a layout element. If provided, this restriction will always be enforced. If a parent layout element’s minimum size is smaller than its child’s minimum size, the child’s minimum size will be enforced and its size will extend out of the layout spec’s.

+

For example, if you set a preferred relative width of 50% and a minimum width of 200 points on an element in a full screen container, this would result in a width of 160 points on an iPhone screen. However, since 160 pts is lower than the minimum width of 200 pts, the minimum width would be used.

`CGSize .style.maxSize`

An optional property that provides a maximum size bound for a layout element. If provided, this restriction will always be enforced. If a child layout element’s maximum size is smaller than its parent, the child’s maximum size will be enforced and its size will extend out of the layout spec’s.

+

For example, if you set a preferred relative width of 50% and a maximum width of 120 points on an element in a full screen container, this would result in a width of 160 points on an iPhone screen. However, since 160 pts is higher than the maximum width of 120 pts, the maximum width would be used.

`ASLayoutSize .style.preferredLayoutSize`Provides a suggested RELATIVE size for a layout element. An ASLayoutSize uses percentages rather than points to specify layout. E.g. width should be 50% of the parent’s width. If the optional minLayoutSize or maxLayoutSize are provided, and the preferredLayoutSize exceeds these, the minLayoutSize or maxLayoutSize will be enforced. If this optional value is not provided, the layout element’s size will default to its intrinsic content size provided `calculateSizeThatFits:`
`ASLayoutSize .style.minLayoutSize`An optional property that provides a minimum RELATIVE size bound for a layout element. If provided, this restriction will always be enforced. If a parent layout element’s minimum relative size is smaller than its child’s minimum relative size, the child’s minimum relative size will be enforced and its size will extend out of the layout spec’s.
`ASLayoutSize .style.maxLayoutSize`An optional property that provides a maximum RELATIVE size bound for a layout element. If provided, this restriction will always be enforced. If a parent layout element’s maximum relative size is smaller than its child’s maximum relative size, the child’s maximum relative size will be enforced and its size will extend out of the layout spec’s.
diff --git a/docs/_docs/layout2-layoutSpecThatFits.md b/docs/_docs/layout2-layoutSpecThatFits.md new file mode 100755 index 0000000000..cd3e3097cc --- /dev/null +++ b/docs/_docs/layout2-layoutSpecThatFits.md @@ -0,0 +1,120 @@ +--- +title: Composing Layout Specs +layout: docs +permalink: /docs/layout2-layoutSpecThatFits.html +--- + +The composing of layout specs and layoutables are happening within the `layoutSpecThatFits:` method. This is where you will put the majority of your layout code. It defines the layout and does the heavy calculation on a background thread. + +Every `ASDisplayNode` that would like to layout it's subnodes should should do this by implementing the `layoutSpecThatFits:` method. This method is where you build out a layout spec object that will produce the size of the node, as well as the size and position of all subnodes. + +The following `layoutSpecThatFits:` implementation is from the Kittens example and will implement an easy stack layout with an image with a constrained size on the left and a text to the right. The great thing is, by using a `ASStackLayoutSpec` the height is dynamically calculated based on the image height and the height of the text. + +
+ + Swift + Objective-C + + +
+
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
+{
+  // Set an intrinsic size for the image node
+  CGSize imageSize = _isImageEnlarged ? CGSizeMake(2.0 * kImageSize, 2.0 * kImageSize)
+                                      : CGSizeMake(kImageSize, kImageSize);
+  [_imageNode setSizeFromCGSize:imageSize];
+
+  // Shrink the text node in case the image + text gonna be too wide
+  _textNode.flexShrink = YES;
+
+  // Configure stack
+  ASStackLayoutSpec *stackLayoutSpec =
+  [ASStackLayoutSpec
+   stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal
+   spacing:kInnerPadding
+   justifyContent:ASStackLayoutJustifyContentStart
+   alignItems:ASStackLayoutAlignItemsStart
+   children:_swappedTextAndImage ? @[_textNode, _imageNode] : @[_imageNode, _textNode]];
+
+  // Add inset
+  return [ASInsetLayoutSpec
+          insetLayoutSpecWithInsets:UIEdgeInsetsMake(kOuterPadding, kOuterPadding, kOuterPadding, kOuterPadding)
+          child:stackLayoutSpec];
+}
+  
+ + +
+
+ + +The result looks like the following: +![Kittens Node](https://d3vv6lp55qjaqc.cloudfront.net/items/2l133Y2B3r1F231a310q/Screen%20Shot%202016-08-23%20at%202.29.12%20PM.png) + +Let's look at some more advanced composition of layout spec and layoutable implementation from the `ASDKGram` example that should give you a feel how layout specs and layoutables can be combined to compose a difficult layout. You can also find this code in the `examples/ASDKGram` folder. + +
+ + Swift + Objective-C + + +
+
+  
+ + +
+
+ +After the layout pass happened the result will look like the following: +![ASDKGram](https://d3vv6lp55qjaqc.cloudfront.net/items/1l0t352p441K3k0C3y1l/layout-example-2.png) + +The layout spec object that you create in `layoutSpecThatFits:` is mutable up until the point that it is return in this method. After this point, it will be immutable. It's important to remember not to cache layout specs for use later but instead to recreate them when necessary. + +Note: Because it is run on a background thread, you should not set any node.view or node.layer properties here. Also, unless you know what you are doing, do not create any nodes in this method. Additionally, it is not necessary to begin this method with a call to super, unlike other method overrides. \ No newline at end of file diff --git a/docs/_docs/layout2-layoutspec-types-examples.md b/docs/_docs/layout2-layoutspec-types-examples.md new file mode 100755 index 0000000000..6b851879ff --- /dev/null +++ b/docs/_docs/layout2-layoutspec-types-examples.md @@ -0,0 +1,26 @@ +--- +title: Layout Spec Composition Examples +layout: docs +permalink: /docs/layout2-layoutspec-types-examples.html +--- + +## Text Overlaid on an Image + + +
+SwiftObjective-C + +
+
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
+{
+  ...
+  UIEdgeInsets *insets = UIEdgeInsetsMake(0, HORIZONTAL_BUFFER, 0, HORIZONTAL_BUFFER);
+  ASInsetLayoutSpec *headerWithInset = [ASInsetLayoutSpec alloc] initWithInsets:insets child:textNode];
+  ...
+}
+
+ +
+
\ No newline at end of file diff --git a/docs/_docs/layout2-layoutspec-types.md b/docs/_docs/layout2-layoutspec-types.md new file mode 100755 index 0000000000..e816ab3ca7 --- /dev/null +++ b/docs/_docs/layout2-layoutspec-types.md @@ -0,0 +1,474 @@ +--- +title: Layout Specs +layout: docs +permalink: /docs/layout2-layoutspec-types.html +prevPage: automatic-layout-examples-2.html +nextPage: layout2-layout-element-properties.html +--- + +The following `ASLayoutSpec` subclasses can be used to compose simple or very complex layouts. + + + +You may also subclass `ASLayoutSpec` in order to make your own, custom layout specs. + +## ASWrapperLayoutSpec + +`ASWrapperLayoutSpec` is a simple `ASLayoutSpec` subclass that can wrap a `ASLayoutElement` and calculate the layout of the child based on the size set on the layout element. + +`ASWrapperLayoutSpec` is ideal for easily returning a single subnode from `-layoutSpecThatFits:`. Optionally, this subnode can have sizing information set on it. However, if you need to set a position in addition to a size, use `ASAbsoluteLayoutSpec` instead. + +
+ + Swift + Objective-C + + +
+
+// return a single subnode from layoutSpecThatFits: 
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
+{
+  return [ASWrapperLayoutSpec wrapperWithLayoutElement:_subnode];
+}
+
+// set a size (but not position)
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
+{
+  _subnode.style.preferredSize = CGSizeMake(constrainedSize.max.width,
+                                            constrainedSize.max.height / 2.0);
+  return [ASWrapperLayoutSpec wrapperWithLayoutElement:subnode];
+}
+
+ + +
+
+ +## ASStackLayoutSpec (Flexbox Container) +Of all the layoutSpecs in Texture, `ASStackLayoutSpec` is the most useful and powerful. `ASStackLayoutSpec` uses the flexbox algorithm to determine the position and size of its children. Flexbox is designed to provide a consistent layout on different screen sizes. In a stack layout you align items in either a vertical or horizontal stack. A stack layout can be a child of another stack layout, which makes it possible to create almost any layout using a stack layout spec. + +`ASStackLayoutSpec` has 7 properties in addition to its `` properties: + +- `direction`. Specifies the direction children are stacked in. If horizontalAlignment and verticalAlignment were set, +they will be resolved again, causing justifyContent and alignItems to be updated accordingly. +- `spacing`. The amount of space between each child. +- `horizontalAlignment`. Specifies how children are aligned horizontally. Depends on the stack direction, setting the alignment causes either + justifyContent or alignItems to be updated. The alignment will remain valid after future direction changes. + Thus, it is preferred to those properties. +- `verticalAlignment`. Specifies how children are aligned vertically. Depends on the stack direction, setting the alignment causes either + justifyContent or alignItems to be updated. The alignment will remain valid after future direction changes. + Thus, it is preferred to those properties. +- `justifyContent`. The amount of space between each child. +- `alignItems`. Orientation of children along cross axis. +- `flexWrap`. Whether children are stacked into a single or multiple lines. Defaults to single line. +- `alignContent`. Orientation of lines along cross axis if there are multiple lines. + +
+ + Swift + Objective-C + + +
+
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
+{
+  ASStackLayoutSpec *mainStack = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal
+                       spacing:6.0
+                justifyContent:ASStackLayoutJustifyContentStart
+                    alignItems:ASStackLayoutAlignItemsCenter
+                      children:@[_iconNode, _countNode]];
+
+  // Set some constrained size to the stack
+  mainStack.style.minWidth = ASDimensionMakeWithPoints(60.0);
+  mainStack.style.maxHeight = ASDimensionMakeWithPoints(40.0);
+
+  return mainStack;
+}
+
+ + +
+
+ +Flexbox works the same way in Texture as it does in CSS on the web, with a few exceptions. For example, the defaults are different and there is no `flex` parameter. See Web Flexbox Differences for more information. + +
+ +## ASInsetLayoutSpec +During the layout pass, the `ASInsetLayoutSpec` passes its `constrainedSize.max` `CGSize` to its child, after subtracting its insets. Once the child determines it's final size, the inset spec passes its final size up as the size of its child plus its inset margin. Since the inset layout spec is sized based on the size of it's child, the child **must** have an instrinsic size or explicitly set its size. + + + +If you set `INFINITY` as a value in the `UIEdgeInsets`, the inset spec will just use the intrinisic size of the child. See an example of this. + +
+ + Swift + Objective-C + + +
+
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
+{
+  ...
+  UIEdgeInsets *insets = UIEdgeInsetsMake(10, 10, 10, 10);
+  ASInsetLayoutSpec *headerWithInset = [ASInsetLayoutSpec insetLayoutSpecWithInsets:insets child:textNode];
+  ...
+}
+
+ + +
+
+ +## ASOverlayLayoutSpec +`ASOverlayLayoutSpec` lays out its child (blue), stretching another component on top of it as an overlay (red). + + + +The overlay spec's size is calculated from the child's size. In the diagram below, the child is the blue layer. The child's size is then passed as the `constrainedSize` to the overlay layout element (red layer). Thus, it is important that the child (blue layer) **must** have an intrinsic size or a size set on it. + +
+When using Automatic Subnode Management with the ASOverlayLayoutSpec, the nodes may sometimes appear in the wrong order. This is a known issue that will be fixed soon. The current workaround is to add the nodes manually, with the overlay layout element (red) must added as a subnode to the parent node after the child layout element (blue). +
+ +
+ + Swift + Objective-C + + +
+
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
+{
+  ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor blueColor]);
+  ASDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor]);
+  return [ASOverlayLayoutSpec overlayLayoutSpecWithChild:backgroundNode overlay:foregroundNode];
+}
+
+ + +
+
+ +## ASBackgroundLayoutSpec +`ASBackgroundLayoutSpec` lays out a component (blue), stretching another component behind it as a backdrop (red). + + + +The background spec's size is calculated from the child's size. In the diagram below, the child is the blue layer. The child's size is then passed as the `constrainedSize` to the background layout element (red layer). Thus, it is important that the child (blue layer) **must** have an intrinsic size or a size set on it. + +
+When using Automatic Subnode Management with the ASOverlayLayoutSpec, the nodes may sometimes appear in the wrong order. This is a known issue that will be fixed soon. The current workaround is to add the nodes manually, with the child layout element (blue) must added as a subnode to the parent node after the child background element (red). +
+ +
+ + Swift + Objective-C + + +
+
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
+{
+  ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor]);
+  ASDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor blueColor]);
+
+  return [ASBackgroundLayoutSpec backgroundLayoutSpecWithChild:foregroundNode background:backgroundNode];
+}
+
+ + +
+
+ +Note: The order in which subnodes are added matters for this layout spec; the background object must be added as a subnode to the parent node before the foreground object. Using ASM does not currently guarantee this order! + +## ASCenterLayoutSpec +`ASCenterLayoutSpec` centers its child within its max `constrainedSize`. + + + +If the center spec's width or height is unconstrained, it shrinks to the size of the child. + +`ASCenterLayoutSpec` has two properties: + +- `centeringOptions`. Determines how the child is centered within the center spec. Options include: None, X, Y, XY. +- `sizingOptions`. Determines how much space the center spec will take up. Options include: Default, minimum X, minimum Y, minimum XY. + +
+ + Swift + Objective-C + + +
+
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
+{
+  ASStaticSizeDisplayNode *subnode = ASDisplayNodeWithBackgroundColor([UIColor greenColor], CGSizeMake(70, 100));
+  return [ASCenterLayoutSpec centerLayoutSpecWithCenteringOptions:ASCenterLayoutSpecCenteringXY
+                                                    sizingOptions:ASCenterLayoutSpecSizingOptionDefault
+                                                            child:subnode]
+}
+
+ + +
+
+ +## ASRatioLayoutSpec +`ASRatioLayoutSpec` lays out a component at a fixed aspect ratio which can scale. This spec **must** have a width or a height passed to it as a constrainedSize as it uses this value to scale itself. + + + +It is very common to use a ratio spec to provide an intrinsic size for `ASNetworkImageNode` or `ASVideoNode`, as both do not have an intrinsic size until the content returns from the server. + +
+ + Swift + Objective-C + + +
+
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
+{
+  // Half Ratio
+  ASStaticSizeDisplayNode *subnode = ASDisplayNodeWithBackgroundColor([UIColor greenColor], CGSizeMake(100, 100));
+  return [ASRatioLayoutSpec ratioLayoutSpecWithRatio:0.5 child:subnode];
+}
+
+ + +
+
+ +## ASRelativeLayoutSpec +Lays out a component and positions it within the layout bounds according to vertical and horizontal positional specifiers. Similar to the “9-part” image areas, a child can be positioned at any of the 4 corners, or the middle of any of the 4 edges, as well as the center. + +This is a very powerful class, but too complex to cover in this overview. For more information, look into `ASRelativeLayoutSpec`'s `-calculateLayoutThatFits:` method + properties. + +
+ + Swift + Objective-C + + +
+
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
+{
+  ...
+  ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor]);
+  ASStaticSizeDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor greenColor], CGSizeMake(70, 100));
+
+  ASRelativeLayoutSpec *relativeSpec = [ASRelativeLayoutSpec relativePositionLayoutSpecWithHorizontalPosition:ASRelativeLayoutSpecPositionStart
+                                  verticalPosition:ASRelativeLayoutSpecPositionStart
+                                      sizingOption:ASRelativeLayoutSpecSizingOptionDefault
+                                             child:foregroundNode]
+
+  ASBackgroundLayoutSpec *backgroundSpec = [ASBackgroundLayoutSpec backgroundLayoutSpecWithChild:relativeSpec background:backgroundNode];
+  ...
+}
+
+ + +
+
+ +## ASAbsoluteLayoutSpec +Within `ASAbsoluteLayoutSpec` you can specify exact locations (x/y coordinates) of its children by setting their `layoutPosition` property. Absolute layouts are less flexible and harder to maintain than other types of layouts. + +`ASAbsoluteLayoutSpec` has one property: + +- `sizing`. Determines how much space the absolute spec will take up. Options include: Default, and Size to Fit. *Note* that the Size to Fit option will replicate the behavior of the old `ASStaticLayoutSpec`. + +
+ + Swift + Objective-C + + +
+
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
+{
+  CGSize maxConstrainedSize = constrainedSize.max;
+
+  // Layout all nodes absolute in a static layout spec
+  guitarVideoNode.layoutPosition = CGPointMake(0, 0);
+  guitarVideoNode.size = ASSizeMakeFromCGSize(CGSizeMake(maxConstrainedSize.width, maxConstrainedSize.height / 3.0));
+
+  nicCageVideoNode.layoutPosition = CGPointMake(maxConstrainedSize.width / 2.0, maxConstrainedSize.height / 3.0);
+  nicCageVideoNode.size = ASSizeMakeFromCGSize(CGSizeMake(maxConstrainedSize.width / 2.0, maxConstrainedSize.height / 3.0));
+
+  simonVideoNode.layoutPosition = CGPointMake(0.0, maxConstrainedSize.height - (maxConstrainedSize.height / 3.0));
+  simonVideoNode.size = ASSizeMakeFromCGSize(CGSizeMake(maxConstrainedSize.width/2, maxConstrainedSize.height / 3.0));
+
+  hlsVideoNode.layoutPosition = CGPointMake(0.0, maxConstrainedSize.height / 3.0);
+  hlsVideoNode.size = ASSizeMakeFromCGSize(CGSizeMake(maxConstrainedSize.width / 2.0, maxConstrainedSize.height / 3.0));
+
+  return [ASAbsoluteLayoutSpec absoluteLayoutSpecWithChildren:@[guitarVideoNode, nicCageVideoNode, simonVideoNode, hlsVideoNode]];
+}
+
+ + +
+
+ +## ASLayoutSpec +`ASLayoutSpec` is the main class from that all layout spec's are subclassed. It's main job is to handle all the children management, but it also can be used to create custom layout specs. Only the super advanced should want / need to create a custom subclasses of `ASLayoutSpec` though. Instead try to use provided layout specs and compose them together to create more advanced layouts. + +Another use of `ASLayoutSpec` is to be used as a spacer in a `ASStackLayoutSpec` with other children, when `.flexGrow` and/or `.flexShrink` is applied. + +
+ + Swift + Objective-C + + +
+
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
+{
+  ...
+  // ASLayoutSpec as spacer
+  ASLayoutSpec *spacer = [[ASLayoutSpec alloc] init];
+  spacer.flexGrow = true;
+
+  stack.children = @[imageNode, spacer, textNode];
+  ...
+}
+
+ + +
+
diff --git a/docs/_docs/layout2-manual-layout.md b/docs/_docs/layout2-manual-layout.md new file mode 100755 index 0000000000..fef0ccf505 --- /dev/null +++ b/docs/_docs/layout2-manual-layout.md @@ -0,0 +1,174 @@ +--- +title: Manual Layout +layout: docs +permalink: /docs/layout2-manual-layout.html +--- + +## Manual Layout +After diving in to the automatic way for layout in Texture there is still the _old_ way to layout manually available. For the sake of completness here is a short description how to accomplish that within Texture. + +### Manual Layout UIKit + +Sizing and layout of custom view hierarchies are typically done all at once on the main thread. For example, a custom UIView that minimally encloses a text view and an image view might look like this: + +
+ + Swift + Objective-C + + +
+
+- (CGSize)sizeThatFits:(CGSize)size
+{
+  // size the image
+  CGSize imageSize = [_imageView sizeThatFits:size];
+
+  // size the text view
+  CGSize maxTextSize = CGSizeMake(size.width - imageSize.width, size.height);
+  CGSize textSize = [_textView sizeThatFits:maxTextSize];
+
+  // make sure everything fits
+  CGFloat minHeight = MAX(imageSize.height, textSize.height);
+  return CGSizeMake(size.width, minHeight);
+}
+
+- (void)layoutSubviews
+{
+  CGSize size = self.bounds.size; // convenience
+
+  // size and layout the image
+  CGSize imageSize = [_imageView sizeThatFits:size];
+  _imageView.frame = CGRectMake(size.width - imageSize.width, 0.0f,
+                                imageSize.width, imageSize.height);
+
+  // size and layout the text view
+  CGSize maxTextSize = CGSizeMake(size.width - imageSize.width, size.height);
+  CGSize textSize = [_textView sizeThatFits:maxTextSize];
+  _textView.frame = (CGRect){ CGPointZero, textSize };
+}
+  
+ + +
+
+ +This isn't ideal. We're sizing our subviews twice — once to figure out how big our view needs to be and once when laying it out — and while our layout arithmetic is cheap and quick, we're also blocking the main thread on expensive text sizing. + +We could improve the situation by manually cacheing our subviews' sizes, but that solution comes with its own set of problems. Just adding `_imageSize` and `_textSize` ivars wouldn't be enough: for example, if the text were to change, we'd need to recompute its size. The boilerplate would quickly become untenable. + +Further, even with a cache, we'll still be blocking the main thread on sizing *sometimes*. We could try to shift sizing to a background thread with `dispatch_async()`, but even if our own code is thread-safe, UIView methods are documented to [only work on the main thread](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIView_Class/index.html): + +> Manipulations to your application’s user interface must occur on the main +> thread. Thus, you should always call the methods of the UIView class from +> code running in the main thread of your application. The only time this may +> not be strictly necessary is when creating the view object itself but all +> other manipulations should occur on the main thread. + +This is a pretty deep rabbit hole. We could attempt to work around the fact that UILabels and UITextViews cannot safely be sized on background threads by manually creating a TextKit stack and sizing the text ourselves... but that's a laborious duplication of work. Further, if UITextView's layout behaviour changes in an iOS update, our sizing code will break. (And did we mention that TextKit isn't thread-safe either?) + +### Manual Layout Texture + +Manual layout within Texture are realized within two methods: + +#### `calculateSizeThatFits` and `layout` + +Within `calculateSizeThatFits:` you should provide a intrinsic content size for the node based on the given `constrainedSize`. This method is called on a background thread so perform expensive sizing operations within it. + +
+ + Swift + Objective-C + + +
+
+- [ASDisplayNode calculateSizeThatFits:]
+  
+ + +
+
+ +After measurement and layout pass happens further layout can be done in `layout`. This method is called on the main thread. In there, layout operations can be done for nodes that are not playing within the automatic layout system and are referenced within `layoutSpecThatFits:`. + +
+ + Swift + Objective-C + + +
+
+- [ASDisplayNode layout]
+  
+ + +
+
+ +#### Example +Our custom node looks like this: + +
+ + Swift + Objective-C + + +
+
+#import 
+
+...
+
+// perform expensive sizing operations on a background thread
+- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize
+{
+  // size the image
+  CGSize imageSize = [_imageNode layoutThatFits:ASSizeRangeMake(CGSizeZero, constrainedSize)].size;
+
+  // size the text node
+  CGSize maxTextSize = CGSizeMake(constrainedSize.width - imageSize.width,
+                                  constrainedSize.height);
+
+  CGSize textSize = [_textNode layoutThatFits:ASSizeRangeMake(CGSizeZero, maxTextSize)].size;
+
+  // make sure everything fits
+  CGFloat minHeight = MAX(imageSize.height, textSize.height);
+  return CGSizeMake(constrainedSize.width, minHeight);
+}
+
+// do as little work as possible in main-thread layout
+- (void)layout
+{
+  // layout the image using its cached size
+  CGSize imageSize = _imageNode.calculatedSize;
+  _imageNode.frame = CGRectMake(self.bounds.size.width - imageSize.width, 0.0f,
+                                imageSize.width, imageSize.height);
+
+  // layout the text view using its cached size
+  CGSize textSize = _textNode.calculatedSize;
+  _textNode.frame = (CGRect){ CGPointZero, textSize };
+}
+  
+ + +
+
+ +`ASImageNode` and `ASTextNode`, like the rest of Texture, are thread-safe, so we can size them on background threads. The `-layoutThatFits:` method is like `-sizeThatFits:`, but with side effects: it caches the (`calculatedSize`) for quick access later on — like in our now-snappy `-layout` implementation. + +As you can see, node hierarchies are sized and laid out in much the same way as their view counterparts. Manually layed out nodes do need to be written with a few things in mind: + +* Nodes must recursively measure all of their subnodes in their `-calculateSizeThatFits:` implementations. Note that the `-layoutThatFits:` machinery will only call `-calculateSizeThatFits:` if a new measurement pass is needed (e.g., if the constrained size has changed) and `layoutSpecThatFits:` is *not* implemented. + +* Nodes should perform any other expensive pre-layout calculations in `-calculateSizeThatFits:`, caching useful intermediate results in ivars as appropriate. + +* Nodes should call `[self invalidateCalculatedSize]` when necessary. For example, `ASTextNode` invalidates its calculated size when its `attributedString` property is changed. + +As already mentioned, automatic layout is preferred over manual layout and should be the way to go in most cases. \ No newline at end of file diff --git a/docs/_docs/layout2-quickstart.md b/docs/_docs/layout2-quickstart.md new file mode 100755 index 0000000000..798e0a2ec4 --- /dev/null +++ b/docs/_docs/layout2-quickstart.md @@ -0,0 +1,100 @@ +--- +title: Quickstart +layout: docs +permalink: /docs/layout2-quickstart.html +prevPage: multiplex-image-node.html +nextPage: automatic-layout-examples-2.html +--- + +## Motivation & Benefits + +The Layout API was created as a performant alternative to UIKit's Auto Layout, which becomes exponentially expensive for complicated view hierarchies. Texture's Layout API has many benefits over using UIKit's Auto Layout: + +- **Fast**: As fast as manual layout code and significantly faster than Auto Layout +- **Asynchronous & Concurrent:** Layouts can be computed on background threads so user interactions are not interrupted. +- **Declarative**: Layouts are declared with immutable data structures. This makes layout code easier to develop, document, code review, test, debug, profile, and maintain. +- **Cacheable**: Layout results are immutable data structures so they can be precomputed in the background and cached to increase user perceived performance. +- **Extensible**: Easy to share code between classes. + +## Inspired by CSS Flexbox + +Those who are familiar with Flexbox will notice many similarities in the two systems. However, Texture's Layout API does not re-implement all of CSS. + +## Basic Concepts + +Texture's layout system is centered around two basic concepts: + +1. Layout Specs +2. Layout Elements + + +### Layout Specs + +A layout spec, short for "layout specification", has no physical presence. Instead, layout specs act as containers for other layout elements by understanding how these children layout elements relate to each other. + +Texture provides several subclasses of `ASLayoutSpec`, from a simple layout specification that insets a single child, to a more complex layout specification that arranges multiple children in varying stack configurations. + +### Layout Elements + +Layout specs contain and arrange layout elements. + +All `ASDisplayNode`s and `ASLayoutSpec`s conform to the `` protocol. This means that you can compose layout specs from both nodes and other layout specs. Cool! + +The `ASLayoutElement` protocol has several properties that can be used to create very complex layouts. In addition, layout specs have their own set of properties that can be used to adjust the arrangment of the layout elements. + +### Combine Layout Specs & Layout Elements to Make Complex UI + +Here you can see how `ASTextNode`s (highlighted in yellow), an `ASVideoNode` (top image) and an `ASStackLayoutSpec` ("stack layout spec") can be combined to create a complex layout. + + + +The play button on top of the `ASVideoNode` (top image) is placed using an `ASCenterLayoutSpec` ("center layout spec") and an `ASOverlayLayoutSpec` ("overlay layout spec"). + + + +### Some nodes need Sizes Set + + + +Some elements have an "intrinsic size" based on their immediately available content. For example, ASTextNode can calculate its size based on its attributed string. Other nodes that have an intrinsic size include + +- `ASImageNode` +- `ASTextNode` +- `ASButtonNode` + +All other nodes either do not have an intrinsic size or lack an intrinsic size until their external resource is loaded. For example, an `ASNetworkImageNode` does not know its size until the image has been downloaded from the URL. These sorts of elements include + +- `ASVideoNode` +- `ASVideoPlayerNode` +- `ASNetworkImageNode` +- `ASEditableTextNode` + +These nodes that lack an initial intrinsic size must have an initial size set for them using an `ASRatioLayoutSpec`, an `ASAbsoluteLayoutSpec` or the size properties on the style object. + +### Layout Debugging + +Calling `-asciiArtString` on any `ASDisplayNode` or `ASLayoutSpec` returns an ascii-art representation of the object and its children. Optionally, if you set the `.debugName` on any node or layout spec, that will also be included in the ascii art. An example is seen below. + +
+
+
+-----------------------ASStackLayoutSpec----------------------
+|  -----ASStackLayoutSpec-----  -----ASStackLayoutSpec-----  |
+|  |       ASImageNode       |  |       ASImageNode       |  |
+|  |       ASImageNode       |  |       ASImageNode       |  |
+|  ---------------------------  ---------------------------  |
+--------------------------------------------------------------
+
+
+
+ +You can also print out the style object on any `ASLayoutElement` (node or layout spec). This is especially useful when debugging the sizing properties. + +
+
+
+(lldb) po _photoImageNode.style
+Layout Size = min {414pt, 414pt} <= preferred {20%, 50%} <= max {414pt, 414pt}
+
+
+
diff --git a/docs/_docs/layout2-web-flexbox-differences.md b/docs/_docs/layout2-web-flexbox-differences.md new file mode 100755 index 0000000000..73dac79fe7 --- /dev/null +++ b/docs/_docs/layout2-web-flexbox-differences.md @@ -0,0 +1,21 @@ +--- +title: Web Flexbox Differences +layout: docs +permalink: /docs/layout2-web-flexbox-differences.html +--- + +The goal of Texture's Layout API is *not* to re-implement all of CSS. It only targets a subset of CSS and Flexbox container, and there are no plans to implement support for tables, floats, or any other CSS concepts. The Texture Layout API also does not plan to support styling properties which do not affect layout such as color or background properties. + +The layout system tries to stay as close as possible to CSS. There are, however, certain cases where it differs from the web, these include: + +### Naming properties + +Certain properties have a different naming as on the web. For example `min-height` equivalent is the `minHeight` property. The full list of properties that control layout is documented in the Layout Properties section. + +### No margin / padding properties + +Layoutables don't have a padding or margin property. Instead wrapping a layoutable within an `ASInsetLayoutSpec` to apply padding or margin to the layoutable is the recommended way. See `ASInsetLayout` section for more information. + +### Missing features + +Certain features are not supported currently. See Layout Properties for the full list of properties that are supported. diff --git a/docs/_docs/map-node.md b/docs/_docs/map-node.md new file mode 100755 index 0000000000..6245306193 --- /dev/null +++ b/docs/_docs/map-node.md @@ -0,0 +1,146 @@ +--- +title: ASMapNode +layout: docs +permalink: /docs/map-node.html +prevPage: video-node.html +nextPage: control-node.html +--- + +`ASMapNode` allows you to easily specify a geographic region to show to your users. + +### Basic Usage + +Let's say you'd like to show a snapshot of San Francisco. All you need are the coordinates. + +
+SwiftObjective-C + +
+
+ASMapNode *mapNode = [[ASMapNode alloc] init];
+mapNode.style.preferredSize = CGSizeMake(300.0, 300.0);
+
+// San Francisco
+CLLocationCoordinate2D coord = CLLocationCoordinate2DMake(37.7749, -122.4194);
+
+// show 20,000 square meters
+mapNode.region = MKCoordinateRegionMakeWithDistance(coord, 20000, 20000);
+
+ + +
+
+ + + +The region value is actually just one piece of a property called `options` of type `MKMapSnapshotOptions`. + + +### MKMapSnapshotOptions + +A map node's main components can be defined directly through its `options` property. The snapshot options object contains the following: + +
    +
  • An MKMapCamera: used to configure altitude and pitch of the camera
  • +
  • An MKMapRect: basically a CGRect
  • +
  • An MKMapRegion: Controls the coordinate of focus, and the size around that focus to show
  • +
  • An MKMapType: Can be set to Standard, Satellite, etc.
  • +
+ +To do something like changing your map to a satellite map, you just need to create an options object and set its properties accordingly. + +
+SwiftObjective-C + +
+
+MKMapSnapshotOptions *options = [[MKMapSnapshotOptions alloc] init];
+options.mapType = MKMapTypeSatellite;
+options.region = MKCoordinateRegionMakeWithDistance(coord, 20000, 20000);
+
+mapNode.options = options;
+
+ +
+
+ +Results in: + + + +One thing to note is that setting the options value will overwrite a previously set region. + +### Annotations + +To set annotations, all you need to do is assign an array of annotations to your `ASMapNode`. + +Say you want to show a pin directly in the middle of your map of San Francisco. + +
+SwiftObjective-C + +
+
+MKPointAnnotation *annotation = [[MKPointAnnotation alloc] init];
+annotation.coordinate = CLLocationCoordinate2DMake(37.7749, -122.4194);
+
+mapNode.annotations = @[annotation];
+
+ +
+
+ + + +No problem. + +### Live Map Mode + +Chaning your map node from a static view of some region, into a fully interactable cartographic playground is as easy as: + +
+SwiftObjective-C + +
+
+mapNode.liveMap = YES;
+
+ +
+
+ +This enables "live map mode" in which the node will use an MKMapView to render an interactive version of your map. + + + +As with UIKit views, the `MKMapView` used in live map mode is not thread-safe. + +### MKMapView Delegate + +If live map mode has been enabled and you need to react to any events associated with the map node, you can set the `mapDelegate` property. This delegate should conform to the MKMapViewDelegate protocol. + + + + diff --git a/docs/_docs/multiplex-image-node.md b/docs/_docs/multiplex-image-node.md new file mode 100755 index 0000000000..d219becb80 --- /dev/null +++ b/docs/_docs/multiplex-image-node.md @@ -0,0 +1,109 @@ +--- +title: ASMultiplexImageNode +layout: docs +permalink: /docs/multiplex-image-node.html +prevPage: editable-text-node.html +--- + +Let's say your API is out of your control and the images in your app can't be progressive jpegs but you can retrieve a few different sizes of the image asset you want to display. This is where you would use an `ASMultiplexImageNode` instead of an ASNetworkImageNode. + +In the following example, you're using a multiplex image node in an `ASCellNode` subclass. After initialization, you typically need to do two things. First, make sure to set `downloadsIntermediateImages` to `YES` so that the lesser quality images will be downloaded. + +Then, assign an array of keys to the property `imageIdentifiers`. This list should be in descending order of image quality and will be used by the node to determine what URL to call for each image it will try to load. + +
+SwiftObjective-C + +
+
+- (instancetype)initWithURLs:(NSDictionary *)urls
+{
+    ...
+     _imageURLs = urls;          // something like @{@"thumb": "/smallImageUrl", @"medium": ...}
+
+    _multiplexImageNode = [[ASMultiplexImageNode alloc] initWithCache:nil 
+                                                           downloader:[ASBasicImageDownloader sharedImageDownloader]];
+    _multiplexImageNode.downloadsIntermediateImages = YES;
+    _multiplexImageNode.imageIdentifiers = @[ @"original", @"medium", @"thumb" ];
+
+    _multiplexImageNode.dataSource = self;
+    _multiplexImageNode.delegate   = self;
+    ...
+}
+    
+ + +
+
+ + +Then, if you've set up a simple dictionary that holds the keys you provided earlier pointing to URLs of the various versions of your image, you can simply return the URL for the given key in: + +
+SwiftObjective-C + +
+
+#pragma mark Multiplex Image Node Datasource
+
+- (NSURL *)multiplexImageNode:(ASMultiplexImageNode *)imageNode 
+        URLForImageIdentifier:(id)imageIdentifier
+{
+    return _imageURLs[imageIdentifier];
+}
+
+ + +
+
+ +There are also delegate methods provided to update you on things such as the progress of an image's download, when it has finished displaying etc. They're all optional so feel free to use them as necessary. + +For example, in the case that you want to react to the fact that a new image arrived, you can use the following delegate callback. + +
+SwiftObjective-C + +
+
+#pragma mark Multiplex Image Node Delegate
+
+- (void)multiplexImageNode:(ASMultiplexImageNode *)imageNode 
+            didUpdateImage:(UIImage *)image 
+            withIdentifier:(id)imageIdentifier 
+                 fromImage:(UIImage *)previousImage 
+            withIdentifier:(id)previousImageIdentifier;
+{    
+        // this is optional, in case you want to react to the fact that a new image came in
+}
+
+ + +
+
+ diff --git a/docs/_docs/network-image-node.md b/docs/_docs/network-image-node.md new file mode 100755 index 0000000000..2753afe3ed --- /dev/null +++ b/docs/_docs/network-image-node.md @@ -0,0 +1,119 @@ +--- +title: ASNetworkImageNode +layout: docs +permalink: /docs/network-image-node.html +prevPage: image-node.html +nextPage: video-node.html +--- + +`ASNetworkImageNode` can be used any time you need to display an image that is being hosted remotely. All you have to do is set the `.URL` property with the appropriate `NSURL` instance and the image will be asynchonously loaded and concurrently rendered for you. + +
+SwiftObjective-C + +
+
+ASNetworkImageNode *imageNode = [[ASNetworkImageNode alloc] init];
+imageNode.URL = [NSURL URLWithString:@"https://someurl.com/image_uri"];
+	
+ + +
+
+ +### Laying Out a Network Image Node + +Since an `ASNetworkImageNode` has no intrinsic content size when it is created, it is necessary for you to explicitly specify how they should be laid out. + +

Option 1: .style.preferredSize

+ +If you have a standard size you want the image node's frame size to be you can use the `.style.preferredSize` property. + +
+SwiftObjective-C + +
+
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constraint
+{
+	imageNode.style.preferredSize = CGSizeMake(100, 200);
+	...
+	return finalLayoutSpec;
+}
+
+ + +
+
+ +

Option 2: ASRatioLayoutSpec

+ +This is also a perfect place to use `ASRatioLayoutSpec`. Instead of assigning a static size for the image, you can assign a ratio and the image will maintain that ratio when it has finished loading and is displayed. + +
+SwiftObjective-C + +
+
+- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constraint
+{
+	CGFloat ratio = 3.0/1.0;
+	ASRatioLayoutSpec *imageRatioSpec = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:ratio child:self.imageNode];
+	...
+	return finalLayoutSpec;
+}
+
+ + +
+
+ +### Under the Hood + +
If you choose not to include the PINRemoteImage and PINCache dependencies you will lose progressive jpeg support and be required to include your own custom cache that conforms to ASImageCacheProtocol.
+ +#### Progressive JPEG Support + +Thanks to the inclusion of PINRemoteImage, network image nodes now offer full support for loading progressive JPEGs. This means that if your server provides them, your images will display quickly at a lower quality that will scale up as more data is loaded. + +To enable progressive loading, just set `shouldRenderProgressImages` to `YES` like so: + +
+SwiftObjective-C + +
+
+networkImageNode.shouldRenderProgressImages = YES;
+
+ + +
+
+ +It's important to remember that this is using one image that is progressively loaded. If your server is constrained to using regular JPEGs, but provides you with multiple versions of increasing quality, you should check out ASMultiplexImageNode instead. + +#### Automatic Caching + +`ASNetworkImageNode` now uses PINCache under the hood by default to cache network images automatically. + +#### GIF Support + +`ASNetworkImageNode` provides GIF support through `PINRemoteImage`'s beta `PINAnimatedImage`. Of note! This support will not work for local files unless `shouldCacheImage` is set to `NO`. diff --git a/docs/_docs/node-overview.md b/docs/_docs/node-overview.md new file mode 100755 index 0000000000..6f3b4ca1a7 --- /dev/null +++ b/docs/_docs/node-overview.md @@ -0,0 +1,81 @@ +--- +title: Node Subclasses +layout: docs +permalink: /docs/node-overview.html +prevPage: containers-overview.html +nextPage: subclassing.html +--- + +Texture offers the following nodes. + +A key advantage of using nodes over UIKit components is that **all nodes preform layout and display off of the main thread**, so that the main thread is available to immediately respond to user interaction events. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Texture NodeUIKit Equivalent
ASDisplayNodein place of UIKit's UIView
+ The root Texture node, from which all other nodes inherit.
ASCellNodein place of UIKit's UITableViewCell & UICollectionViewCell
+ ASCellNodes are used in ASTableNode, ASCollectionNode and ASPagerNode.
ASScrollNodein place of UIKit's UIScrollView +

This node is useful for creating a customized scrollable region that contains other nodes.

ASEditableTextNode
+ ASTextNode
in place of UIKit's UITextView
+ in place of UIKit's UILabel
ASImageNode
+ ASNetworkImageNode
+ ASMultiplexImageNode
in place of UIKit's UIImage
ASVideoNode
+ ASVideoPlayerNode
in place of UIKit's AVPlayerLayer
+ in place of UIKit's UIMoviePlayer
ASControlNodein place of UIKit's UIControl
ASButtonNodein place of UIKit's UIButton
ASMapNodein place of UIKit's MKMapView
+ +
+Despite having rough equivalencies to UIKit components, in general, Texture nodes offer more advanced features and conveniences. For example, an `ASNetworkImageNode` does automatic loading and cache management, and even supports progressive jpeg and animated gifs. + +The `AsyncDisplayKitOverview` example app gives basic implementations of each of the nodes listed above. + + +# Node Inheritance Hierarchy + +All Texture nodes inherit from `ASDisplayNode`. + +node inheritance flowchart + +The nodes highlighted in blue are synchronous wrappers of UIKit elements. For example, `ASScrollNode` wraps a `UIScrollView`, and `ASCollectionNode` wraps a `UICollectionView`. An `ASMapNode` in `liveMapMode` is a synchronous wrapper of `UIMapView`. + + + + diff --git a/docs/_docs/overlay-layout-spec.md b/docs/_docs/overlay-layout-spec.md new file mode 100755 index 0000000000..3ddbe8231c --- /dev/null +++ b/docs/_docs/overlay-layout-spec.md @@ -0,0 +1,7 @@ +--- +title: ASOverlayLayoutSpec +layout: docs +permalink: /docs/overlay-layout-spec.html +--- + +
😑 This page is coming soon...
\ No newline at end of file diff --git a/docs/_docs/philosophy.md b/docs/_docs/philosophy.md new file mode 100755 index 0000000000..48481180af --- /dev/null +++ b/docs/_docs/philosophy.md @@ -0,0 +1,47 @@ +--- +title: Philosophy +layout: docs +permalink: /docs/philosophy.html +prevPage: getting-started.html +nextPage: installation.html +--- + +#Asynchronous Performance Gains + +Texture is a UI framework that was originally born from Facebook’s Paper app. It came as an answer to one of the core questions the Paper team faced. **How can you keep the main thread as clear as possible?** + +Nowadays, many apps have a user experience that relies heavily upon continuous gestures and physics based animations. At the very least, your UI is probably dependent on some form of scroll view. These types of user interfaces depend entirely on the main thread and are extremely sensitive to main thread stalls. **A clogged main thread means dropped frames and an unpleasant user experience.** + +Texture Nodes are a thread-safe abstraction layer over UIViews and CALayers: + +logo + +You can access most view and layer properties when using nodes, the difference is that nodes are rendered concurrently by default, and measured and laid out asynchronously when used correctly! + +Too see asynchronous performance gains in action, check out the `examples/ASDKgram` app which compares a UIKit-implemented social media feed with an Texture-implemented social media feed! + +On an iPhone 6+, the performance may not be radically different, but on a 4S, the difference is dramatic! Which leads us to Texture's next priority... + +#A Great App Experience for All Users + +Texture's performance gains allow you to easily design a great experience for every app user - across all devices, on all network connections. + +##A Great Developer Experience + +Texture also strives to make the developer experience great +- platform compatability: iOS & tvOS +- language compatability: Objective-C & Swift +- requires fewer lines of code to build advanced apps (see `examples/ASDKgram` for a direct comparison of a UIKit implemention of an app vs. an equivalent Texture implementation) +- cleaner architecture patterns +- robust code (some really brilliant minds have worked on this for 3+ years). + +#Advanced Developer Tools + +As Texture has grown, some of the brightest iOS engineers have contributed advanced technologies that will save you, as a developer using Texture, development time. + +###Advanced Technology +- ASRunLoopQueue +- ASRangeController with Intelligent Preloading + +###Network Code Savings +- automatic batch fetching (e.g. JSON payloads) diff --git a/docs/_docs/placeholder-fade-duration.md b/docs/_docs/placeholder-fade-duration.md new file mode 100755 index 0000000000..0b5ca8b131 --- /dev/null +++ b/docs/_docs/placeholder-fade-duration.md @@ -0,0 +1,32 @@ +--- +title: Placeholders +layout: docs +permalink: /docs/placeholder-fade-duration.html +prevPage: image-modification-block.html +nextPage: accessibility.html +--- + +## ASDisplayNodes may Implement Placeholders + +Any `ASDisplayNode` subclass may implement the `-placeholderImage` method to provide a placeholder that covers content until a node's contents are finished displaying. To use placeholders, set `.placeholderEnabled = YES` and optionally set a `.placeholderFadeDuration`; + +For image drawing, use the node's `.calculatedSize` property. + +
+The `-placeholderImage` function may be called on a background thread, so it is important that this function is thread safe. Note that `-[UIImage imageNamed:]` is not thread safe when using image assets. Instead use `-[UIImage imageWithContentsOfFile:]`. +
+ + +An ideal resource for creating placeholder images, including rounded rect solid colored ones or simple square corner ones is the `UIImage+ASConvenience` category methods in Texture. + +See our ancient Placeholders sample app to see this concept, first invented by the Facebook Paper team, in action. + +## `.neverShowPlaceholders` + +Hear Scott Goodson explain placeholders, `.neverShowPlaceholders` and why UIKit doesn't have them. + +## ASNetworkImageNode also have Default Images + +In _addition_ to placeholders, `ASNetworkImageNode`s also have a `.defaultImage` property. While placeholders are meant to be transient, default images will persist if the image node's `.URL` property is `nil` or if the URL fails to load. + +We suggest using default images for avatars, while using placeholder images for photos. diff --git a/docs/_docs/principles.md b/docs/_docs/principles.md new file mode 100755 index 0000000000..aec7fb2dfd --- /dev/null +++ b/docs/_docs/principles.md @@ -0,0 +1,33 @@ +--- +title: Principles +layout: docs +permalink: /docs/principles.html +--- + +The following principles guide the design and development of the Texture framework. + +## 1. Reliable + +- **What:** Behavior should match the documentation. The framework shouldn't crash in production, even when used incorrectly. +- **Why:** If the framework is not reliable, then it cannot be used in production apps. More importantly, it will drain the morale of the engineers working on it. +- **How:** Meaningful, stable unit tests. We will devote a significant chunk of our resources to build unit tests. + +## 2. Familiar + +- **What:** Interfaces should match industry standards such as UIKit and CSS when possible. When we diverge from these standards, the interfaces should as be intuitive and direct as possible. +- **Why:** If the framework is not familiar, then companies will be wary about adopting it. Engineers trained in UIKit, especially junior ones, will be frustrated and unproductive. +- **How:** Compare API to other mature frameworks, reach out to users when developing new API to get feedback. Be generous with abstraction layers – as long as we don't sacrifice Reliable. + +## 3. Lean + +- **What:** Speed and memory conservation should be industry-leading, the API should be concise, and implementation code should be short and organized. +- **Why:** Performance is at the heart of Texture. It's what we do and we do it better than anyone else. In addition, a concise codebase and API are easier to maintain and learn. Plus it's just the right thing to do. +- **How:** Look for opportunities to improve performance. Think about the performance implications of each line of code. Dedicate resources to refactoring. Build tools to gather and expose performance metrics. + +## 4. Bold + +- **What:** Ambitious features, such as animated layout transitioning or our visibility-depth system, should be added from time to time. +- **Why:** Cutting-edge, never-before-seen tech gets people excited about the framework, and can raise the bar for the entire industry. They really move the needle on the user experience in subtle ways. Plus it's fun! +- **How:** Propose crazy ideas. See them through – ensure they get into the workflow and get resources allocated for them. + + \ No newline at end of file diff --git a/docs/_docs/relative-layout-spec.md b/docs/_docs/relative-layout-spec.md new file mode 100755 index 0000000000..a9e6a157b8 --- /dev/null +++ b/docs/_docs/relative-layout-spec.md @@ -0,0 +1,7 @@ +--- +title: ASRelativeLayoutSpec +layout: docs +permalink: /docs/relative-layout-spec.html +--- + +
😑 This page is coming soon...
\ No newline at end of file diff --git a/docs/_docs/resources.md b/docs/_docs/resources.md new file mode 100755 index 0000000000..62113da1e2 --- /dev/null +++ b/docs/_docs/resources.md @@ -0,0 +1,49 @@ +--- +title: Resources +layout: docs +permalink: /docs/resources.html +prevPage: getting-started.html +nextPage: installation.html +--- + +### Slack + +Join 700+ Texture developers and the Texture core team on Slack for real-time debugging, the latest updates, and asynchronous banter. Signup here. + +### Examples +Browse through our many example projects. + +If you are new to Texture, we recommend that you start with the ASDKgram example app which compares a photo feed implemented with UIKit to an identical feed implemented with Texture. The app features: +
    +
  1. An infinitely scrolling home feed that demonstrates Texture's smoother scrolling performance.
  2. +
  3. A significantly sized code base to demonstrate how much less code it takes to design apps using Texture.
  4. +
+ +### Videos + + +### Tutorials / Articles + + + +### Layout Resources +Texture's powerful layout system is based on the CSS FlexBox model. These sites are useful for learning the basics of this system. + diff --git a/docs/_docs/roadmap.md b/docs/_docs/roadmap.md new file mode 100755 index 0000000000..2ef0873623 --- /dev/null +++ b/docs/_docs/roadmap.md @@ -0,0 +1,49 @@ +--- +title: Roadmap +layout: docs +permalink: /docs/roadmap.html +--- + +This document outlines some of the upcoming plans for Texture. Since Texture is a fast-moving project with a small core team, this roadmap will change over time. + +The Texture roadmap is driven by the framework's four key qualities. You can read read more about the principles here. + +## 2.1 Release + +#### Familiar + +- Increase investment in Swift over time. +- Adopt a more regular release cadence. +- Reversible 0-100% transitions for our Layout Transition API. + +#### Bold + +- Declarative collection node API. [Try it out]() and give us feedback! + +## 2.5+ Release + +#### Reliable + +- Audit typography features. + +#### Familiar + +- Better supplementary node support. + +#### Lean + +- True asynchronous layout. + +#### Bold + +- First class transitions with the Layout Transition API. +- Extreme debuggability. +- AsyncKit? + +## Ways to Get Involved + +- Connect on GitHub, Slack and Twitter. +- Vet our documentation. Submit or suggest ways to improve. +- Share your experience using Texture. Thanks Buffer! +- Contribute layout examples. +- Contribute code. Try to implement one of our "Needs Volunteer" issues. diff --git a/docs/_docs/scroll-node.md b/docs/_docs/scroll-node.md new file mode 100755 index 0000000000..344faebc13 --- /dev/null +++ b/docs/_docs/scroll-node.md @@ -0,0 +1,81 @@ +--- +title: ASScrollNode +layout: docs +permalink: /docs/scroll-node.html +prevPage: control-node.html +nextPage: editable-text-node.html +--- + +`ASScrollNode` is an `ASDisplayNode` whose underlying view is an `UIScrollView`. This class offers the ability to automatically adopt its `ASLayoutSpec`'s size as the scrollable `contentSize`. + +### automaticallyManagesContentSize + +When enabled, the size calculated by the `ASScrolNode`'s layout spec defines the `.contentSize` of the scroll view. This is in contrast to most nodes, where the `layoutSpec` size is applied to the bounds (and in turn, frame). In this mode, the bounds of the scroll view always fills the parent's size. + +`automaticallyManagesContentSize` is useful both for subclasses of `ASScrollNode` implementing `layoutSpecThatFits:` or may also be used as the base class with `.layoutSpecBlock` set. In both cases, it is common use `.automaticallyManagesSubnodes` so that the nodes in the layout spec are added to the scrollable area automatically. + +With this approach there is no need to capture the layout size, use an absolute layout spec as a wrapper, or set `contentSize` anywhere in the code and it will update as the layout changes! Instead, it is very common and useful to simply return an `ASStackLayoutSpec` and the scrollable area will allow you to see all of it. + +### scrollableDirections + +This option is useful when using `automaticallyManagesContentSize`, especially if you want horizontal content (because the default is vertical). + +This property controls how the `constrainedSize` is interpreted when sizing the content. Options include: + + + + + + + + + + + + + + +
VerticalThe `constrainedSize` is interpreted as having unbounded `.height` (`CGFLOAT_MAX`), allowing stacks and other content in the layout spec to expand and result in scrollable content.
HorizontalThe `constrainedSize` is interpreted as having unbounded `.width` (`CGFLOAT_MAX`).
Vertical & HorizontalThe `constrainedSize` is interpreted as unbounded in both directions.
+ +### Example + +In case you're not familiar with scroll views, they are basically windows into content that would take up more space than can fit in that area. + +Say you have a giant image, but you only want to take up 200x200 pts on the screen. + +
+SwiftObjective-C + +
+
+// NOTE: If you are using a horizontal stack, set scrollNode.scrollableDirections.
+ASScrollNode *scrollNode = [[ASScrollNode alloc] init];
+scrollNode.automaticallyManagesSubnodes = YES;
+scrollNode.automaticallyManagesContentSize = YES;
+
+scrollNode.layoutSpecBlock = ^(ASDisplayNode *node, ASSizeRange constrainedSize){
+  ASStackLayoutSpec *stack = [ASStackLayoutSpec verticalStackLayoutSpec];
+  // Add children to the stack.
+  return stack;
+};
+
+
+ +
+
+ +As you can see, the `scrollNode`'s underlying view is a `ASScrollNode`. + diff --git a/docs/_docs/static-layout-spec.md b/docs/_docs/static-layout-spec.md new file mode 100755 index 0000000000..01318bf4ac --- /dev/null +++ b/docs/_docs/static-layout-spec.md @@ -0,0 +1,7 @@ +--- +title: ASStaticLayoutSpec +layout: docs +permalink: /docs/static-layout-spec.html +--- + +
😑 This page is coming soon...
\ No newline at end of file diff --git a/docs/_docs/subclassing.md b/docs/_docs/subclassing.md new file mode 100755 index 0000000000..e284f252c5 --- /dev/null +++ b/docs/_docs/subclassing.md @@ -0,0 +1,110 @@ +--- +title: Subclassing +layout: docs +permalink: /docs/subclassing.html +prevPage: containers-overview.html +nextPage: faq.html +--- +The most important distinction when creating a subclass is whether you writing an ASViewController or an ASDisplayNode. This sounds obvious, but because some of these differences are subtle, it is important to keep this top of mind. + +## ASDisplayNode +
+While subclassing nodes is similar to writing a UIView subclass, there are a few guidelines to follow to ensure that both that you're utilizing the framework to its full potential and that your nodes behave as expected. + +### `-init` + +This method is called on a **background thread** when using nodeBlocks. However, because no other method can run until -init is finished, it should never be necessary to have a lock in this method. + +The most important thing to remember is that your init method must be capable of being called on any queue. Most notably, this means you should never initialize any UIKit objects, touch the view or layer of a node (e.g. `node.layer.X` or `node.view.X`) or add any gesture recognizers in your initializer. Instead, do these things in `-didLoad`. + +### `-didLoad` + +This method is conceptually similar to UIViewController's `-viewDidLoad` method and is the point where the backing view has been loaded. It is guaranteed to be called on the **main thread** and is the appropriate place to do any UIKit things (such as adding gesture recognizers, touching the view / layer, initializing UIKIt objects). + +### `-layoutSpecThatFits:` + +This method defines the layout and does the heavy calculation on a **background thread**. This method is where you build out a layout spec object that will produce the size of the node, as well as the size and position of all subnodes. This is where you will put the majority of your layout code. + +The layout spec object that you create is malleable up until the point that it is return in this method. After this point, it will be immutable. It's important to remember not to cache layout specs for use later but instead to recreate them when necessary. + +Because it is run on a background thread, you should not set any `node.view` or `node.layer` properties here. Also, unless you know what you are doing, do not create any nodes in this method. Additionally, it is not neccessary to begin this method with a call to super, unlike other method overrides. + +### `-layout` + +The call to super in this method is where the results of the layoutSpec are applied; Right after the call to super in this method, the layout spec will have been calculated and all subnodes will have been measured and positioned. + +`-layout` is conceptually similar to UIViewController's `-viewWillLayoutSubviews`. This is a good spot to change the hidden property, set view based properties if needed (not layoutable properties) or set background colors. You could put background color setting in -layoutSpecThatFits:, but there may be timing problems. If you happen to be using any UIViews, you can set their frames here. However, you can always create a node wrapper with `-initWithViewBlock:` and then size this on the background thread elsewhere. + +This method is called on the **main thread**. However, if you are using layout Specs, you shouldn't rely on this method too much, as it is much preferable to do layout off the main thread. Less than 1 in 10 subclasses will need this. + +One great use of `-layout` is for the specific case in which you want a subnode to be your exact size. E.g. when you want a collectionNode to take up the full screen. This case is not supported well by layout specs and it is often easiest to set the frame manually with a single line in this method: + +``` +subnode.frame = self.bounds; +``` + +If you desire the same effect in a ASViewController, you can do the same thing in -viewWillLayoutSubviews, unless your node is the node in initWithNode: and in that case it will do this automatically. + +## ASViewController +
+An `ASViewController` is a regular `UIViewController` subclass that has special features to manage nodes. Since it is a UIViewController subclass, all methods are called on the **main thread** (and you should always create an ASViewController on the main thread). + +### `-init` + +This method is called once, at the very begining of an ASViewController's lifecycle. As with UIViewController initialization, it is best practice to **never access** `self.view` or `self.node.view` in this method as it will force the view to be created early. Instead, do any view access in -viewDidLoad. + +ASViewController's designated initializer is `initWithNode:`. A typical initializer will look something like the code below. Note how the ASViewController's node is created _before_ calling super. An ASViewController manages a node similarly to how a UIViewController manages a view, but the initialization is slightly different. + + +
+SwiftObjective-C + +
+
+- (instancetype)init
+{
+  _pagerNode = [[ASPagerNode alloc] init];
+  self = [super initWithNode:_pagerNode];
+  
+  // setup any instance variables or properties here
+  if (self) {
+    _pagerNode.dataSource = self;
+    _pagerNode.delegate = self;
+  }
+  
+  return self;
+}
+
+ +
+
+ +### `-loadView` + +We recommend that you do not use this method because it is has no particular advantages over `-viewDidLoad` and has some disadvantages. However, it is safe to use as long as you do not set the `self.view` property to a different value. The call to [super loadView] will set it to the `node.view` for you. + +### `-viewDidLoad` + +This method is called once in a ASViewController's lifecycle, immediately after `-loadView`. This is the earliest time at which you should access the node's view. It is a great spot to put any **setup code that should only be run once and requires access to the view/layer**, such as adding a gesture recognizer. + +Layout code should never be put in this method, because it will not be called again when geometry changes. Note this is equally true for UIViewController; it is bad practice to put layout code in this method even if you don't currently expect geometry changes. + +### `-viewWillLayoutSubviews` + +This method is called at the exact same time as a node's `-layout` method and it may be called multiple times in a ASViewController's lifecycle; it will be called whenever the bounds of the ASViewController's node are changed (including rotation, split screen, keyboard presentation) as well as when there are changes to the hierarchy (children being added, removed, or changed in size). + +For consistency, it is best practice to put all layout code in this method. Because it is not called very frequently, even code that does not directly depend on the size belongs here. + +### `-viewWillAppear:` / `-viewDidDisappear:` + +These methods are called just before the ASViewController's node appears on screen (the earliest time that it is visible) and just after it is removed from the view hierarchy (the earliest time that it is no longer visible). These methods provide a good opportunity to start or stop animations related to the presentation or dismissal of your controller. This is also a good place to make a log of a user action. + +Although these methods may be called multiple times and geometry information is available, they are not called for all geometry changes and so should not be used for core layout code (beyond setup required for specific animations). diff --git a/docs/_docs/subtree-rasterization.md b/docs/_docs/subtree-rasterization.md new file mode 100755 index 0000000000..8671a524ff --- /dev/null +++ b/docs/_docs/subtree-rasterization.md @@ -0,0 +1,26 @@ +--- +title: Subtree Rasterization +layout: docs +permalink: /docs/subtree-rasterization.html +prevPage: layer-backing.html +nextPage: synchronous-concurrency.html +--- + +Flattening an entire view hierarchy into a single layer improves performance, but with UIKit, comes with a hit to maintainability and hierarchy-based reasoning. + +With all Texture nodes, enabling precompositing is as simple as: + +
+SwiftObjective-C +
+
+rootNode.shouldRasterizeDescendants = YES;
+
+ +
+
+
+ +This line will cause the entire node hierarchy from that point on to be rendered into one layer. diff --git a/docs/_docs/synchronous-concurrency.md b/docs/_docs/synchronous-concurrency.md new file mode 100755 index 0000000000..68e9aa4575 --- /dev/null +++ b/docs/_docs/synchronous-concurrency.md @@ -0,0 +1,32 @@ +--- +title: Synchronous Concurrency +layout: docs +permalink: /docs/synchronous-concurrency.html +prevPage: subtree-rasterization.html +nextPage: corner-rounding.html +--- + +Both `ASViewController` and `ASCellNode` have a property called `neverShowPlaceholders`. + +By setting this property to YES, the main thread will be blocked until display has completed for the cell or view controller's view. + +Using this option does not eliminate all of the performance advantages of Texture. Normally, a given node has been preloading and is almost done when it reaches the screen, so the blocking time is very short. Even if the rangeTuningParameters are set to 0 this option outperforms UIKit. While the main thread is waiting, all subnode display executes concurrently, thus synchronous concurrency. + +See the NSSpain 2015 talk video for a visual walkthrough of this behavior. + +
+SwiftObjective-C +
+
+node.neverShowPlaceholders = YES;
+
+ +
+
+
+ +Usually, if a cell hasn't finished its display pass before it has reached the screen it will show placeholders until it has drawing its content. Setting this option to YES makes your scrolling node or ASViewController act more like UIKit, and in fact makes Texture scrolling visually indistinguishable from UIKit's, except that it's faster. + + diff --git a/docs/_docs/team.md b/docs/_docs/team.md new file mode 100755 index 0000000000..307b6a9047 --- /dev/null +++ b/docs/_docs/team.md @@ -0,0 +1,44 @@ +--- +title: Pinterest Team +layout: docs +permalink: /docs/team.html +--- + + + + + + + + + + + + + + + + + + + + + + +

Scott Goodson (@appleguy) is an original author of Texture and, most recently, a driving force behind making Pinterest's design vision a reality with the recent rewrite of the iOS app.

+

Previously, Scott managed the Facebook Paper and Instagram iOS engineering teams, and helped lead the native code rewrite of the core Facebook iOS app. He also spent four years at Apple where he was one of the first ten engineers to work on iPhone OS 1.0, and developed apps like Stocks and Calculator.

+

Scott is deeply passionate about building Texture into a framework that allows effortless development of polished and performant apps that serve all users, regardless of device age, internet connection, or language.

Michael Schneider (@maicki) is especially passionate about API design and recently led the re-architecture of the layout API for the 2.0 release. As our resident layout expert, Michael volunteers much of his own time to help developers on Texture's public slack channel. Previous, Michael worked on Pocket for iOS, Mac and Chrome and the Instapaper Mac app.

Huy Nguyen (@nguyenhuy ) joined the Pinterest team after authoring Texture's automatic layout feature, which has become the foundation for the Texture's 2.0 release. To date, the Layout API has been the largest contribution to the framework by a community member!

Garrett Moon (@garrettmoon ) is the fearless leader of Pinterest's framework team. He also authored PINRemoteImage - a threadsafe, performant, feature rich image fetcher, and PINCache, a non-deadlocking fork of TMCache. Both are used as the backing store for ASNetworkImageNode.

Adlai ("Ad-lee") Holler (@adlai-holler) joined the Pinterest team after making major contributions to the framework while writing Tripstr in Swift with Texture.

+
+ +# Join us! + +We are looking for senior developers familiar with Texture to join our team! + +We have an exciting roadmap that we believe will continue to push the boundaries of what is possible on the iOS platform, while making the framework easier to use than ever before. + +As part of the team, you would work on Texture, [PINRemoteImage](https://github.com/pinterest/PINRemoteImage), and [PINCache](https://github.com/pinterest/PINCache) (the backing store for ASNetworkImageNode), while using all three in Pinterest's [app](https://itunes.apple.com/us/app/pinterest/id429047995). + +One interesting thing to note is that Pinterest does not have an internal fork of Texture. Everything is developed on master, with release branches cut from master only a few weeks before our public application launches. This allows us to move exceptionally quickly in developing and launching improvements to millions of users. + +Sound interesting? +Send us an email at textureframework@gmail.com. diff --git a/docs/_docs/text-cell-node.md b/docs/_docs/text-cell-node.md new file mode 100755 index 0000000000..e9d0438b53 --- /dev/null +++ b/docs/_docs/text-cell-node.md @@ -0,0 +1,45 @@ +--- +title: ASTextCellNode +layout: docs +permalink: /docs/text-cell-node.html +prevPage: cell-node.html +nextPage: control-node.html +--- + +ASTextCellNode is a simple ASCellNode subclass you can use when all you need is a cell with styled text. + +
+SwiftObjective-C +
+
+ASTextCellNode *textCell = [[ASTextCellNode alloc]
+            initWithAttributes:@{NSFontAttributeName: [UIFont fontWithName:@"SomeFont" size:16.0]} 												  insets:UIEdgeInsetsMake(8, 16, 8, 16)];
+  
+ +
+
+ +The text can be configured on initialization or after the fact. + +
+SwiftObjective-C +
+
+ASTextCellNode *textCell = [[ASTextCellNode alloc] init];
+
+textCellNode.text         = @"Some dang ol' text";
+textCellNode.attributes   = @{NSFontAttributeName: [UIFont fontWithName:@"SomeFont" size:16.0]};
+textCellNode.insets       = UIEdgeInsetsMake(8, 16, 8, 16);
+  
+ +
+
\ No newline at end of file diff --git a/docs/_docs/text-node.md b/docs/_docs/text-node.md new file mode 100755 index 0000000000..3ab3481dac --- /dev/null +++ b/docs/_docs/text-node.md @@ -0,0 +1,226 @@ +--- +title: ASTextNode +layout: docs +permalink: /docs/text-node.html +prevPage: button-node.html +nextPage: image-node.html +--- + +`ASTextNode` is Texture's main text node and can be used any time you would normally use a `UILabel`. It includes full rich text support and is a subclass of `ASControlNode` meaning it can be used any time you would normally create a UIButton with just its titleLabel set. + +### Basic Usage +`ASTextNode`'s interface should be familiar to anyone who's used a `UILabel`. The first difference you may notice, is that text node's only use attributed strings instead of having the option of using a plain string. + +
+SwiftObjective-C + +
+
+NSDictionary *attrs = @{ NSFontAttributeName: [UIFont fontWithName:@"HelveticaNeue" size:12.0f] };
+NSAttributedString *string = [[NSAttributedString alloc] initWithString:@"Hey, here's some text." attributes:attrs];
+
+_node = [[ASTextNode alloc] init];
+_node.attributedText = string;
+
+ + +
+
+ +As you can see, to create a basic text node, all you need to do is use a standard alloc-init and then set up the attributed string for the text you wish to display. + +### Truncation + +In any case where you need your text node to fit into a space that is smaller than what would be necessary to display all the text it contains, as much as possible will be shown, and whatever is cut off will be replaced with a truncation string. + + +
+SwiftObjective-C + +
+
+_textNode = [[ASTextNode alloc] init];
+_textNode.attributedText = string;
+_textNode.truncationAttributedText = [[NSAttributedString alloc]
+												initWithString:@"¶¶¶"];
+
+ + +
+
+ +This results in something like: + + + +By default, the truncation string will be "…" so you don't need to set it if that's all you need. + + +### Link Attributes + +In order to designate chunks of your text as a link, you first need to set the `linkAttributes` array to an array of strings which will be used as keys of links in your attributed string. Then, when setting up the attributes of your string, you can use these keys to point to appropriate `NSURL`s. + +
+SwiftObjective-C + +
+
+_textNode.linkAttributeNames = @[ kLinkAttributeName ];
+
+NSString *blurb = @"kittens courtesy placekitten.com \U0001F638";
+NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString:blurb];
+[string addAttribute:NSFontAttributeName value:[UIFont fontWithName:@"HelveticaNeue-Light" size:16.0f] range:NSMakeRange(0, blurb.length)];
+[string addAttributes:@{
+                      kLinkAttributeName: [NSURL URLWithString:@"http://placekitten.com/"],
+                      NSForegroundColorAttributeName: [UIColor grayColor],
+                      NSUnderlineStyleAttributeName: @(NSUnderlineStyleSingle | NSUnderlinePatternDot),
+                      }
+              range:[blurb rangeOfString:@"placekitten.com"]];
+_textNode.attributedText = string;
+
+ + +
+
+ +Which results in a light gray link with a dash-dot style underline! + + + +As you can see, it's relatively convenient to apply various styles to each link given its range in the attributed string. + +### ASTextNodeDelegate + +Conforming to `ASTextNodeDelegate` allows your class to react to various events associated with a text node. For example, if you want to react to one of your links being tapped: + +
+SwiftObjective-C + +
+
+- (void)textNode:(ASTextNode *)richTextNode tappedLinkAttribute:(NSString *)attribute value:(NSURL *)URL atPoint:(CGPoint)point textRange:(NSRange)textRange
+{
+  // the link was tapped, open it
+  [[UIApplication sharedApplication] openURL:URL];
+}
+
+ + +
+
+ +In a similar way, you can react to long presses and highlighting with the following methods: + +`– textNode:longPressedLinkAttribute:value:atPoint:textRange:` + +`– textNode:shouldHighlightLinkAttribute:value:atPoint:` + +`– textNode:shouldLongPressLinkAttribute:value:atPoint:` + + +### Incorrect maximum number of lines with line spacing + +Using a `NSParagraphStyle` with a non-default `lineSpacing` can cause problems if multiline text with a maximum number of lines is wanted. For example see the following code: + +
+SwiftObjective-C + +
+
+// ...
+NSString *someLongString = @"...";
+
+NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
+paragraphStyle.lineSpacing = 10.0;
+
+UIFont *font = [UIFont fontWithName:@"SomeFontName" size:15];
+
+NSDictionary *attributes = @{
+	NSFontAttributeName : font,
+	NSParagraphStyleAttributeName: paragraphStyle
+};
+
+ASTextNode *textNode = [[ASTextNode alloc] init];
+textNode.maximumNumberOfLines = 4;
+textNode.attributedText = [[NSAttributedString	alloc] initWithString:someLongString
+																												   attributes:attributes];
+// ...
+
+ + +
+
+ +`ASTextNode` uses Text Kit internally to calculate the amount to shrink needed to result in the specified maximum number of lines. Unfortunately, in certain cases this will result in the text shrinking too much in the above example; Instead of 4 lines of text, 3 lines of text and a weird gap at the bottom will show up. To get around this issue for now, you have to set the `truncationMode` explicitly to `NSLineBreakByTruncatingTail` on the text node: + +
+SwiftObjective-C + +
+
+// ...
+ASTextNode *textNode = [[ASTextNode alloc] init];
+textNode.maximumNumberOfLines = 4;
+textNode.truncationMode = NSLineBreakByTruncatingTail;
+textNode.attributedText = [[NSAttributedString	alloc] initWithString:someLongString
+																												   attributes:attributes];
+// ...
+
+ + +
+
+``` diff --git a/docs/_docs/tip-1-nodeBlocks.md b/docs/_docs/tip-1-nodeBlocks.md new file mode 100755 index 0000000000..423cf8068e --- /dev/null +++ b/docs/_docs/tip-1-nodeBlocks.md @@ -0,0 +1,133 @@ +--- +title: Prefer `nodeBlocks` for Performance +layout: docs +permalink: /docs/tip-1-nodeBlocks.html +--- + +Texture’s `ASCollectionNode` replaces `UICollectionView`’s required method + +
+ + Swift + Objective-C + + +
+
+collectionNode:cellForItemAtIndexPath:
+  
+ + +
+
+ +
+with your choice of **one** of the two following methods + +
+ + Swift + Objective-C + + +
+
+// called on main thread, ASCellNode initialized on main and then returned 
+collectionNode:nodeForItemAtIndexPath: 
+
+OR
+
+// called on main thread, ASCellNodeBlock returned, then
+// ASCellNode initialized in background when block is called by system
+collectionNode:nodeBlockForItemAtIndexPath: 
+  
+ + +
+
+ +
+`ASTableNode` has the same options: + +
+ + Swift + Objective-C + + +
+
+`tableNode:nodeForRow:`
+`tableNode:nodeBlockforRow:`    // preferred
+  
+ + +
+
+ +`ASPagerNode` does as well: + +
+ + Swift + Objective-C + + +
+
+`pagerNode:nodeAtIndex:`
+`pagerNode:nodeBlockAtIndex:`   // preferred
+  
+ + +
+
+ + +We recommend that you use nodeBlocks. Using the nodeBlock method allows table and collections to request blocks for each cell node, and execute them **concurrently** across multiple threads, which allows us to **parallelize the allocation costs** (in addition to layout measurement). + +This leaves our main thread more free to handle touch events and other time sensitive work, keeping our user's taps happy and responsive. + +### Access your data source outside of the nodeBlock + +Because nodeBlocks are executed on a background thread, it is very important they be thread-safe. + +The most important aspect to consider is accessing properties on self that may change, such as an array of data models. This can be handled safely by ensuring that any immutable state is collected above the node block. + +**Using the indexPath parameter to access a mutable collection inside the node block is not safe.** This is because by the time the block runs, the dataSource may have changed. + +Here's an example of a simple nodeBlock: + +
+ + Swift + Objective-C + + +
+
+- (ASCellNodeBlock)collectionNode:(ASCollectionNode *)collectionNode nodeBlockForItemAtIndexPath:(NSIndexPath *)indexPath
+{
+    // data model is accessed outside of the node block
+    Board *board = [self.boards objectAtIndex:indexPath.item];
+    return ^{
+        BoardScrubberCellNode *node = [[BoardScrubberCellNode alloc] initWithBoard:board];
+        return node;
+    };
+}
+  
+ +
+
+ +
+Note that it is okay to use the indexPath if it is used strictly for its integer values and not to index a value from a mutable data source. + +## Do not return nil from a nodeBlock + +Just as when UIKit requests a cell, returning `nil` will crash the app, so it is important to ensure a valid ASCellNode is returned for either the node or nodeBlock method. Your code should ensure that at least a blank ASCellNode is returned, but ideally the number of items reported to the collection would prevent the method from being called when there is no data to display. diff --git a/docs/_docs/uicollectionview-challenges.md b/docs/_docs/uicollectionview-challenges.md new file mode 100755 index 0000000000..4916bf3e94 --- /dev/null +++ b/docs/_docs/uicollectionview-challenges.md @@ -0,0 +1,160 @@ +--- +title: UICollectionView Challenges +layout: docs +permalink: /docs/uicollectionview-challenges.html +--- + +`UICollectionView` is one of the most commonly used classes and many challenges with iOS development are related to its architecture. + +## How `UICollectionView` Works + +There are two important methods that `UICollectionView` requires. + +

Cell Measurement

+ +For each item in the data source, the collection must know its size to understand which items should be visible at a given momement. This is provided by: + +
+SwiftObjective-C +
+
+- (CGSize)collectionView:(UICollectionView *)collectionView 
+                  layout:(UICollectionViewLayout *)collectionViewLayout 
+  sizeForItemAtIndexPath:(NSIndexPath *)indexPath;
+
+ +
+
+ +Although not formally named by Apple, we refer to this process as "measuring". Implementing this method is always difficult, because the view that implements the cell layout is never available at the time of this method call. + +This means that logic must be duplicated between the implementation of this method and the `-layoutSubviews` implementation of the cell subclass. This presents a tremendous maintainence burden, as the implementations must always match their behavior for any combination of content displayed. + +Additionally, once measurement is complete, there's no easy way to cache that information to use it during the layout process. As a result, expensive text measurements must be repeated. + +

Cell Allocation

+ +Once an item reaches the screen, a view representing it is requested: + +
+SwiftObjective-C +
+
+- (UICollectionViewCell *)cellForItemAtIndexPath:(NSIndexPath *)indexPath;
+
+ +
+
+ +In order to provide a cell, all subviews must be configured with the data that they are intended to display. Immediately afterwards, the layout of the cell is calculated, and finally the display (rendering) of the individual elements (text, images) contained within. + +
+For those who are curious, this extremely detailed diagram shows the full process of UICollectionView communicating with its data source and delegate to display itself. +
+ +

Limitations in `UICollectionView`'s Architecture

+ +There are several issues with the architecture outlined above: + +Lots of main thread work, which may degrade the user's experience, including + +
    +
  • cell measurement
  • +
  • cell creation + setup / reuse
  • +
  • layout
  • +
  • display (rendering)
+ +Duplicated layout logic + +You must have duplicate copies of your cell sizing logic for the cell measurement and cell layout stages. For example, if you want to add a price tag to your cell, both -sizeForItemAtIndexPath and the cell's own -layoutSubviews must be aware of how to size the tag. + +No automatic content loading + +There is no easy, universal way to handle loading content such as: +
    +
  • data pages - such as JSON fetching
  • +
  • other info - such as images or secondary JSON requests
  • +
+ +## How `ASCollectionNode` works + +

Unified Cell Measurement & Allocation

+ +Texture takes both of the important collection methods explained above: + +
+SwiftObjective-C +
+
+- (UICollectionViewCell *)cellForItemAtIndexPath:(NSIndexPath *)indexPath;
+
+- (CGSize)collectionView:(UICollectionView *)collectionView 
+                  layout:(UICollectionViewLayout *)collectionViewLayout 
+  sizeForItemAtIndexPath:(NSIndexPath *)indexPath;
+
+ +
+
+ +and replaces them with a single method*: + +
+SwiftObjective-C +
+
+- (ASCellNode *)collectionNode:(ASCollectionNode *)collectionNode nodeForItemAtIndexPath:(NSIndexPath *)indexPath;
+
+ +
+
+ +or with the asynchronous versions + +
+SwiftObjective-C +
+
+- (ASCellNodeBlock)collectionNode:(ASCollectionNode *)collectionNode nodeBlockForItemAtIndexPath:(NSIndexPath *)indexPath;
+
+ +
+
+ +*Note that there is an optional method to provide a constrained size for the cell, but it is not needed by most apps. + +ASCellNode, is Texture's universal cell class. They are light-weight enough to be created an an earlier time in the program (concurrently in the background) and they understand how to calculate their own size. `ASCellNode` automatically caches its measurement so that it can be quickly applied during the layout pass. + +
+As a comparison to the diagram above, this detailed diagram shows the full process of an ASCollectionView communicating with its data source and delegate to display itself.. Note that ASCollectionView is ASCollectionNode's underlying UICollectionView subclass. +
+ +

Benefits of Texture's Architecture

+ +Elimination of all of the types of main thread work described above (cell allocation, measurement, layout, display)! In addition, all of this work is preformed concurrently on multiple threads. + +Because `ASCollectionNode` is aware of the position of all of its nodes, it can automatically determine when content loading is needed. The Batch Fetching API handles loading of data pages (like JSON) and Intelligent Preloading automatically manages the loading of images and text. Additionally, convenient callbacks allow implementing accurate visibility logging and secondary data model requests. + +Lastly, almost all of the concepts we've discussed here apply to `UITableView` / `ASTableNode` and `UIPageViewController` / `ASPagerNode`. + +## iOS 10 Cell Pre-fetching +Inspired by Texture, iOS 10 introduced a cell pre-fetching. This API increases the number of cells that the collection tracks at any given time, which helps, but isn't anywhere as performance centric as being aware of all cells in the data source. + +Additionally, iOS9 still constitutes a substantial precentage of most app's userbase and will not reduce in number anywhere close to as quickly as the sunset trajectory of iOS 7 and iOS 8 devices. Whereas iOS 9 is the last supported version for about a half-dozen devices, there were zero devices that were deprecated on iOS 8 and only one deivce deprecated on iOS 7. + +Unfortunately, these iOS 9 devices are the ones in which performance is most key! diff --git a/docs/_docs/uicollectionviewinterop.md b/docs/_docs/uicollectionviewinterop.md new file mode 100755 index 0000000000..26fc1942e7 --- /dev/null +++ b/docs/_docs/uicollectionviewinterop.md @@ -0,0 +1,81 @@ +--- +title: UICollectionViewCell Interoperability +layout: docs +permalink: /docs/uicollectionviewinterop.html +prevPage: placeholder-fade-duration.html +nextPage: accessibility.html +--- + +Texture's `ASCollectionNode` offers compatibility with synchronous, standard `UICollectionViewCell` objects alongside native `ASCellNodes`. + +Note that these UIKit cells will **not** have the performance benefits of `ASCellNodes` (like preloading, async layout, and async drawing), even when mixed within the same `ASCollectionNode`. + +However, this interoperability allows developers the flexibility to test out the framework without needing to convert all of their cells at once. + +## Implementing Interoperability + +In order to use this feature, you must: + +
    +
  1. Conform to ASCollectionDataSourceInterop and, optionally, ASCollectionDelegateInterop.
  2. +
  3. Call registerCellClass: on the collectionNode.view (in viewDidLoad, or register an onDidLoad: block).
  4. +
  5. Return nil from the nodeBlockForItem...: or nodeForItem...: method. Note: it is an error to return nil from within a nodeBlock, if you have returned a nodeBlock object.
  6. +
  7. Lastly, you must implement a method to provide the size for the cell. There are two ways this is done:
  8. +
      +
    1. UICollectionViewFlowLayout (incl. ASPagerNode). Implement + collectionNode:constrainedSizeForItemAtIndexPath:.
    2. +
    3. Custom collection layouts. Set .view.layoutInspector and have it implement + collectionView:constrainedSizeForNodeAtIndexPath:.
    4. +
    +
+ +By default, the interop data source will only be consulted in cases where no `ASCellNode` is provided to Texture. However, if .dequeuesCellsForNodeBackedItems is enabled, then the interop data source will always be consulted to dequeue cells, and will be expected to return _ASCollectionViewCells in cases where a node was provided. + +## CustomCollectionView Example App + +The [CustomCollectionView](https://github.com/texturegroup/texture/tree/master/examples/CustomCollectionView) example project demonstrates how to use raw `UIKit` cells alongside native `ASCellNodes`. + +Open the app and verify that `kShowUICollectionViewCells` is enabled in `Sample/ViewController.m`. + +For this example, the data source method `collectionNode:nodeBlockForItemAtIndexPath:` is setup to return nil for every third cell. When nil is returned, `ASCollectionNode` will automatically query the `cellForItemAtIndexPath:` data source method. + +
+ + Swift + Objective-C + + +
+
+- (ASCellNodeBlock)collectionNode:(ASCollectionNode *)collectionNode 
+      nodeBlockForItemAtIndexPath:(NSIndexPath *)indexPath
+{
+  if (kShowUICollectionViewCells && indexPath.item % 3 == 1) {
+    // When enabled, return nil for every third cell and then 
+    // cellForItemAtIndexPath: will be called.
+    return nil;
+  }
+  
+  UIImage *image = _sections[indexPath.section][indexPath.item];
+  return ^{
+    return [[ImageCellNode alloc] initWithImage:image];
+  };
+}
+
+- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView 
+                  cellForItemAtIndexPath:(NSIndexPath *)indexPath
+{
+  return [_collectionNode.view dequeueReusableCellWithReuseIdentifier:kReuseIdentifier 
+                                                         forIndexPath:indexPath];
+}
+  
+ + +
+
+ +Run the app to see the orange `UICollectionViewCells` interspersed every 3rd cell among the `ASCellNodes` containing images. + diff --git a/docs/_docs/video-node.md b/docs/_docs/video-node.md new file mode 100755 index 0000000000..6a81074a85 --- /dev/null +++ b/docs/_docs/video-node.md @@ -0,0 +1,87 @@ +--- +title: ASVideoNode +layout: docs +permalink: /docs/video-node.html +prevPage: network-image-node.html +nextPage: map-node.html +--- + +`ASVideoNode` provides a convenient and performant way to display videos in your app. + +
Note: If you use `ASVideoNode` in your application, you must link `AVFoundation` since it uses `AVPlayerLayer` and other `AVFoundation` classes under the hood.
+ +### Basic Usage + +The easiest way to use `ASVideoNode` is to assign it an `AVAsset`. + +
+SwiftObjective-C + +
+
+ASVideoNode *videoNode = [[ASVideoNode alloc] init];
+
+AVAsset *asset = [AVAsset assetWithURL:[NSURL URLWithString:@"http://www.w3schools.com/html/mov_bbb.mp4"]];
+videoNode.asset = asset;
+
+ + +
+
+ +### Autoplay, Autorepeat, and Muting + +You can configure the way your video node reacts to various events with a few simple `BOOL`s. + +If you'd like your video to automaticaly play when it enters the visible range, set the `shouldAutoplay` property to `YES`. Setting `shouldAutoRepeat` to `YES` will cause the video to loop indefinitely, and, of course, setting `muted` to `YES` will turn the video's sound off. + +To set up a node that automatically plays once silently, you would just do the following. + +
+SwiftObjective-C + +
+
+videoNode.shouldAutoplay = YES;
+videoNode.shouldAutorepeat = NO;
+videoNode.muted = YES;
+
+ +
+
+ +### Placeholder Image + +Since video nodes inherit from `ASNetworkImageNode`, you can use the `URL` property to assign a placeholder image. If you decide not to, the first frame of your video will automatically decoded and used as the placeholder instead. + + + + +### ASVideoNode Delegate + +There are a ton of delegate methods available to you that allow you to react to what's happening with your video. For example, if you want to react to the player's state changing, you can use: + +
+SwiftObjective-C + +
+
+- (void)videoNode:(ASVideoNode *)videoNode willChangePlayerState:(ASVideoNodePlayerState)state toState:(ASVideoNodePlayerState)toState;
+
+ +
+
+ +The easiest way to see them all is to take a look at the `ASVideoNode` header file. + diff --git a/docs/_includes/analytics.html b/docs/_includes/analytics.html new file mode 100755 index 0000000000..71dbfbce70 --- /dev/null +++ b/docs/_includes/analytics.html @@ -0,0 +1,10 @@ + diff --git a/docs/_includes/footer.html b/docs/_includes/footer.html new file mode 100755 index 0000000000..f24db5c9ab --- /dev/null +++ b/docs/_includes/footer.html @@ -0,0 +1,19 @@ + + + + + + diff --git a/docs/_includes/header.html b/docs/_includes/header.html new file mode 100755 index 0000000000..e22b49e1cd --- /dev/null +++ b/docs/_includes/header.html @@ -0,0 +1,64 @@ + + + + + Texture | {{ page.title }} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {% if jekyll.environment == 'production' %} + {% include analytics.html %} + {% endif %} + + +
+
+

AsyncDisplayKit is now Texture! LEARN MORE

+
+
+
+
+

+ + Texture + +

+ +
+
diff --git a/docs/_includes/hero.html b/docs/_includes/hero.html new file mode 100755 index 0000000000..e94f6b5f1b --- /dev/null +++ b/docs/_includes/hero.html @@ -0,0 +1,9 @@ +
+
+
+

Texture

+ +
+
diff --git a/docs/_includes/nav_docs.html b/docs/_includes/nav_docs.html new file mode 100755 index 0000000000..4248dd367a --- /dev/null +++ b/docs/_includes/nav_docs.html @@ -0,0 +1,21 @@ + diff --git a/docs/_layouts/apidiff.html b/docs/_layouts/apidiff.html new file mode 100755 index 0000000000..8b404ac0dc --- /dev/null +++ b/docs/_layouts/apidiff.html @@ -0,0 +1,14 @@ +--- +sectionid: appledocs +--- + +{% include header.html %} + +
+
+ +
+ {{ content }} +
+
+
\ No newline at end of file diff --git a/docs/_layouts/appledocs.html b/docs/_layouts/appledocs.html new file mode 100755 index 0000000000..25b9391d93 --- /dev/null +++ b/docs/_layouts/appledocs.html @@ -0,0 +1,15 @@ +--- +sectionid: appledocs +--- + +{% include header.html %} + +
+
+ +
+ {{ content }} +
+
+
+{% include footer.html %} diff --git a/docs/_layouts/default.html b/docs/_layouts/default.html new file mode 100755 index 0000000000..c9b8ad18ec --- /dev/null +++ b/docs/_layouts/default.html @@ -0,0 +1,16 @@ +{% include header.html %} + +{% if page.hero %} + {% include hero.html %} +{% endif %} + +
+
+
+ +{{ content }} + +
+
+
+{% include footer.html %} diff --git a/docs/_layouts/docs.html b/docs/_layouts/docs.html new file mode 100755 index 0000000000..8b2bb555d7 --- /dev/null +++ b/docs/_layouts/docs.html @@ -0,0 +1,39 @@ +--- +sectionid: docs +--- + +{% include header.html %} + +
+ + + +
+

+ {{ page.title }} +

+

{{ page.description }}

+ + {{ content }} + +

Edit on GitHub

+ + +
+ {% if page.prevPage %} + ← Prev + {% endif %} + {% if page.nextPage %} + Next → + {% endif %} +
+ + +
+ +
+
+ +{% include footer.html %} diff --git a/docs/_layouts/redirect.html b/docs/_layouts/redirect.html new file mode 100755 index 0000000000..c24f817484 --- /dev/null +++ b/docs/_layouts/redirect.html @@ -0,0 +1,6 @@ + + + + + + diff --git a/docs/_layouts/slack.html b/docs/_layouts/slack.html new file mode 100755 index 0000000000..95d73c3484 --- /dev/null +++ b/docs/_layouts/slack.html @@ -0,0 +1,14 @@ +--- +sectionid: slack +--- + +{% include header.html %} + +
+
+ +
+ {{ content }} +
+
+
diff --git a/docs/_sass/_code.scss b/docs/_sass/_code.scss new file mode 100644 index 0000000000..dd4df0e23e --- /dev/null +++ b/docs/_sass/_code.scss @@ -0,0 +1,221 @@ +code { + letter-spacing: 0.02em; + padding: 2px 4px; + /*font-weight: 700; + color: #008ED4; + font-size: 14px; + vertical-align: baseline; + font-family: 'Droid Sans Mono',sans-serif;*/ +} + +p code, span code, li code { + background-color: rgba(135, 215, 255, 0.2); +} + +.paddingBetweenCols { + th { + text-align: left; + padding: 15px 15px 15px 15px; + } + td { + padding: 15px 15px 15px 15px; + /* border: 1 px solid black;*/ + } +} + +.highlight pre, .redhighlight pre { + font-size: 13px; + line-height: 20px; + padding: 0 10px 20px 10px; +} + +.highlighttable { + margin-left: 10px; + margin-bottom: 20px; + margin-right: 10px; + border-collapse: separate !important; + .highlight pre, .redhighlight pre { + padding: 0; + } + code { + padding-right: 10px; + margin: 0; + padding-left: 2px; + font-size: 12px; + display: block; + line-height: 20px; + white-space: pre; + color: hsl(210, 100%, 8%); + } +} + +.highlight pre code, .redhighlight pre code, .highlighttable { + overflow-x: scroll; + display: block; + padding: 0; + font-size: 13px; + border: 1px solid rgb(220, 220, 220); + padding: 5px 10px; + box-sizing: border-box; + border-radius: 3; +} + +.highlight pre code, .highlighttable { + background-color: rgba(90, 140, 140, 0.1); +} + +.redhighlight pre code { + background-color: rgba(180, 80, 80, 0.1); +} + +.showcasetable code { + padding: 10px 10px 30px 30px; + margin: 10; +} + +.highlight pre code, .redhighlight pre code { + width: 100%; + white-space: pre; +} + +.lineno { + color: rgb(214, 139, 0); + &::after { + content: ';'; + font-size: 0; + } +} + +.highlight + .highlight + .highlight, .highlighttable + .highlight + .highlight, .redhighlight + .redhighlight + .redhighlight .highlighttable + .redhighlight + .redhighlight { + margin-top: -20px; +} + +/** + * Syntax highlighting styles + */ +/* not official Xcode colors, but looks better on the web */ + +.highlight { + background: #fff; + .c { + color: #008d14; + font-style: italic; + } + .err { + color: #a61717; + background-color: #e3d2d2; + } + .k { + color: #103ffb; + } + .cm { + color: #008d14; + font-style: italic; + } + .cp { + color: #b72748; + } + .c1 { + color: #008d14; + font-style: italic; + } + .cs { + color: #008d14; + font-weight: bold; + font-style: italic; + } + .gd { + color: #000; + background-color: #fdd; + .x { + color: #000; + background-color: #faa; + } + } + .ge { + font-style: italic; + } + .gr { + color: #a00; + } + .gh { + color: #999; + } + .gi { + color: #000; + background-color: #dfd; + .x { + color: #000; + background-color: #afa; + } + } + .go { + color: #888; + } + .gp { + color: #555; + } + .gs { + font-weight: bold; + } + .gu { + color: #aaa; + } + .gt { + color: #a00; + } + .kc, .kd { + color: orange; + } + .kp, .kr { + color: #008d14; + } + .kt { + color: #103ffb; + } + .m { + color: orange; + } + .s { + color: #b72748; + } + .na { + color: orange; + } + .nb { + color: #103ffb; + } + .nc { + color: #3a95ba; + } + .no, .ni, .ne, .nn, .nt { + color: orange; + } + .w { + color: #bbb; + } + .mh, .mi, .mo, .il { + color: black; + } + .sb, .sc, .sd, .s2, .se, .sh, .si, .sx { + color: #d14; + } + .sr { + color: orange; + } + .s1, .ss { + color: #b72748; + } + .bp, .vc { + color: #3a95ba; + } + .vg { + color: black; + } + .vi { + color: orange; + } + .nl { + color: #3a95ba; + } +} \ No newline at end of file diff --git a/docs/apidiff/ASDK_API_Diff_1.9.92_to_2.0.html b/docs/apidiff/ASDK_API_Diff_1.9.92_to_2.0.html new file mode 100755 index 0000000000..991484bb9c --- /dev/null +++ b/docs/apidiff/ASDK_API_Diff_1.9.92_to_2.0.html @@ -0,0 +1,2224 @@ + + + + + + +
+
ASAbsoluteLayoutElement.h
+ +
+
Added ASAbsoluteLayoutElement
+
Added ASAbsoluteLayoutElement.layoutPosition
+
Added ASAbsoluteLayoutElement.sizeRange
+
+ +
+ +
+
ASAbsoluteLayoutSpec.h
+ +
+
Added ASAbsoluteLayoutSpecSizing
+
Added ASAbsoluteLayoutSpecSizingDefault
+
Added ASAbsoluteLayoutSpecSizingSizeToFit
+
Added ASAbsoluteLayoutSpec
+
Added ASAbsoluteLayoutSpec.sizing
+
Added +[ASAbsoluteLayoutSpec absoluteLayoutSpecWithSizing:children:]
+
Added +[ASAbsoluteLayoutSpec absoluteLayoutSpecWithChildren:]
+
+ + +
+
Modified ASStaticLayoutSpec
+ + + + +
HeaderSuperclass
FromASStaticLayoutSpec.hASLayoutSpec
ToASAbsoluteLayoutSpec.hASAbsoluteLayoutSpec
+
+
Modified +[ASStaticLayoutSpec staticLayoutSpecWithChildren:]
+ + + + +
Availability
FromAvailable
ToDeprecated
+
+
+ +
+ +
+
ASAbstractLayoutController.h
+ +
+
Added ASAbstractLayoutController (Unavailable)
+
+ +
+ +
+
ASAsciiArtBoxCreator.h
+ +
+
Removed ASLayoutableAsciiArtProtocol
+
Removed -[ASLayoutableAsciiArtProtocol asciiArtString]
+
Removed -[ASLayoutableAsciiArtProtocol asciiArtName]
+
+ + +
+
Added ASLayoutElementAsciiArtProtocol
+
Added -[ASLayoutElementAsciiArtProtocol asciiArtString]
+
Added -[ASLayoutElementAsciiArtProtocol asciiArtName]
+
+ +
+ +
+
ASAvailability.h
+ +
+
Added #def kCFCoreFoundationVersionNumber_iOS_10_0
+
Added #def AS_AT_LEAST_IOS10
+
+ +
+ +
+
ASBaseDefines.h
+ +
+
Added #def ASDISPLAYNODE_DEPRECATED_MSG
+
Added #def AS_UNAVAILABLE
+
Added #def AS_WARN_UNUSED_RESULT
+
Added #def ASOVERLOADABLE
+
+ +
+ +
+
ASBasicImageDownloader.h
+ +
+
Modified ASBasicImageDownloader
+ + + + +
Protocols
FromASImageDownloaderProtocolDeprecated, ASImageDownloaderProtocol
ToASImageDownloaderProtocol
+
+
+ +
+ +
+
ASButtonNode.h
+ +
+
Added ASButtonNodeImageAlignment
+
Added ASButtonNodeImageAlignmentBeginning
+
Added ASButtonNodeImageAlignmentEnd
+
Added ASButtonNode.imageAlignment
+
+ +
+ +
+
ASCellNode.h
+ +
+
Added ASCellNode.supplementaryElementKind
+
Added ASCellNode.layoutAttributes
+
Added ASCellNode.indexPath
+
Added ASCellNode.owningNode
+
Added ASCellNode (Unavailable)
+
+ +
+ +
+
ASCollectionNode.h
+ +
+
Added ASCollectionNode.allowsSelection
+
Added ASCollectionNode.allowsMultipleSelection
+
Added -[ASCollectionNode scrollToItemAtIndexPath:atScrollPosition:animated:]
+
Added -[ASCollectionNode registerSupplementaryNodeOfKind:]
+
Added -[ASCollectionNode performBatchAnimated:updates:completion:]
+
Added -[ASCollectionNode performBatchUpdates:completion:]
+
Added -[ASCollectionNode waitUntilAllUpdatesAreCommitted]
+
Added -[ASCollectionNode insertSections:]
+
Added -[ASCollectionNode deleteSections:]
+
Added -[ASCollectionNode reloadSections:]
+
Added -[ASCollectionNode moveSection:toSection:]
+
Added -[ASCollectionNode insertItemsAtIndexPaths:]
+
Added -[ASCollectionNode deleteItemsAtIndexPaths:]
+
Added -[ASCollectionNode reloadItemsAtIndexPaths:]
+
Added -[ASCollectionNode moveItemAtIndexPath:toIndexPath:]
+
Added -[ASCollectionNode relayoutItems]
+
Added ASCollectionNode.indexPathsForSelectedItems
+
Added -[ASCollectionNode selectItemAtIndexPath:animated:scrollPosition:]
+
Added -[ASCollectionNode deselectItemAtIndexPath:animated:]
+
Added -[ASCollectionNode numberOfItemsInSection:]
+
Added ASCollectionNode.numberOfSections
+
Added ASCollectionNode.visibleNodes
+
Added -[ASCollectionNode nodeForItemAtIndexPath:]
+
Added -[ASCollectionNode indexPathForNode:]
+
Added ASCollectionNode.indexPathsForVisibleItems
+
Added -[ASCollectionNode indexPathForItemAtPoint:]
+
Added -[ASCollectionNode cellForItemAtIndexPath:]
+
Added -[ASCollectionNode contextForSection:]
+
Added ASCollectionNode (Deprecated)
+
Added -[ASCollectionDataSource collectionNode:numberOfItemsInSection:]
+
Added -[ASCollectionDataSource numberOfSectionsInCollectionNode:]
+
Added -[ASCollectionDataSource collectionNode:nodeBlockForItemAtIndexPath:]
+
Added -[ASCollectionDataSource collectionNode:nodeForItemAtIndexPath:]
+
Added -[ASCollectionDataSource collectionNode:nodeForSupplementaryElementOfKind:atIndexPath:]
+
Added -[ASCollectionDataSource collectionNode:contextForSection:]
+
Added -[ASCollectionDelegate collectionNode:constrainedSizeForItemAtIndexPath:]
+
Added -[ASCollectionDelegate collectionNode:willDisplayItemWithNode:]
+
Added -[ASCollectionDelegate collectionNode:didEndDisplayingItemWithNode:]
+
Added -[ASCollectionDelegate collectionNode:willDisplaySupplementaryElementWithNode:]
+
Added -[ASCollectionDelegate collectionNode:didEndDisplayingSupplementaryElementWithNode:]
+
Added -[ASCollectionDelegate collectionNode:shouldHighlightItemAtIndexPath:]
+
Added -[ASCollectionDelegate collectionNode:didHighlightItemAtIndexPath:]
+
Added -[ASCollectionDelegate collectionNode:didUnhighlightItemAtIndexPath:]
+
Added -[ASCollectionDelegate collectionNode:shouldSelectItemAtIndexPath:]
+
Added -[ASCollectionDelegate collectionNode:shouldDeselectItemAtIndexPath:]
+
Added -[ASCollectionDelegate collectionNode:didSelectItemAtIndexPath:]
+
Added -[ASCollectionDelegate collectionNode:didDeselectItemAtIndexPath:]
+
Added -[ASCollectionDelegate collectionNode:shouldShowMenuForItemAtIndexPath:]
+
Added -[ASCollectionDelegate collectionNode:canPerformAction:forItemAtIndexPath:sender:]
+
Added -[ASCollectionDelegate collectionNode:performAction:forItemAtIndexPath:sender:]
+
Added -[ASCollectionDelegate collectionNode:willBeginBatchFetchWithContext:]
+
Added -[ASCollectionDelegate shouldBatchFetchForCollectionNode:]
+
Added -[ASCollectionDelegate collectionView:constrainedSizeForNodeAtIndexPath:]
+
Added -[ASCollectionDelegate collectionView:willDisplayNode:forItemAtIndexPath:]
+
+ + +
+
Modified -[ASCollectionNode reloadDataImmediately]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse -reloadData / -reloadDataWithCompletion: followed by -waitUntilAllUpdatesAreCommitted instead.
+
+
Modified ASCollectionDataSource
+ + + + +
HeaderProtocols
FromASCollectionView.hASCommonCollectionViewDataSource
ToASCollectionNode.hASCommonCollectionDataSource
+
+
Modified -[ASCollectionDataSource collectionView:nodeForItemAtIndexPath:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASCollectionNode's method instead.
+
+
Modified -[ASCollectionDataSource collectionView:nodeBlockForItemAtIndexPath:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASCollectionNode's method instead.
+
+
Modified -[ASCollectionDataSource collectionView:nodeForSupplementaryElementOfKind:atIndexPath:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASCollectionNode's method instead.
+
+
Modified ASCollectionDelegate
+ + + + +
HeaderProtocols
FromASCollectionView.hASCommonCollectionViewDelegate, NSObject
ToASCollectionNode.hASCommonCollectionDelegate, NSObject
+
+
Modified -[ASCollectionDelegate collectionView:didEndDisplayingNode:forItemAtIndexPath:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASCollectionNode's method instead.
+
+
Modified -[ASCollectionDelegate collectionView:willBeginBatchFetchWithContext:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASCollectionNode's method instead.
+
+
Modified -[ASCollectionDelegate shouldBatchFetchForCollectionView:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASCollectionNode's method instead.
+
+
Modified -[ASCollectionDelegate collectionView:willDisplayNodeForItemAtIndexPath:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASCollectionNode's method instead.
+
+
+ +
+ +
+
ASCollectionNode+Beta.h
+ +
+
Modified -[ASCollectionNode beginUpdates]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse -performBatchUpdates:completion: instead.
+
+
Modified -[ASCollectionNode endUpdatesAnimated:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse -performBatchUpdates:completion: instead.
+
+
Modified -[ASCollectionNode endUpdatesAnimated:completion:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse -performBatchUpdates:completion: instead.
+
+
+ +
+ +
+
ASCollectionView.h
+ +
+
Removed -[ASCollectionView clearContents]
+
Removed -[ASCollectionView clearFetchedData]
+
Removed #def ASCollectionViewDataSource
+
Removed -[ASCollectionDataSource collectionView:constrainedSizeForNodeAtIndexPath:]
+
Removed #def ASCollectionViewDelegate
+
Removed -[ASCollectionDelegate collectionView:didEndDisplayingNodeForItemAtIndexPath:]
+
Removed -[ASCollectionView initWithFrame:collectionViewLayout:asyncDataFetching:]
+
+ + +
+
Added -[ASCollectionView contextForSection:]
+
Added -[ASCollectionView cellForItemAtIndexPath:]
+
Added -[ASCollectionView scrollToItemAtIndexPath:atScrollPosition:animated:]
+
Added -[ASCollectionView selectItemAtIndexPath:animated:scrollPosition:]
+
Added ASCollectionView.indexPathsForVisibleItems
+
Added ASCollectionView.indexPathsForSelectedItems
+
Added ASCollectionViewDataSource
+
Added ASCollectionViewDelegate
+
+ + +
+
Modified -[ASCollectionView initWithCollectionViewLayout:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedPlease use ASCollectionNode instead of ASCollectionView.
+
+
Modified -[ASCollectionView initWithFrame:collectionViewLayout:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedPlease use ASCollectionNode instead of ASCollectionView.
+
+
Modified -[ASCollectionView tuningParametersForRangeType:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASCollectionNode method instead.
+
+
Modified -[ASCollectionView setTuningParameters:forRangeType:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASCollectionNode method instead.
+
+
Modified -[ASCollectionView tuningParametersForRangeMode:rangeType:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASCollectionNode method instead.
+
+
Modified -[ASCollectionView setTuningParameters:forRangeMode:rangeType:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASCollectionNode method instead.
+
+
Modified -[ASCollectionView performBatchAnimated:updates:completion:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASCollectionNode method instead.
+
+
Modified -[ASCollectionView performBatchUpdates:completion:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASCollectionNode method instead.
+
+
Modified -[ASCollectionView reloadDataWithCompletion:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASCollectionNode method instead.
+
+
Modified -[ASCollectionView reloadData]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASCollectionNode method instead.
+
+
Modified -[ASCollectionView reloadDataImmediately]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASCollectionNode's -reloadDataWithCompletion: followed by -waitUntilAllUpdatesAreCommitted instead.
+
+
Modified -[ASCollectionView relayoutItems]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASCollectionNode method instead.
+
+
Modified -[ASCollectionView waitUntilAllUpdatesAreCommitted]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASCollectionNode method instead.
+
+
Modified -[ASCollectionView registerSupplementaryNodeOfKind:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASCollectionNode method instead.
+
+
Modified -[ASCollectionView insertSections:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASCollectionNode method instead.
+
+
Modified -[ASCollectionView deleteSections:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASCollectionNode method instead.
+
+
Modified -[ASCollectionView reloadSections:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASCollectionNode method instead.
+
+
Modified -[ASCollectionView moveSection:toSection:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASCollectionNode method instead.
+
+
Modified -[ASCollectionView insertItemsAtIndexPaths:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASCollectionNode method instead.
+
+
Modified -[ASCollectionView deleteItemsAtIndexPaths:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASCollectionNode method instead.
+
+
Modified -[ASCollectionView reloadItemsAtIndexPaths:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASCollectionNode method instead.
+
+
Modified ASCollectionDataSource
+ + + + +
HeaderProtocols
FromASCollectionView.hASCommonCollectionViewDataSource
ToASCollectionNode.hASCommonCollectionDataSource
+
+
Modified -[ASCollectionView moveItemAtIndexPath:toIndexPath:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASCollectionNode method instead.
+
+
Modified -[ASCollectionView calculatedSizeForNodeAtIndexPath:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedCall -calculatedSize on the node of interest instead.
+
+
Modified -[ASCollectionView visibleNodes]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASCollectionNode method instead.
+
+
Modified -[ASCollectionView indexPathForNode:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASCollectionNode method instead.
+
+
Modified ASCollectionDelegate
+ + + + +
HeaderProtocols
FromASCollectionView.hASCommonCollectionViewDelegate, NSObject
ToASCollectionNode.hASCommonCollectionDelegate, NSObject
+
+
+ +
+ +
+
ASCollectionViewFlowLayoutInspector.h
+ +
+
Added -[ASCollectionViewLayoutInspecting scrollableDirections]
+
+ + +
+
Modified -[ASCollectionViewLayoutInspecting collectionView:numberOfSectionsForSupplementaryNodeOfKind:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASCollectionNode's method instead.
+
+
+ +
+ +
+
ASCollectionViewProtocols.h
+ +
+
Removed ASCommonCollectionViewDataSource
+
Removed -[ASCommonCollectionViewDataSource collectionView:numberOfItemsInSection:]
+
Removed -[ASCommonCollectionViewDataSource numberOfSectionsInCollectionView:]
+
Removed -[ASCommonCollectionViewDataSource collectionView:viewForSupplementaryElementOfKind:atIndexPath:]
+
Removed ASCommonCollectionViewDelegate
+
Removed -[ASCommonCollectionViewDelegate collectionView:transitionLayoutForOldLayout:newLayout:]
+
Removed -[ASCommonCollectionViewDelegate collectionView:willDisplaySupplementaryView:forElementKind:atIndexPath:]
+
Removed -[ASCommonCollectionViewDelegate collectionView:didEndDisplayingSupplementaryView:forElementOfKind:atIndexPath:]
+
Removed -[ASCommonCollectionViewDelegate collectionView:shouldHighlightItemAtIndexPath:]
+
Removed -[ASCommonCollectionViewDelegate collectionView:didHighlightItemAtIndexPath:]
+
Removed -[ASCommonCollectionViewDelegate collectionView:didUnhighlightItemAtIndexPath:]
+
Removed -[ASCommonCollectionViewDelegate collectionView:shouldSelectItemAtIndexPath:]
+
Removed -[ASCommonCollectionViewDelegate collectionView:didSelectItemAtIndexPath:]
+
Removed -[ASCommonCollectionViewDelegate collectionView:shouldDeselectItemAtIndexPath:]
+
Removed -[ASCommonCollectionViewDelegate collectionView:didDeselectItemAtIndexPath:]
+
Removed -[ASCommonCollectionViewDelegate collectionView:shouldShowMenuForItemAtIndexPath:]
+
Removed -[ASCommonCollectionViewDelegate collectionView:canPerformAction:forItemAtIndexPath:withSender:]
+
Removed -[ASCommonCollectionViewDelegate collectionView:performAction:forItemAtIndexPath:withSender:]
+
+ + +
+
Added ASCommonCollectionDataSource
+
Added -[ASCommonCollectionDataSource collectionView:numberOfItemsInSection:]
+
Added -[ASCommonCollectionDataSource numberOfSectionsInCollectionView:]
+
Added -[ASCommonCollectionDataSource collectionView:viewForSupplementaryElementOfKind:atIndexPath:]
+
Added ASCommonCollectionDelegate
+
Added -[ASCommonCollectionDelegate collectionView:transitionLayoutForOldLayout:newLayout:]
+
Added -[ASCommonCollectionDelegate collectionView:willDisplaySupplementaryView:forElementKind:atIndexPath:]
+
Added -[ASCommonCollectionDelegate collectionView:didEndDisplayingSupplementaryView:forElementOfKind:atIndexPath:]
+
Added -[ASCommonCollectionDelegate collectionView:shouldHighlightItemAtIndexPath:]
+
Added -[ASCommonCollectionDelegate collectionView:didHighlightItemAtIndexPath:]
+
Added -[ASCommonCollectionDelegate collectionView:didUnhighlightItemAtIndexPath:]
+
Added -[ASCommonCollectionDelegate collectionView:shouldSelectItemAtIndexPath:]
+
Added -[ASCommonCollectionDelegate collectionView:didSelectItemAtIndexPath:]
+
Added -[ASCommonCollectionDelegate collectionView:shouldDeselectItemAtIndexPath:]
+
Added -[ASCommonCollectionDelegate collectionView:didDeselectItemAtIndexPath:]
+
Added -[ASCommonCollectionDelegate collectionView:shouldShowMenuForItemAtIndexPath:]
+
Added -[ASCommonCollectionDelegate collectionView:canPerformAction:forItemAtIndexPath:withSender:]
+
Added -[ASCommonCollectionDelegate collectionView:performAction:forItemAtIndexPath:withSender:]
+
+ +
+ +
+
ASDataController.h
+ +
+
Added -[ASDataController initWithDataSource:]
+
Added -[ASDataController completedNumberOfSections]
+
Added -[ASDataController completedNumberOfRowsInSection:]
+
Added -[ASDataController nodeAtCompletedIndexPath:]
+
Added -[ASDataController completedIndexPathForNode:]
+
Added -[ASDataController moveCompletedNodeAtIndexPath:toIndexPath:]
+
+ +
+ +
+
ASDimension.h
+ +
+
Removed ASRelativeDimensionTypePercent
+
Removed ASRelativeDimension
+
Removed ASRelativeDimensionUnconstrained
+
Removed #def isValidForLayout
+
Removed ASRelativeDimensionMakeWithPoints()
+
Removed ASRelativeDimensionMakeWithPercent()
+
Removed ASRelativeDimensionCopy()
+
Removed ASRelativeDimensionEqualToRelativeDimension()
+
Removed NSStringFromASRelativeDimension()
+
Removed ASRelativeDimensionResolve()
+
+ + +
+
Added ASPointsValidForLayout()
+
Added ASIsCGSizeValidForLayout()
+
Added ASPointsValidForSize()
+
Added ASIsCGSizeValidForSize()
+
Added ASDimensionUnit
+
Added ASDimensionUnitAuto
+
Added ASDimensionUnitPoints
+
Added ASDimensionUnitFraction
+
Added ASDimension
+
Added ASLayoutElementSize
+
Added ASDimensionAuto
+
Added ASDimensionMake()
+
Added ASDimensionMakeWithPoints()
+
Added ASDimensionMakeWithFraction()
+
Added ASDimensionEqualToDimension()
+
Added NSStringFromASDimension()
+
Added ASDimensionResolve()
+
Added NSNumber (ASDimension)
+
Added NSNumber.as_pointDimension
+
Added NSNumber.as_fractionDimension
+
Added ASLayoutSize
+
Added ASLayoutSizeAuto
+
Added ASLayoutSizeMake()
+
Added NSStringFromASLayoutSize()
+
Added ASLayoutElementSizeMake()
+
Added ASLayoutElementSizeMakeFromCGSize()
+
Added ASLayoutElementSizeEqualToLayoutElementSize()
+
Added NSStringFromASLayoutElementSize()
+
Added ASLayoutElementSizeResolveAutoSize()
+
Added ASLayoutElementSizeResolve()
+
Added ASRelativeDimensionTypeAuto
+
Added ASRelativeDimensionTypeFraction
+
Added #def ASRelativeDimension
+
Added #def ASRelativeSize
+
Added #def ASRelativeDimensionMakeWithPoints
+
Added #def ASRelativeDimensionMakeWithFraction
+
Added ASRelativeSizeMakeWithFraction()
+
Added ASRelativeSizeRangeMakeWithExactFraction()
+
+ + +
+
Modified ASSizeRangeMakeExactSize()
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASSizeRangeMake instead.
+
+
Modified ASRelativeSizeRange
+ + + + +
Header
FromASRelativeSize.h
ToASDimension.h
+
+
Modified ASRelativeSizeRangeUnconstrained
+ + + + +
Header
FromASRelativeSize.h
ToASDimension.h
+
+
Modified ASRelativeDimensionMake()
+ + + + +
Availability
FromAvailable
ToDeprecated
+
+
Modified ASRelativeSizeMake()
+ + + + +
HeaderAvailability
FromASRelativeSize.hAvailable
ToASDimension.hDeprecated
+
+
Modified ASRelativeSizeMakeWithCGSize()
+ + + + +
HeaderAvailability
FromASRelativeSize.hAvailable
ToASDimension.hDeprecated
+
+
Modified ASRelativeSizeEqualToRelativeSize()
+ + + + +
HeaderAvailability
FromASRelativeSize.hAvailable
ToASDimension.hDeprecated
+
+
Modified NSStringFromASRelativeSize()
+ + + + +
HeaderAvailability
FromASRelativeSize.hAvailable
ToASDimension.hDeprecated
+
+
Modified ASRelativeSizeRangeMake()
+ + + + +
HeaderAvailability
FromASRelativeSize.hAvailable
ToASDimension.hDeprecated
+
+
Modified ASRelativeSizeRangeMakeWithExactRelativeSize()
+ + + + +
HeaderAvailability
FromASRelativeSize.hAvailable
ToASDimension.hDeprecated
+
+
Modified ASRelativeSizeRangeMakeWithExactCGSize()
+ + + + +
HeaderAvailability
FromASRelativeSize.hAvailable
ToASDimension.hDeprecated
+
+
Modified ASRelativeSizeRangeMakeWithExactRelativeDimensions()
+ + + + +
HeaderAvailability
FromASRelativeSize.hAvailable
ToASDimension.hDeprecated
+
+
Modified ASRelativeSizeRangeEqualToRelativeSizeRange()
+ + + + +
HeaderAvailability
FromASRelativeSize.hAvailable
ToASDimension.hDeprecated
+
+
Modified ASRelativeSizeRangeResolve()
+ + + + +
HeaderAvailability
FromASRelativeSize.hAvailable
ToASDimension.hDeprecated
+
+
+ +
+ +
+
ASDisplayNode.h
+ +
+
Removed ASInterfaceStateFetchData
+
Removed -[ASDisplayNode measureWithSizeRange:]
+
Removed ASDisplayNode (ASDisplayNodeAsyncTransactionContainer)
+
Removed -[ASDisplayNode reclaimMemory]
+
Removed -[ASDisplayNode recursivelyReclaimMemory]
+
Removed ASDisplayNode.placeholderFadesOut
+
+ + +
+
Added ASInterfaceStatePreload
+
Added -[ASDisplayNode onDidLoad:]
+
Added ASDisplayNode.visible
+
Added ASDisplayNode.inPreloadState
+
Added ASDisplayNode.inDisplayState
+
Added -[ASDisplayNode layoutThatFits:]
+
Added ASDisplayNode.allowsGroupOpacity
+
Added ASDisplayNode (LayoutTransitioning)
+
Added ASDisplayNode.defaultLayoutTransitionDuration
+
Added ASDisplayNode.defaultLayoutTransitionDelay
+
Added ASDisplayNode.defaultLayoutTransitionOptions
+
Added -[ASDisplayNode cancelLayoutTransition]
+
Added ASDisplayNode (AutomaticSubnodeManagement)
+
Added ASDisplayNode.automaticallyManagesSubnodes
+
Added ASDisplayNode (ASAsyncTransactionContainer)
+
+ + +
+
Modified ASDisplayNode
+ + + + +
Protocols
FromASLayoutable
ToASLayoutElement
+
+
Modified ASDisplayNode (Debugging)
+ + + + +
Protocols
FromASLayoutableAsciiArtProtocol
ToASLayoutElementAsciiArtProtocol
+
+
Modified ASDisplayNode (Deprecated)
+ + + + +
Header
FromASDisplayNode.h
ToASDisplayNode+Deprecated.h
+
+
+ +
+ +
+
ASDisplayNode+Beta.h
+ +
+
Removed +[ASDisplayNode usesImplicitHierarchyManagement]
+
Removed +[ASDisplayNode setUsesImplicitHierarchyManagement:]
+
+ + +
+
Added #def ASDISPLAYNODE_EVENTLOG_CAPACITY
+
Added #def ASDISPLAYNODE_EVENTLOG_ENABLE
+
Added #def ASDisplayNodeLogEvent
+
Added ASDisplayNodePerformanceMeasurementOptions
+
Added ASDisplayNodePerformanceMeasurementOptionLayoutSpec
+
Added ASDisplayNodePerformanceMeasurementOptionLayoutComputation
+
Added ASDisplayNodePerformanceMeasurements
+
Added ASDisplayNode.measurementOptions
+
Added ASDisplayNode.performanceMeasurements
+
+ +
+ +
+
ASDisplayNode+Deprecated.h
+ +
+
Added ASDisplayNode.alignSelf
+
Added ASDisplayNode.ascender
+
Added ASDisplayNode.descender
+
Added ASDisplayNode.flexBasis
+
Added ASDisplayNode.flexGrow
+
Added ASDisplayNode.flexShrink
+
Added ASDisplayNode.layoutPosition
+
Added ASDisplayNode.sizeRange
+
Added ASDisplayNode.spacingAfter
+
Added ASDisplayNode.spacingBefore
+
+ + +
+
Modified ASDisplayNode (Deprecated)
+ + + + +
Header
FromASDisplayNode.h
ToASDisplayNode+Deprecated.h
+
+
Modified ASDisplayNode.name
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse .debugName instead.
+
+
Modified ASDisplayNode.preferredFrameSize
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse .style.preferredSize instead OR set individual values with .style.height and .style.width.
+
+
Modified -[ASDisplayNode measure:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse layoutThatFits: with a constrained size of (CGSizeZero, constrainedSize) and call size on the returned ASLayout.
+
+
Modified -[ASDisplayNode visibilityDidChange:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse -didEnterVisibleState / -didExitVisibleState instead.
+
+
Modified -[ASDisplayNode visibleStateDidChange:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse -didEnterVisibleState / -didExitVisibleState instead.
+
+
Modified -[ASDisplayNode displayStateDidChange:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse -didEnterDisplayState / -didExitDisplayState instead.
+
+
Modified -[ASDisplayNode loadStateDidChange:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse -didEnterPreloadState / -didExitPreloadState instead.
+
+
Modified -[ASDisplayNode cancelLayoutTransitionsInProgress]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse -cancelLayoutTransition instead.
+
+
Modified ASDisplayNode.usesImplicitHierarchyManagement
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedSet .automaticallyManagesSubnodes instead.
+
+
+ +
+ +
+
ASDisplayNode+Subclasses.h
+ +
+
Added -[ASDisplayNode calculateLayoutThatFits:restrictedToSize:relativeToParentSize:]
+
Added -[ASDisplayNode displayWillStartAsynchronously:]
+
Added -[ASDisplayNode didEnterVisibleState]
+
Added -[ASDisplayNode didExitVisibleState]
+
Added -[ASDisplayNode didEnterDisplayState]
+
Added -[ASDisplayNode didExitDisplayState]
+
Added -[ASDisplayNode didEnterPreloadState]
+
Added -[ASDisplayNode didExitPreloadState]
+
+ +
+ +
+
ASDisplayNodeExtras.h
+ +
+
Removed ASInterfaceStateIncludesFetchData()
+
+ + +
+
Added ASInterfaceStateIncludesPreload()
+
+ +
+ +
+
ASEditableTextNode.h
+ +
+
Added ASEditableTextNode (Unavailable)
+
+ +
+ +
+
ASEnvironment.h
+ +
+
Removed -[ASEnvironment supportsUpwardPropagation]
+
+ + +
+
Added NSStringFromASEnvironmentTraitCollection()
+
+ +
+ +
+
ASImageNode.h
+ +
+
Added ASImageNode.forcedSize
+
Added ASImageNode (Unavailable)
+
+ +
+ +
+
ASImageProtocols.h
+ +
+
Removed ASImageDownloaderProtocolDeprecated
+
Removed -[ASImageDownloaderProtocolDeprecated downloadImageWithURL:callbackQueue:downloadProgressBlock:completion:]
+
Removed ASImageCacheProtocolDeprecated
+
Removed -[ASImageCacheProtocolDeprecated fetchCachedImageWithURL:callbackQueue:completion:]
+
+ +
+ +
+
ASLayout.h
+ +
+
Removed ASLayout.constrainedSizeRange
+
Removed ASLayout.dirty
+
Removed -[ASLayout initWithLayoutableObject:constrainedSizeRange:size:position:sublayouts:]
+
Removed +[ASLayout layoutWithLayoutableObject:constrainedSizeRange:size:position:sublayouts:]
+
Removed +[ASLayout flattenedLayoutWithLayoutableObject:constrainedSizeRange:size:sublayouts:]
+
+ + +
+
Added ASCalculateRootLayout()
+
Added ASCalculateLayout()
+
Added ASLayout.layoutElement
+
Added -[ASLayout initWithLayoutElement:size:position:sublayouts:]
+
Added +[ASLayout layoutWithLayoutElement:size:position:sublayouts:]
+
Added +[ASLayout layoutWithLayoutElement:size:sublayouts:]
+
Added +[ASLayout layoutWithLayoutElement:size:]
+
Added ASLayout (Unavailable)
+
Added ASLayout (Deprecated)
+
+ + +
+
Modified -[ASLayout layoutableObject]
+ + + + +
Availability
FromAvailable
ToDeprecated
+
+
Modified +[ASLayout layoutWithLayoutableObject:constrainedSizeRange:size:]
+ + + + +
Availability
FromAvailable
ToDeprecated
+
+
Modified +[ASLayout layoutWithLayoutableObject:constrainedSizeRange:size:sublayouts:]
+ + + + +
Availability
FromAvailable
ToDeprecated
+
+
+ +
+ +
+
ASLayoutable.h
+ +
+
Removed ASLayoutableType
+
Removed ASLayoutableTypeLayoutSpec
+
Removed ASLayoutableTypeDisplayNode
+
Removed ASLayoutable
+
Removed ASLayoutable.layoutableType
+
Removed ASLayoutable.canLayoutAsynchronous
+
Removed -[ASLayoutable measureWithSizeRange:]
+
Removed ASLayoutable.spacingBefore
+
Removed ASLayoutable.spacingAfter
+
Removed ASLayoutable.flexGrow
+
Removed ASLayoutable.flexShrink
+
Removed ASLayoutable.flexBasis
+
Removed ASLayoutable.alignSelf
+
Removed ASLayoutable.ascender
+
Removed ASLayoutable.descender
+
Removed ASLayoutable.sizeRange
+
Removed ASLayoutable.layoutPosition
+
+ +
+ +
+
ASLayoutableExtensibility.h
+ +
+
Removed ASLayoutableExtensibility
+
Removed -[ASLayoutableExtensibility setLayoutOptionExtensionBool:atIndex:]
+
Removed -[ASLayoutableExtensibility layoutOptionExtensionBoolAtIndex:]
+
Removed -[ASLayoutableExtensibility setLayoutOptionExtensionInteger:atIndex:]
+
Removed -[ASLayoutableExtensibility layoutOptionExtensionIntegerAtIndex:]
+
Removed -[ASLayoutableExtensibility setLayoutOptionExtensionEdgeInsets:atIndex:]
+
Removed -[ASLayoutableExtensibility layoutOptionExtensionEdgeInsetsAtIndex:]
+
+ +
+ +
+
ASLayoutablePrivate.h
+ +
+
Removed ASLayoutableContextInvalidTransitionID
+
Removed ASLayoutableContextDefaultTransitionID
+
Removed ASLayoutableContextNull
+
Removed ASLayoutableContextIsNull()
+
Removed ASLayoutableContextMake()
+
Removed ASLayoutableSetCurrentContext()
+
Removed ASLayoutableGetCurrentContext()
+
Removed ASLayoutableClearCurrentContext()
+
Removed ASLayoutablePrivate
+
Removed -[ASLayoutablePrivate finalLayoutable]
+
Removed ASLayoutablePrivate.isFinalLayoutable
+
Removed #def ASEnvironmentLayoutOptionsForwarding
+
+ + +
+
Modified #def ASEnvironmentLayoutExtensibilityForwarding
+ + + + +
Header
FromASLayoutablePrivate.h
ToASLayoutElementPrivate.h
+
+
+ +
+ +
+
ASLayoutElement.h
+ +
+
Added ASLayoutElementParentDimensionUndefined
+
Added ASLayoutElementParentSizeUndefined
+
Added ASLayoutElementType
+
Added ASLayoutElementTypeLayoutSpec
+
Added ASLayoutElementTypeDisplayNode
+
Added ASLayoutElement
+
Added ASLayoutElement.layoutElementType
+
Added ASLayoutElement.canLayoutAsynchronous
+
Added ASLayoutElement.style
+
Added ASLayoutElement.debugName
+
Added -[ASLayoutElement layoutThatFits:]
+
Added -[ASLayoutElement layoutThatFits:parentSize:]
+
Added -[ASLayoutElement calculateLayoutThatFits:]
+
Added -[ASLayoutElement calculateLayoutThatFits:restrictedToSize:relativeToParentSize:]
+
Added -[ASLayoutElement measureWithSizeRange:]
+
Added ASLayoutElementStyleWidthProperty
+
Added ASLayoutElementStyleMinWidthProperty
+
Added ASLayoutElementStyleMaxWidthProperty
+
Added ASLayoutElementStyleHeightProperty
+
Added ASLayoutElementStyleMinHeightProperty
+
Added ASLayoutElementStyleMaxHeightProperty
+
Added ASLayoutElementStyleSpacingBeforeProperty
+
Added ASLayoutElementStyleSpacingAfterProperty
+
Added ASLayoutElementStyleFlexGrowProperty
+
Added ASLayoutElementStyleFlexShrinkProperty
+
Added ASLayoutElementStyleFlexBasisProperty
+
Added ASLayoutElementStyleAlignSelfProperty
+
Added ASLayoutElementStyleAscenderProperty
+
Added ASLayoutElementStyleDescenderProperty
+
Added ASLayoutElementStyleLayoutPositionProperty
+
Added ASLayoutElementStyleDelegate
+
Added -[ASLayoutElementStyleDelegate style:propertyDidChange:]
+
Added ASLayoutElementStyle
+
Added -[ASLayoutElementStyle initWithDelegate:]
+
Added ASLayoutElementStyle.delegate
+
Added ASLayoutElementStyle.width
+
Added ASLayoutElementStyle.height
+
Added ASLayoutElementStyle.minHeight
+
Added ASLayoutElementStyle.maxHeight
+
Added ASLayoutElementStyle.minWidth
+
Added ASLayoutElementStyle.maxWidth
+
Added ASLayoutElementStyle.preferredSize
+
Added ASLayoutElementStyle.minSize
+
Added ASLayoutElementStyle.maxSize
+
Added ASLayoutElementStyle.preferredLayoutSize
+
Added ASLayoutElementStyle.minLayoutSize
+
Added ASLayoutElementStyle.maxLayoutSize
+
Added ASLayoutElementStylability
+
Added -[ASLayoutElementStylability styledWithBlock:]
+
+ +
+ +
+
ASLayoutElementExtensibility.h
+ +
+
Added ASLayoutElementExtensibility
+
Added -[ASLayoutElementExtensibility setLayoutOptionExtensionBool:atIndex:]
+
Added -[ASLayoutElementExtensibility layoutOptionExtensionBoolAtIndex:]
+
Added -[ASLayoutElementExtensibility setLayoutOptionExtensionInteger:atIndex:]
+
Added -[ASLayoutElementExtensibility layoutOptionExtensionIntegerAtIndex:]
+
Added -[ASLayoutElementExtensibility setLayoutOptionExtensionEdgeInsets:atIndex:]
+
Added -[ASLayoutElementExtensibility layoutOptionExtensionEdgeInsetsAtIndex:]
+
+ +
+ +
+
ASLayoutElementPrivate.h
+ +
+
Added ASLayoutElementContextInvalidTransitionID
+
Added ASLayoutElementContextDefaultTransitionID
+
Added ASLayoutElementContextNull
+
Added ASLayoutElementContextIsNull()
+
Added ASLayoutElementContextMake()
+
Added ASLayoutElementSetCurrentContext()
+
Added ASLayoutElementGetCurrentContext()
+
Added ASLayoutElementClearCurrentContext()
+
Added ASLayoutElementPrivate
+
Added -[ASLayoutElementPrivate finalLayoutElement]
+
Added ASLayoutElementPrivate.isFinalLayoutElement
+
Added #def ASLayoutElementStyleForwardingDeclaration
+
Added #def ASLayoutElementStyleForwarding
+
+ + +
+
Modified #def ASEnvironmentLayoutExtensibilityForwarding
+ + + + +
Header
FromASLayoutablePrivate.h
ToASLayoutElementPrivate.h
+
+
+ +
+ +
+
ASLayoutRangeType.h
+ +
+
Removed ASLayoutRangeTypeFetchData
+
+ + +
+
Added ASLayoutRangeTypePreload
+
+ +
+ +
+
ASLayoutSpec.h
+ +
+
Removed -[ASLayoutSpec init]
+
Removed -[ASLayoutSpec setChild:forIndex:]
+
Removed -[ASLayoutSpec childForIndex:]
+
+ + +
+
Added ASWrapperLayoutSpec
+
Added +[ASWrapperLayoutSpec wrapperWithLayoutElement:]
+
Added +[ASWrapperLayoutSpec wrapperWithLayoutElements:]
+
Added -[ASWrapperLayoutSpec initWithLayoutElement:]
+
Added -[ASWrapperLayoutSpec initWithLayoutElements:]
+
Added ASLayoutSpec (Deprecated)
+
Added ASLayoutSpec.alignSelf
+
Added ASLayoutSpec.ascender
+
Added ASLayoutSpec.descender
+
Added ASLayoutSpec.flexBasis
+
Added ASLayoutSpec.flexGrow
+
Added ASLayoutSpec.flexShrink
+
Added ASLayoutSpec.layoutPosition
+
Added ASLayoutSpec.sizeRange
+
Added ASLayoutSpec.spacingAfter
+
Added ASLayoutSpec.spacingBefore
+
+ + +
+
Modified ASLayoutSpec
+ + + + +
Protocols
FromASLayoutable
ToASLayoutElement
+
+
Modified ASLayoutSpec (Debugging)
+ + + + +
Protocols
FromASLayoutableAsciiArtProtocol
ToASLayoutElementAsciiArtProtocol
+
+
+ +
+ +
+
ASLog.h
+ +
+
Added #def ASProfilingSignpost
+
Added #def ASProfilingSignpostStart
+
Added #def ASProfilingSignpostEnd
+
+ +
+ +
+
ASMapNode.h
+ +
+
Added ASMapNode.imageForStaticMapAnnotationBlock
+
+ +
+ +
+
ASObjectDescriptionHelpers.h
+ +
+
Added ASDebugDescriptionProvider
+
Added -[ASDebugDescriptionProvider propertiesForDebugDescription]
+
Added ASDescriptionProvider
+
Added -[ASDescriptionProvider propertiesForDescription]
+
Added ASObjectDescriptionMakeWithoutObject()
+
Added ASObjectDescriptionMake()
+
Added ASObjectDescriptionMakeTiny()
+
Added ASStringWithQuotesIfMultiword()
+
+ +
+ +
+
ASPagerNode.h
+ +
+
Removed -[ASPagerDataSource pagerNode:constrainedSizeForNodeAtIndexPath:]
+
+ + +
+
Added -[ASPagerDelegate pagerNode:constrainedSizeForNodeAtIndex:]
+
+ +
+ +
+
ASRangeController.h
+ +
+
Added -[ASRangeControllerDataSource nameForRangeControllerDataSource]
+
+ +
+ +
+
ASRelativeLayoutSpec.h
+ +
+
Added ASRelativeLayoutSpecPositionNone
+
+ +
+ +
+
ASRelativeSize.h
+ +
+
Removed ASRelativeSize
+
Removed ASRelativeSizeMakeWithPercent()
+
Removed ASRelativeSizeResolveSize()
+
Removed ASRelativeSizeRangeMakeWithExactPercent()
+
+ + +
+
Modified ASRelativeSizeRange
+ + + + +
Header
FromASRelativeSize.h
ToASDimension.h
+
+
Modified ASRelativeSizeRangeUnconstrained
+ + + + +
Header
FromASRelativeSize.h
ToASDimension.h
+
+
Modified ASRelativeSizeMake()
+ + + + +
HeaderAvailability
FromASRelativeSize.hAvailable
ToASDimension.hDeprecated
+
+
Modified ASRelativeSizeMakeWithCGSize()
+ + + + +
HeaderAvailability
FromASRelativeSize.hAvailable
ToASDimension.hDeprecated
+
+
Modified ASRelativeSizeEqualToRelativeSize()
+ + + + +
HeaderAvailability
FromASRelativeSize.hAvailable
ToASDimension.hDeprecated
+
+
Modified NSStringFromASRelativeSize()
+ + + + +
HeaderAvailability
FromASRelativeSize.hAvailable
ToASDimension.hDeprecated
+
+
Modified ASRelativeSizeRangeMake()
+ + + + +
HeaderAvailability
FromASRelativeSize.hAvailable
ToASDimension.hDeprecated
+
+
Modified ASRelativeSizeRangeMakeWithExactRelativeSize()
+ + + + +
HeaderAvailability
FromASRelativeSize.hAvailable
ToASDimension.hDeprecated
+
+
Modified ASRelativeSizeRangeMakeWithExactCGSize()
+ + + + +
HeaderAvailability
FromASRelativeSize.hAvailable
ToASDimension.hDeprecated
+
+
Modified ASRelativeSizeRangeMakeWithExactRelativeDimensions()
+ + + + +
HeaderAvailability
FromASRelativeSize.hAvailable
ToASDimension.hDeprecated
+
+
Modified ASRelativeSizeRangeEqualToRelativeSizeRange()
+ + + + +
HeaderAvailability
FromASRelativeSize.hAvailable
ToASDimension.hDeprecated
+
+
Modified ASRelativeSizeRangeResolve()
+ + + + +
HeaderAvailability
FromASRelativeSize.hAvailable
ToASDimension.hDeprecated
+
+
+ +
+ +
+
ASRunLoopQueue.h
+ +
+
Added ASRunLoopQueue.ensureExclusiveMembership
+
Added ASDeallocQueue
+
Added +[ASDeallocQueue sharedDeallocationQueue]
+
Added -[ASDeallocQueue releaseObjectInBackground:]
+
+ +
+ +
+
ASSectionContext.h
+ +
+
Added ASSectionContext
+
Added ASSectionContext.sectionName
+
Added ASSectionContext.collectionView
+
+ +
+ +
+
ASStackLayoutable.h
+ +
+
Removed ASStackLayoutable
+
Removed ASStackLayoutable.spacingBefore
+
Removed ASStackLayoutable.spacingAfter
+
Removed ASStackLayoutable.flexGrow
+
Removed ASStackLayoutable.flexShrink
+
Removed ASStackLayoutable.flexBasis
+
Removed ASStackLayoutable.alignSelf
+
Removed ASStackLayoutable.ascender
+
Removed ASStackLayoutable.descender
+
+ +
+ +
+
ASStackLayoutElement.h
+ +
+
Added ASStackLayoutElement
+
Added ASStackLayoutElement.spacingBefore
+
Added ASStackLayoutElement.spacingAfter
+
Added ASStackLayoutElement.flexGrow
+
Added ASStackLayoutElement.flexShrink
+
Added ASStackLayoutElement.flexBasis
+
Added ASStackLayoutElement.alignSelf
+
Added ASStackLayoutElement.ascender
+
Added ASStackLayoutElement.descender
+
+ +
+ +
+
ASStaticLayoutable.h
+ +
+
Removed ASStaticLayoutable
+
Removed ASStaticLayoutable.sizeRange
+
Removed ASStaticLayoutable.layoutPosition
+
+ +
+ +
+
ASStaticLayoutSpec.h
+ +
+
Modified ASStaticLayoutSpec
+ + + + +
HeaderSuperclass
FromASStaticLayoutSpec.hASLayoutSpec
ToASAbsoluteLayoutSpec.hASAbsoluteLayoutSpec
+
+
+ +
+ +
+
ASTableNode.h
+ +
+
Added ASTableNode.allowsSelection
+
Added ASTableNode.allowsSelectionDuringEditing
+
Added ASTableNode.allowsMultipleSelection
+
Added ASTableNode.allowsMultipleSelectionDuringEditing
+
Added -[ASTableNode tuningParametersForRangeType:]
+
Added -[ASTableNode setTuningParameters:forRangeType:]
+
Added -[ASTableNode tuningParametersForRangeMode:rangeType:]
+
Added -[ASTableNode setTuningParameters:forRangeMode:rangeType:]
+
Added -[ASTableNode scrollToRowAtIndexPath:atScrollPosition:animated:]
+
Added -[ASTableNode reloadDataWithCompletion:]
+
Added -[ASTableNode reloadData]
+
Added -[ASTableNode relayoutItems]
+
Added -[ASTableNode performBatchAnimated:updates:completion:]
+
Added -[ASTableNode performBatchUpdates:completion:]
+
Added -[ASTableNode waitUntilAllUpdatesAreCommitted]
+
Added -[ASTableNode insertSections:withRowAnimation:]
+
Added -[ASTableNode deleteSections:withRowAnimation:]
+
Added -[ASTableNode reloadSections:withRowAnimation:]
+
Added -[ASTableNode moveSection:toSection:]
+
Added -[ASTableNode insertRowsAtIndexPaths:withRowAnimation:]
+
Added -[ASTableNode deleteRowsAtIndexPaths:withRowAnimation:]
+
Added -[ASTableNode reloadRowsAtIndexPaths:withRowAnimation:]
+
Added -[ASTableNode moveRowAtIndexPath:toIndexPath:]
+
Added -[ASTableNode selectRowAtIndexPath:animated:scrollPosition:]
+
Added -[ASTableNode deselectRowAtIndexPath:animated:]
+
Added -[ASTableNode numberOfRowsInSection:]
+
Added ASTableNode.numberOfSections
+
Added ASTableNode.visibleNodes
+
Added -[ASTableNode nodeForRowAtIndexPath:]
+
Added -[ASTableNode indexPathForNode:]
+
Added -[ASTableNode rectForRowAtIndexPath:]
+
Added -[ASTableNode cellForRowAtIndexPath:]
+
Added ASTableNode.indexPathForSelectedRow
+
Added ASTableNode.indexPathsForSelectedRows
+
Added -[ASTableNode indexPathForRowAtPoint:]
+
Added -[ASTableNode indexPathsForRowsInRect:]
+
Added -[ASTableNode indexPathsForVisibleRows]
+
Added -[ASTableDataSource numberOfSectionsInTableNode:]
+
Added -[ASTableDataSource tableNode:numberOfRowsInSection:]
+
Added -[ASTableDataSource tableNode:nodeBlockForRowAtIndexPath:]
+
Added -[ASTableDataSource tableNode:nodeForRowAtIndexPath:]
+
Added -[ASTableDelegate tableNode:willDisplayRowWithNode:]
+
Added -[ASTableDelegate tableNode:didEndDisplayingRowWithNode:]
+
Added -[ASTableDelegate tableNode:willSelectRowAtIndexPath:]
+
Added -[ASTableDelegate tableNode:didSelectRowAtIndexPath:]
+
Added -[ASTableDelegate tableNode:willDeselectRowAtIndexPath:]
+
Added -[ASTableDelegate tableNode:didDeselectRowAtIndexPath:]
+
Added -[ASTableDelegate tableNode:shouldHighlightRowAtIndexPath:]
+
Added -[ASTableDelegate tableNode:didHighlightRowAtIndexPath:]
+
Added -[ASTableDelegate tableNode:didUnhighlightRowAtIndexPath:]
+
Added -[ASTableDelegate tableNode:shouldShowMenuForRowAtIndexPath:]
+
Added -[ASTableDelegate tableNode:canPerformAction:forRowAtIndexPath:withSender:]
+
Added -[ASTableDelegate tableNode:performAction:forRowAtIndexPath:withSender:]
+
Added -[ASTableDelegate tableNode:constrainedSizeForRowAtIndexPath:]
+
Added -[ASTableDelegate tableNode:willBeginBatchFetchWithContext:]
+
Added -[ASTableDelegate shouldBatchFetchForTableNode:]
+
Added -[ASTableDelegate tableView:willDisplayNode:forRowAtIndexPath:]
+
+ + +
+
Modified ASTableDataSource
+ + + + +
HeaderProtocols
FromASTableView.hASCommonTableViewDataSource, NSObject
ToASTableNode.hASCommonTableDataSource, NSObject
+
+
Modified -[ASTableDataSource tableView:nodeForRowAtIndexPath:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASTableNode's method instead.
+
+
Modified -[ASTableDataSource tableView:nodeBlockForRowAtIndexPath:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASTableNode's method instead.
+
+
Modified ASTableDelegate
+ + + + +
Header
FromASTableView.h
ToASTableNode.h
+
+
Modified -[ASTableDelegate tableView:didEndDisplayingNode:forRowAtIndexPath:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASTableNode's method instead.
+
+
Modified -[ASTableDelegate tableView:willBeginBatchFetchWithContext:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASTableNode's method instead.
+
+
Modified -[ASTableDelegate shouldBatchFetchForTableView:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASTableNode's method instead.
+
+
Modified -[ASTableDelegate tableView:constrainedSizeForRowAtIndexPath:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASTableNode's method instead.
+
+
Modified -[ASTableDelegate tableView:willDisplayNodeForRowAtIndexPath:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASTableNode's method instead.
+
+
+ +
+ +
+
ASTableView.h
+ +
+
Removed -[ASTableDelegate tableView:didEndDisplayingNodeForRowAtIndexPath:]
+
Removed -[ASTableView initWithFrame:style:asyncDataFetching:]
+
+ + +
+
Added ASTableView.tableNode
+
Added -[ASTableView cellForRowAtIndexPath:]
+
Added -[ASTableView scrollToRowAtIndexPath:atScrollPosition:animated:]
+
Added -[ASTableView selectRowAtIndexPath:animated:scrollPosition:]
+
Added ASTableView.indexPathForSelectedRow
+
Added ASTableView.indexPathsForSelectedRows
+
Added ASTableView.indexPathsForVisibleRows
+
Added -[ASTableView indexPathForRowAtPoint:]
+
Added -[ASTableView indexPathsForRowsInRect:]
+
+ + +
+
Modified -[ASTableView initWithFrame:style:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedPlease use ASTableNode instead of ASTableView.
+
+
Modified -[ASTableView tuningParametersForRangeType:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASTableNode method instead.
+
+
Modified -[ASTableView setTuningParameters:forRangeType:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASTableNode method instead.
+
+
Modified -[ASTableView tuningParametersForRangeMode:rangeType:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASTableNode method instead.
+
+
Modified -[ASTableView setTuningParameters:forRangeMode:rangeType:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASTableNode method instead.
+
+
Modified -[ASTableView visibleNodes]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASTableNode method instead.
+
+
Modified -[ASTableView indexPathForNode:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASTableNode method instead.
+
+
Modified -[ASTableView reloadDataWithCompletion:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASTableNode method instead.
+
+
Modified -[ASTableView reloadData]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASTableNode method instead.
+
+
Modified -[ASTableView reloadDataImmediately]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASTableNode's reloadDataWithCompletion: followed by ASTableNode's -waitUntilAllUpdatesAreCommitted instead.
+
+
Modified -[ASTableView relayoutItems]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASTableNode method instead.
+
+
Modified -[ASTableView beginUpdates]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASTableNode's -performBatchUpdates:completion: instead.
+
+
Modified -[ASTableView endUpdates]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASTableNode's -performBatchUpdates:completion: instead.
+
+
Modified -[ASTableView endUpdatesAnimated:completion:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASTableNode's -performBatchUpdates:completion: instead.
+
+
Modified -[ASTableView waitUntilAllUpdatesAreCommitted]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASTableNode method instead.
+
+
Modified -[ASTableView insertSections:withRowAnimation:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASTableNode method instead.
+
+
Modified -[ASTableView deleteSections:withRowAnimation:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASTableNode method instead.
+
+
Modified -[ASTableView reloadSections:withRowAnimation:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASTableNode method instead.
+
+
Modified -[ASTableView moveSection:toSection:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASTableNode method instead.
+
+
Modified -[ASTableView insertRowsAtIndexPaths:withRowAnimation:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASTableNode method instead.
+
+
Modified -[ASTableView deleteRowsAtIndexPaths:withRowAnimation:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASTableNode method instead.
+
+
Modified -[ASTableView reloadRowsAtIndexPaths:withRowAnimation:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASTableNode method instead.
+
+
Modified -[ASTableView moveRowAtIndexPath:toIndexPath:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse ASTableNode method instead.
+
+
Modified -[ASTableView clearContents]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedYou should not call this method directly. Intead, rely on the Interstate State callback methods.
+
+
Modified -[ASTableView clearFetchedData]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedYou should not call this method directly. Intead, rely on the Interstate State callback methods.
+
+
Modified ASTableViewDataSource
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedRenamed to ASTableDataSource.
+
+
Modified ASTableViewDelegate
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedRenamed to ASTableDelegate.
+
+
Modified ASTableDataSource
+ + + + +
HeaderProtocols
FromASTableView.hASCommonTableViewDataSource, NSObject
ToASTableNode.hASCommonTableDataSource, NSObject
+
+
Modified ASTableDelegate
+ + + + +
Header
FromASTableView.h
ToASTableNode.h
+
+
+ +
+ +
+
ASTableViewProtocols.h
+ +
+
Removed ASCommonTableViewDataSource
+
Removed -[ASCommonTableViewDataSource tableView:numberOfRowsInSection:]
+
Removed -[ASCommonTableViewDataSource numberOfSectionsInTableView:]
+
Removed -[ASCommonTableViewDataSource tableView:titleForHeaderInSection:]
+
Removed -[ASCommonTableViewDataSource tableView:titleForFooterInSection:]
+
Removed -[ASCommonTableViewDataSource tableView:canEditRowAtIndexPath:]
+
Removed -[ASCommonTableViewDataSource tableView:canMoveRowAtIndexPath:]
+
Removed -[ASCommonTableViewDataSource sectionIndexTitlesForTableView:]
+
Removed -[ASCommonTableViewDataSource tableView:sectionForSectionIndexTitle:atIndex:]
+
Removed -[ASCommonTableViewDataSource tableView:commitEditingStyle:forRowAtIndexPath:]
+
Removed -[ASCommonTableViewDataSource tableView:moveRowAtIndexPath:toIndexPath:]
+
+ + +
+
Added ASCommonTableDataSource
+
Added -[ASCommonTableDataSource tableView:numberOfRowsInSection:]
+
Added -[ASCommonTableDataSource numberOfSectionsInTableView:]
+
Added -[ASCommonTableDataSource tableView:titleForHeaderInSection:]
+
Added -[ASCommonTableDataSource tableView:titleForFooterInSection:]
+
Added -[ASCommonTableDataSource tableView:canEditRowAtIndexPath:]
+
Added -[ASCommonTableDataSource tableView:canMoveRowAtIndexPath:]
+
Added -[ASCommonTableDataSource sectionIndexTitlesForTableView:]
+
Added -[ASCommonTableDataSource tableView:sectionForSectionIndexTitle:atIndex:]
+
Added -[ASCommonTableDataSource tableView:commitEditingStyle:forRowAtIndexPath:]
+
Added -[ASCommonTableDataSource tableView:moveRowAtIndexPath:toIndexPath:]
+
+ + +
+
Modified -[ASCommonTableViewDelegate tableView:shouldHighlightRowAtIndexPath:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedImplement -tableNode:shouldHighlightRowAtIndexPath: instead.
+
+
Modified -[ASCommonTableViewDelegate tableView:didHighlightRowAtIndexPath:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedImplement -tableNode:didHighlightRowAtIndexPath: instead.
+
+
Modified -[ASCommonTableViewDelegate tableView:didUnhighlightRowAtIndexPath:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedImplement -tableNode:didUnhighlightRowAtIndexPath: instead.
+
+
Modified -[ASCommonTableViewDelegate tableView:willSelectRowAtIndexPath:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedImplement -tableNode:willSelectRowAtIndexPath: instead.
+
+
Modified -[ASCommonTableViewDelegate tableView:willDeselectRowAtIndexPath:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedImplement -tableNode:willDeselectRowAtIndexPath: instead.
+
+
Modified -[ASCommonTableViewDelegate tableView:didSelectRowAtIndexPath:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedImplement -tableNode:didSelectRowAtIndexPath: instead.
+
+
Modified -[ASCommonTableViewDelegate tableView:didDeselectRowAtIndexPath:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedImplement -tableNode:didDeselectRowAtIndexPath: instead.
+
+
Modified -[ASCommonTableViewDelegate tableView:shouldShowMenuForRowAtIndexPath:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedImplement -tableNode:shouldShowMenuForRowAtIndexPath: instead.
+
+
Modified -[ASCommonTableViewDelegate tableView:canPerformAction:forRowAtIndexPath:withSender:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedImplement -tableNode:canPerformAction:forRowAtIndexPath:withSender: instead.
+
+
Modified -[ASCommonTableViewDelegate tableView:performAction:forRowAtIndexPath:withSender:]
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedImplement -tableNode:performAction:forRowAtIndexPath:withSender: instead.
+
+
+ +
+ +
+
ASTextNode.h
+ +
+
Added ASTextNode (Unavailable)
+
+ + +
+
Modified ASTextNode.attributedString
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse .attributedText instead.
+
+
Modified ASTextNode.truncationAttributedString
+ + + + +
AvailabilityDeprecation Message
FromAvailablenone
ToDeprecatedUse .truncationAttributedText instead.
+
+
+ +
+ +
+
ASTraceEvent.h
+ +
+
Added ASTraceEvent
+
Added -[ASTraceEvent initWithObject:backtrace:format:arguments:]
+
Added ASTraceEvent.backtrace
+
Added ASTraceEvent.message
+
Added ASTraceEvent.timestamp
+
+ +
+ +
+
ASVideoNode.h
+ +
+
Removed -[ASVideoNodeDelegate videoPlaybackDidFinish:]
+
Removed -[ASVideoNodeDelegate videoNodeWasTapped:]
+
Removed -[ASVideoNodeDelegate videoNode:didPlayToSecond:]
+
+ + +
+
Added ASVideoNode.playerLayer
+
Added ASVideoNode (Unavailable)
+
+ +
+ +
+
ASViewController.h
+ +
+
Added ASViewController (Unavailable)
+
+ +
+ +
+
ASWeakSet.h
+ +
+
Added ASWeakSet
+
Added ObjectType
+
Added ASWeakSet.empty
+
Added -[ASWeakSet containsObject:]
+
Added -[ASWeakSet addObject:]
+
Added -[ASWeakSet removeObject:]
+
Added -[ASWeakSet removeAllObjects]
+
Added -[ASWeakSet allObjects]
+
Added ASWeakSet.count
+
+ +
+ +
+
AsyncDisplayKit+Debug.h
+ +
+
Added ASRangeController (Debugging)
+
Added +[ASRangeController setShouldShowRangeDebugOverlay:]
+
Added +[ASRangeController shouldShowRangeDebugOverlay]
+
Added +[ASRangeController layoutDebugOverlayIfNeeded]
+
Added -[ASRangeController addRangeControllerToRangeDebugOverlay]
+
Added -[ASRangeController updateRangeController:withScrollableDirections:scrollDirection:rangeMode:displayTuningParameters:preloadTuningParameters:interfaceState:]
+
+ +
+ +
+
CGRect+ASConvenience.h
+ +
+
Modified ASDirectionalScreenfulBuffer
+ + + + +
Header
FromCGRect+ASConvenience.h
ToCoreGraphics+ASConvenience.h
+
+
Modified ASDirectionalScreenfulBufferHorizontal()
+ + + + +
Header
FromCGRect+ASConvenience.h
ToCoreGraphics+ASConvenience.h
+
+
Modified ASDirectionalScreenfulBufferVertical()
+ + + + +
Header
FromCGRect+ASConvenience.h
ToCoreGraphics+ASConvenience.h
+
+
Modified CGRectExpandToRangeWithScrollableDirections()
+ + + + +
Header
FromCGRect+ASConvenience.h
ToCoreGraphics+ASConvenience.h
+
+
+ +
+ +
+
CoreGraphics+ASConvenience.h
+ +
+
Added #def CGFLOAT_EPSILON
+
Added ASCGFloatFromString()
+
Added ASCGFloatFromNumber()
+
Added CGSizeEqualToSizeWithIn()
+
+ + +
+
Modified ASDirectionalScreenfulBuffer
+ + + + +
Header
FromCGRect+ASConvenience.h
ToCoreGraphics+ASConvenience.h
+
+
Modified ASDirectionalScreenfulBufferHorizontal()
+ + + + +
Header
FromCGRect+ASConvenience.h
ToCoreGraphics+ASConvenience.h
+
+
Modified ASDirectionalScreenfulBufferVertical()
+ + + + +
Header
FromCGRect+ASConvenience.h
ToCoreGraphics+ASConvenience.h
+
+
Modified CGRectExpandToRangeWithScrollableDirections()
+ + + + +
Header
FromCGRect+ASConvenience.h
ToCoreGraphics+ASConvenience.h
+
+
+ +
+ +
+
NSArray+Diffing.h
+ +
+
Added NSArray (Diffing)
+
Added -[NSArray asdk_diffWithArray:insertions:deletions:]
+
Added -[NSArray asdk_diffWithArray:insertions:deletions:compareBlock:]
+
+ +
+ +
+
UIView+ASConvenience.h
+ +
+
Added ASDisplayProperties.allowsGroupOpacity
+
+ +
+ + diff --git a/docs/apidiff/apidiff.css b/docs/apidiff/apidiff.css new file mode 100755 index 0000000000..822ae40118 --- /dev/null +++ b/docs/apidiff/apidiff.css @@ -0,0 +1,87 @@ +body { + font: 12px 'Lucida Grande', 'Lucida Sans Unicode', Helvetica, Arial, Verdana, sans-serif; + margin: 0; + padding: 0 2em 2em 2em; +} + +h1 { + margin-top: 30px; + margin-bottom: 30px; + font-size: 28px; + font-weight: bold; +} + +.headerFile { + margin-left: 20px; +} + +.headerName { + margin: 15px 0px 10px -20px; + padding: 4px 4px 4px 20px; + font-weight: bold; + font-size: 120%; + background-color: #f8f8f8; +} + +.differenceGroup { + margin-top: 5px; +} + +.difference { + padding-left: 20px; + font-family: Courier, Consolas, monospace; + font-size: 110%; +} + +.status { + font-style: italic; + font-size: 80%; +} + +.removed { + color: red; +} + +.added { + color: blue; +} + +.modified { + color: #080; +} + +.declaration { + font-family: Courier, Consolas, monospace; +} + +table { + border: 1px #888 solid; + padding: 2px; + border-spacing: 0px; + border-collapse: collapse; + margin-left: 40px; + margin-top: 7px; +} + +td, th { + font-size: 10px; + border: 1px #888 solid; + padding:3px 6px; +} + +th { + font-size: 10px; + text-align: center; + background-color: #eee; +} + +td { + font-size: 90%; + text-align: left; +} + +.message { + margin-left: 20px; + font-style: italic; + color: #888; +} diff --git a/docs/appledoc/Blocks/ASDisplayNodeContextModifier.html b/docs/appledoc/Blocks/ASDisplayNodeContextModifier.html new file mode 100755 index 0000000000..f52dec6351 --- /dev/null +++ b/docs/appledoc/Blocks/ASDisplayNodeContextModifier.html @@ -0,0 +1,128 @@ + + + + + + ASDisplayNodeContextModifier Block Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASDisplayNodeContextModifier Block Reference

+ + +
+ + + + +
Declared inASDisplayNode.h
+ + + + + + + + + + +

Block Definition

+

ASDisplayNodeContextModifier

+ + +
+

ASDisplayNode will / did render node content in context.

+
+ + + +typedef void (^ASDisplayNodeContextModifier) (CGContextRef context) + + + + + + + + + + + +
+

Declared In

+ ASDisplayNode.h
+
+ + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Blocks/ASDisplayNodeDidLoadBlock.html b/docs/appledoc/Blocks/ASDisplayNodeDidLoadBlock.html new file mode 100755 index 0000000000..eba4dee2c3 --- /dev/null +++ b/docs/appledoc/Blocks/ASDisplayNodeDidLoadBlock.html @@ -0,0 +1,128 @@ + + + + + + ASDisplayNodeDidLoadBlock Block Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASDisplayNodeDidLoadBlock Block Reference

+ + +
+ + + + +
Declared inASDisplayNode.h
+ + + + + + + + + + +

Block Definition

+

ASDisplayNodeDidLoadBlock

+ + +
+

ASDisplayNode loaded callback block. This block is called BEFORE the -didLoad method and is always called on the main thread.

+
+ + + +typedef void (^ASDisplayNodeDidLoadBlock) (__kindof ASDisplayNode, * node) + + + + + + + + + + + +
+

Declared In

+ ASDisplayNode.h
+
+ + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Categories/ASCellNode+.html b/docs/appledoc/Categories/ASCellNode+.html new file mode 100755 index 0000000000..23e0e26c26 --- /dev/null +++ b/docs/appledoc/Categories/ASCellNode+.html @@ -0,0 +1,222 @@ + + + + + + ASCellNode() Category Reference + + + + + + +
+
+ +

+ +

+ +

+ Texture +

+ +
+
+ + + +
+
+
+
+

ASCellNode() Category Reference

+ + +
+ + + + +
Declared inASCellNode+Internal.h
+ + + + + + +
+ + + + + + +
+
+ +

  layoutAttributes +

+ +
+
+ +
+ + +
+

This could be declared @c copy, but since this is only settable internally, we can ensure +that it’s always safe simply to retain it, and copy if needed. Since @c UICollectionViewLayoutAttributes +is always mutable, @c copy is never “free” like it is for e.g. NSString.

+
+ + + +
@property (nonatomic, strong, nullable) UICollectionViewLayoutAttributes *layoutAttributes
+ + + + + + + + + +
+

Discussion

+

Note: This could be declared @c copy, but since this is only settable internally, we can ensure +that it’s always safe simply to retain it, and copy if needed. Since @c UICollectionViewLayoutAttributes +is always mutable, @c copy is never “free” like it is for e.g. NSString.

+
+ + + + + + + +
+

Declared In

+

ASCellNode+Internal.h

+
+ + +
+
+
+ +

  supplementaryElementKind +

+ +
+
+ +
+ + +
+

readwrite variant of the readonly public property.

+
+ + + +
@property (nonatomic, copy, nullable) NSString *supplementaryElementKind
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASCellNode+Internal.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ + +
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Categories/ASCollectionNode+Deprecated.html b/docs/appledoc/Categories/ASCollectionNode+Deprecated.html new file mode 100755 index 0000000000..3a528cffbd --- /dev/null +++ b/docs/appledoc/Categories/ASCollectionNode+Deprecated.html @@ -0,0 +1,176 @@ + + + + + + ASCollectionNode(Deprecated) Category Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASCollectionNode(Deprecated) Category Reference

+ + +
+ + + + +
Declared inASCollectionNode.h
+ + + + + + +
+ + + + + + +
+
+ +

– reloadDataImmediately +

+ +
+
+ +
+ + +
+

Reload everything from scratch, destroying the working range and all cached nodes. (Deprecated: This method is deprecated in 2.0. Use @c reloadDataWithCompletion: and +then @c waitUntilAllUpdatesAreCommitted instead.)

+
+ + + +
- (void)reloadDataImmediately
+ + + + + + + + + +
+

Discussion

+

Warning: This method is substantially more expensive than UICollectionView’s version.

+
+ + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Categories/ASCollectionView+Deprecated.html b/docs/appledoc/Categories/ASCollectionView+Deprecated.html new file mode 100755 index 0000000000..9d4741fee3 --- /dev/null +++ b/docs/appledoc/Categories/ASCollectionView+Deprecated.html @@ -0,0 +1,1616 @@ + + + + + + ASCollectionView(Deprecated) Category Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASCollectionView(Deprecated) Category Reference

+ + +
+ + + + +
Declared inASCollectionView.h
+ + + + + + +
+ + + + + + +
+
+ +

– initWithCollectionViewLayout: +

+ +
+
+ +
+ + +
+

Initializes an ASCollectionView

+
+ + + +
- (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout
+ + + +
+

Parameters

+ + + + + + + +
layout

The layout object to use for organizing items. The collection view stores a strong reference to the specified object. Must not be nil.

+
+ + + + + + + +
+

Discussion

+

Initializes and returns a newly allocated collection view object with the specified layout.

+
+ + + + + + + +
+

Declared In

+

ASCollectionView.h

+
+ + +
+
+
+ +

– initWithFrame:collectionViewLayout: +

+ +
+
+ +
+ + +
+

Initializes an ASCollectionView

+
+ + + +
- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout
+ + + +
+

Parameters

+ + + + + + + + + + + + +
frame

The frame rectangle for the collection view, measured in points. The origin of the frame is relative to the superview in which you plan to add it. This frame is passed to the superclass during initialization.

layout

The layout object to use for organizing items. The collection view stores a strong reference to the specified object. Must not be nil.

+
+ + + + + + + +
+

Discussion

+

Initializes and returns a newly allocated collection view object with the specified frame and layout.

+
+ + + + + + + +
+

Declared In

+

ASCollectionView.h

+
+ + +
+
+
+ +

– tuningParametersForRangeType: +

+ +
+
+ +
+ + +
+

Tuning parameters for a range type in full mode.

+
+ + + +
- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType
+ + + +
+

Parameters

+ + + + + + + +
rangeType

The range type to get the tuning parameters for.

+
+ + + +
+

Return Value

+

A tuning parameter value for the given range type in full mode.

+
+ + + + + + + + + + + +
+

Declared In

+

ASCollectionView.h

+
+ + +
+
+
+ +

– setTuningParameters:forRangeType: +

+ +
+
+ +
+ + +
+

Set the tuning parameters for a range type in full mode.

+
+ + + +
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType
+ + + +
+

Parameters

+ + + + + + + + + + + + +
tuningParameters

The tuning parameters to store for a range type.

rangeType

The range type to set the tuning parameters for.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASCollectionView.h

+
+ + +
+
+
+ +

– tuningParametersForRangeMode:rangeType: +

+ +
+
+ +
+ + +
+

Tuning parameters for a range type in the specified mode.

+
+ + + +
- (ASRangeTuningParameters)tuningParametersForRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType
+ + + +
+

Parameters

+ + + + + + + + + + + + +
rangeMode

The range mode to get the running parameters for.

rangeType

The range type to get the tuning parameters for.

+
+ + + +
+

Return Value

+

A tuning parameter value for the given range type in the given mode.

+
+ + + + + + + + + + + +
+

Declared In

+

ASCollectionView.h

+
+ + +
+
+
+ +

– setTuningParameters:forRangeMode:rangeType: +

+ +
+
+ +
+ + +
+

Set the tuning parameters for a range type in the specified mode.

+
+ + + +
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + +
tuningParameters

The tuning parameters to store for a range type.

rangeMode

The range mode to set the running parameters for.

rangeType

The range type to set the tuning parameters for.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASCollectionView.h

+
+ + +
+
+
+ +

– performBatchAnimated:updates:completion: +

+ +
+
+ +
+ + +
+

Perform a batch of updates asynchronously, optionally disabling all animations in the batch. This method must be called from the main thread. +The asyncDataSource must be updated to reflect the changes before the update block completes.

+
+ + + +
- (void)performBatchAnimated:(BOOL)animated updates:(nullable __attribute ( ( noescape ) ) void ( ^ ) ( ))updates completion:(nullable void ( ^ ) ( BOOL finished ))completion
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + +
animated

NO to disable animations for this batch

updates

The block that performs the relevant insert, delete, reload, or move operations.

completion

A completion handler block to execute when all of the operations are finished. This block takes a single +Boolean parameter that contains the value YES if all of the related animations completed successfully or +NO if they were interrupted. This parameter may be nil. If supplied, the block is run on the main thread.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASCollectionView.h

+
+ + +
+
+
+ +

– performBatchUpdates:completion: +

+ +
+
+ +
+ + +
+

Perform a batch of updates asynchronously. This method must be called from the main thread. +The asyncDataSource must be updated to reflect the changes before update block completes.

+
+ + + +
- (void)performBatchUpdates:(nullable __attribute ( ( noescape ) ) void ( ^ ) ( ))updates completion:(nullable void ( ^ ) ( BOOL finished ))completion
+ + + +
+

Parameters

+ + + + + + + + + + + + +
updates

The block that performs the relevant insert, delete, reload, or move operations.

completion

A completion handler block to execute when all of the operations are finished. This block takes a single +Boolean parameter that contains the value YES if all of the related animations completed successfully or +NO if they were interrupted. This parameter may be nil. If supplied, the block is run on the main thread.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASCollectionView.h

+
+ + +
+
+
+ +

– reloadDataWithCompletion: +

+ +
+
+ +
+ + +
+

Reload everything from scratch, destroying the working range and all cached nodes.

+
+ + + +
- (void)reloadDataWithCompletion:(nullable void ( ^ ) ( ))completion
+ + + +
+

Parameters

+ + + + + + + +
completion

block to run on completion of asynchronous loading or nil. If supplied, the block is run on +the main thread.

+
+ + + + + + + +
+

Discussion

+

Warning: This method is substantially more expensive than UICollectionView’s version.

+
+ + + + + + + +
+

Declared In

+

ASCollectionView.h

+
+ + +
+
+
+ +

– reloadData +

+ +
+
+ +
+ + +
+

Reload everything from scratch, destroying the working range and all cached nodes.

+
+ + + +
- (void)reloadData
+ + + + + + + + + +
+

Discussion

+

Warning: This method is substantially more expensive than UICollectionView’s version.

+
+ + + + + + + +
+

Declared In

+

ASCollectionView.h

+
+ + +
+
+
+ +

– reloadDataImmediately +

+ +
+
+ +
+ + +
+

Reload everything from scratch entirely on the main thread, destroying the working range and all cached nodes.

+
+ + + +
- (void)reloadDataImmediately
+ + + + + + + + + +
+

Discussion

+

Warning: This method is substantially more expensive than UICollectionView’s version and will block the main thread +while all the cells load.

+
+ + + + + + + +
+

Declared In

+

ASCollectionView.h

+
+ + +
+
+
+ +

– relayoutItems +

+ +
+
+ +
+ + +
+

Triggers a relayout of all nodes.

+
+ + + +
- (void)relayoutItems
+ + + + + + + + + +
+

Discussion

+

This method invalidates and lays out every cell node in the collection.

+
+ + + + + + + +
+

Declared In

+

ASCollectionView.h

+
+ + +
+
+
+ +

– waitUntilAllUpdatesAreCommitted +

+ +
+
+ +
+ + +
+

Blocks execution of the main thread until all section and row updates are committed. This method must be called from the main thread.

+
+ + + +
- (void)waitUntilAllUpdatesAreCommitted
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASCollectionView.h

+
+ + +
+
+
+ +

– registerSupplementaryNodeOfKind: +

+ +
+
+ +
+ + +
+

Registers the given kind of supplementary node for use in creating node-backed supplementary views.

+
+ + + +
- (void)registerSupplementaryNodeOfKind:(NSString *)elementKind
+ + + +
+

Parameters

+ + + + + + + +
elementKind

The kind of supplementary node that will be requested through the data source.

+
+ + + + + + + +
+

Discussion

+

Use this method to register support for the use of supplementary nodes in place of the default +registerClass:forSupplementaryViewOfKind:withReuseIdentifier: and registerNib:forSupplementaryViewOfKind:withReuseIdentifier: +methods. This method will register an internal backing view that will host the contents of the supplementary nodes +returned from the data source.

+
+ + + + + + + +
+

Declared In

+

ASCollectionView.h

+
+ + +
+
+
+ +

– insertSections: +

+ +
+
+ +
+ + +
+

Inserts one or more sections.

+
+ + + +
- (void)insertSections:(NSIndexSet *)sections
+ + + +
+

Parameters

+ + + + + + + +
sections

An index set that specifies the sections to insert.

+
+ + + + + + + +
+

Discussion

+

This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes +before this method is called.

+
+ + + + + + + +
+

Declared In

+

ASCollectionView.h

+
+ + +
+
+
+ +

– deleteSections: +

+ +
+
+ +
+ + +
+

Deletes one or more sections.

+
+ + + +
- (void)deleteSections:(NSIndexSet *)sections
+ + + +
+

Parameters

+ + + + + + + +
sections

An index set that specifies the sections to delete.

+
+ + + + + + + +
+

Discussion

+

This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes +before this method is called.

+
+ + + + + + + +
+

Declared In

+

ASCollectionView.h

+
+ + +
+
+
+ +

– reloadSections: +

+ +
+
+ +
+ + +
+

Reloads the specified sections.

+
+ + + +
- (void)reloadSections:(NSIndexSet *)sections
+ + + +
+

Parameters

+ + + + + + + +
sections

An index set that specifies the sections to reload.

+
+ + + + + + + +
+

Discussion

+

This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes +before this method is called.

+
+ + + + + + + +
+

Declared In

+

ASCollectionView.h

+
+ + +
+
+
+ +

– moveSection:toSection: +

+ +
+
+ +
+ + +
+

Moves a section to a new location.

+
+ + + +
- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection
+ + + +
+

Parameters

+ + + + + + + + + + + + +
section

The index of the section to move.

newSection

The index that is the destination of the move for the section.

+
+ + + + + + + +
+

Discussion

+

This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes +before this method is called.

+
+ + + + + + + +
+

Declared In

+

ASCollectionView.h

+
+ + +
+
+
+ +

– insertItemsAtIndexPaths: +

+ +
+
+ +
+ + +
+

Inserts items at the locations identified by an array of index paths.

+
+ + + +
- (void)insertItemsAtIndexPaths:(NSArray<NSIndexPath*> *)indexPaths
+ + + +
+

Parameters

+ + + + + + + +
indexPaths

An array of NSIndexPath objects, each representing an item index and section index that together identify an item.

+
+ + + + + + + +
+

Discussion

+

This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes +before this method is called.

+
+ + + + + + + +
+

Declared In

+

ASCollectionView.h

+
+ + +
+
+
+ +

– deleteItemsAtIndexPaths: +

+ +
+
+ +
+ + +
+

Deletes the items specified by an array of index paths.

+
+ + + +
- (void)deleteItemsAtIndexPaths:(NSArray<NSIndexPath*> *)indexPaths
+ + + +
+

Parameters

+ + + + + + + +
indexPaths

An array of NSIndexPath objects identifying the items to delete.

+
+ + + + + + + +
+

Discussion

+

This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes +before this method is called.

+
+ + + + + + + +
+

Declared In

+

ASCollectionView.h

+
+ + +
+
+
+ +

– reloadItemsAtIndexPaths: +

+ +
+
+ +
+ + +
+

Reloads the specified items.

+
+ + + +
- (void)reloadItemsAtIndexPaths:(NSArray<NSIndexPath*> *)indexPaths
+ + + +
+

Parameters

+ + + + + + + +
indexPaths

An array of NSIndexPath objects identifying the items to reload.

+
+ + + + + + + +
+

Discussion

+

This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes +before this method is called.

+
+ + + + + + + +
+

Declared In

+

ASCollectionView.h

+
+ + +
+
+
+ +

– moveItemAtIndexPath:toIndexPath: +

+ +
+
+ +
+ + +
+

Moves the item at a specified location to a destination location.

+
+ + + +
- (void)moveItemAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath
+ + + +
+

Parameters

+ + + + + + + + + + + + +
indexPath

The index path identifying the item to move.

newIndexPath

The index path that is the destination of the move for the item.

+
+ + + + + + + +
+

Discussion

+

This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes +before this method is called.

+
+ + + + + + + +
+

Declared In

+

ASCollectionView.h

+
+ + +
+
+
+ +

– calculatedSizeForNodeAtIndexPath: +

+ +
+
+ +
+ + +
+

Query the sized node at @c indexPath for its calculatedSize.

+
+ + + +
- (CGSize)calculatedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath
+ + + +
+

Parameters

+ + + + + + + +
indexPath

The index path for the node of interest.

+ +

This method is deprecated. Call @c calculatedSize on the node of interest instead. First deprecated in version 2.0.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASCollectionView.h

+
+ + +
+
+
+ +

– visibleNodes +

+ +
+
+ +
+ + +
+

Similar to -visibleCells.

+
+ + + +
- (NSArray<__kindofASCellNode*> *)visibleNodes
+ + + + + +
+

Return Value

+

an array containing the nodes being displayed on screen.

+
+ + + + + + + + + + + +
+

Declared In

+

ASCollectionView.h

+
+ + +
+
+
+ +

– indexPathForNode: +

+ +
+
+ +
+ + +
+

Similar to -indexPathForCell:.

+
+ + + +
- (nullable NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode
+ + + +
+

Parameters

+ + + + + + + +
cellNode

a cellNode in the collection view

+
+ + + +
+

Return Value

+

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.

+
+ + + + + + + +
+

Declared In

+

ASCollectionView.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Categories/ASControlNode+Debugging.html b/docs/appledoc/Categories/ASControlNode+Debugging.html new file mode 100755 index 0000000000..5f1703f06c --- /dev/null +++ b/docs/appledoc/Categories/ASControlNode+Debugging.html @@ -0,0 +1,187 @@ + + + + + + ASControlNode(Debugging) Category Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASControlNode(Debugging) Category Reference

+ + +
+ + + + +
Declared inAsyncDisplayKit+Debug.h
+ + + + + + +
+ + + + + + +
+
+ +

+ setEnableHitTestDebug: +

+ +
+
+ +
+ + +
+

Class method to enable a visualization overlay of the tappable area on the ASControlNode. For app debugging purposes only. +NOTE: GESTURE RECOGNIZERS, (including tap gesture recognizers on a control node) WILL NOT BE VISUALIZED!!! +Overlay = translucent GREEN color, +edges that are clipped by the tappable area of any parent (their bounds + hitTestSlop) in the hierarchy = DARK GREEN BORDERED EDGE, +edges that are clipped by clipToBounds = YES of any parent in the hierarchy = ORANGE BORDERED EDGE (may still receive touches beyond +overlay rect, but can’t be visualized).

+
+ + + +
+ (void)setEnableHitTestDebug:(BOOL)enable
+ + + +
+

Parameters

+ + + + + + + +
enable

Specify YES to make this debug feature enabled when messaging the ASControlNode class.

+
+ + + + + + + + + + + + + +
+

Declared In

+

AsyncDisplayKit+Debug.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Categories/ASControlNode+Subclassing.html b/docs/appledoc/Categories/ASControlNode+Subclassing.html new file mode 100755 index 0000000000..cfff640cdc --- /dev/null +++ b/docs/appledoc/Categories/ASControlNode+Subclassing.html @@ -0,0 +1,497 @@ + + + + + + ASControlNode(Subclassing) Category Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASControlNode(Subclassing) Category Reference

+ + +
+ + + + +
Declared inASControlNode+Subclasses.h
+ + + + +
+ +

Overview

+

The subclass header ASControlNode+Subclasses defines methods to be +overridden by custom nodes that subclass ASControlNode.

+ +

These methods should never be called directly by other classes.

+
+ + + + + +
+ + + + + + +
+
+ +

– sendActionsForControlEvents:withEvent: +

+ +
+
+ +
+ + +
+

Sends action messages for the given control events.

+
+ + + +
- (void)sendActionsForControlEvents:(ASControlNodeEvent)controlEvents withEvent:(nullable UIEvent *)touchEvent
+ + + +
+

Parameters

+ + + + + + + + + + + + +
controlEvents

A bitmask whose set flags specify the control events for which action messages are sent. See “Control Events” in ASControlNode.h for bitmask constants.

touchEvent

An event object encapsulating the information specific to the user event.

+
+ + + + + + + +
+

Discussion

+

ASControlNode implements this method to send all action messages associated with controlEvents. The list of targets is constructed from prior invocations of addTarget:action:forControlEvents:.

+
+ + + + + + + +
+

Declared In

+

ASControlNode+Subclasses.h

+
+ + +
+
+
+ +

– beginTrackingWithTouch:withEvent: +

+ +
+
+ +
+ + +
+

Sent to the control when tracking begins.

+
+ + + +
- (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(nullable UIEvent *)touchEvent
+ + + +
+

Parameters

+ + + + + + + + + + + + +
touch

The touch on the receiving control.

touchEvent

An event object encapsulating the information specific to the user event.

+
+ + + +
+

Return Value

+

YES if the receiver should respond continuously (respond when touch is dragged); NO otherwise.

+
+ + + + + + + + + + + +
+

Declared In

+

ASControlNode+Subclasses.h

+
+ + +
+
+
+ +

– continueTrackingWithTouch:withEvent: +

+ +
+
+ +
+ + +
+

Sent continuously to the control as it tracks a touch within the control’s bounds.

+
+ + + +
- (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(nullable UIEvent *)touchEvent
+ + + +
+

Parameters

+ + + + + + + + + + + + +
touch

The touch on the receiving control.

touchEvent

An event object encapsulating the information specific to the user event.

+
+ + + +
+

Return Value

+

YES if touch tracking should continue; NO otherwise.

+
+ + + + + + + + + + + +
+

Declared In

+

ASControlNode+Subclasses.h

+
+ + +
+
+
+ +

– cancelTrackingWithEvent: +

+ +
+
+ +
+ + +
+

Sent to the control when tracking should be cancelled.

+
+ + + +
- (void)cancelTrackingWithEvent:(nullable UIEvent *)touchEvent
+ + + +
+

Parameters

+ + + + + + + +
touchEvent

An event object encapsulating the information specific to the user event. This parameter may be nil, indicating that the cancelation was caused by something other than an event, such as the display node being removed from its supernode.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASControlNode+Subclasses.h

+
+ + +
+
+
+ +

– endTrackingWithTouch:withEvent: +

+ +
+
+ +
+ + +
+

Sent to the control when the last touch completely ends, telling it to stop tracking.

+
+ + + +
- (void)endTrackingWithTouch:(nullable UITouch *)touch withEvent:(nullable UIEvent *)touchEvent
+ + + +
+

Parameters

+ + + + + + + + + + + + +
touch

The touch that ended.

touchEvent

An event object encapsulating the information specific to the user event.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASControlNode+Subclasses.h

+
+ + +
+
+
+ +

  highlighted +

+ +
+
+ +
+ + +
+

Settable version of highlighted property.

+
+ + + +
@property (nonatomic, readwrite, assign, getter=isHighlighted) BOOL highlighted
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASControlNode+Subclasses.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Categories/ASDisplayNode+AutomaticSubnodeManagement.html b/docs/appledoc/Categories/ASDisplayNode+AutomaticSubnodeManagement.html new file mode 100755 index 0000000000..d0bcbb6698 --- /dev/null +++ b/docs/appledoc/Categories/ASDisplayNode+AutomaticSubnodeManagement.html @@ -0,0 +1,177 @@ + + + + + + ASDisplayNode(AutomaticSubnodeManagement) Category Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASDisplayNode(AutomaticSubnodeManagement) Category Reference

+ + +
+ + + + +
Declared inASDisplayNode.h
+ + + + + + +
+ + + + + + +
+
+ +

  automaticallyManagesSubnodes +

+ +
+
+ +
+ + +
+

A boolean that shows whether the node automatically inserts and removes nodes based on the presence or +absence of the node and its subnodes is completely determined in its layoutSpecThatFits: method.

+
+ + + +
@property (nonatomic, assign) BOOL automaticallyManagesSubnodes
+ + + + + + + + + +
+

Discussion

+

If flag is YES the node no longer require addSubnode: or removeFromSupernode method calls. The presence +or absence of subnodes is completely determined in its layoutSpecThatFits: method.

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Categories/ASDisplayNode+Beta.html b/docs/appledoc/Categories/ASDisplayNode+Beta.html new file mode 100755 index 0000000000..3e7b7af638 --- /dev/null +++ b/docs/appledoc/Categories/ASDisplayNode+Beta.html @@ -0,0 +1,635 @@ + + + + + + ASDisplayNode(Beta) Category Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASDisplayNode(Beta) Category Reference

+ + +
+ + + + +
Declared inASDisplayNode+Beta.h
+ + + + + + +
+ + + + + + +
+
+ +

+ suppressesInvalidCollectionUpdateExceptions +

+ +
+
+ +
+ + +
+

ASTableView and ASCollectionView now throw exceptions on invalid updates +like their UIKit counterparts. If YES, these classes will log messages +on invalid updates rather than throwing exceptions.

+
+ + + +
+ (BOOL)suppressesInvalidCollectionUpdateExceptions
+ + + + + + + + + +
+

Discussion

+

Note that even if AsyncDisplayKit’s exception is suppressed, the app may still crash +as it proceeds with an invalid update.

+ +

This property defaults to NO. It will be removed in a future release.

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode+Beta.h

+
+ + +
+
+
+ +

– recursivelyEnsureDisplaySynchronously: +

+ +
+
+ +
+ + +
+

Recursively ensures node and all subnodes are displayed.

+
+ + + +
- (void)recursivelyEnsureDisplaySynchronously:(BOOL)synchronously
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASDisplayNode+Beta.h

+
+ + +
+
+
+ +

  willDisplayNodeContentWithRenderingContext +

+ +
+
+ +
+ + +
+

allow modification of a context before the node’s content is drawn

+
+ + + +
@property (nonatomic, copy, nullable) ASDisplayNodeContextModifier willDisplayNodeContentWithRenderingContext
+ + + + + + + + + +
+

Discussion

+

Set the block to be called after the context has been created and before the node’s content is drawn. +You can override this to modify the context before the content is drawn. You are responsible for saving and +restoring context if necessary. Restoring can be done in contextDidDisplayNodeContent +This block can be called from any thread and it is unsafe to access any UIKit main thread properties from it.

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode+Beta.h

+
+ + +
+
+
+ +

  didDisplayNodeContentWithRenderingContext +

+ +
+
+ +
+ + +
+

allow modification of a context after the node’s content is drawn

+
+ + + +
@property (nonatomic, copy, nullable) ASDisplayNodeContextModifier didDisplayNodeContentWithRenderingContext
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASDisplayNode+Beta.h

+
+ + +
+
+
+ +

  measurementOptions +

+ +
+
+ +
+ + +
+

A bitmask representing which actions (layout spec, layout generation) should be measured.

+
+ + + +
@property (nonatomic, assign) ASDisplayNodePerformanceMeasurementOptions measurementOptions
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASDisplayNode+Beta.h

+
+ + +
+
+
+ +

  performanceMeasurements +

+ +
+
+ +
+ + +
+

A simple struct representing performance measurements collected.

+
+ + + +
@property (nonatomic, assign, readonly) ASDisplayNodePerformanceMeasurements performanceMeasurements
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASDisplayNode+Beta.h

+
+ + +
+
+
+ +

– placeholderShouldPersist +

+ +
+
+ +
+ + +
+

Currently used by ASNetworkImageNode and ASMultiplexImageNode to allow their placeholders to stay if they are loading an image from the network. +Otherwise, a display pass is scheduled and completes, but does not actually draw anything - and ASDisplayNode considers the element finished.

+
+ + + +
- (BOOL)placeholderShouldPersist
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASDisplayNode+Beta.h

+
+ + +
+
+
+ +

– hierarchyDisplayDidFinish +

+ +
+
+ +
+ + +
+

Indicates that the receiver and all subnodes have finished displaying. May be called more than once, for example if the receiver has +a network image node. This is called after the first display pass even if network image nodes have not downloaded anything (text would be done, +and other nodes that are ready to do their final display). Each render of every progressive jpeg network node would cause this to be called, so +this hook could be called up to 1 + (pJPEGcount * pJPEGrenderCount) times. The render count depends on how many times the downloader calls the +progressImage block.

+
+ + + +
- (void)hierarchyDisplayDidFinish
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASDisplayNode+Beta.h

+
+ + +
+
+
+ +

+ setRangeModeForMemoryWarnings: +

+ +
+
+ +
+ + +
+

Only ASLayoutRangeModeVisibleOnly or ASLayoutRangeModeLowMemory are recommended. Default is ASLayoutRangeModeVisibleOnly, +because this is the only way to ensure an application will not have blank / flashing views as the user navigates back after +a memory warning. Apps that wish to use the more effective / aggressive ASLayoutRangeModeLowMemory may need to take steps +to mitigate this behavior, including: restoring a larger range mode to the next controller before the user navigates there, +enabling .neverShowPlaceholders on ASCellNodes so that the navigation operation is blocked on redisplay completing, etc.

+
+ + + +
+ (void)setRangeModeForMemoryWarnings:(ASLayoutRangeMode)rangeMode
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASDisplayNode+Beta.h

+
+ + +
+
+
+ +

– _logEventWithBacktrace:format: +

+ +
+
+ +
+ + +
+

The primitive event tracing method. You shouldn’t call this. Use the ASDisplayNodeLogEvent macro instead.

+
+ + + +
- (void)_logEventWithBacktrace:(NSArray<NSString*> *)backtrace format:(NSString *)format, ...
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASDisplayNode+Beta.h

+
+ + +
+
+
+ +

  eventLog +

+ +
+
+ +
+ + +
+

The most recent trace events for this node. Max count is ASDISPLAYNODE_EVENTLOG_CAPACITY.

+
+ + + +
@property (readonly, copy) NSArray *eventLog
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASDisplayNode+Beta.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Categories/ASDisplayNode+Debugging.html b/docs/appledoc/Categories/ASDisplayNode+Debugging.html new file mode 100755 index 0000000000..33e97cf949 --- /dev/null +++ b/docs/appledoc/Categories/ASDisplayNode+Debugging.html @@ -0,0 +1,188 @@ + + + + + + ASDisplayNode(Debugging) Category Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASDisplayNode(Debugging) Category Reference

+ + +
+ + + + + + + +
Conforms toASLayoutElementAsciiArtProtocol
Declared inASDisplayNode.h
+ + + + +
+ +

Overview

+

Convenience methods for debugging.

+
+ + + + + +
+ + + + + + +
+
+ +

– displayNodeRecursiveDescription +

+ +
+
+ +
+ + +
+

Return a description of the node hierarchy.

+
+ + + +
- (NSString *)displayNodeRecursiveDescription
+ + + + + + + + + +
+

Discussion

+

For debugging: (lldb) po [node displayNodeRecursiveDescription]

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Categories/ASDisplayNode+Deprecated.html b/docs/appledoc/Categories/ASDisplayNode+Deprecated.html new file mode 100755 index 0000000000..5eb821eb29 --- /dev/null +++ b/docs/appledoc/Categories/ASDisplayNode+Deprecated.html @@ -0,0 +1,487 @@ + + + + + + ASDisplayNode(Deprecated) Category Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASDisplayNode(Deprecated) Category Reference

+ + +
+ + + + +
Declared inASDisplayNode+Deprecated.h
+ + + + + + +
+ + + + + + +
+
+ +

  ) +

+ +
+
+ +
+ + +
+

The name of this node, which will be displayed in description. The default value is nil. (Deprecated: Deprecated in version 2.0: Use .debugName instead. This value will display in +results of the -asciiArtString method (@see ASLayoutElementAsciiArtProtocol).)

+
+ + + +
@property (nullable, nonatomic, copy) NSString *ASDISPLAYNODE_DEPRECATED_MSG ( "Use .debugName instead." )
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASDisplayNode+Deprecated.h

+
+ + +
+
+
+ +

– measure: +

+ +
+
+ +
+ + +
+

Asks the node to measure and return the size that best fits its subnodes. (Deprecated: Deprecated in version 2.0: Use layoutThatFits: with a constrained size of (CGSizeZero, constrainedSize) and call size on the returned ASLayout)

+
+ + + +
- (CGSize)measure:(CGSize)constrainedSize
+ + + +
+

Parameters

+ + + + + + + +
constrainedSize

The maximum size the receiver should fit in.

+
+ + + +
+

Return Value

+

A new size that fits the receiver’s subviews.

+
+ + + + + +
+

Discussion

+

Though this method does not set the bounds of the view, it does have side effects–caching both the +constraint and the result.

Warning: Subclasses must not override this; it calls -measureWithSizeRange: with zero min size. +-measureWithSizeRange: caches results from -calculateLayoutThatFits:. Calling this method may +be expensive if result is not cached.

+
+ + + + + + + + + +
+

Declared In

+

ASDisplayNode+Deprecated.h

+
+ + +
+
+
+ +

– visibilityDidChange: +

+ +
+
+ +
+ + +
+

Called whenever the visiblity of the node changed. (Deprecated: @see didEnterVisibleState @see didExitVisibleState)

+
+ + + +
- (void)visibilityDidChange:(BOOL)isVisible
+ + + + + + + + + +
+

Discussion

+

Subclasses may use this to monitor when they become visible.

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode+Deprecated.h

+
+ + +
+
+
+ +

– visibleStateDidChange: +

+ +
+
+ +
+ + +
+

Called whenever the visiblity of the node changed. (Deprecated: @see didEnterVisibleState @see didExitVisibleState)

+
+ + + +
- (void)visibleStateDidChange:(BOOL)isVisible
+ + + + + + + + + +
+

Discussion

+

Subclasses may use this to monitor when they become visible.

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode+Deprecated.h

+
+ + +
+
+
+ +

– displayStateDidChange: +

+ +
+
+ +
+ + +
+

Called whenever the the node has entered or exited the display state. (Deprecated: @see didEnterDisplayState @see didExitDisplayState)

+
+ + + +
- (void)displayStateDidChange:(BOOL)inDisplayState
+ + + + + + + + + +
+

Discussion

+

Subclasses may use this to monitor when a node should be rendering its content.

Note: This method can be called from any thread and should therefore be thread safe.

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode+Deprecated.h

+
+ + +
+
+
+ +

– loadStateDidChange: +

+ +
+
+ +
+ + +
+

Called whenever the the node has entered or left the load state. (Deprecated: @see didEnterPreloadState @see didExitPreloadState)

+
+ + + +
- (void)loadStateDidChange:(BOOL)inLoadState
+ + + + + + + + + +
+

Discussion

+

Subclasses may use this to monitor data for a node should be loaded, either from a local or remote source.

Note: This method can be called from any thread and should therefore be thread safe.

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode+Deprecated.h

+
+ + +
+
+
+ +

– cancelLayoutTransitionsInProgress +

+ +
+
+ +
+ + +
+

Cancels all performing layout transitions. Can be called on any thread. (Deprecated: Deprecated in version 2.0: Use cancelLayoutTransition)

+
+ + + +
- (void)cancelLayoutTransitionsInProgress
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASDisplayNode+Deprecated.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Categories/ASDisplayNode+LayoutTransitioning.html b/docs/appledoc/Categories/ASDisplayNode+LayoutTransitioning.html new file mode 100755 index 0000000000..986923fb2a --- /dev/null +++ b/docs/appledoc/Categories/ASDisplayNode+LayoutTransitioning.html @@ -0,0 +1,560 @@ + + + + + + ASDisplayNode(LayoutTransitioning) Category Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASDisplayNode(LayoutTransitioning) Category Reference

+ + +
+ + + + +
Declared inASDisplayNode.h
+ + + + + + +
+ + + + + + +
+
+ +

  defaultLayoutTransitionDuration +

+ +
+
+ +
+ + +
+

The amount of time it takes to complete the default transition animation. Default is 0.2.

+
+ + + +
@property (nonatomic, assign) NSTimeInterval defaultLayoutTransitionDuration
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+ +

  defaultLayoutTransitionDelay +

+ +
+
+ +
+ + +
+

The amount of time (measured in seconds) to wait before beginning the default transition animation. +Default is 0.0.

+
+ + + +
@property (nonatomic, assign) NSTimeInterval defaultLayoutTransitionDelay
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+ +

  defaultLayoutTransitionOptions +

+ +
+
+ +
+ + +
+

A mask of options indicating how you want to perform the default transition animations. +For a list of valid constants, see UIViewAnimationOptions.

+
+ + + +
@property (nonatomic, assign) UIViewAnimationOptions defaultLayoutTransitionOptions
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+ +

– animateLayoutTransition: +

+ +
+
+ +
+ + +
+

A place to perform your animation. New nodes have been inserted here. You can also use this time to re-order the hierarchy.

+
+ + + +
- (void)animateLayoutTransition:(nonnull id<ASContextTransitioning>)context
+ + + + + + + + + +
+

Discussion

+

A place to perform your animation. New nodes have been inserted here. You can also use this time to re-order the hierarchy.

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+ +

– didCompleteLayoutTransition: +

+ +
+
+ +
+ + +
+

A place to clean up your nodes after the transition

+
+ + + +
- (void)didCompleteLayoutTransition:(nonnull id<ASContextTransitioning>)context
+ + + + + + + + + +
+

Discussion

+

A place to clean up your nodes after the transition

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+ +

– transitionLayoutWithSizeRange:animated:shouldMeasureAsync:measurementCompletion: +

+ +
+
+ +
+ + +
+

Transitions the current layout with a new constrained size. Must be called on main thread.

+
+ + + +
- (void)transitionLayoutWithSizeRange:(ASSizeRange)constrainedSize animated:(BOOL)animated shouldMeasureAsync:(BOOL)shouldMeasureAsync measurementCompletion:(nullable void ( ^ ) ( ))completion
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + +
animated

Animation is optional, but will still proceed through your animateLayoutTransition implementation with isAnimated == NO.

shouldMeasureAsync

Measure the layout asynchronously.

measurementCompletion

Optional completion block called only if a new layout is calculated. +It is called on main, right after the measurement and before -animateLayoutTransition:.

+
+ + + + + + + +
+

Discussion

+

If the passed constrainedSize is the the same as the node’s current constrained size, this method is noop. If passed YES to shouldMeasureAsync it’s guaranteed that measurement is happening on a background thread, otherwise measaurement will happen on the thread that the method was called on. The measurementCompletion callback is always called on the main thread right after the measurement and before -animateLayoutTransition:.

+
+ + + + + +
+

See Also

+ +
+ + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+ +

– transitionLayoutWithAnimation:shouldMeasureAsync:measurementCompletion: +

+ +
+
+ +
+ + +
+

Invalidates the current layout and begins a relayout of the node with the current constrainedSize. Must be called on main thread.

+
+ + + +
- (void)transitionLayoutWithAnimation:(BOOL)animated shouldMeasureAsync:(BOOL)shouldMeasureAsync measurementCompletion:(nullable void ( ^ ) ( ))completion
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + +
animated

Animation is optional, but will still proceed through your animateLayoutTransition implementation with isAnimated == NO.

shouldMeasureAsync

Measure the layout asynchronously.

measurementCompletion

Optional completion block called only if a new layout is calculated.

+
+ + + + + + + +
+

Discussion

+

It is called right after the measurement and before -animateLayoutTransition:.

+
+ + + + + +
+

See Also

+ +
+ + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+ +

– cancelLayoutTransition +

+ +
+
+ +
+ + +
+

Cancels all performing layout transitions. Can be called on any thread.

+
+ + + +
- (void)cancelLayoutTransition
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Categories/ASDisplayNode+Subclassing.html b/docs/appledoc/Categories/ASDisplayNode+Subclassing.html new file mode 100755 index 0000000000..f8844132c0 --- /dev/null +++ b/docs/appledoc/Categories/ASDisplayNode+Subclassing.html @@ -0,0 +1,2562 @@ + + + + + + ASDisplayNode(Subclassing) Category Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASDisplayNode(Subclassing) Category Reference

+ + +
+ + + + +
Declared inASDisplayNode+Subclasses.h
+ + + + +
+ +

Overview

+

The subclass header ASDisplayNode+Subclasses defines the following methods that either must or can be overriden by +subclasses of ASDisplayNode.

+ +

These methods should never be called directly by other classes.

+ +

Drawing

+ +

Implement one of +displayWithParameters:isCancelled: or +drawRect:withParameters:isCancelled: to provide +drawing for your node.

+ +

Use -drawParametersForAsyncLayer: to copy any properties that are involved in drawing into an immutable object for +use on the display queue. The display and drawRect implementations MUST be thread-safe, as they can be called on +the displayQueue (asynchronously) or the main thread (synchronously/displayImmediately).

+ +

Class methods that require passing in copies of the values are used to minimize the need for locking around instance +variable access, and the possibility of the asynchronous display pass grabbing an inconsistent state across multiple +variables.

+
+ + + + + +
+ + + + +

Properties

+ +
+
+ +

  calculatedLayout +

+ +
+
+ +
+ + +
+

Return the calculated layout.

+
+ + + +
@property (nullable, nonatomic, readonly, assign) ASLayout *calculatedLayout
+ + + + + +
+

Return Value

+

Layout that wraps calculated size returned by -calculateSizeThatFits: (in manual layout mode), +or layout already calculated from layout spec returned by -layoutSpecThatFits: (in automatic layout mode).

+
+ + + + + +
+

Discussion

+

For node subclasses that implement manual layout (e.g., they have a custom -layout method), +calculatedLayout may be accessed on subnodes to retrieved cached information about their size. +This allows -layout to be very fast, saving time on the main thread. +Note: .calculatedLayout will only be set for nodes that have had -measure: called on them. +For manual layout, make sure you call -measure: in your implementation of -calculateSizeThatFits:.

+ +

For node subclasses that use automatic layout (e.g., they implement -layoutSpecThatFits:), +it is typically not necessary to use .calculatedLayout at any point. For these nodes, +the ASLayoutSpec implementation will automatically call -measureWithSizeRange: on all of the subnodes, +and the ASDisplayNode base class implementation of -layout will automatically make use of .calculatedLayout on the subnodes.

Warning: Subclasses must not override this; it returns the last cached layout and is never expensive.

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode+Subclasses.h

+
+ + +
+
+
+
+ + + +

View Lifecycle

+ +
+
+ +

– didLoad +

+ +
+
+ +
+ + +
+

Called on the main thread immediately after self.view is created.

+
+ + + +
- (void)didLoad
+ + + + + + + + + +
+

Discussion

+

This is the best time to add gesture recognizers to the view.

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode+Subclasses.h

+
+ + +
+
+
+
+ + + +

Layout

+ +
+
+ +

– layout +

+ +
+
+ +
+ + +
+

Called on the main thread by the view’s -layoutSubviews.

+
+ + + +
- (void)layout
+ + + + + + + + + +
+

Discussion

+

Subclasses override this method to layout all subnodes or subviews.

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode+Subclasses.h

+
+ + +
+
+
+ +

– layoutDidFinish +

+ +
+
+ +
+ + +
+

Called on the main thread by the view’s -layoutSubviews, after -layout.

+
+ + + +
- (void)layoutDidFinish
+ + + + + + + + + +
+

Discussion

+

Gives a chance for subclasses to perform actions after the subclass and superclass have finished laying +out.

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode+Subclasses.h

+
+ + +
+
+
+ +

– calculatedLayoutDidChange +

+ +
+
+ +
+ + +
+

Called on a background thread if !isNodeLoaded - called on the main thread if isNodeLoaded.

+
+ + + +
- (void)calculatedLayoutDidChange
+ + + + + + + + + +
+

Discussion

+

When the .calculatedLayout property is set to a new ASLayout (directly from -calculateLayoutThatFits: or +calculated via use of -layoutSpecThatFits:), subclasses may inspect it here.

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode+Subclasses.h

+
+ + +
+
+
+
+ + + +

Layout calculation

+ +
+
+ +

– calculateLayoutThatFits: +

+ +
+
+ +
+ + +
+

Calculate a layout based on given size range.

+
+ + + +
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
+ + + +
+

Parameters

+ + + + + + + +
constrainedSize

The minimum and maximum sizes the receiver should fit in.

+
+ + + +
+

Return Value

+

An ASLayout instance defining the layout of the receiver (and its children, if the box layout model is used).

+
+ + + + + +
+

Discussion

+

This method is called on a non-main thread. The default implementation calls either -layoutSpecThatFits: +or -calculateSizeThatFits:, whichever method is overriden. Subclasses rarely need to override this method, +override -layoutSpecThatFits: or -calculateSizeThatFits: instead.

Note: This method should not be called directly outside of ASDisplayNode; use -measure: or -calculatedLayout instead.

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode+Subclasses.h

+
+ + +
+
+
+ +

– calculateLayoutThatFits:restrictedToSize:relativeToParentSize: +

+ +
+
+ +
+ + +
+

ASDisplayNode’s implementation of -layoutThatFits:parentSize: calls this method to resolve the node’s size +against parentSize, intersect it with constrainedSize, and call -calculateLayoutThatFits: with the result.

+
+ + + +
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize restrictedToSize:(ASLayoutElementSize)size relativeToParentSize:(CGSize)parentSize
+ + + + + + + + + +
+

Discussion

+

In certain advanced cases, you may want to customize this logic. Overriding this method allows you to receive all +three parameters and do the computation yourself.

Warning: Overriding this method should be done VERY rarely.

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode+Subclasses.h

+
+ + +
+
+
+ +

– calculateSizeThatFits: +

+ +
+
+ +
+ + +
+

Return the calculated size.

+
+ + + +
- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize
+ + + +
+

Parameters

+ + + + + + + +
constrainedSize

The maximum size the receiver should fit in.

+
+ + + + + + + +
+

Discussion

+

Subclasses that override should expect this method to be called on a non-main thread. The returned size +is wrapped in an ASLayout and cached for quick access during -layout. Other expensive work that needs to +be done before display can be performed here, and using ivars to cache any valuable intermediate results is +encouraged.

Note: Subclasses that override are committed to manual layout. Therefore, -layout: must be overriden to layout all subnodes or subviews.

Note: This method should not be called directly outside of ASDisplayNode; use -layoutThatFits: or layoutThatFits:parentSize: instead.

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode+Subclasses.h

+
+ + +
+
+
+ +

– layoutSpecThatFits: +

+ +
+
+ +
+ + +
+

Return a layout spec that describes the layout of the receiver and its children.

+
+ + + +
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
+ + + +
+

Parameters

+ + + + + + + +
constrainedSize

The minimum and maximum sizes the receiver should fit in.

+
+ + + + + + + +
+

Discussion

+

Subclasses that override should expect this method to be called on a non-main thread. The returned layout spec +is used to calculate an ASLayout and cached by ASDisplayNode for quick access during -layout. Other expensive work that needs to +be done before display can be performed here, and using ivars to cache any valuable intermediate results is +encouraged.

Note: This method should not be called directly outside of ASDisplayNode; use -measure: or -calculatedLayout instead.

Warning: Subclasses that implement -layoutSpecThatFits: must not use .layoutSpecBlock. Doing so will trigger an +exception. A future version of the framework may support using both, calling them serially, with the .layoutSpecBlock +superseding any values set by the method override.

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode+Subclasses.h

+
+ + +
+
+
+ +

– invalidateCalculatedLayout +

+ +
+
+ +
+ + +
+

Invalidate previously measured and cached layout.

+
+ + + +
- (void)invalidateCalculatedLayout
+ + + + + + + + + +
+

Discussion

+

Subclasses should call this method to invalidate the previously measured and cached layout for the display +node, when the contents of the node change in such a way as to require measuring it again.

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode+Subclasses.h

+
+ + +
+
+
+
+ + + +

Drawing

+ +
+
+ +

+ drawRect:withParameters:isCancelled:isRasterizing: +

+ +
+
+ +
+ + +
+

@summary Delegate method to draw layer contents into a CGBitmapContext. The current UIGraphics context will be set +to an appropriate context.

+
+ + + +
+ (void)drawRect:(CGRect)bounds withParameters:(nullable id<NSObject>)parameters isCancelled:(asdisplaynode_iscancelled_block_t)isCancelledBlock isRasterizing:(BOOL)isRasterizing
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
bounds

Region to draw in.

parameters

An object describing all of the properties you need to draw. Return this from +-drawParametersForAsyncLayer:

isCancelledBlock

Execute this block to check whether the current drawing operation has been cancelled to avoid +unnecessary work. A return value of YES means cancel drawing and return.

isRasterizing

YES if the layer is being rasterized into another layer, in which case drawRect: probably wants +to avoid doing things like filling its bounds with a zero-alpha color to clear the backing store.

+
+ + + + + + + +
+

Discussion

+

Note: Called on the display queue and/or main queue (MUST BE THREAD SAFE)

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode+Subclasses.h

+
+ + +
+
+
+ +

+ displayWithParameters:isCancelled: +

+ +
+
+ +
+ + +
+

@summary Delegate override to provide new layer contents as a UIImage.

+
+ + + +
+ (nullable UIImage *)displayWithParameters:(nullable id<NSObject>)parameters isCancelled:(asdisplaynode_iscancelled_block_t)isCancelledBlock
+ + + +
+

Parameters

+ + + + + + + + + + + + +
parameters

An object describing all of the properties you need to draw. Return this from +-drawParametersForAsyncLayer:

isCancelledBlock

Execute this block to check whether the current drawing operation has been cancelled to avoid +unnecessary work. A return value of YES means cancel drawing and return.

+
+ + + +
+

Return Value

+

A UIImage with contents that are ready to display on the main thread. Make sure that the image is already +decoded before returning it here.

+
+ + + + + +
+

Discussion

+

Note: Called on the display queue and/or main queue (MUST BE THREAD SAFE)

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode+Subclasses.h

+
+ + +
+
+
+ +

– drawParametersForAsyncLayer: +

+ +
+
+ +
+ + +
+

Delegate override for drawParameters

+
+ + + +
- (nullable id<NSObject>)drawParametersForAsyncLayer:(_ASDisplayLayer *)layer
+ + + +
+

Parameters

+ + + + + + + +
layer

The layer that will be drawn into.

+
+ + + + + + + +
+

Discussion

+

Note: Called on the main thread only

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode+Subclasses.h

+
+ + +
+
+
+ +

– displayWillStart +

+ +
+
+ +
+ + +
+

Indicates that the receiver is about to display.

+
+ + + +
- (void)displayWillStart
+ + + + + + + + + +
+

Discussion

+

Subclasses may override this method to be notified when display (asynchronous or synchronous) is +about to begin.

Note: Called on the main thread only

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode+Subclasses.h

+
+ + +
+
+
+ +

– displayDidFinish +

+ +
+
+ +
+ + +
+

Indicates that the receiver has finished displaying.

+
+ + + +
- (void)displayDidFinish
+ + + + + + + + + +
+

Discussion

+

Subclasses may override this method to be notified when display (asynchronous or synchronous) has +completed.

Note: Called on the main thread only

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode+Subclasses.h

+
+ + +
+
+
+
+ + + +

Observing node-related changes

+ +
+
+ +

– interfaceStateDidChange:fromState: +

+ +
+
+ +
+ + +
+

Called whenever any bit in the ASInterfaceState bitfield is changed.

+
+ + + +
- (void)interfaceStateDidChange:(ASInterfaceState)newState fromState:(ASInterfaceState)oldState
+ + + + + + + + + +
+

Discussion

+

Subclasses may use this to monitor when they become visible, should free cached data, and much more.

+
+ + + + + +
+

See Also

+ +
+ + + +
+

Declared In

+

ASDisplayNode+Subclasses.h

+
+ + +
+
+
+ +

– didEnterVisibleState +

+ +
+
+ +
+ + +
+

Called whenever the node becomes visible.

+
+ + + +
- (void)didEnterVisibleState
+ + + + + + + + + +
+

Discussion

+

Subclasses may use this to monitor when they become visible.

Note: This method is guaranteed to be called on main.

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode+Subclasses.h

+
+ + +
+
+
+ +

– didExitVisibleState +

+ +
+
+ +
+ + +
+

Called whenever the node is no longer visible.

+
+ + + +
- (void)didExitVisibleState
+ + + + + + + + + +
+

Discussion

+

Subclasses may use this to monitor when they are no longer visible.

Note: This method is guaranteed to be called on main.

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode+Subclasses.h

+
+ + +
+
+
+ +

– didEnterDisplayState +

+ +
+
+ +
+ + +
+

Called whenever the the node has entered the display state.

+
+ + + +
- (void)didEnterDisplayState
+ + + + + + + + + +
+

Discussion

+

Subclasses may use this to monitor when a node should be rendering its content.

Note: This method is guaranteed to be called on main.

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode+Subclasses.h

+
+ + +
+
+
+ +

– didExitDisplayState +

+ +
+
+ +
+ + +
+

Called whenever the the node has exited the display state.

+
+ + + +
- (void)didExitDisplayState
+ + + + + + + + + +
+

Discussion

+

Subclasses may use this to monitor when a node should no longer be rendering its content.

Note: This method is guaranteed to be called on main.

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode+Subclasses.h

+
+ + +
+
+
+ +

– didEnterPreloadState +

+ +
+
+ +
+ + +
+

Called whenever the the node has entered the preload state.

+
+ + + +
- (void)didEnterPreloadState
+ + + + + + + + + +
+

Discussion

+

Subclasses may use this to monitor data for a node should be preloaded, either from a local or remote source.

Note: This method is guaranteed to be called on main.

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode+Subclasses.h

+
+ + +
+
+
+ +

– didExitPreloadState +

+ +
+
+ +
+ + +
+

Called whenever the the node has exited the preload state.

+
+ + + +
- (void)didExitPreloadState
+ + + + + + + + + +
+

Discussion

+

Subclasses may use this to monitor whether preloading data for a node should be canceled.

Note: This method is guaranteed to be called on main.

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode+Subclasses.h

+
+ + +
+
+
+ +

– willEnterHierarchy +

+ +
+
+ +
+ + +
+

Called just before the view is added to a window.

+
+ + + +
- (void)willEnterHierarchy
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASDisplayNode+Subclasses.h

+
+ + +
+
+
+ +

– didExitHierarchy +

+ +
+
+ +
+ + +
+

Called after the view is removed from the window.

+
+ + + +
- (void)didExitHierarchy
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASDisplayNode+Subclasses.h

+
+ + +
+
+
+ +

  inHierarchy +

+ +
+
+ +
+ + +
+

Whether the view or layer of this display node is currently in a window

+
+ + + +
@property (nonatomic, readonly, assign, getter=isInHierarchy) BOOL inHierarchy
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASDisplayNode+Subclasses.h

+
+ + +
+
+
+ +

– fetchData +

+ +
+
+ +
+ + +
+

Indicates that the node should fetch any external data, such as images.

+
+ + + +
- (void)fetchData
+ + + + + + + + + +
+

Discussion

+

Subclasses may override this method to be notified when they should begin to fetch data. Fetching +should be done asynchronously. The node is also responsible for managing the memory of any data. +The data may be remote and accessed via the network, but could also be a local database query.

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode+Subclasses.h

+
+ + +
+
+
+ +

– clearFetchedData +

+ +
+
+ +
+ + +
+

Provides an opportunity to clear any fetched data (e.g. remote / network or database-queried) on the current node.

+
+ + + +
- (void)clearFetchedData
+ + + + + + + + + +
+

Discussion

+

This will not clear data recursively for all subnodes. Either call -recursivelyClearFetchedData or +selectively clear fetched data.

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode+Subclasses.h

+
+ + +
+
+
+ +

– clearContents +

+ +
+
+ +
+ + +
+

Provides an opportunity to clear backing store and other memory-intensive intermediates, such as text layout managers +on the current node.

+
+ + + +
- (void)clearContents
+ + + + + + + + + +
+

Discussion

+

Called by -recursivelyClearContents. Base class implements self.contents = nil, clearing any backing +store, for asynchronous regeneration when needed.

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode+Subclasses.h

+
+ + +
+
+
+ +

– subnodeDisplayWillStart: +

+ +
+
+ +
+ + +
+

Indicates that the receiver is about to display its subnodes. This method is not called if there are no +subnodes present.

+
+ + + +
- (void)subnodeDisplayWillStart:(ASDisplayNode *)subnode
+ + + +
+

Parameters

+ + + + + + + +
subnode

The subnode of which display is about to begin.

+
+ + + + + + + +
+

Discussion

+

Subclasses may override this method to be notified when subnode display (asynchronous or synchronous) is +about to begin.

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode+Subclasses.h

+
+ + +
+
+
+ +

– subnodeDisplayDidFinish: +

+ +
+
+ +
+ + +
+

Indicates that the receiver is finished displaying its subnodes. This method is not called if there are +no subnodes present.

+
+ + + +
- (void)subnodeDisplayDidFinish:(ASDisplayNode *)subnode
+ + + +
+

Parameters

+ + + + + + + +
subnode

The subnode of which display is about to completed.

+
+ + + + + + + +
+

Discussion

+

Subclasses may override this method to be notified when subnode display (asynchronous or synchronous) has +completed.

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode+Subclasses.h

+
+ + +
+
+
+ +

– setNeedsDisplayAtScale: +

+ +
+
+ +
+ + +
+

Marks the receiver’s bounds as needing to be redrawn, with a scale value.

+
+ + + +
- (void)setNeedsDisplayAtScale:(CGFloat)contentsScale
+ + + +
+

Parameters

+ + + + + + + +
contentsScale

The scale at which the receiver should be drawn.

+
+ + + + + + + +
+

Discussion

+

Subclasses should override this if they don’t want their contentsScale changed.

Note: This changes an internal property. +-setNeedsDisplay is also available to trigger display without changing contentsScaleForDisplay.

+
+ + + + + +
+

See Also

+ +
+ + + +
+

Declared In

+

ASDisplayNode+Subclasses.h

+
+ + +
+
+
+ +

– recursivelySetNeedsDisplayAtScale: +

+ +
+
+ +
+ + +
+

Recursively calls setNeedsDisplayAtScale: on subnodes.

+
+ + + +
- (void)recursivelySetNeedsDisplayAtScale:(CGFloat)contentsScale
+ + + +
+

Parameters

+ + + + + + + +
contentsScale

The scale at which the receiver’s subnode hierarchy should be drawn.

+
+ + + + + + + +
+

Discussion

+

Subclasses may override this if they require modifying the scale set on their child nodes.

Note: Only the node tree is walked, not the view or layer trees.

+
+ + + + + + + + + +
+

Declared In

+

ASDisplayNode+Subclasses.h

+
+ + +
+
+
+ +

  contentsScaleForDisplay +

+ +
+
+ +
+ + +
+

The scale factor to apply to the rendering.

+
+ + + +
@property (nonatomic, assign, readonly) CGFloat contentsScaleForDisplay
+ + + + + + + + + +
+

Discussion

+

Use setNeedsDisplayAtScale: to set a value and then after display, the display node will set the layer’s +contentsScale. This is to prevent jumps when re-rasterizing at a different contentsScale. +Read this property if you need to know the future contentsScale of your layer, eg in drawParameters.

+
+ + + + + +
+

See Also

+ +
+ + + +
+

Declared In

+

ASDisplayNode+Subclasses.h

+
+ + +
+
+
+
+ + + +

Touch handling

+ +
+
+ +

– touchesBegan:withEvent: +

+ +
+
+ +
+ + +
+

Tells the node when touches began in its view.

+
+ + + +
- (void)touchesBegan:(NSSet<UITouch*> *)touches withEvent:(nullable UIEvent *)event
+ + + +
+

Parameters

+ + + + + + + + + + + + +
touches

A set of UITouch instances.

event

A UIEvent associated with the touch.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASDisplayNode+Subclasses.h

+
+ + +
+
+
+ +

– touchesMoved:withEvent: +

+ +
+
+ +
+ + +
+

Tells the node when touches moved in its view.

+
+ + + +
- (void)touchesMoved:(NSSet<UITouch*> *)touches withEvent:(nullable UIEvent *)event
+ + + +
+

Parameters

+ + + + + + + + + + + + +
touches

A set of UITouch instances.

event

A UIEvent associated with the touch.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASDisplayNode+Subclasses.h

+
+ + +
+
+
+ +

– touchesEnded:withEvent: +

+ +
+
+ +
+ + +
+

Tells the node when touches ended in its view.

+
+ + + +
- (void)touchesEnded:(NSSet<UITouch*> *)touches withEvent:(nullable UIEvent *)event
+ + + +
+

Parameters

+ + + + + + + + + + + + +
touches

A set of UITouch instances.

event

A UIEvent associated with the touch.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASDisplayNode+Subclasses.h

+
+ + +
+
+
+ +

– touchesCancelled:withEvent: +

+ +
+
+ +
+ + +
+

Tells the node when touches was cancelled in its view.

+
+ + + +
- (void)touchesCancelled:(nullable NSSet<UITouch*> *)touches withEvent:(nullable UIEvent *)event
+ + + +
+

Parameters

+ + + + + + + + + + + + +
touches

A set of UITouch instances.

event

A UIEvent associated with the touch.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASDisplayNode+Subclasses.h

+
+ + +
+
+
+
+ + + +

Managing Gesture Recognizers

+ +
+
+ +

– gestureRecognizerShouldBegin: +

+ +
+
+ +
+ + +
+

Asks the node if a gesture recognizer should continue tracking touches.

+
+ + + +
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
+ + + +
+

Parameters

+ + + + + + + +
gestureRecognizer

A gesture recognizer trying to recognize a gesture.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASDisplayNode+Subclasses.h

+
+ + +
+
+
+
+ + + +

Hit Testing

+ +
+
+ +

– hitTest:withEvent: +

+ +
+
+ +
+ + +
+

Returns the view that contains the point.

+
+ + + +
- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event
+ + + +
+

Parameters

+ + + + + + + + + + + + +
point

A point specified in the node’s local coordinate system (bounds).

event

The event that warranted a call to this method.

+
+ + + +
+

Return Value

+

Returns a UIView, not ASDisplayNode, for two reasons: +1) allows sending events to plain UIViews that don’t have attached nodes, +2) hitTest: is never called before the views are created.

+
+ + + + + +
+

Discussion

+

Override to make this node respond differently to touches: (e.g. hide touches from subviews, send all +touches to certain subviews (hit area maximizing), etc.)

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode+Subclasses.h

+
+ + +
+
+
+
+ + + +

Placeholders

+ +
+
+ +

– placeholderImage +

+ +
+
+ +
+ + +
+

Optionally provide an image to serve as the placeholder for the backing store while the contents are being +displayed.

+
+ + + +
- (nullable UIImage *)placeholderImage
+ + + + + + + + + +
+

Discussion

+

Note: Called on the display queue and/or main queue (MUST BE THREAD SAFE)

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode+Subclasses.h

+
+ + +
+
+
+
+ + + +

Description

+ +
+
+ +

– descriptionForRecursiveDescription +

+ +
+
+ +
+ + +
+

Return a description of the node

+
+ + + +
- (NSString *)descriptionForRecursiveDescription
+ + + + + + + + + +
+

Discussion

+

The function that gets called for each display node in -recursiveDescription

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode+Subclasses.h

+
+ + +
+
+
+ +

– asyncTraitCollectionDidChange +

+ +
+
+ +
+ + +
+

Called when the node’s ASTraitCollection changes

+
+ + + +
- (void)asyncTraitCollectionDidChange
+ + + + + + + + + +
+

Discussion

+

Subclasses can override this method to react to a trait collection change.

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode+Subclasses.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Categories/ASDisplayNode+UIViewBridge.html b/docs/appledoc/Categories/ASDisplayNode+UIViewBridge.html new file mode 100755 index 0000000000..97f565ab80 --- /dev/null +++ b/docs/appledoc/Categories/ASDisplayNode+UIViewBridge.html @@ -0,0 +1,348 @@ + + + + + + ASDisplayNode(UIViewBridge) Category Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASDisplayNode(UIViewBridge) Category Reference

+ + +
+ + + + +
Declared inASDisplayNode.h
+ + + + +
+ +

Overview

+

UIView bridge

+ +

ASDisplayNode provides thread-safe access to most of UIView and CALayer properties and methods, traditionally unsafe.

+ +

Using them will not cause the actual view/layer to be created, and will be applied when it is created (when the view +or layer property is accessed).

+ +
    +
  • NOTE: After the view or layer is created, the properties pass through to the view or layer directly and must be called on the main thread.
  • +
+ + +

See UIView and CALayer for documentation on these common properties.

+
+ + + + + +
+ + + + + + +
+
+ +

– setNeedsDisplay +

+ +
+
+ +
+ + +
+

Marks the view as needing display. Convenience for use whether the view / layer is loaded or not. Safe to call from a background thread.

+
+ + + +
- (void)setNeedsDisplay
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+ +

– setNeedsLayout +

+ +
+
+ +
+ + +
+

Marks the node as needing layout. Convenience for use whether the view / layer is loaded or not. Safe to call from a background thread.

+
+ + + +
- (void)setNeedsLayout
+ + + + + + + + + +
+

Discussion

+

If this node was measured, calling this method triggers an internal relayout: the calculated layout is invalidated, +and the supernode is notified or (if this node is the root one) a full measurement pass is executed using the old constrained size.

+ +

Note: ASCellNode has special behavior in that calling this method will automatically notify +the containing ASTableView / ASCollectionView that the cell should be resized, if necessary.

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+ +

  backgroundColor +

+ +
+
+ +
+ + +
+

The node view’s background color.

+
+ + + +
@property (nonatomic, strong, nullable) UIColor *backgroundColor
+ + + + + + + + + +
+

Discussion

+

In contrast to UIView, setting a transparent color will not set opaque = NO. +This only affects nodes that implement +drawRect like ASTextNode.

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+ +

  contentMode +

+ +
+
+ +
+ + +
+

A flag used to determine how a node lays out its content when its bounds change.

+
+ + + +
@property (nonatomic, assign) UIViewContentMode contentMode
+ + + + + + + + + +
+

Discussion

+

This is like UIView’s contentMode property, but better. We do our own mapping to layer.contentsGravity in +_ASDisplayView. You can set needsDisplayOnBoundsChange independently. +Thus, UIViewContentModeRedraw is not allowed; use needsDisplayOnBoundsChange = YES instead, and pick an appropriate +contentMode for your content while it’s being re-rendered.

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Categories/ASImageNode+AnimatedImage.html b/docs/appledoc/Categories/ASImageNode+AnimatedImage.html new file mode 100755 index 0000000000..ff83bcb37f --- /dev/null +++ b/docs/appledoc/Categories/ASImageNode+AnimatedImage.html @@ -0,0 +1,275 @@ + + + + + + ASImageNode(AnimatedImage) Category Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASImageNode(AnimatedImage) Category Reference

+ + +
+ + + + +
Declared inASImageNode.h
+ + + + + + +
+ + + + + + +
+
+ +

  animatedImage +

+ +
+
+ +
+ + +
+

The animated image to playback

+
+ + + +
@property (nullable, nonatomic, strong) id<ASAnimatedImageProtocol> animatedImage
+ + + + + + + + + +
+

Discussion

+

Set this to an object which conforms to ASAnimatedImageProtocol +to have the ASImageNode playback an animated image.

+
+ + + + + + + +
+

Declared In

+

ASImageNode.h

+
+ + +
+
+
+ +

  animatedImagePaused +

+ +
+
+ +
+ + +
+

Pause the playback of an animated image.

+
+ + + +
@property (nonatomic, assign) BOOL animatedImagePaused
+ + + + + + + + + +
+

Discussion

+

Set to YES to pause playback of an animated image and NO to resume +playback.

+
+ + + + + + + +
+

Declared In

+

ASImageNode.h

+
+ + +
+
+
+ +

  animatedImageRunLoopMode +

+ +
+
+ +
+ + +
+

The runloop mode used to animate the image.

+
+ + + +
@property (nonatomic, strong) NSString *animatedImageRunLoopMode
+ + + + + + + + + +
+

Discussion

+

Defaults to NSRunLoopCommonModes. Another commonly used mode is NSDefaultRunLoopMode. +Setting NSDefaultRunLoopMode will cause animation to pause while scrolling (if the ASImageNode is +in a scroll view), which may improve scroll performance in some use cases.

+
+ + + + + + + +
+

Declared In

+

ASImageNode.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Categories/ASImageNode+Debugging.html b/docs/appledoc/Categories/ASImageNode+Debugging.html new file mode 100755 index 0000000000..d60ea6fd94 --- /dev/null +++ b/docs/appledoc/Categories/ASImageNode+Debugging.html @@ -0,0 +1,184 @@ + + + + + + ASImageNode(Debugging) Category Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASImageNode(Debugging) Category Reference

+ + +
+ + + + +
Declared inAsyncDisplayKit+Debug.h
+ + + + + + +
+ + + + + + +
+
+ +

+ setShouldShowImageScalingOverlay: +

+ +
+
+ +
+ + +
+

Enables an ASImageNode debug label that shows the ratio of pixels in the source image to those in +the displayed bounds (including cropRect). This helps detect excessive image fetching / downscaling, +as well as upscaling (such as providing a URL not suitable for a Retina device). For dev purposes only.

+
+ + + +
+ (void)setShouldShowImageScalingOverlay:(BOOL)show
+ + + +
+

Parameters

+ + + + + + + +
enabled

Specify YES to show the label on all ASImageNodes with non-1.0x source-to-bounds pixel ratio.

+
+ + + + + + + + + + + + + +
+

Declared In

+

AsyncDisplayKit+Debug.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Categories/ASLayout+.html b/docs/appledoc/Categories/ASLayout+.html new file mode 100755 index 0000000000..f07aea41ee --- /dev/null +++ b/docs/appledoc/Categories/ASLayout+.html @@ -0,0 +1,175 @@ + + + + + + ASLayout() Category Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASLayout() Category Reference

+ + +
+ + + + +
Declared inASLayoutSpec+Subclasses.h
+ + + + + + +
+ + + + + + +
+
+ +

  position +

+ +
+
+ +
+ + +
+

Position in parent. Default to CGPointNull.

+
+ + + +
@property (nonatomic, assign, readwrite) CGPoint position
+ + + + + + + + + +
+

Discussion

+

When being used as a sublayout, this property must not equal CGPointNull.

+
+ + + + + + + +
+

Declared In

+

ASLayoutSpec+Subclasses.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Categories/ASLayout+Debugging.html b/docs/appledoc/Categories/ASLayout+Debugging.html new file mode 100755 index 0000000000..0b9fc0a9e4 --- /dev/null +++ b/docs/appledoc/Categories/ASLayout+Debugging.html @@ -0,0 +1,170 @@ + + + + + + ASLayout(Debugging) Category Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASLayout(Debugging) Category Reference

+ + +
+ + + + +
Declared inASLayout.h
+ + + + + + +
+ + + + + + +
+
+ +

– recursiveDescription +

+ +
+
+ +
+ + +
+

Recrusively output the description of the layout tree.

+
+ + + +
- (NSString *)recursiveDescription
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASLayout.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Categories/ASLayoutElementStyle+.html b/docs/appledoc/Categories/ASLayoutElementStyle+.html new file mode 100755 index 0000000000..0bc980f565 --- /dev/null +++ b/docs/appledoc/Categories/ASLayoutElementStyle+.html @@ -0,0 +1,221 @@ + + + + + + ASLayoutElementStyle() Category Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASLayoutElementStyle() Category Reference

+ + +
+ + + + + + + +
Conforms toASDescriptionProvider
Declared inASLayoutElementStylePrivate.h
+ + + + + + +
+ + + + + + +
+
+ +

  delegate +

+ +
+
+ +
+ + +
+

The object that acts as the delegate of the style.

+
+ + + +
@property (nullable, nonatomic, weak) id<ASLayoutElementStyleDelegate> delegate
+ + + + + + + + + +
+

Discussion

+

The delegate must adopt the ASLayoutElementStyleDelegate protocol. The delegate is not retained.

+
+ + + + + + + +
+

Declared In

+

ASLayoutElementStylePrivate.h

+
+ + +
+
+
+ +

  size +

+ +
+
+ +
+ + +
+

A size constraint that should apply to this ASLayoutElement.

+
+ + + +
@property (nonatomic, assign, readonly) ASLayoutElementSize size
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASLayoutElementStylePrivate.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Categories/ASLayoutSpec+Debugging.html b/docs/appledoc/Categories/ASLayoutSpec+Debugging.html new file mode 100755 index 0000000000..c039aa2246 --- /dev/null +++ b/docs/appledoc/Categories/ASLayoutSpec+Debugging.html @@ -0,0 +1,173 @@ + + + + + + ASLayoutSpec(Debugging) Category Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASLayoutSpec(Debugging) Category Reference

+ + +
+ + + + + + + +
Conforms toASLayoutElementAsciiArtProtocol
Declared inASLayoutSpec.h
+ + + + + + +
+ + + + + + +
+
+ +

+ asciiArtStringForChildren:parentName:direction: +

+ +
+
+ +
+ + +
+

Used by other layout specs to create ascii art debug strings

+
+ + + +
+ (NSString *)asciiArtStringForChildren:(NSArray *)children parentName:(NSString *)parentName direction:(ASStackLayoutDirection)direction
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASLayoutSpec.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Categories/ASLayoutSpec+Subclassing.html b/docs/appledoc/Categories/ASLayoutSpec+Subclassing.html new file mode 100755 index 0000000000..177cf376e9 --- /dev/null +++ b/docs/appledoc/Categories/ASLayoutSpec+Subclassing.html @@ -0,0 +1,315 @@ + + + + + + ASLayoutSpec(Subclassing) Category Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASLayoutSpec(Subclassing) Category Reference

+ + +
+ + + + +
Declared inASLayoutSpec+Subclasses.h
+ + + + + + +
+ + + + + + +
+
+ +

– layoutElementToAddFromLayoutElement: +

+ +
+
+ +
+ + +
+

Helper method for finalLayoutElement support

+
+ + + +
- (id<ASLayoutElement>)layoutElementToAddFromLayoutElement:(id<ASLayoutElement>)child
+ + + + + + + + + +
+

Discussion

+

Warning: If you are getting recursion crashes here after implementing finalLayoutElement, make sure +that you are setting isFinalLayoutElement flag to YES. This must be one BEFORE adding a child +to the new ASLayoutElement.

+ +

For example: +- (idASLayoutElement)finalLayoutElement +{ +ASInsetLayoutSpec *insetSpec = [[ASInsetLayoutSpec alloc] init]; +insetSpec.insets = UIEdgeInsetsMake(10,10,10,10); +insetSpec.isFinalLayoutElement = YES; +[insetSpec setChild:self]; +return insetSpec; +}

+
+ + + + + + + +
+

Declared In

+

ASLayoutSpec+Subclasses.h

+
+ + +
+
+
+ +

– setChild:atIndex: +

+ +
+
+ +
+ + +
+

Adds a child with the given identifier to this layout spec.

+
+ + + +
- (void)setChild:(id<ASLayoutElement>)child atIndex:(NSUInteger)index
+ + + +
+

Parameters

+ + + + + + + + + + + + +
child

A child to be added.

index

An index associated with the child.

+
+ + + + + + + +
+

Discussion

+

Every ASLayoutSpec must act on at least one child. The ASLayoutSpec base class takes the +responsibility of holding on to the spec children. Some layout specs, like ASInsetLayoutSpec, +only require a single child.

+ +

For layout specs that require a known number of children (ASBackgroundLayoutSpec, for example) +a subclass can use the setChild method to set the “primary” child. It should then use this method +to set any other required children. Ideally a subclass would hide this from the user, and use the +setChild:forIndex: internally. For example, ASBackgroundLayoutSpec exposes a backgroundChild +property that behind the scenes is calling setChild:forIndex:.

+
+ + + + + + + +
+

Declared In

+

ASLayoutSpec+Subclasses.h

+
+ + +
+
+
+ +

– childAtIndex: +

+ +
+
+ +
+ + +
+

Returns the child added to this layout spec using the given index.

+
+ + + +
- (nullable id<ASLayoutElement>)childAtIndex:(NSUInteger)index
+ + + +
+

Parameters

+ + + + + + + +
index

An identifier associated with the the child.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASLayoutSpec+Subclasses.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Categories/ASRangeController+ASRangeControllerUpdateRangeProtocol.html b/docs/appledoc/Categories/ASRangeController+ASRangeControllerUpdateRangeProtocol.html new file mode 100755 index 0000000000..cbdbcb323a --- /dev/null +++ b/docs/appledoc/Categories/ASRangeController+ASRangeControllerUpdateRangeProtocol.html @@ -0,0 +1,198 @@ + + + + + + ASRangeController(ASRangeControllerUpdateRangeProtocol) Category Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASRangeController(ASRangeControllerUpdateRangeProtocol) Category Reference

+ + +
+ + + + + + + +
Conforms toASRangeControllerUpdateRangeProtocol
Declared inASRangeController.h
+ + + + + + +
+ + + + + + +
+
+ +

– updateCurrentRangeWithMode: +

+ +
+
+ +
+ + +
+
    +
  • Update the range mode for a range controller to a explicitly set mode until the node that contains the range
  • +
  • controller becomes visible again +*
  • +
  • Logic for the automatic range mode:
  • +
    1. +
    2. If there are no visible node paths available nothing is to be done and no range update will happen
    3. +
    +
  • +
    1. +
    2. The initial range update if the range controller is visible always will be ASLayoutRangeModeCount
    3. +
    +
  • +
  • (ASLayoutRangeModeMinimum) as it’s the initial fetch
  • +
    1. +
    2. 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
    3. +
    +
  • +
    1. +
    2. If range mode is not explicitly set the range mode is variying based if the range controller is visible or not
    3. +
    +
  • +
+ +
+ + + +
- (void)updateCurrentRangeWithMode:(ASLayoutRangeMode)rangeMode
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASRangeController.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Categories/ASRangeController+Debugging.html b/docs/appledoc/Categories/ASRangeController+Debugging.html new file mode 100755 index 0000000000..e85b0d47a3 --- /dev/null +++ b/docs/appledoc/Categories/ASRangeController+Debugging.html @@ -0,0 +1,183 @@ + + + + + + ASRangeController(Debugging) Category Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASRangeController(Debugging) Category Reference

+ + +
+ + + + +
Declared inAsyncDisplayKit+Debug.h
+ + + + + + +
+ + + + + + +
+
+ +

+ setShouldShowRangeDebugOverlay: +

+ +
+
+ +
+ + +
+

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];

+
+ + + +
+ (void)setShouldShowRangeDebugOverlay:(BOOL)show
+ + + +
+

Parameters

+ + + + + + + +
enable

Specify YES to make this debug feature enabled when messaging the ASRangeController class.

+
+ + + + + + + + + + + + + +
+

Declared In

+

AsyncDisplayKit+Debug.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Categories/ASTableView+Deprecated.html b/docs/appledoc/Categories/ASTableView+Deprecated.html new file mode 100755 index 0000000000..c13c9fc87c --- /dev/null +++ b/docs/appledoc/Categories/ASTableView+Deprecated.html @@ -0,0 +1,948 @@ + + + + + + ASTableView(Deprecated) Category Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASTableView(Deprecated) Category Reference

+ + +
+ + + + +
Declared inASTableView.h
+ + + + + + +
+ + + + + + +
+
+ +

– initWithFrame:style: +

+ +
+
+ +
+ + +
+

Initializer.

+
+ + + +
- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style
+ + + +
+

Parameters

+ + + + + + + + + + + + +
frame

A rectangle specifying the initial location and size of the table view in its superview’€™s coordinates. +The frame of the table view changes as table cells are added and deleted.

style

A constant that specifies the style of the table view. See UITableViewStyle for descriptions of valid constants.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASTableView.h

+
+ + +
+
+
+ +

– tuningParametersForRangeType: +

+ +
+
+ +
+ + +
+

Tuning parameters for a range type in full mode.

+
+ + + +
- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType
+ + + +
+

Parameters

+ + + + + + + +
rangeType

The range type to get the tuning parameters for.

+
+ + + +
+

Return Value

+

A tuning parameter value for the given range type in full mode.

+
+ + + + + + + + + + + +
+

Declared In

+

ASTableView.h

+
+ + +
+
+
+ +

– setTuningParameters:forRangeType: +

+ +
+
+ +
+ + +
+

Set the tuning parameters for a range type in full mode.

+
+ + + +
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType
+ + + +
+

Parameters

+ + + + + + + + + + + + +
tuningParameters

The tuning parameters to store for a range type.

rangeType

The range type to set the tuning parameters for.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASTableView.h

+
+ + +
+
+
+ +

– tuningParametersForRangeMode:rangeType: +

+ +
+
+ +
+ + +
+

Tuning parameters for a range type in the specified mode.

+
+ + + +
- (ASRangeTuningParameters)tuningParametersForRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType
+ + + +
+

Parameters

+ + + + + + + + + + + + +
rangeMode

The range mode to get the running parameters for.

rangeType

The range type to get the tuning parameters for.

+
+ + + +
+

Return Value

+

A tuning parameter value for the given range type in the given mode.

+
+ + + + + + + + + + + +
+

Declared In

+

ASTableView.h

+
+ + +
+
+
+ +

– setTuningParameters:forRangeMode:rangeType: +

+ +
+
+ +
+ + +
+

Set the tuning parameters for a range type in the specified mode.

+
+ + + +
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + +
tuningParameters

The tuning parameters to store for a range type.

rangeMode

The range mode to set the running parameters for.

rangeType

The range type to set the tuning parameters for.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASTableView.h

+
+ + +
+
+
+ +

– visibleNodes +

+ +
+
+ +
+ + +
+

Similar to -visibleCells.

+
+ + + +
- (NSArray<ASCellNode*> *)visibleNodes
+ + + + + +
+

Return Value

+

an array containing the cell nodes being displayed on screen.

+
+ + + + + + + + + + + +
+

Declared In

+

ASTableView.h

+
+ + +
+
+
+ +

– indexPathForNode: +

+ +
+
+ +
+ + +
+

Similar to -indexPathForCell:.

+
+ + + +
- (nullable NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode
+ + + +
+

Parameters

+ + + + + + + +
cellNode

a cellNode part of the table view

+
+ + + +
+

Return Value

+

an indexPath for this cellNode

+
+ + + + + + + + + + + +
+

Declared In

+

ASTableView.h

+
+ + +
+
+
+ +

– reloadDataWithCompletion: +

+ +
+
+ +
+ + +
+

Reload everything from scratch, destroying the working range and all cached nodes.

+
+ + + +
- (void)reloadDataWithCompletion:(void ( ^ _Nullable ) ( ))completion
+ + + +
+

Parameters

+ + + + + + + +
completion

block to run on completion of asynchronous loading or nil. If supplied, the block is run on +the main thread.

+
+ + + + + + + +
+

Discussion

+

Warning: This method is substantially more expensive than UITableView’s version.

+
+ + + + + + + +
+

Declared In

+

ASTableView.h

+
+ + +
+
+
+ +

– reloadData +

+ +
+
+ +
+ + +
+

Reload everything from scratch, destroying the working range and all cached nodes.

+
+ + + +
- (void)reloadData
+ + + + + + + + + +
+

Discussion

+

Warning: This method is substantially more expensive than UITableView’s version.

+
+ + + + + + + +
+

Declared In

+

ASTableView.h

+
+ + +
+
+
+ +

– reloadDataImmediately +

+ +
+
+ +
+ + +
+

Reload everything from scratch entirely on the main thread, destroying the working range and all cached nodes.

+
+ + + +
- (void)reloadDataImmediately
+ + + + + + + + + +
+

Discussion

+

Warning: This method is substantially more expensive than UITableView’s version and will block the main thread while +all the cells load.

+
+ + + + + + + +
+

Declared In

+

ASTableView.h

+
+ + +
+
+
+ +

– relayoutItems +

+ +
+
+ +
+ + +
+

Triggers a relayout of all nodes.

+
+ + + +
- (void)relayoutItems
+ + + + + + + + + +
+

Discussion

+

This method invalidates and lays out every cell node in the table view.

+
+ + + + + + + +
+

Declared In

+

ASTableView.h

+
+ + +
+
+
+ +

– endUpdatesAnimated:completion: +

+ +
+
+ +
+ + +
+

Concludes a series of method calls that insert, delete, select, or reload rows and sections of the table view. +You call this method to bracket a series of method calls that begins with beginUpdates and that consists of operations +to insert, delete, select, and reload rows and sections of the table view. When you call endUpdates, ASTableView begins animating +the operations simultaneously. This method is must be called from the main thread. It’s important to remember that the ASTableView will +be processing the updates asynchronously after this call and are not guaranteed to be reflected in the ASTableView until +the completion block is executed.

+
+ + + +
- (void)endUpdatesAnimated:(BOOL)animated completion:(void ( ^ _Nullable ) ( BOOL completed ))completion
+ + + +
+

Parameters

+ + + + + + + + + + + + +
animated

NO to disable all animations.

completion

A completion handler block to execute when all of the operations are finished. This block takes a single +Boolean parameter that contains the value YES if all of the related animations completed successfully or +NO if they were interrupted. This parameter may be nil. If supplied, the block is run on the main thread.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASTableView.h

+
+ + +
+
+
+ +

– waitUntilAllUpdatesAreCommitted +

+ +
+
+ +
+ + +
+

Blocks execution of the main thread until all section and row updates are committed. This method must be called from the main thread.

+
+ + + +
- (void)waitUntilAllUpdatesAreCommitted
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASTableView.h

+
+ + +
+
+
+ +

– clearContents +

+ +
+
+ +
+ + +
+

Deprecated in 2.0. You should not call this method.

+
+ + + +
- (void)clearContents
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASTableView.h

+
+ + +
+
+
+ +

– clearFetchedData +

+ +
+
+ +
+ + +
+

Deprecated in 2.0. You should not call this method.

+
+ + + +
- (void)clearFetchedData
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASTableView.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Categories/ASTableView+Internal.html b/docs/appledoc/Categories/ASTableView+Internal.html new file mode 100755 index 0000000000..e21fda7dad --- /dev/null +++ b/docs/appledoc/Categories/ASTableView+Internal.html @@ -0,0 +1,453 @@ + + + + + + ASTableView(Internal) Category Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASTableView(Internal) Category Reference

+ + +
+ + + + +
Declared inASTableViewInternal.h
+ + + + + + +
+ + + + + + +
+
+ +

– _initWithFrame:style:dataControllerClass: +

+ +
+
+ +
+ + +
+

Initializer.

+
+ + + +
- (instancetype)_initWithFrame:(CGRect)frame style:(UITableViewStyle)style dataControllerClass:(Class)dataControllerClass
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + +
frame

A rectangle specifying the initial location and size of the table view in its superview’€™s coordinates. +The frame of the table view changes as table cells are added and deleted.

style

A constant that specifies the style of the table view. See UITableViewStyle for descriptions of valid constants.

dataControllerClass

A controller class injected to and used to create a data controller for the table view.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASTableViewInternal.h

+
+ + +
+
+
+ +

  test_enableSuperUpdateCallLogging +

+ +
+
+ +
+ + +
+

Set YES and we’ll log every time we call [super insertRows…] etc

+
+ + + +
@property (nonatomic) BOOL test_enableSuperUpdateCallLogging
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASTableViewInternal.h

+
+ + +
+
+
+ +

– convertIndexPathFromTableNode:waitingIfNeeded: +

+ +
+
+ +
+ + +
+

Attempt to get the view-layer index path for the row with the given index path.

+
+ + + +
- (NSIndexPath *)convertIndexPathFromTableNode:(NSIndexPath *)indexPath waitingIfNeeded:(BOOL)wait
+ + + +
+

Parameters

+ + + + + + + + + + + + +
indexPath

The index path of the row.

wait

If the item hasn’t reached the view yet, this attempts to wait for updates to commit.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASTableViewInternal.h

+
+ + +
+
+
+ +

– convertIndexPathToTableNode: +

+ +
+
+ +
+ + +
+

Attempt to get the node index path given the view-layer index path.

+
+ + + +
- (NSIndexPath *)convertIndexPathToTableNode:(NSIndexPath *)indexPath
+ + + +
+

Parameters

+ + + + + + + +
indexPath

The index path of the row.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASTableViewInternal.h

+
+ + +
+
+
+ +

– convertIndexPathsToTableNode: +

+ +
+
+ +
+ + +
+

Attempt to get the node index paths given the view-layer index paths.

+
+ + + +
- (NSArray<NSIndexPath*> *)convertIndexPathsToTableNode:(NSArray<NSIndexPath*> *)indexPaths
+ + + +
+

Parameters

+ + + + + + + +
indexPaths

An array of index paths in the view space

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASTableViewInternal.h

+
+ + +
+
+
+ +

– sectionIndexWidth +

+ +
+
+ +
+ + +
+

Returns the width of the section index view on the right-hand side of the table, if one is present.

+
+ + + +
- (CGFloat)sectionIndexWidth
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASTableViewInternal.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Categories/ASTextNode+.html b/docs/appledoc/Categories/ASTextNode+.html new file mode 100755 index 0000000000..ec3bdd4fbb --- /dev/null +++ b/docs/appledoc/Categories/ASTextNode+.html @@ -0,0 +1,226 @@ + + + + + + ASTextNode() Category Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASTextNode() Category Reference

+ + +
+ + + + +
Declared inASTextNode+Beta.h
+ + + + + + +
+ + + + + + +
+
+ +

  pointSizeScaleFactors +

+ +
+
+ +
+ + +
+

An array of descending scale factors that will be applied to this text node to try to make it fit within its constrained size

+
+ + + +
@property (nullable, nonatomic, copy) NSArray<NSNumber*> *pointSizeScaleFactors
+ + + + + + + + + +
+

Discussion

+

This array should be in descending order and NOT contain the scale factor 1.0. For example, it could return @[@(.9), @(.85), @(.8)]; +@default nil (no scaling)

+
+ + + + + + + +
+

Declared In

+

ASTextNode+Beta.h

+
+ + +
+
+
+ +

  textContainerInset +

+ +
+
+ +
+ + +
+

Text margins for text laid out in the text node.

+
+ + + +
@property (nonatomic, assign) UIEdgeInsets textContainerInset
+ + + + + + + + + +
+

Discussion

+

defaults to UIEdgeInsetsZero. +This property can be useful for handling text which does not fit within the view by default. An example: like UILabel, +ASTextNode will clip the left and right of the string “judar” if it’s rendered in an italicised font.

+
+ + + + + + + +
+

Declared In

+

ASTextNode+Beta.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Categories/ASTextNode+Deprecated.html b/docs/appledoc/Categories/ASTextNode+Deprecated.html new file mode 100755 index 0000000000..a310bc3f27 --- /dev/null +++ b/docs/appledoc/Categories/ASTextNode+Deprecated.html @@ -0,0 +1,175 @@ + + + + + + ASTextNode(Deprecated) Category Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASTextNode(Deprecated) Category Reference

+ + +
+ + + + +
Declared inASTextNode.h
+ + + + + + + + +
+ + + + + + +
+
+ +

  ) +

+ +
+
+ +
+ + +
+

The attributedString and attributedText properties are equivalent, but attributedText is now the standard API +name in order to match UILabel and ASEditableTextNode.

+
+ + + +
@property (nullable, nonatomic, copy) NSAttributedString *ASDISPLAYNODE_DEPRECATED_MSG ( "Use .attributedText instead." )
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASTextNode.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Categories/ASViewController+ASRangeControllerUpdateRangeProtocol.html b/docs/appledoc/Categories/ASViewController+ASRangeControllerUpdateRangeProtocol.html new file mode 100755 index 0000000000..0c0d6ab95f --- /dev/null +++ b/docs/appledoc/Categories/ASViewController+ASRangeControllerUpdateRangeProtocol.html @@ -0,0 +1,176 @@ + + + + + + ASViewController(ASRangeControllerUpdateRangeProtocol) Category Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASViewController(ASRangeControllerUpdateRangeProtocol) Category Reference

+ + +
+ + + + +
Declared inASViewController.h
+ + + + + + +
+ + + + + + +
+
+ +

  automaticallyAdjustRangeModeBasedOnViewEvents +

+ +
+
+ +
+ + +
+

Automatically adjust range mode based on view events. If you set this to YES, the view controller or its node +must conform to the ASRangeControllerUpdateRangeProtocol.

+
+ + + +
@property (nonatomic, assign) BOOL automaticallyAdjustRangeModeBasedOnViewEvents
+ + + + + + + + + +
+

Discussion

+

Default value is YES if node or view controller conform to ASRangeControllerUpdateRangeProtocol otherwise it is NO.

+
+ + + + + + + +
+

Declared In

+

ASViewController.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Categories/CALayer+AsyncDisplayKit.html b/docs/appledoc/Categories/CALayer+AsyncDisplayKit.html new file mode 100755 index 0000000000..33a9fc83d1 --- /dev/null +++ b/docs/appledoc/Categories/CALayer+AsyncDisplayKit.html @@ -0,0 +1,182 @@ + + + + + + CALayer(AsyncDisplayKit) Category Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

CALayer(AsyncDisplayKit) Category Reference

+ + +
+ + + + +
Declared inASDisplayNode.h
+ + + + + + +
+ + + + + + +
+
+ +

– addSubnode: +

+ +
+
+ +
+ + +
+

Convenience method, equivalent to [layer addSublayer:node.layer].

+
+ + + +
- (void)addSubnode:(nonnull ASDisplayNode *)node
+ + + +
+

Parameters

+ + + + + + + +
node

The node to be added.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Categories/NSNumber+ASDimension.html b/docs/appledoc/Categories/NSNumber+ASDimension.html new file mode 100755 index 0000000000..7d11d7936e --- /dev/null +++ b/docs/appledoc/Categories/NSNumber+ASDimension.html @@ -0,0 +1,118 @@ + + + + + + NSNumber(ASDimension) Category Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

NSNumber(ASDimension) Category Reference

+ + +
+ + + + +
Declared inASDimension.h
+ + + + +
+ +

Overview

+

Resolve this dimension to a parent size.

+
+ + + + + + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Categories/NSURL+ASPhotosFrameworkURLs.html b/docs/appledoc/Categories/NSURL+ASPhotosFrameworkURLs.html new file mode 100755 index 0000000000..1ca4bce449 --- /dev/null +++ b/docs/appledoc/Categories/NSURL+ASPhotosFrameworkURLs.html @@ -0,0 +1,176 @@ + + + + + + NSURL(ASPhotosFrameworkURLs) Category Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

NSURL(ASPhotosFrameworkURLs) Category Reference

+ + +
+ + + + +
Declared inASMultiplexImageNode.h
+ + + + + + +
+ + + + + + +
+
+ +

+ URLWithAssetLocalIdentifier:targetSize:contentMode:options: +

+ +
+
+ +
+ + +
+

Create an NSURL that specifies an image from the Photos framework.

+
+ + + +
+ (NSURL *)URLWithAssetLocalIdentifier:(NSString *)assetLocalIdentifier targetSize:(CGSize)targetSize contentMode:(PHImageContentMode)contentMode options:(PHImageRequestOptions *)options
+ + + + + + + + + +
+

Discussion

+

When implementing -multiplexImageNode:URLForImageIdentifier:, you can return a URL +created by this method and the image node will attempt to load the image from the Photos framework.

Note: The synchronous flag in options is ignored.

Note: The Opportunistic delivery mode is not supported and will be treated as HighQualityFormat.

+
+ + + + + + + +
+

Declared In

+

ASMultiplexImageNode.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Categories/UIImage+ASDKAdditions.html b/docs/appledoc/Categories/UIImage+ASDKAdditions.html new file mode 100755 index 0000000000..6bf7903113 --- /dev/null +++ b/docs/appledoc/Categories/UIImage+ASDKAdditions.html @@ -0,0 +1,352 @@ + + + + + + UIImage(ASDKAdditions) Category Reference + + + + + + +
+
+ +

+ +

+ +

+ Texture +

+ +
+
+ + + +
+
+
+
+

UIImage(ASDKAdditions) Category Reference

+ + +
+ + + + +
Declared inUIImage+ASConvenience.h
+ + + + + + +
+ + + + + + +
+
+ +

+ as_resizableRoundedImageWithCornerRadius:cornerColor:fillColor: +

+ +
+
+ +
+ + +
+

This generates a flat-color, rounded-corner resizeable image

+
+ + + +
+ (UIImage *)as_resizableRoundedImageWithCornerRadius:(CGFloat)cornerRadius cornerColor:(UIColor *)cornerColor fillColor:(UIColor *)fillColor
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + +
cornerRadius

The radius of the rounded-corner

cornerColor

The fill color of the corners (For Alpha corners use clearColor)

fillColor

The fill color of the rounded-corner image

+
+ + + + + + + + + + + + + +
+

Declared In

+

UIImage+ASConvenience.h

+
+ + +
+
+
+ +

+ as_resizableRoundedImageWithCornerRadius:cornerColor:fillColor:borderColor:borderWidth: +

+ +
+
+ +
+ + +
+

This generates a flat-color, rounded-corner resizeable image with a border

+
+ + + +
+ (UIImage *)as_resizableRoundedImageWithCornerRadius:(CGFloat)cornerRadius cornerColor:(UIColor *)cornerColor fillColor:(UIColor *)fillColor borderColor:(nullable UIColor *)borderColor borderWidth:(CGFloat)borderWidth
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
cornerRadius

The radius of the rounded-corner

cornerColor

The fill color of the corners (For Alpha corners use clearColor)

fillColor

The fill color of the rounded-corner image

borderColor

The border color. Set to nil for no border.

borderWidth

The border width. Dummy value if borderColor = nil.

+
+ + + + + + + + + + + + + +
+

Declared In

+

UIImage+ASConvenience.h

+
+ + +
+
+
+ +

+ as_resizableRoundedImageWithCornerRadius:cornerColor:fillColor:borderColor:borderWidth:roundedCorners:scale: +

+ +
+
+ +
+ + +
+

This generates a flat-color, rounded-corner resizeable image with a border

+
+ + + +
+ (UIImage *)as_resizableRoundedImageWithCornerRadius:(CGFloat)cornerRadius cornerColor:(UIColor *)cornerColor fillColor:(UIColor *)fillColor borderColor:(nullable UIColor *)borderColor borderWidth:(CGFloat)borderWidth roundedCorners:(UIRectCorner)roundedCorners scale:(CGFloat)scale
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
cornerRadius

The radius of the rounded-corner

cornerColor

The fill color of the corners (For Alpha corners use clearColor)

fillColor

The fill color of the rounded-corner image

borderColor

The border color. Set to nil for no border.

borderWidth

The border width. Dummy value if borderColor = nil.

roundedCorners

Select individual or multiple corners to round. Set to UIRectCornerAllCorners to round all 4 corners.

scale

The number of pixels per point. Provide 0.0 to use the screen scale.

+
+ + + + + + + + + + + + + +
+

Declared In

+

UIImage+ASConvenience.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ + +
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Categories/UIView+AsyncDisplayKit.html b/docs/appledoc/Categories/UIView+AsyncDisplayKit.html new file mode 100755 index 0000000000..a1bcd6c40b --- /dev/null +++ b/docs/appledoc/Categories/UIView+AsyncDisplayKit.html @@ -0,0 +1,192 @@ + + + + + + UIView(AsyncDisplayKit) Category Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

UIView(AsyncDisplayKit) Category Reference

+ + +
+ + + + +
Declared inASDisplayNode.h
+ + + + +
+ +

Overview

+

UIVIew(AsyncDisplayKit) defines convenience method for adding sub-ASDisplayNode to an UIView.

+
+ + + + + +
+ + + + + + +
+
+ +

– addSubnode: +

+ +
+
+ +
+ + +
+

Convenience method, equivalent to [view addSubview:node.view] or [view.layer addSublayer:node.layer] if layer-backed.

+
+ + + +
- (void)addSubnode:(nonnull ASDisplayNode *)node
+ + + +
+

Parameters

+ + + + + + + +
node

The node to be added.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Classes/ASAbsoluteLayoutSpec.html b/docs/appledoc/Classes/ASAbsoluteLayoutSpec.html new file mode 100755 index 0000000000..4a526d9b96 --- /dev/null +++ b/docs/appledoc/Classes/ASAbsoluteLayoutSpec.html @@ -0,0 +1,302 @@ + + + + + + ASAbsoluteLayoutSpec Class Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASAbsoluteLayoutSpec Class Reference

+ + +
+ + + + + + + +
Inherits fromASLayoutSpec : NSObject
Declared inASAbsoluteLayoutSpec.h
+ + + + +
+ +

Overview

+

A layout spec that positions children at fixed positions.

+
+ + + + + +
+ + + + + + +
+
+ +

  sizing +

+ +
+
+ +
+ + +
+

How much space will the spec taken up

+
+ + + +
@property (nonatomic, assign) ASAbsoluteLayoutSpecSizing sizing
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASAbsoluteLayoutSpec.h

+
+ + +
+
+
+ +

+ absoluteLayoutSpecWithSizing:children: +

+ +
+
+ +
+ + +
+

How much space the spec will take up

+
+ + + +
+ (instancetype)absoluteLayoutSpecWithSizing:(ASAbsoluteLayoutSpecSizing)sizing children:(NSArray<id<ASLayoutElement> > *)children
+ + + +
+

Parameters

+ + + + + + + + + + + + +
sizing

How much space the spec will take up

children

Children to be positioned at fixed positions

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASAbsoluteLayoutSpec.h

+
+ + +
+
+
+ +

+ absoluteLayoutSpecWithChildren: +

+ +
+
+ +
+ + +
+

Children to be positioned at fixed positions

+
+ + + +
+ (instancetype)absoluteLayoutSpecWithChildren:(NSArray<id<ASLayoutElement> > *)children
+ + + +
+

Parameters

+ + + + + + + +
children

Children to be positioned at fixed positions

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASAbsoluteLayoutSpec.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Classes/ASAsciiArtBoxCreator.html b/docs/appledoc/Classes/ASAsciiArtBoxCreator.html new file mode 100755 index 0000000000..de14a95656 --- /dev/null +++ b/docs/appledoc/Classes/ASAsciiArtBoxCreator.html @@ -0,0 +1,236 @@ + + + + + + ASAsciiArtBoxCreator Class Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASAsciiArtBoxCreator Class Reference

+ + +
+ + + + + + + +
Inherits fromNSObject
Declared inASAsciiArtBoxCreator.h
+ + + + +
+ +

Overview

+

A that takes a parent and its children and renders as ascii art box.

+
+ + + + + +
+ + + + + + +
+
+ +

+ horizontalBoxStringForChildren:parent: +

+ +
+
+ +
+ + +
+

Renders an ascii art box with the children aligned horizontally +Example: +————ASStackLayoutSpec———–

+ +

| ASTextNode ASTextNode ASTextNode |

+
+ + + +
+ (NSString *)horizontalBoxStringForChildren:(NSArray<NSString*> *)children parent:(NSString *)parent
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASAsciiArtBoxCreator.h

+
+ + +
+
+
+ +

+ verticalBoxStringForChildren:parent: +

+ +
+
+ +
+ + +
+

Renders an ascii art box with the children aligned vertically. +Example: +–ASStackLayoutSpec– +| ASTextNode | +| ASTextNode |

+ +

| ASTextNode |

+
+ + + +
+ (NSString *)verticalBoxStringForChildren:(NSArray<NSString*> *)children parent:(NSString *)parent
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASAsciiArtBoxCreator.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Classes/ASBackgroundLayoutSpec.html b/docs/appledoc/Classes/ASBackgroundLayoutSpec.html new file mode 100755 index 0000000000..e860a8b322 --- /dev/null +++ b/docs/appledoc/Classes/ASBackgroundLayoutSpec.html @@ -0,0 +1,247 @@ + + + + + + ASBackgroundLayoutSpec Class Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASBackgroundLayoutSpec Class Reference

+ + +
+ + + + + + + +
Inherits fromASLayoutSpec : NSObject
Declared inASBackgroundLayoutSpec.h
+ + + + +
+ +

Overview

+

Lays out a single layoutElement child, then lays out a background layoutElement instance behind it stretched to its size.

+
+ + + + + +
+ + + + + + +
+
+ +

  background +

+ +
+
+ +
+ + +
+

Background layoutElement for this layout spec

+
+ + + +
@property (nullable, nonatomic, strong) id<ASLayoutElement> background
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASBackgroundLayoutSpec.h

+
+ + +
+
+
+ +

+ backgroundLayoutSpecWithChild:background: +

+ +
+
+ +
+ + +
+

Creates and returns an ASBackgroundLayoutSpec object

+
+ + + +
+ (instancetype)backgroundLayoutSpecWithChild:(id<ASLayoutElement>)child background:(nullable id<ASLayoutElement>)background
+ + + +
+

Parameters

+ + + + + + + + + + + + +
child

A child that is laid out to determine the size of this spec.

background

A layoutElement object that is laid out behind the child. If this is nil, the background is omitted.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASBackgroundLayoutSpec.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Classes/ASButtonNode.html b/docs/appledoc/Classes/ASButtonNode.html new file mode 100755 index 0000000000..3aca43f70f --- /dev/null +++ b/docs/appledoc/Classes/ASButtonNode.html @@ -0,0 +1,831 @@ + + + + + + ASButtonNode Class Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASButtonNode Class Reference

+ + +
+ + + + + + + +
Inherits fromASControlNode : ASDisplayNode : ASDealloc2MainObject
Declared inASButtonNode.h
+ + + + + + +
+ + + + + + +
+
+ +

  contentSpacing +

+ +
+
+ +
+ + +
+

Spacing between image and title. Defaults to 8.0.

+
+ + + +
@property (nonatomic, assign) CGFloat contentSpacing
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASButtonNode.h

+
+ + +
+
+
+ +

  laysOutHorizontally +

+ +
+
+ +
+ + +
+

Whether button should be laid out vertically (image on top of text) or horizontally (image to the left of text). +ASButton node does not yet support RTL but it should be fairly easy to implement. +Defaults to YES.

+
+ + + +
@property (nonatomic, assign) BOOL laysOutHorizontally
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASButtonNode.h

+
+ + +
+
+
+ +

  contentHorizontalAlignment +

+ +
+
+ +
+ + +
+

Horizontally align content (text or image). +Defaults to ASHorizontalAlignmentMiddle.

+
+ + + +
@property (nonatomic, assign) ASHorizontalAlignment contentHorizontalAlignment
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASButtonNode.h

+
+ + +
+
+
+ +

  contentVerticalAlignment +

+ +
+
+ +
+ + +
+

Vertically align content (text or image). +Defaults to ASVerticalAlignmentCenter.

+
+ + + +
@property (nonatomic, assign) ASVerticalAlignment contentVerticalAlignment
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASButtonNode.h

+
+ + +
+
+
+ +

  contentEdgeInsets +

+ +
+
+ +
+ + +
+

The insets used around the title and image node

+
+ + + +
@property (nonatomic, assign) UIEdgeInsets contentEdgeInsets
+ + + + + + + + + +
+

Discussion

+

The insets used around the title and image node

+
+ + + + + + + +
+

Declared In

+

ASButtonNode.h

+
+ + +
+
+
+ +

  imageAlignment +

+ +
+
+ +
+ + +
+

@discusstion Whether the image should be aligned at the beginning or at the end of node. Default is ASButtonNodeImageAlignmentBeginning.

+
+ + + +
@property (nonatomic, assign) ASButtonNodeImageAlignment imageAlignment
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASButtonNode.h

+
+ + +
+
+
+ +

– attributedTitleForState: +

+ +
+
+ +
+ + +
+

Returns the styled title associated with the specified state.

+
+ + + +
- (NSAttributedString *_Nullable)attributedTitleForState:(ASControlState)state
+ + + +
+

Parameters

+ + + + + + + +
state

The state that uses the styled title. The possible values are described in ASControlState.

+
+ + + +
+

Return Value

+

The title for the specified state.

+
+ + + + + + + + + + + +
+

Declared In

+

ASButtonNode.h

+
+ + +
+
+
+ +

– setAttributedTitle:forState: +

+ +
+
+ +
+ + +
+

Sets the styled title to use for the specified state. This will reset styled title previously set with -setTitle:withFont:withColor:forState.

+
+ + + +
- (void)setAttributedTitle:(nullable NSAttributedString *)title forState:(ASControlState)state
+ + + +
+

Parameters

+ + + + + + + + + + + + +
title

The styled text string to use for the title.

state

The state that uses the specified title. The possible values are described in ASControlState.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASButtonNode.h

+
+ + +
+
+
+ +

– setTitle:withFont:withColor:forState: +

+ +
+
+ +
+ + +
+

Sets the title to use for the specified state. This will reset styled title previously set with -setAttributedTitle:forState.

+
+ + + +
- (void)setTitle:(NSString *)title withFont:(nullable UIFont *)font withColor:(nullable UIColor *)color forState:(ASControlState)state
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
title

The styled text string to use for the title.

font

The font to use for the title.

color

The color to use for the title.

state

The state that uses the specified title. The possible values are described in ASControlState.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASButtonNode.h

+
+ + +
+
+
+ +

– imageForState: +

+ +
+
+ +
+ + +
+

Returns the image used for a button state.

+
+ + + +
- (nullable UIImage *)imageForState:(ASControlState)state
+ + + +
+

Parameters

+ + + + + + + +
state

The state that uses the image. Possible values are described in ASControlState.

+
+ + + +
+

Return Value

+

The image used for the specified state.

+
+ + + + + + + + + + + +
+

Declared In

+

ASButtonNode.h

+
+ + +
+
+
+ +

– setImage:forState: +

+ +
+
+ +
+ + +
+

Sets the image to use for the specified state.

+
+ + + +
- (void)setImage:(nullable UIImage *)image forState:(ASControlState)state
+ + + +
+

Parameters

+ + + + + + + + + + + + +
image

The image to use for the specified state.

state

The state that uses the specified title. The values are described in ASControlState.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASButtonNode.h

+
+ + +
+
+
+ +

– setBackgroundImage:forState: +

+ +
+
+ +
+ + +
+

Sets the background image to use for the specified state.

+
+ + + +
- (void)setBackgroundImage:(nullable UIImage *)image forState:(ASControlState)state
+ + + +
+

Parameters

+ + + + + + + + + + + + +
image

The image to use for the specified state.

state

The state that uses the specified title. The values are described in ASControlState.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASButtonNode.h

+
+ + +
+
+
+ +

– backgroundImageForState: +

+ +
+
+ +
+ + +
+

Returns the background image used for a button state.

+
+ + + +
- (nullable UIImage *)backgroundImageForState:(ASControlState)state
+ + + +
+

Parameters

+ + + + + + + +
state

The state that uses the image. Possible values are described in ASControlState.

+
+ + + +
+

Return Value

+

The background image used for the specified state.

+
+ + + + + + + + + + + +
+

Declared In

+

ASButtonNode.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Classes/ASCellNode.html b/docs/appledoc/Classes/ASCellNode.html new file mode 100755 index 0000000000..2f92770841 --- /dev/null +++ b/docs/appledoc/Classes/ASCellNode.html @@ -0,0 +1,558 @@ + + + + + + ASCellNode Class Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASCellNode Class Reference

+ + +
+ + + + + + + +
Inherits fromASDisplayNode : ASDealloc2MainObject
Declared inASCellNode.h
+ + + + +
+ +

Overview

+
    +
  • Generic cell node. Subclass this instead of ASDisplayNode to use with ASTableView and ASCollectionView.

  • +
  • @note When a cell node is contained inside a collection view (or table view),

  • +
  • calling -setNeedsLayout will also notify the collection on the main thread
  • +
  • so that the collection can update its item layout if the cell’s size changed.
  • +
+ +
+ + + + + +
+ + + + + + +
+
+ +

  neverShowPlaceholders +

+ +
+
+ +
+ + +
+

When enabled, ensures that the cell is completely displayed before allowed onscreen.

+ +

@default NO

+
+ + + +
@property (nonatomic, assign) BOOL neverShowPlaceholders
+ + + + + + + + + +
+

Discussion

+

Normally, ASCellNodes are preloaded and have finished display before they are onscreen. +However, if the Table or Collection’s rangeTuningParameters are set to small values (or 0), +or if the user is scrolling rapidly on a slow device, it is possible for a cell’s display to +be incomplete when it becomes visible.

+ +

In this case, normally placeholder states are shown and scrolling continues uninterrupted. +The finished, drawn content is then shown as soon as it is ready.

+ +

With this property set to YES, the main thread will be blocked until display is complete for +the cell. This is more similar to UIKit, and in fact makes AsyncDisplayKit scrolling visually +indistinguishable from UIKit’s, except being faster.

+ +

Using this option does not eliminate all of the performance advantages of AsyncDisplayKit. +Normally, a cell has been preloading and is almost done when it reaches the screen, so the +blocking time is very short. If the rangeTuningParameters are set to 0, still this option +outperforms UIKit: while the main thread is waiting, subnode display executes concurrently.

+
+ + + + + + + +
+

Declared In

+

ASCellNode.h

+
+ + +
+
+
+ +

  selected +

+ +
+
+ +
+ + +
+

A Boolean value that is synchronized with the underlying collection or tableView cell property. +Setting this value is equivalent to calling selectItem / deselectItem on the collection or table.

+
+ + + +
@property (nonatomic, assign, getter=isSelected) BOOL selected
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASCellNode.h

+
+ + +
+
+
+ +

  highlighted +

+ +
+
+ +
+ + +
+

A Boolean value that is synchronized with the underlying collection or tableView cell property. +Setting this value is equivalent to calling highlightItem / unHighlightItem on the collection or table.

+
+ + + +
@property (nonatomic, assign, getter=isHighlighted) BOOL highlighted
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASCellNode.h

+
+ + +
+
+
+ +

  indexPath +

+ +
+
+ +
+ + +
+

The current index path of this cell node, or @c nil if this node is +not a valid item inside a table node or collection node.

+
+ + + +
@property (nonatomic, readonly, nullable) NSIndexPath *indexPath
+ + + + + + + + + +
+

Discussion

+

Note: This property must be accessed on the main thread.

+
+ + + + + + + +
+

Declared In

+

ASCellNode.h

+
+ + +
+
+
+ +

  owningNode +

+ +
+
+ +
+ + +
+

The owning node (ASCollectionNode/ASTableNode) of this cell node, or @c nil if this node is +not a valid item inside a table node or collection node or if those nodes are nil.

+
+ + + +
@property (weak, nonatomic, readonly, nullable) ASDisplayNode *owningNode
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASCellNode.h

+
+ + +
+
+
+ +

– applyLayoutAttributes: +

+ +
+
+ +
+ + +
+

Called by the system when ASCellNode is used with an ASCollectionNode. It will not be called by ASTableNode. +When the UICollectionViewLayout object returns a new UICollectionViewLayoutAttributes object, the corresponding ASCellNode will be updated. +See UICollectionViewCell’s applyLayoutAttributes: for a full description.

+
+ + + +
- (void)applyLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASCellNode.h

+
+ + +
+
+
+ +

– initWithViewControllerBlock:didLoadBlock: +

+ +
+
+ +
+ + +
+

Initializes a cell with a given view controller block.

+
+ + + +
- (instancetype)initWithViewControllerBlock:(ASDisplayNodeViewControllerBlock)viewControllerBlock didLoadBlock:(nullable ASDisplayNodeDidLoadBlock)didLoadBlock
+ + + +
+

Parameters

+ + + + + + + + + + + + +
viewControllerBlock

The block that will be used to create the backing view controller.

didLoadBlock

The block that will be called after the view controller’s view is loaded.

+
+ + + +
+

Return Value

+

An ASCellNode created using the root view of the view controller provided by the viewControllerBlock. +The view controller’s root view is resized to match the calculated size produced during layout.

+
+ + + + + + + + + + + +
+

Declared In

+

ASCellNode.h

+
+ + +
+
+
+ +

– cellNodeVisibilityEvent:inScrollView:withCellFrame: +

+ +
+
+ +
+ + +
+

Notifies the cell node of certain visibility events, such as changing visible rect.

+
+ + + +
- (void)cellNodeVisibilityEvent:(ASCellNodeVisibilityEvent)event inScrollView:(nullable UIScrollView *)scrollView withCellFrame:(CGRect)cellFrame
+ + + + + + + + + +
+

Discussion

+

Warning: In cases where an ASCellNode is used as a plain node – i.e. not returned from the +nodeBlockForItemAtIndexPath/nodeForItemAtIndexPath data source methods – this method will +deliver only the Visible and Invisible events, scrollView will be nil, and +cellFrame will be the zero rect.

+
+ + + + + + + +
+

Declared In

+

ASCellNode.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Classes/ASCenterLayoutSpec.html b/docs/appledoc/Classes/ASCenterLayoutSpec.html new file mode 100755 index 0000000000..fe4f01cffd --- /dev/null +++ b/docs/appledoc/Classes/ASCenterLayoutSpec.html @@ -0,0 +1,207 @@ + + + + + + ASCenterLayoutSpec Class Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASCenterLayoutSpec Class Reference

+ + +
+ + + + + + + +
Inherits fromASRelativeLayoutSpec : ASLayoutSpec : NSObject
Declared inASCenterLayoutSpec.h
+ + + + +
+ +

Overview

+

Lays out a single layoutElement child and position it so that it is centered into the layout bounds. +NOTE: ASRelativeLayoutSpec offers all of the capabilities of Center, and more. +Check it out if you would like to be able to position the child at any corner or the middle of an edge.

+
+ + + + + +
+ + + + + + +
+
+ +

+ centerLayoutSpecWithCenteringOptions:sizingOptions:child: +

+ +
+
+ +
+ + +
+

Initializer.

+
+ + + +
+ (instancetype)centerLayoutSpecWithCenteringOptions:(ASCenterLayoutSpecCenteringOptions)centeringOptions sizingOptions:(ASCenterLayoutSpecSizingOptions)sizingOptions child:(id<ASLayoutElement>)child
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + +
centeringOptions

How the child is centered.

sizingOptions

How much space will be taken up.

child

The child to center.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASCenterLayoutSpec.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Classes/ASCollectionNode.html b/docs/appledoc/Classes/ASCollectionNode.html new file mode 100755 index 0000000000..2471991465 --- /dev/null +++ b/docs/appledoc/Classes/ASCollectionNode.html @@ -0,0 +1,2405 @@ + + + + + + ASCollectionNode Class Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASCollectionNode Class Reference

+ + +
+ + + + + + + + + + +
Inherits fromASDisplayNode : ASDealloc2MainObject
Conforms toASRangeControllerUpdateRangeProtocol
Declared inASCollectionNode.h
+ + + + +
+ +

Overview

+

ASCollectionNode is a node based class that wraps an ASCollectionView. It can be used +as a subnode of another node, and provide room for many (great) features and improvements later on.

+
+ + + + + +
+ + + + + + +
+
+ +

– initWithCollectionViewLayout: +

+ +
+
+ +
+ + +
+

Initializes an ASCollectionNode

+
+ + + +
- (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout
+ + + +
+

Parameters

+ + + + + + + +
layout

The layout object to use for organizing items. The collection view stores a strong reference to the specified object. Must not be nil.

+
+ + + + + + + +
+

Discussion

+

Initializes and returns a newly allocated collection node object with the specified layout.

+
+ + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

– initWithFrame:collectionViewLayout: +

+ +
+
+ +
+ + +
+

Initializes an ASCollectionNode

+
+ + + +
- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout
+ + + +
+

Parameters

+ + + + + + + + + + + + +
frame

The frame rectangle for the collection view, measured in points. The origin of the frame is relative to the superview in which you plan to add it. This frame is passed to the superclass during initialization.

layout

The layout object to use for organizing items. The collection view stores a strong reference to the specified object. Must not be nil.

+
+ + + + + + + +
+

Discussion

+

Initializes and returns a newly allocated collection node object with the specified frame and layout.

+
+ + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

  view +

+ +
+
+ +
+ + +
+

Returns the corresponding ASCollectionView

+
+ + + +
@property (strong, nonatomic, readonly) ASCollectionView *view
+ + + + + +
+

Return Value

+

view The corresponding ASCollectionView.

+
+ + + + + + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

  delegate +

+ +
+
+ +
+ + +
+

The object that acts as the asynchronous delegate of the collection view

+
+ + + +
@property (weak, nonatomic) id<ASCollectionDelegate> delegate
+ + + + + + + + + +
+

Discussion

+

The delegate must adopt the ASCollectionDelegate protocol. The collection view maintains a weak reference to the delegate object.

+ +

The delegate object is responsible for providing size constraints for nodes and indicating whether batch fetching should begin.

Note: This is a convenience method which sets the asyncDelegate on the collection node’s collection view.

+
+ + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

  dataSource +

+ +
+
+ +
+ + +
+

The object that acts as the asynchronous data source of the collection view

+
+ + + +
@property (weak, nonatomic) id<ASCollectionDataSource> dataSource
+ + + + + + + + + +
+

Discussion

+

The datasource must adopt the ASCollectionDataSource protocol. The collection view maintains a weak reference to the datasource object.

+ +

The datasource object is responsible for providing nodes or node creation blocks to the collection view.

Note: This is a convenience method which sets the asyncDatasource on the collection node’s collection view.

+
+ + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

  allowsSelection +

+ +
+
+ +
+ + +
+

A Boolean value that indicates whether users can select items in the collection node. +If the value of this property is YES (the default), users can select items. If you want more fine-grained control over the selection of items, you must provide a delegate object and implement the appropriate methods of the UICollectionNodeDelegate protocol.

+
+ + + +
@property (nonatomic, assign) BOOL allowsSelection
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

  allowsMultipleSelection +

+ +
+
+ +
+ + +
+

A Boolean value that determines whether users can select more than one item in the collection node. +This property controls whether multiple items can be selected simultaneously. The default value of this property is NO. +When the value of this property is YES, tapping a cell adds it to the current selection (assuming the delegate permits the cell to be selected). Tapping the cell again removes it from the selection.

+
+ + + +
@property (nonatomic, assign) BOOL allowsMultipleSelection
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

– tuningParametersForRangeType: +

+ +
+
+ +
+ + +
+

Tuning parameters for a range type in full mode.

+
+ + + +
- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType
+ + + +
+

Parameters

+ + + + + + + +
rangeType

The range type to get the tuning parameters for.

+
+ + + +
+

Return Value

+

A tuning parameter value for the given range type in full mode.

+
+ + + + + + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

– setTuningParameters:forRangeType: +

+ +
+
+ +
+ + +
+

Set the tuning parameters for a range type in full mode.

+
+ + + +
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType
+ + + +
+

Parameters

+ + + + + + + + + + + + +
tuningParameters

The tuning parameters to store for a range type.

rangeType

The range type to set the tuning parameters for.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

– tuningParametersForRangeMode:rangeType: +

+ +
+
+ +
+ + +
+

Tuning parameters for a range type in the specified mode.

+
+ + + +
- (ASRangeTuningParameters)tuningParametersForRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType
+ + + +
+

Parameters

+ + + + + + + + + + + + +
rangeMode

The range mode to get the running parameters for.

rangeType

The range type to get the tuning parameters for.

+
+ + + +
+

Return Value

+

A tuning parameter value for the given range type in the given mode.

+
+ + + + + + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

– setTuningParameters:forRangeMode:rangeType: +

+ +
+
+ +
+ + +
+

Set the tuning parameters for a range type in the specified mode.

+
+ + + +
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + +
tuningParameters

The tuning parameters to store for a range type.

rangeMode

The range mode to set the running parameters for.

rangeType

The range type to set the tuning parameters for.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

– scrollToItemAtIndexPath:atScrollPosition:animated: +

+ +
+
+ +
+ + +
+

Scrolls the collection to the given item.

+
+ + + +
- (void)scrollToItemAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UICollectionViewScrollPosition)scrollPosition animated:(BOOL)animated
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + +
indexPath

The index path of the item.

scrollPosition

Where the item should end up after the scroll.

animated

Whether the scroll should be animated or not.

+ +

This method must be called on the main thread.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

– registerSupplementaryNodeOfKind: +

+ +
+
+ +
+ + +
+

Registers the given kind of supplementary node for use in creating node-backed supplementary elements.

+
+ + + +
- (void)registerSupplementaryNodeOfKind:(NSString *)elementKind
+ + + +
+

Parameters

+ + + + + + + +
elementKind

The kind of supplementary node that will be requested through the data source.

+
+ + + + + + + +
+

Discussion

+

Use this method to register support for the use of supplementary nodes in place of the default +registerClass:forSupplementaryViewOfKind:withReuseIdentifier: and registerNib:forSupplementaryViewOfKind:withReuseIdentifier: +methods. This method will register an internal backing view that will host the contents of the supplementary nodes +returned from the data source.

+
+ + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

– performBatchAnimated:updates:completion: +

+ +
+
+ +
+ + +
+

Perform a batch of updates asynchronously, optionally disabling all animations in the batch. This method must be called from the main thread. +The data source must be updated to reflect the changes before the update block completes.

+
+ + + +
- (void)performBatchAnimated:(BOOL)animated updates:(nullable __attribute ( ( noescape ) ) void ( ^ ) ( ))updates completion:(nullable void ( ^ ) ( BOOL finished ))completion
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + +
animated

NO to disable animations for this batch

updates

The block that performs the relevant insert, delete, reload, or move operations.

completion

A completion handler block to execute when all of the operations are finished. This block takes a single +Boolean parameter that contains the value YES if all of the related animations completed successfully or +NO if they were interrupted. This parameter may be nil. If supplied, the block is run on the main thread.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

– performBatchUpdates:completion: +

+ +
+
+ +
+ + +
+

Perform a batch of updates asynchronously, optionally disabling all animations in the batch. This method must be called from the main thread. +The data source must be updated to reflect the changes before the update block completes.

+
+ + + +
- (void)performBatchUpdates:(nullable __attribute ( ( noescape ) ) void ( ^ ) ( ))updates completion:(nullable void ( ^ ) ( BOOL finished ))completion
+ + + +
+

Parameters

+ + + + + + + + + + + + +
updates

The block that performs the relevant insert, delete, reload, or move operations.

completion

A completion handler block to execute when all of the operations are finished. This block takes a single +Boolean parameter that contains the value YES if all of the related animations completed successfully or +NO if they were interrupted. This parameter may be nil. If supplied, the block is run on the main thread.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

– waitUntilAllUpdatesAreCommitted +

+ +
+
+ +
+ + +
+

Blocks execution of the main thread until all section and item updates are committed to the view. This method must be called from the main thread.

+
+ + + +
- (void)waitUntilAllUpdatesAreCommitted
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

– insertSections: +

+ +
+
+ +
+ + +
+

Inserts one or more sections.

+
+ + + +
- (void)insertSections:(NSIndexSet *)sections
+ + + +
+

Parameters

+ + + + + + + +
sections

An index set that specifies the sections to insert.

+
+ + + + + + + +
+

Discussion

+

This method must be called from the main thread. The data source must be updated to reflect the changes +before this method is called.

+
+ + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

– deleteSections: +

+ +
+
+ +
+ + +
+

Deletes one or more sections.

+
+ + + +
- (void)deleteSections:(NSIndexSet *)sections
+ + + +
+

Parameters

+ + + + + + + +
sections

An index set that specifies the sections to delete.

+
+ + + + + + + +
+

Discussion

+

This method must be called from the main thread. The data source must be updated to reflect the changes +before this method is called.

+
+ + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

– reloadSections: +

+ +
+
+ +
+ + +
+

Reloads the specified sections.

+
+ + + +
- (void)reloadSections:(NSIndexSet *)sections
+ + + +
+

Parameters

+ + + + + + + +
sections

An index set that specifies the sections to reload.

+
+ + + + + + + +
+

Discussion

+

This method must be called from the main thread. The data source must be updated to reflect the changes +before this method is called.

+
+ + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

– moveSection:toSection: +

+ +
+
+ +
+ + +
+

Moves a section to a new location.

+
+ + + +
- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection
+ + + +
+

Parameters

+ + + + + + + + + + + + +
section

The index of the section to move.

newSection

The index that is the destination of the move for the section.

+
+ + + + + + + +
+

Discussion

+

This method must be called from the main thread. The data source must be updated to reflect the changes +before this method is called.

+
+ + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

– insertItemsAtIndexPaths: +

+ +
+
+ +
+ + +
+

Inserts items at the locations identified by an array of index paths.

+
+ + + +
- (void)insertItemsAtIndexPaths:(NSArray<NSIndexPath*> *)indexPaths
+ + + +
+

Parameters

+ + + + + + + +
indexPaths

An array of NSIndexPath objects, each representing an item index and section index that together identify an item.

+
+ + + + + + + +
+

Discussion

+

This method must be called from the main thread. The data source must be updated to reflect the changes +before this method is called.

+
+ + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

– deleteItemsAtIndexPaths: +

+ +
+
+ +
+ + +
+

Deletes the items specified by an array of index paths.

+
+ + + +
- (void)deleteItemsAtIndexPaths:(NSArray<NSIndexPath*> *)indexPaths
+ + + +
+

Parameters

+ + + + + + + +
indexPaths

An array of NSIndexPath objects identifying the items to delete.

+
+ + + + + + + +
+

Discussion

+

This method must be called from the main thread. The data source must be updated to reflect the changes +before this method is called.

+
+ + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

– reloadItemsAtIndexPaths: +

+ +
+
+ +
+ + +
+

Reloads the specified items.

+
+ + + +
- (void)reloadItemsAtIndexPaths:(NSArray<NSIndexPath*> *)indexPaths
+ + + +
+

Parameters

+ + + + + + + +
indexPaths

An array of NSIndexPath objects identifying the items to reload.

+
+ + + + + + + +
+

Discussion

+

This method must be called from the main thread. The data source must be updated to reflect the changes +before this method is called.

+
+ + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

– moveItemAtIndexPath:toIndexPath: +

+ +
+
+ +
+ + +
+

Moves the item at a specified location to a destination location.

+
+ + + +
- (void)moveItemAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath
+ + + +
+

Parameters

+ + + + + + + + + + + + +
indexPath

The index path identifying the item to move.

newIndexPath

The index path that is the destination of the move for the item.

+
+ + + + + + + +
+

Discussion

+

This method must be called from the main thread. The data source must be updated to reflect the changes +before this method is called.

+
+ + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

– reloadDataWithCompletion: +

+ +
+
+ +
+ + +
+

Reload everything from scratch, destroying the working range and all cached nodes.

+
+ + + +
- (void)reloadDataWithCompletion:(nullable void ( ^ ) ( ))completion
+ + + +
+

Parameters

+ + + + + + + +
completion

block to run on completion of asynchronous loading or nil. If supplied, the block is run on +the main thread.

+
+ + + + + + + +
+

Discussion

+

Warning: This method is substantially more expensive than UICollectionView’s version.

+
+ + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

– reloadData +

+ +
+
+ +
+ + +
+

Reload everything from scratch, destroying the working range and all cached nodes.

+
+ + + +
- (void)reloadData
+ + + + + + + + + +
+

Discussion

+

Warning: This method is substantially more expensive than UICollectionView’s version.

+
+ + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

– relayoutItems +

+ +
+
+ +
+ + +
+

Triggers a relayout of all nodes.

+
+ + + +
- (void)relayoutItems
+ + + + + + + + + +
+

Discussion

+

This method invalidates and lays out every cell node in the collection view.

+
+ + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

  indexPathsForSelectedItems +

+ +
+
+ +
+ + +
+

The index paths of the selected items, or @c nil if no items are selected.

+
+ + + +
@property (nonatomic, readonly, nullable) NSArray<NSIndexPath*> *indexPathsForSelectedItems
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

– selectItemAtIndexPath:animated:scrollPosition: +

+ +
+
+ +
+ + +
+

Selects the item at the specified index path and optionally scrolls it into view. +If the allowsSelection property is NO, calling this method has no effect. If there is an existing selection with a different index path and the allowsMultipleSelection property is NO, calling this method replaces the previous selection. +This method does not cause any selection-related delegate methods to be called.

+
+ + + +
- (void)selectItemAtIndexPath:(nullable NSIndexPath *)indexPath animated:(BOOL)animated scrollPosition:(UICollectionViewScrollPosition)scrollPosition
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + +
indexPath

The index path of the item to select. Specifying nil for this parameter clears the current selection.

animated

Specify YES to animate the change in the selection or NO to make the change without animating it.

scrollPosition

An option that specifies where the item should be positioned when scrolling finishes. For a list of possible values, see UICollectionViewScrollPosition.

+
+ + + + + + + +
+

Discussion

+

This method must be called from the main thread.

+
+ + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

– deselectItemAtIndexPath:animated: +

+ +
+
+ +
+ + +
+

Deselects the item at the specified index. +If the allowsSelection property is NO, calling this method has no effect. +This method does not cause any selection-related delegate methods to be called.

+
+ + + +
- (void)deselectItemAtIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated
+ + + +
+

Parameters

+ + + + + + + + + + + + +
indexPath

The index path of the item to select. Specifying nil for this parameter clears the current selection.

animated

Specify YES to animate the change in the selection or NO to make the change without animating it.

+
+ + + + + + + +
+

Discussion

+

This method must be called from the main thread.

+
+ + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

– numberOfItemsInSection: +

+ +
+
+ +
+ + +
+

Retrieves the number of items in the given section.

+
+ + + +
- (NSInteger)numberOfItemsInSection:(NSInteger)section
+ + + +
+

Parameters

+ + + + + + + +
section

The section.

+
+ + + +
+

Return Value

+

The number of items.

+
+ + + + + + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

  numberOfSections +

+ +
+
+ +
+ + +
+

The number of sections.

+
+ + + +
@property (nonatomic, readonly) NSInteger numberOfSections
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

  visibleNodes +

+ +
+
+ +
+ + +
+

Similar to -visibleCells.

+
+ + + +
@property (nonatomic, readonly) NSArray<__kindofASCellNode*> *visibleNodes
+ + + + + +
+

Return Value

+

an array containing the nodes being displayed on screen. This must be called on the main thread.

+
+ + + + + + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

– nodeForItemAtIndexPath: +

+ +
+
+ +
+ + +
+

Retrieves the node for the item at the given index path.

+
+ + + +
- (nullable __kindof ASCellNode *)nodeForItemAtIndexPath:(NSIndexPath *)indexPath
+ + + +
+

Parameters

+ + + + + + + +
indexPath

The index path of the requested item.

+
+ + + +
+

Return Value

+

The node for the given item, or @c nil if no item exists at the specified path.

+
+ + + + + + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

– indexPathForNode: +

+ +
+
+ +
+ + +
+

Retrieve the index path for the item with the given node.

+
+ + + +
- (nullable NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode
+ + + +
+

Parameters

+ + + + + + + +
cellNode

A node for an item in the collection node.

+
+ + + +
+

Return Value

+

The indexPath for this item.

+
+ + + + + + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

  indexPathsForVisibleItems +

+ +
+
+ +
+ + +
+

Retrieve the index paths of all visible items.

+
+ + + +
@property (nonatomic, readonly) NSArray<NSIndexPath*> *indexPathsForVisibleItems
+ + + + + +
+

Return Value

+

an array containing the index paths of all visible items. This must be called on the main thread.

+
+ + + + + + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

– indexPathForItemAtPoint: +

+ +
+
+ +
+ + +
+

Retrieve the index path of the item at the given point.

+
+ + + +
- (nullable NSIndexPath *)indexPathForItemAtPoint:(CGPoint)point
+ + + +
+

Parameters

+ + + + + + + +
point

The point of the requested item.

+
+ + + +
+

Return Value

+

The indexPath for the item at the given point. This must be called on the main thread.

+
+ + + + + + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

– cellForItemAtIndexPath: +

+ +
+
+ +
+ + +
+

Retrieve the cell at the given index path.

+
+ + + +
- (nullable UICollectionViewCell *)cellForItemAtIndexPath:(NSIndexPath *)indexPath
+ + + +
+

Parameters

+ + + + + + + +
indexPath

The index path of the requested item.

+
+ + + +
+

Return Value

+

The cell for the given index path. This must be called on the main thread.

+
+ + + + + + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

– contextForSection: +

+ +
+
+ +
+ + +
+

Retrieves the context object for the given section, as provided by the data source in +the @c collectionNode:contextForSection: method.

+
+ + + +
- (nullable id<ASSectionContext>)contextForSection:(NSInteger)section
+ + + +
+

Parameters

+ + + + + + + +
section

The section to get the context for.

+
+ + + +
+

Return Value

+

The context object, or @c nil if no context was provided.

+ +

TODO: This method currently accepts @c section in the view index space, but it should +be in the node index space. To get the context in the view index space (e.g. for subclasses +of @c UICollectionViewLayout, the user will call the same method on @c ASCollectionView.

+
+ + + + + + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Classes/ASCollectionView.html b/docs/appledoc/Classes/ASCollectionView.html new file mode 100755 index 0000000000..c726f1a145 --- /dev/null +++ b/docs/appledoc/Classes/ASCollectionView.html @@ -0,0 +1,731 @@ + + + + + + ASCollectionView Class Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASCollectionView Class Reference

+ + +
+ + + + + + + +
Inherits fromUICollectionView
Declared inASCollectionView.h
+ + + + +
+ +

Overview

+

Asynchronous UICollectionView with Intelligent Preloading capabilities.

ASCollectionView is a true subclass of UICollectionView, meaning it is pointer-compatible +with code that currently uses UICollectionView.

+ +

The main difference is that asyncDataSource expects -nodeForItemAtIndexPath, an ASCellNode, and +the sizeForItemAtIndexPath: method is eliminated (as are the performance problems caused by it). +This is made possible because ASCellNodes can calculate their own size, and preload ahead of time.

Note: ASCollectionNode is strongly recommended over ASCollectionView. This class exists for adoption convenience.

+
+ + + + + +
+ + + + + + +
+
+ +

  asyncDelegate +

+ +
+
+ +
+ + +
+

The object that acts as the asynchronous delegate of the collection view

+
+ + + +
@property (nonatomic, weak) id<ASCollectionDelegate> asyncDelegate
+ + + + + + + + + +
+

Discussion

+

The delegate must adopt the ASCollectionDelegate protocol. The collection view maintains a weak reference to the delegate object.

+ +

The delegate object is responsible for providing size constraints for nodes and indicating whether batch fetching should begin.

+
+ + + + + + + +
+

Declared In

+

ASCollectionView.h

+
+ + +
+
+
+ +

  asyncDataSource +

+ +
+
+ +
+ + +
+

The object that acts as the asynchronous data source of the collection view

+
+ + + +
@property (nonatomic, weak) id<ASCollectionDataSource> asyncDataSource
+ + + + + + + + + +
+

Discussion

+

The datasource must adopt the ASCollectionDataSource protocol. The collection view maintains a weak reference to the datasource object.

+ +

The datasource object is responsible for providing nodes or node creation blocks to the collection view.

+
+ + + + + + + +
+

Declared In

+

ASCollectionView.h

+
+ + +
+
+
+ +

  collectionNode +

+ +
+
+ +
+ + +
+

Returns the corresponding ASCollectionNode

+
+ + + +
@property (nonatomic, weak, readonly) ASCollectionNode *collectionNode
+ + + + + +
+

Return Value

+

collectionNode The corresponding ASCollectionNode, if one exists.

+
+ + + + + + + + + + + +
+

Declared In

+

ASCollectionView.h

+
+ + +
+
+
+ +

  leadingScreensForBatching +

+ +
+
+ +
+ + +
+

The number of screens left to scroll before the delegate -collectionView:beginBatchFetchingWithContext: is called.

+
+ + + +
@property (nonatomic, assign) CGFloat leadingScreensForBatching
+ + + + + + + + + +
+

Discussion

+

Defaults to two screenfuls.

+
+ + + + + + + +
+

Declared In

+

ASCollectionView.h

+
+ + +
+
+
+ +

  layoutInspector +

+ +
+
+ +
+ + +
+

Optional introspection object for the collection view’s layout.

+
+ + + +
@property (nonatomic, weak) id<ASCollectionViewLayoutInspecting> layoutInspector
+ + + + + + + + + +
+

Discussion

+

Since supplementary and decoration views are controlled by the collection view’s layout, this object +is used as a bridge to provide information to the internal data controller about the existence of these views and +their associated index paths. For collection views using UICollectionViewFlowLayout, a default inspector +implementation ASCollectionViewFlowLayoutInspector is created and set on this property by default. Custom +collection view layout subclasses will need to provide their own implementation of an inspector object for their +supplementary views to be compatible with ASCollectionView’s supplementary node support.

+
+ + + + + + + +
+

Declared In

+

ASCollectionView.h

+
+ + +
+
+
+ +

– nodeForItemAtIndexPath: +

+ +
+
+ +
+ + +
+

Retrieves the node for the item at the given index path.

+
+ + + +
- (nullable ASCellNode *)nodeForItemAtIndexPath:(NSIndexPath *)indexPath
+ + + +
+

Parameters

+ + + + + + + +
indexPath

The index path of the requested node.

+
+ + + +
+

Return Value

+

The node at the given index path, or @c nil if no item exists at the specified path.

+
+ + + + + + + + + + + +
+

Declared In

+

ASCollectionView.h

+
+ + +
+
+
+ +

– supplementaryNodeForElementKind:atIndexPath: +

+ +
+
+ +
+ + +
+

Similar to -supplementaryViewForElementKind:atIndexPath:

+
+ + + +
- (nullable ASCellNode *)supplementaryNodeForElementKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath
+ + + +
+

Parameters

+ + + + + + + + + + + + +
elementKind

The kind of supplementary node to locate.

indexPath

The index path of the requested supplementary node.

+
+ + + +
+

Return Value

+

The specified supplementary node or @c nil.

+
+ + + + + + + + + + + +
+

Declared In

+

ASCollectionView.h

+
+ + +
+
+
+ +

– contextForSection: +

+ +
+
+ +
+ + +
+

Retrieves the context object for the given section, as provided by the data source in +the @c collectionNode:contextForSection: method. This method must be called on the main thread.

+
+ + + +
- (nullable id<ASSectionContext>)contextForSection:(NSInteger)section
+ + + +
+

Parameters

+ + + + + + + +
section

The section to get the context for.

+
+ + + +
+

Return Value

+

The context object, or @c nil if no context was provided.

+
+ + + + + + + + + + + +
+

Declared In

+

ASCollectionView.h

+
+ + +
+
+
+ +

  scrollDirection +

+ +
+
+ +
+ + +
+

Determines collection view’s current scroll direction. Supports 2-axis collection views.

+
+ + + +
@property (nonatomic, readonly) ASScrollDirection scrollDirection
+ + + + + +
+

Return Value

+

a bitmask of ASScrollDirection values.

+
+ + + + + + + + + + + +
+

Declared In

+

ASCollectionView.h

+
+ + +
+
+
+ +

  scrollableDirections +

+ +
+
+ +
+ + +
+

Determines collection view’s scrollable directions.

+
+ + + +
@property (nonatomic, readonly) ASScrollDirection scrollableDirections
+ + + + + +
+

Return Value

+

a bitmask of ASScrollDirection values.

+
+ + + + + + + + + + + +
+

Declared In

+

ASCollectionView.h

+
+ + +
+
+
+ +

  zeroContentInsets +

+ +
+
+ +
+ + +
+

Forces the .contentInset to be UIEdgeInsetsZero.

+
+ + + +
@property (nonatomic) BOOL zeroContentInsets
+ + + + + + + + + +
+

Discussion

+

By default, UIKit sets the top inset to the navigation bar height, even for horizontally +scrolling views. This can only be disabled by setting a property on the containing UIViewController, +automaticallyAdjustsScrollViewInsets, which may not be accessible. ASPagerNode uses this to ensure +its flow layout behaves predictably and does not log undefined layout warnings.

+
+ + + + + + + +
+

Declared In

+

ASCollectionView.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Classes/ASControlNode.html b/docs/appledoc/Classes/ASControlNode.html new file mode 100755 index 0000000000..62781e4332 --- /dev/null +++ b/docs/appledoc/Classes/ASControlNode.html @@ -0,0 +1,735 @@ + + + + + + ASControlNode Class Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASControlNode Class Reference

+ + +
+ + + + + + + +
Inherits fromASDisplayNode : ASDealloc2MainObject
Declared inASControlNode.h
+ + + + +
+ +

Overview

+

ASControlNode cannot be used directly. It instead defines the common interface and behavior structure for all its subclasses. Subclasses should import “ASControlNode+Subclasses.h” for information on methods intended to be overriden.

+
+ + + + + +
+ + + + + + +
+
+ +

  enabled +

+ +
+
+ +
+ + +
+

Indicates whether or not the receiver is enabled.

+
+ + + +
@property (nonatomic, assign, getter=isEnabled) BOOL enabled
+ + + + + + + + + +
+

Discussion

+

Specify YES to make the control enabled; otherwise, specify NO to make it disabled. The default value is YES. If the enabled state is NO, the control ignores touch events and subclasses may draw differently.

+
+ + + + + + + +
+

Declared In

+

ASControlNode.h

+
+ + +
+
+
+ +

  highlighted +

+ +
+
+ +
+ + +
+

Indicates whether or not the receiver is highlighted.

+
+ + + +
@property (nonatomic, assign, getter=isHighlighted) BOOL highlighted
+ + + + + + + + + +
+

Discussion

+

This is set automatically when the there is a touch inside the control and removed on exit or touch up. This is different from touchInside in that it includes an area around the control, rather than just for touches inside the control.

+
+ + + + + + + +
+

Declared In

+

ASControlNode.h

+
+ + +
+
+
+ +

  selected +

+ +
+
+ +
+ + +
+

Indicates whether or not the receiver is highlighted.

+
+ + + +
@property (nonatomic, assign, getter=isSelected) BOOL selected
+ + + + + + + + + +
+

Discussion

+

This is set automatically when the receiver is tapped.

+
+ + + + + + + +
+

Declared In

+

ASControlNode.h

+
+ + +
+
+
+ +

  tracking +

+ +
+
+ +
+ + +
+

Indicates whether or not the receiver is currently tracking touches related to an event.

+
+ + + +
@property (nonatomic, readonly, assign, getter=isTracking) BOOL tracking
+ + + + + + + + + +
+

Discussion

+

YES if the receiver is tracking touches; NO otherwise.

+
+ + + + + + + +
+

Declared In

+

ASControlNode.h

+
+ + +
+
+
+ +

  touchInside +

+ +
+
+ +
+ + +
+

Indicates whether or not a touch is inside the bounds of the receiver.

+
+ + + +
@property (nonatomic, readonly, assign, getter=isTouchInside) BOOL touchInside
+ + + + + + + + + +
+

Discussion

+

YES if a touch is inside the receiver’s bounds; NO otherwise.

+
+ + + + + + + +
+

Declared In

+

ASControlNode.h

+
+ + +
+
+
+ +

– addTarget:action:forControlEvents: +

+ +
+
+ +
+ + +
+

Adds a target-action pair for a particular event (or events).

+
+ + + +
- (void)addTarget:(nullable id)target action:(SEL)action forControlEvents:(ASControlNodeEvent)controlEvents
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + +
target

The object to which the action message is sent. If this is nil, the responder chain is searched for an object willing to respond to the action message. target is not retained.

action

A selector identifying an action message. May optionally include the sender and the event as parameters, in that order. May not be NULL.

controlEvents

A bitmask specifying the control events for which the action message is sent. May not be 0. See “Control Events” for bitmask constants.

+
+ + + + + + + +
+

Discussion

+

You may call this method multiple times, and you may specify multiple target-action pairs for a particular event. Targets are held weakly.

+
+ + + + + + + +
+

Declared In

+

ASControlNode.h

+
+ + +
+
+
+ +

– actionsForTarget:forControlEvent: +

+ +
+
+ +
+ + +
+

Returns the actions that are associated with a target and a particular control event.

+
+ + + +
- (nullable NSArray<NSString*> *)actionsForTarget:(id)target forControlEvent:(ASControlNodeEvent)controlEvent
+ + + +
+

Parameters

+ + + + + + + + + + + + +
target

The target object. May not be nil.

controlEvent

A single constant of type ASControlNodeEvent that specifies a particular user action on the control; for a list of these constants, see “Control Events”. May not be 0 or ASControlNodeEventAllEvents.

+
+ + + +
+

Return Value

+

An array of selector names as NSString objects, or nil if there are no action selectors associated with controlEvent.

+
+ + + + + + + + + + + +
+

Declared In

+

ASControlNode.h

+
+ + +
+
+
+ +

– allTargets +

+ +
+
+ +
+ + +
+

Returns all target objects associated with the receiver.

+
+ + + +
- (NSSet *)allTargets
+ + + + + +
+

Return Value

+

A set of all targets for the receiver. The set may include NSNull to indicate at least one nil target (meaning, the responder chain is searched for a target.)

+
+ + + + + + + + + + + +
+

Declared In

+

ASControlNode.h

+
+ + +
+
+
+ +

– removeTarget:action:forControlEvents: +

+ +
+
+ +
+ + +
+

Removes a target-action pair for a particular event.

+
+ + + +
- (void)removeTarget:(nullable id)target action:(nullable SEL)action forControlEvents:(ASControlNodeEvent)controlEvents
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + +
target

The target object. Pass nil to remove all targets paired with action and the specified control events.

action

A selector identifying an action message. Pass NULL to remove all action messages paired with target.

controlEvents

A bitmask specifying the control events associated with target and action. See “Control Events” for bitmask constants. May not be 0.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASControlNode.h

+
+ + +
+
+
+ +

– sendActionsForControlEvents:withEvent: +

+ +
+
+ +
+ + +
+

Sends the actions for the control events for a particular event.

+
+ + + +
- (void)sendActionsForControlEvents:(ASControlNodeEvent)controlEvents withEvent:(nullable UIEvent *)event
+ + + +
+

Parameters

+ + + + + + + + + + + + +
controlEvents

A bitmask specifying the control events for which to send actions. See “Control Events” for bitmask constants. May not be 0.

event

The event which triggered these control actions. May be nil.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASControlNode.h

+
+ + +
+
+
+ +

– setDefaultFocusAppearance +

+ +
+
+ +
+ + +
+

How the node looks when it isn’t focused. Exposed here so that subclasses can override.

+
+ + + +
- (void)setDefaultFocusAppearance
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASControlNode.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Classes/ASDisplayNode.html b/docs/appledoc/Classes/ASDisplayNode.html new file mode 100755 index 0000000000..caf1107240 --- /dev/null +++ b/docs/appledoc/Classes/ASDisplayNode.html @@ -0,0 +1,2727 @@ + + + + + + ASDisplayNode Class Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASDisplayNode Class Reference

+ + +
+ + + + + + + + + + +
Inherits fromASDealloc2MainObject
Conforms toASLayoutElement
Declared inASDisplayNode.h
+ + + + +
+ +

Overview

+

An ASDisplayNode is an abstraction over UIView and CALayer that allows you to perform calculations about a view +hierarchy off the main thread, and could do rendering off the main thread as well.

+ +

The node API is designed to be as similar as possible to UIView. See the README for examples.

+ +

Subclassing

+ +

ASDisplayNode can be subclassed to create a new UI element. The subclass header ASDisplayNode+Subclasses provides +necessary declarations and conveniences.

+ +

Commons reasons to subclass includes making a UIView property available and receiving a callback after async +display.

+
+ + + + + +
+ + + + +

Initializing a node object

+ +
+
+ +

– init +

+ +
+
+ +
+ + +
+

Designated initializer.

+
+ + + +
- (instancetype)init
+ + + + + +
+

Return Value

+

An ASDisplayNode instance whose view will be a subclass that enables asynchronous rendering, and passes +through -layout and touch handling methods.

+
+ + + + + + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+ +

– initWithViewBlock: +

+ +
+
+ +
+ + +
+

Alternative initializer with a block to create the backing view.

+
+ + + +
- (instancetype)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock
+ + + +
+

Parameters

+ + + + + + + +
viewBlock

The block that will be used to create the backing view.

+
+ + + +
+

Return Value

+

An ASDisplayNode instance that loads its view with the given block that is guaranteed to run on the main +queue. The view will render synchronously and -layout and touch handling methods on the node will not be called.

+
+ + + + + + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+ +

– initWithViewBlock:didLoadBlock: +

+ +
+
+ +
+ + +
+

Alternative initializer with a block to create the backing view.

+
+ + + +
- (instancetype)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock didLoadBlock:(nullable ASDisplayNodeDidLoadBlock)didLoadBlock
+ + + +
+

Parameters

+ + + + + + + + + + + + +
viewBlock

The block that will be used to create the backing view.

didLoadBlock

The block that will be called after the view created by the viewBlock is loaded

+
+ + + +
+

Return Value

+

An ASDisplayNode instance that loads its view with the given block that is guaranteed to run on the main +queue. The view will render synchronously and -layout and touch handling methods on the node will not be called.

+
+ + + + + + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+ +

– initWithLayerBlock: +

+ +
+
+ +
+ + +
+

Alternative initializer with a block to create the backing layer.

+
+ + + +
- (instancetype)initWithLayerBlock:(ASDisplayNodeLayerBlock)layerBlock
+ + + +
+

Parameters

+ + + + + + + +
layerBlock

The block that will be used to create the backing layer.

+
+ + + +
+

Return Value

+

An ASDisplayNode instance that loads its layer with the given block that is guaranteed to run on the main +queue. The layer will render synchronously and -layout and touch handling methods on the node will not be called.

+
+ + + + + + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+ +

– initWithLayerBlock:didLoadBlock: +

+ +
+
+ +
+ + +
+

Alternative initializer with a block to create the backing layer.

+
+ + + +
- (instancetype)initWithLayerBlock:(ASDisplayNodeLayerBlock)layerBlock didLoadBlock:(nullable ASDisplayNodeDidLoadBlock)didLoadBlock
+ + + +
+

Parameters

+ + + + + + + + + + + + +
layerBlock

The block that will be used to create the backing layer.

didLoadBlock

The block that will be called after the layer created by the layerBlock is loaded

+
+ + + +
+

Return Value

+

An ASDisplayNode instance that loads its layer with the given block that is guaranteed to run on the main +queue. The layer will render synchronously and -layout and touch handling methods on the node will not be called.

+
+ + + + + + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+ +

– onDidLoad: +

+ +
+
+ +
+ + +
+

Add a block of work to be performed on the main thread when the node’s view or layer is loaded. Thread safe.

+
+ + + +
- (void)onDidLoad:(ASDisplayNodeDidLoadBlock)body
+ + + +
+

Parameters

+ + + + + + + +
body

The work to be performed when the node is loaded.

+ +

@precondition The node is not already loaded.

+
+ + + + + + + +
+

Discussion

+

Warning: Be careful not to retain self in body. Change the block parameter list to ^(MYCustomNode *self) {} if you +want to shadow self (e.g. if calling this during init).

Note: This will only be called the next time the node is loaded. If the node is later added to a subtree of a node +that has shouldRasterizeDescendants=YES, and is unloaded, this block will not be called if it is loaded again.

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+ +

  synchronous +

+ +
+
+ +
+ + +
+

Returns whether the node is synchronous.

+
+ + + +
@property (nonatomic, readonly, assign, getter=isSynchronous) BOOL synchronous
+ + + + + +
+

Return Value

+

NO if the node wraps a _ASDisplayView, YES otherwise.

+
+ + + + + + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+
+ + + +

Getting view and layer

+ +
+
+ +

  view +

+ +
+
+ +
+ + +
+

Returns a view.

+
+ + + +
@property (nonatomic, readonly, strong) UIView *view
+ + + + + + + + + +
+

Discussion

+

The view property is lazily initialized, similar to UIViewController. +To go the other direction, use ASViewToDisplayNode() in ASDisplayNodeExtras.h.

Warning: The first access to it must be on the main thread, and should only be used on the main thread thereafter as +well.

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+ +

  nodeLoaded +

+ +
+
+ +
+ + +
+

Returns whether a node’s backing view or layer is loaded.

+
+ + + +
@property (nonatomic, readonly, assign, getter=isNodeLoaded) BOOL nodeLoaded
+ + + + + +
+

Return Value

+

YES if a view is loaded, or if layerBacked is YES and layer is not nil; NO otherwise.

+
+ + + + + + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+ +

  layerBacked +

+ +
+
+ +
+ + +
+

Returns whether the node rely on a layer instead of a view.

+
+ + + +
@property (nonatomic, assign, getter=isLayerBacked) BOOL layerBacked
+ + + + + +
+

Return Value

+

YES if the node rely on a layer, NO otherwise.

+
+ + + + + + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+ +

  layer +

+ +
+
+ +
+ + +
+

Returns a layer.

+
+ + + +
@property (nonatomic, readonly, strong) CALayer *layer
+ + + + + + + + + +
+

Discussion

+

The layer property is lazily initialized, similar to the view property. +To go the other direction, use ASLayerToDisplayNode() in ASDisplayNodeExtras.h.

Warning: The first access to it must be on the main thread, and should only be used on the main thread thereafter as +well.

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+ +

  visible +

+ +
+
+ +
+ + +
+

Returns YES if the node is – at least partially – visible in a window.

+
+ + + +
@property (readonly, getter=isVisible) BOOL visible
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+ +

  inPreloadState +

+ +
+
+ +
+ + +
+

Returns YES if the node is in the preloading interface state.

+
+ + + +
@property (readonly, getter=isInPreloadState) BOOL inPreloadState
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+ +

  inDisplayState +

+ +
+
+ +
+ + +
+

Returns YES if the node is in the displaying interface state.

+
+ + + +
@property (readonly, getter=isInDisplayState) BOOL inDisplayState
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+ +

  interfaceState +

+ +
+
+ +
+ + +
+

Returns the Interface State of the node.

+
+ + + +
@property (readonly) ASInterfaceState interfaceState
+ + + + + +
+

Return Value

+

The current ASInterfaceState of the node, indicating whether it is visible and other situational properties.

+
+ + + + + + + + + +
+

See Also

+ +
+ + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+
+ + + +

Managing dimensions

+ +
+
+ +

– layoutThatFits: +

+ +
+
+ +
+ + +
+

Asks the node to return a layout based on given size range.

+
+ + + +
- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize
+ + + +
+

Parameters

+ + + + + + + +
constrainedSize

The minimum and maximum sizes the receiver should fit in.

+
+ + + +
+

Return Value

+

An ASLayout instance defining the layout of the receiver (and its children, if the box layout model is used).

+
+ + + + + +
+

Discussion

+

Though this method does not set the bounds of the view, it does have side effects–caching both the +constraint and the result.

Warning: Subclasses must not override this; it caches results from -calculateLayoutThatFits:. Calling this method may +be expensive if result is not cached.

+
+ + + + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+ +

  layoutSpecBlock +

+ +
+
+ +
+ + +
+

Provides a way to declare a block to provide an ASLayoutSpec without having to subclass ASDisplayNode and +implement layoutSpecThatFits:

+
+ + + +
@property (nonatomic, readwrite, copy, nullable) ASLayoutSpecBlock layoutSpecBlock
+ + + + + +
+

Return Value

+

A block that takes a constrainedSize ASSizeRange argument, and must return an ASLayoutSpec that includes all +of the subnodes to position in the layout. This input-output relationship is identical to the subclass override +method -layoutSpecThatFits:

+
+ + + + + +
+

Discussion

+

Warning: Subclasses that implement -layoutSpecThatFits: must not also use .layoutSpecBlock. Doing so will trigger +an exception. A future version of the framework may support using both, calling them serially, with the +.layoutSpecBlock superseding any values set by the method override.

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+ +

  calculatedSize +

+ +
+
+ +
+ + +
+

Return the calculated size.

+
+ + + +
@property (nonatomic, readonly, assign) CGSize calculatedSize
+ + + + + +
+

Return Value

+

Size already calculated by -calculateLayoutThatFits:.

+
+ + + + + +
+

Discussion

+

Ideal for use by subclasses in -layout, having already prompted their subnodes to calculate their size by +calling -measure: on them in -calculateLayoutThatFits.

Warning: Subclasses must not override this; it returns the last cached measurement and is never expensive.

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+ +

  constrainedSizeForCalculatedLayout +

+ +
+
+ +
+ + +
+

Return the constrained size range used for calculating layout.

+
+ + + +
@property (nonatomic, readonly, assign) ASSizeRange constrainedSizeForCalculatedLayout
+ + + + + +
+

Return Value

+

The minimum and maximum constrained sizes used by calculateLayoutThatFits:.

+
+ + + + + + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+
+ + + +

Managing the nodes hierarchy

+ +
+
+ +

– addSubnode: +

+ +
+
+ +
+ + +
+

Add a node as a subnode to this node.

+
+ + + +
- (void)addSubnode:(ASDisplayNode *)subnode
+ + + +
+

Parameters

+ + + + + + + +
subnode

The node to be added.

+
+ + + + + + + +
+

Discussion

+

The subnode’s view will automatically be added to this node’s view, lazily if the views are not created +yet.

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+ +

– insertSubnode:belowSubnode: +

+ +
+
+ +
+ + +
+

Insert a subnode before a given subnode in the list.

+
+ + + +
- (void)insertSubnode:(ASDisplayNode *)subnode belowSubnode:(ASDisplayNode *)below
+ + + +
+

Parameters

+ + + + + + + + + + + + +
subnode

The node to insert below another node.

below

The sibling node that will be above the inserted node.

+
+ + + + + + + +
+

Discussion

+

If the views are loaded, the subnode’s view will be inserted below the given node’s view in the hierarchy +even if there are other non-displaynode views.

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+ +

– insertSubnode:aboveSubnode: +

+ +
+
+ +
+ + +
+

Insert a subnode after a given subnode in the list.

+
+ + + +
- (void)insertSubnode:(ASDisplayNode *)subnode aboveSubnode:(ASDisplayNode *)above
+ + + +
+

Parameters

+ + + + + + + + + + + + +
subnode

The node to insert below another node.

above

The sibling node that will be behind the inserted node.

+
+ + + + + + + +
+

Discussion

+

If the views are loaded, the subnode’s view will be inserted above the given node’s view in the hierarchy +even if there are other non-displaynode views.

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+ +

– insertSubnode:atIndex: +

+ +
+
+ +
+ + +
+

Insert a subnode at a given index in subnodes.

+
+ + + +
- (void)insertSubnode:(ASDisplayNode *)subnode atIndex:(NSInteger)idx
+ + + +
+

Parameters

+ + + + + + + + + + + + +
subnode

The node to insert.

idx

The index in the array of the subnodes property at which to insert the node. Subnodes indices start at 0 +and cannot be greater than the number of subnodes.

+
+ + + + + + + +
+

Discussion

+

If this node’s view is loaded, ASDisplayNode insert the subnode’s view after the subnode at index - 1’s +view even if there are other non-displaynode views.

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+ +

– replaceSubnode:withSubnode: +

+ +
+
+ +
+ + +
+

Replace subnode with replacementSubnode.

+
+ + + +
- (void)replaceSubnode:(ASDisplayNode *)subnode withSubnode:(ASDisplayNode *)replacementSubnode
+ + + +
+

Parameters

+ + + + + + + + + + + + +
subnode

A subnode of self.

replacementSubnode

A node with which to replace subnode.

+
+ + + + + + + +
+

Discussion

+

Should both subnode and replacementSubnode already be subnodes of self, subnode is removed and +replacementSubnode inserted in its place. +If subnode is not a subnode of self, this method will throw an exception. +If replacementSubnode is nil, this method will throw an exception

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+ +

– removeFromSupernode +

+ +
+
+ +
+ + +
+

Remove this node from its supernode.

+
+ + + +
- (void)removeFromSupernode
+ + + + + + + + + +
+

Discussion

+

The node’s view will be automatically removed from the supernode’s view.

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+ +

  subnodes +

+ +
+
+ +
+ + +
+

The receiver’s immediate subnodes.

+
+ + + +
@property (nonatomic, readonly, copy) NSArray<ASDisplayNode*> *subnodes
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+ +

  supernode +

+ +
+
+ +
+ + +
+

The receiver’s supernode.

+
+ + + +
@property (nonatomic, readonly, weak) ASDisplayNode *supernode
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+
+ + + +

Drawing and Updating the View

+ +
+
+ +

  displaysAsynchronously +

+ +
+
+ +
+ + +
+

Whether this node’s view performs asynchronous rendering.

+
+ + + +
@property (nonatomic, assign) BOOL displaysAsynchronously
+ + + + + +
+

Return Value

+

Defaults to YES, except for synchronous views (ie, those created with -initWithViewBlock: / +-initWithLayerBlock:), which are always NO.

+
+ + + + + +
+

Discussion

+

If this flag is set, then the node will participate in the current asyncdisplaykit_async_transaction and +do its rendering on the displayQueue instead of the main thread.

+ +

Asynchronous rendering proceeds as follows:

+ +

When the view is initially added to the hierarchy, it has -needsDisplay true. +After layout, Core Animation will call -display on the _ASDisplayLayer +-display enqueues a rendering operation on the displayQueue +When the render block executes, it calls the delegate display method (-drawRect:… or -display) +The delegate provides contents via this method and an operation is added to the asyncdisplaykit_async_transaction +Once all rendering is complete for the current asyncdisplaykit_async_transaction, +the completion for the block sets the contents on all of the layers in the same frame

+ +

If asynchronous rendering is disabled:

+ +

When the view is initially added to the hierarchy, it has -needsDisplay true. +After layout, Core Animation will call -display on the _ASDisplayLayer +-display calls delegate display method (-drawRect:… or -display) immediately +-display sets the layer contents immediately with the result

+ +

Note: this has nothing to do with -[CALayer drawsAsynchronously].

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+ +

  shouldRasterizeDescendants +

+ +
+
+ +
+ + +
+

Whether to draw all descendant nodes' layers/views into this node’s layer/view’s backing store.

+
+ + + +
@property (nonatomic, assign) BOOL shouldRasterizeDescendants
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+ +

  displaySuspended +

+ +
+
+ +
+ + +
+

Prevent the node’s layer from displaying.

+
+ + + +
@property (nonatomic, assign) BOOL displaySuspended
+ + + + + + + + + +
+

Discussion

+

A subclass may check this flag during -display or -drawInContext: to cancel a display that is already in +progress.

+ +

Defaults to NO. Does not control display for any child or descendant nodes; for that, use +-recursivelySetDisplaySuspended:.

+ +

If a setNeedsDisplay occurs while displaySuspended is YES, and displaySuspended is set to NO, then the +layer will be automatically displayed.

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+ +

  shouldAnimateSizeChanges +

+ +
+
+ +
+ + +
+

Whether size changes should be animated. Default to YES.

+
+ + + +
@property (nonatomic, assign) BOOL shouldAnimateSizeChanges
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+ +

– recursivelySetDisplaySuspended: +

+ +
+
+ +
+ + +
+

Prevent the node and its descendants' layer from displaying.

+
+ + + +
- (void)recursivelySetDisplaySuspended:(BOOL)flag
+ + + +
+

Parameters

+ + + + + + + +
flag

YES if display should be prevented or cancelled; NO otherwise.

+
+ + + + + + + + + + + +
+

See Also

+ +
+ + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+ +

– recursivelyClearContents +

+ +
+
+ +
+ + +
+

Calls -clearContents on the receiver and its subnode hierarchy.

+
+ + + +
- (void)recursivelyClearContents
+ + + + + + + + + +
+

Discussion

+

Clears backing stores and other memory-intensive intermediates. +If the node is removed from a visible hierarchy and then re-added, it will automatically trigger a new asynchronous display, +as long as displaySuspended is not set. +If the node remains in the hierarchy throughout, -setNeedsDisplay is required to trigger a new asynchronous display.

+
+ + + + + +
+

See Also

+ +
+ + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+ +

– recursivelyClearFetchedData +

+ +
+
+ +
+ + +
+

Calls -clearFetchedData on the receiver and its subnode hierarchy.

+
+ + + +
- (void)recursivelyClearFetchedData
+ + + + + + + + + +
+

Discussion

+

Clears any memory-intensive fetched content. +This method is used to notify the node that it should purge any content that is both expensive to fetch and to +retain in memory.

+
+ + + + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+ +

– recursivelyFetchData +

+ +
+
+ +
+ + +
+

Calls -fetchData on the receiver and its subnode hierarchy.

+
+ + + +
- (void)recursivelyFetchData
+ + + + + + + + + +
+

Discussion

+

Fetches content from remote sources for the current node and all subnodes.

+
+ + + + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+ +

– setNeedsDataFetch +

+ +
+
+ +
+ + +
+

Triggers a recursive call to fetchData when the node has an interfaceState of ASInterfaceStatePreload

+
+ + + +
- (void)setNeedsDataFetch
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+ +

  placeholderEnabled +

+ +
+
+ +
+ + +
+

Toggle displaying a placeholder over the node that covers content until the node and all subnodes are +displayed.

+
+ + + +
@property (nonatomic, assign) BOOL placeholderEnabled
+ + + + + + + + + +
+

Discussion

+

Defaults to NO.

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+ +

  placeholderFadeDuration +

+ +
+
+ +
+ + +
+

Set the time it takes to fade out the placeholder when a node’s contents are finished displaying.

+
+ + + +
@property (nonatomic, assign) NSTimeInterval placeholderFadeDuration
+ + + + + + + + + +
+

Discussion

+

Defaults to 0 seconds.

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+ +

  drawingPriority +

+ +
+
+ +
+ + +
+

Determines drawing priority of the node. Nodes with higher priority will be drawn earlier.

+
+ + + +
@property (nonatomic, assign) NSInteger drawingPriority
+ + + + + + + + + +
+

Discussion

+

Defaults to ASDefaultDrawingPriority. There may be multiple drawing threads, and some of them may +decide to perform operations in queued order (regardless of drawingPriority)

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+
+ + + +

Hit Testing

+ +
+
+ +

  hitTestSlop +

+ +
+
+ +
+ + +
+

Bounds insets for hit testing.

+
+ + + +
@property (nonatomic, assign) UIEdgeInsets hitTestSlop
+ + + + + + + + + +
+

Discussion

+

When set to a non-zero inset, increases the bounds for hit testing to make it easier to tap or perform +gestures on this node. Default is UIEdgeInsetsZero.

+ +

This affects the default implementation of -hitTest and -pointInside, so subclasses should call super if you override +it and want hitTestSlop applied.

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+ +

– pointInside:withEvent: +

+ +
+
+ +
+ + +
+

Returns a Boolean value indicating whether the receiver contains the specified point.

+
+ + + +
- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event
+ + + +
+

Parameters

+ + + + + + + + + + + + +
point

A point that is in the receiver’s local coordinate system (bounds).

event

The event that warranted a call to this method.

+
+ + + +
+

Return Value

+

YES if point is inside the receiver’s bounds; otherwise, NO.

+
+ + + + + +
+

Discussion

+

Includes the “slop” factor specified with hitTestSlop.

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+
+ + + +

Converting Between View Coordinate Systems

+ +
+
+ +

– convertPoint:toNode: +

+ +
+
+ +
+ + +
+

Converts a point from the receiver’s coordinate system to that of the specified node.

+
+ + + +
- (CGPoint)convertPoint:(CGPoint)point toNode:(nullable ASDisplayNode *)node
+ + + +
+

Parameters

+ + + + + + + + + + + + +
point

A point specified in the local coordinate system (bounds) of the receiver.

node

The node into whose coordinate system point is to be converted.

+
+ + + +
+

Return Value

+

The point converted to the coordinate system of node.

+
+ + + + + + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+ +

– convertPoint:fromNode: +

+ +
+
+ +
+ + +
+

Converts a point from the coordinate system of a given node to that of the receiver.

+
+ + + +
- (CGPoint)convertPoint:(CGPoint)point fromNode:(nullable ASDisplayNode *)node
+ + + +
+

Parameters

+ + + + + + + + + + + + +
point

A point specified in the local coordinate system (bounds) of node.

node

The node with point in its coordinate system.

+
+ + + +
+

Return Value

+

The point converted to the local coordinate system (bounds) of the receiver.

+
+ + + + + + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+ +

– convertRect:toNode: +

+ +
+
+ +
+ + +
+

Converts a rectangle from the receiver’s coordinate system to that of another view.

+
+ + + +
- (CGRect)convertRect:(CGRect)rect toNode:(nullable ASDisplayNode *)node
+ + + +
+

Parameters

+ + + + + + + + + + + + +
rect

A rectangle specified in the local coordinate system (bounds) of the receiver.

node

The node that is the target of the conversion operation.

+
+ + + +
+

Return Value

+

The converted rectangle.

+
+ + + + + + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+ +

– convertRect:fromNode: +

+ +
+
+ +
+ + +
+

Converts a rectangle from the coordinate system of another node to that of the receiver.

+
+ + + +
- (CGRect)convertRect:(CGRect)rect fromNode:(nullable ASDisplayNode *)node
+ + + +
+

Parameters

+ + + + + + + + + + + + +
rect

A rectangle specified in the local coordinate system (bounds) of node.

node

The node with rect in its coordinate system.

+
+ + + +
+

Return Value

+

The converted rectangle.

+
+ + + + + + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Classes/ASEditableTextNode.html b/docs/appledoc/Classes/ASEditableTextNode.html new file mode 100755 index 0000000000..409bc45979 --- /dev/null +++ b/docs/appledoc/Classes/ASEditableTextNode.html @@ -0,0 +1,700 @@ + + + + + + ASEditableTextNode Class Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASEditableTextNode Class Reference

+ + +
+ + + + + + + + + + +
Inherits fromASDisplayNode : ASDealloc2MainObject
Conforms toUITextInputTraits
Declared inASEditableTextNode.h
+ + + + +
+ +

Overview

+

Does not support layer backing.

+
+ + + + + +
+ + + + + + +
+
+ +

– init +

+ +
+
+ +
+ + +
+

Initializes an editable text node using default TextKit components.

+
+ + + +
- (instancetype)init
+ + + + + +
+

Return Value

+

An initialized ASEditableTextNode.

+
+ + + + + + + + + + + +
+

Declared In

+

ASEditableTextNode.h

+
+ + +
+
+
+ +

– initWithTextKitComponents:placeholderTextKitComponents: +

+ +
+
+ +
+ + +
+

Initializes an editable text node using the provided TextKit components.

+
+ + + +
- (instancetype)initWithTextKitComponents:(ASTextKitComponents *)textKitComponents placeholderTextKitComponents:(ASTextKitComponents *)placeholderTextKitComponents
+ + + +
+

Parameters

+ + + + + + + + + + + + +
textKitComponents

The TextKit stack used to render text.

placeholderTextKitComponents

The TextKit stack used to render placeholder text.

+
+ + + +
+

Return Value

+

An initialized ASEditableTextNode.

+
+ + + + + + + + + + + +
+

Declared In

+

ASEditableTextNode.h

+
+ + +
+
+
+ +

  scrollEnabled +

+ +
+
+ +
+ + +
+

Enable scrolling on the textView +@default true

+
+ + + +
@property (nonatomic) BOOL scrollEnabled
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASEditableTextNode.h

+
+ + +
+
+
+ +

  textView +

+ +
+
+ +
+ + +
+

Access to underlying UITextView for more configuration options.

+
+ + + +
@property (nonatomic, readonly, strong) UITextView *textView
+ + + + + + + + + +
+

Discussion

+

Warning: This property should only be used on the main thread and should not be accessed before the editable text node’s view is created.

+
+ + + + + + + +
+

Declared In

+

ASEditableTextNode.h

+
+ + +
+
+
+ +

– isDisplayingPlaceholder +

+ +
+
+ +
+ + +
+

Indicates if the receiver is displaying the placeholder text.

+
+ + + +
- (BOOL)isDisplayingPlaceholder
+ + + + + +
+

Return Value

+

YES if the placeholder is currently displayed; NO otherwise.

+
+ + + + + +
+

Discussion

+

To update the placeholder, see the attributedPlaceholderText property.

+
+ + + + + + + +
+

Declared In

+

ASEditableTextNode.h

+
+ + +
+
+
+ +

  attributedPlaceholderText +

+ +
+
+ +
+ + +
+

The styled placeholder text displayed by the text node while no text is entered

+
+ + + +
@property (nonatomic, readwrite, strong, nullable) NSAttributedString *attributedPlaceholderText
+ + + + + + + + + +
+

Discussion

+

The placeholder is displayed when the user has not entered any text and the keyboard is not visible.

+
+ + + + + + + +
+

Declared In

+

ASEditableTextNode.h

+
+ + +
+
+
+ +

  attributedText +

+ +
+
+ +
+ + +
+

The styled text displayed by the receiver.

+
+ + + +
@property (nonatomic, readwrite, copy, nullable) NSAttributedString *attributedText
+ + + + + + + + + +
+

Discussion

+

When the placeholder is displayed (as indicated by -isDisplayingPlaceholder), this value is nil. Otherwise, this value is the attributed text the user has entered. This value can be modified regardless of whether the receiver is the first responder (and thus, editing) or not. Changing this value from nil to non-nil will result in the placeholder being hidden, and the new value being displayed.

+
+ + + + + + + +
+

Declared In

+

ASEditableTextNode.h

+
+ + +
+
+
+ +

  textContainerInset +

+ +
+
+ +
+ + +
+

The textContainerInset of both the placeholder and typed textView. This value defaults to UIEdgeInsetsZero.

+
+ + + +
@property (nonatomic, readwrite) UIEdgeInsets textContainerInset
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASEditableTextNode.h

+
+ + +
+
+
+ +

  autocapitalizationType +

+ +
+
+ +
+ + +
+

properties.

+
+ + + +
@property (nonatomic, readwrite, assign) UITextAutocapitalizationType autocapitalizationType
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASEditableTextNode.h

+
+ + +
+
+
+ +

– isFirstResponder +

+ +
+
+ +
+ + +
+

Indicates whether the receiver’s text view is the first responder, and thus has the keyboard visible and is prepared for editing by the user.

+
+ + + +
- (BOOL)isFirstResponder
+ + + + + +
+

Return Value

+

YES if the receiver’s text view is the first-responder; NO otherwise.

+
+ + + + + + + + + + + +
+

Declared In

+

ASEditableTextNode.h

+
+ + +
+
+
+ +

– frameForTextRange: +

+ +
+
+ +
+ + +
+

Returns the frame of the given range of characters.

+
+ + + +
- (CGRect)frameForTextRange:(NSRange)textRange
+ + + +
+

Parameters

+ + + + + + + +
textRange

A range of characters.

+
+ + + +
+

Return Value

+

A CGRect that is the bounding box of the glyphs covered by the given range of characters, in the coordinate system of the receiver.

+
+ + + + + +
+

Discussion

+

This method raises an exception if textRange is not a valid range of characters within the receiver’s attributed text.

+
+ + + + + + + +
+

Declared In

+

ASEditableTextNode.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Classes/ASImageNode.html b/docs/appledoc/Classes/ASImageNode.html new file mode 100755 index 0000000000..387adaa081 --- /dev/null +++ b/docs/appledoc/Classes/ASImageNode.html @@ -0,0 +1,674 @@ + + + + + + ASImageNode Class Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASImageNode Class Reference

+ + +
+ + + + + + + +
Inherits fromASControlNode : ASDisplayNode : ASDealloc2MainObject
Declared inASImageNode.h
+ + + + +
+ +

Overview

+

Supports cropping, tinting, and arbitrary image modification blocks.

+
+ + + + + +
+ + + + + + +
+
+ +

  image +

+ +
+
+ +
+ + +
+

The image to display.

+
+ + + +
@property (nullable, nonatomic, strong) UIImage *image
+ + + + + + + + + +
+

Discussion

+

The node will efficiently display stretchable images by using +the layer’s contentsCenter property. Non-stretchable images work too, of +course.

+
+ + + + + + + +
+

Declared In

+

ASImageNode.h

+
+ + +
+
+
+ +

  placeholderColor +

+ +
+
+ +
+ + +
+

The placeholder color.

+
+ + + +
@property (nullable, nonatomic, strong) UIColor *placeholderColor
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASImageNode.h

+
+ + +
+
+
+ +

  cropEnabled +

+ +
+
+ +
+ + +
+

Indicates whether efficient cropping of the receiver is enabled.

+
+ + + +
@property (nonatomic, assign, getter=isCropEnabled) BOOL cropEnabled
+ + + + + + + + + +
+

Discussion

+

Defaults to YES. See -setCropEnabled:recropImmediately:inBounds: for more +information.

+
+ + + + + + + +
+

Declared In

+

ASImageNode.h

+
+ + +
+
+
+ +

  forceUpscaling +

+ +
+
+ +
+ + +
+

Indicates that efficient downsizing of backing store should not be enabled.

+
+ + + +
@property (nonatomic, assign) BOOL forceUpscaling
+ + + + + + + + + +
+

Discussion

+

Defaults to NO. @see ASCroppedImageBackingSizeAndDrawRectInBounds for more +information.

+
+ + + + + + + +
+

Declared In

+

ASImageNode.h

+
+ + +
+
+
+ +

  forcedSize +

+ +
+
+ +
+ + +
+

Forces image to be rendered at forcedSize.

+
+ + + +
@property (nonatomic, assign) CGSize forcedSize
+ + + + + + + + + +
+

Discussion

+

Defaults to CGSizeZero to indicate that the forcedSize should not be used. +Setting forcedSize to non-CGSizeZero will force the backing of the layer contents to +be forcedSize (automatically adjusted for contentsSize).

+
+ + + + + + + +
+

Declared In

+

ASImageNode.h

+
+ + +
+
+
+ +

– setCropEnabled:recropImmediately:inBounds: +

+ +
+
+ +
+ + +
+

Enables or disables efficient cropping.

+
+ + + +
- (void)setCropEnabled:(BOOL)cropEnabled recropImmediately:(BOOL)recropImmediately inBounds:(CGRect)cropBounds
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + +
cropEnabled

YES to efficiently crop the receiver’s contents such that +contents outside of its bounds are not included; NO otherwise.

recropImmediately

If the receiver has an image, YES to redisplay the +receiver immediately; NO otherwise.

cropBounds

The bounds into which the receiver will be cropped. Useful +if bounds are to change in response to cropping (but have not yet done so).

+
+ + + + + + + +
+

Discussion

+

Efficient cropping is only performed when the receiver’s view’s +contentMode is UIViewContentModeScaleAspectFill. By default, cropping is +enabled. The crop alignment may be controlled via cropAlignmentFactor.

+
+ + + + + + + +
+

Declared In

+

ASImageNode.h

+
+ + +
+
+
+ +

  cropRect +

+ +
+
+ +
+ + +
+

A value that controls how the receiver’s efficient cropping is aligned.

+
+ + + +
@property (nonatomic, readwrite, assign) CGRect cropRect
+ + + + + + + + + +
+

Discussion

+

This value defines a rectangle that is to be featured by the +receiver. The rectangle is specified as a “unit rectangle,” using +fractions of the source image’s width and height, e.g. CGRectMake(0.5, 0, +0.5, 1.0) will feature the full right half a photo. If the cropRect is +empty, the content mode of the receiver will be used to determine its +dimensions, and only the cropRect’s origin will be used for positioning. The +default value of this property is CGRectMake(0.5, 0.5, 0.0, 0.0).

+
+ + + + + + + +
+

Declared In

+

ASImageNode.h

+
+ + +
+
+
+ +

  imageModificationBlock +

+ +
+
+ +
+ + +
+

An optional block which can perform drawing operations on image +during the display phase.

+
+ + + +
@property (nullable, nonatomic, readwrite, copy) asimagenode_modification_block_t imageModificationBlock
+ + + + + + + + + +
+

Discussion

+

Can be used to add image effects (such as rounding, adding +borders, or other pattern overlays) without extraneous display calls.

+
+ + + + + + + +
+

Declared In

+

ASImageNode.h

+
+ + +
+
+
+ +

– setNeedsDisplayWithCompletion: +

+ +
+
+ +
+ + +
+

Marks the receiver as needing display and performs a block after +display has finished.

+
+ + + +
- (void)setNeedsDisplayWithCompletion:(nullable void ( ^ ) ( BOOL canceled ))displayCompletionBlock
+ + + +
+

Parameters

+ + + + + + + +
displayCompletionBlock

The block to be performed after display has +finished. Its canceled property will be YES if display was prevented or +canceled (via displaySuspended); NO otherwise.

+
+ + + + + + + +
+

Discussion

+

displayCompletionBlock will be performed on the main-thread. If +displaySuspended is YES, displayCompletionBlock is will be +performed immediately and YES will be passed for canceled.

+
+ + + + + + + +
+

Declared In

+

ASImageNode.h

+
+ + +
+
+
+ +

  isDefaultFocusAppearance +

+ +
+
+ +
+ + +
+

A bool to track if the current appearance of the node +is the default focus appearance. +Exposed here so the category methods can set it.

+
+ + + +
@property (nonatomic, assign) BOOL isDefaultFocusAppearance
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASImageNode.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Classes/ASInsetLayoutSpec.html b/docs/appledoc/Classes/ASInsetLayoutSpec.html new file mode 100755 index 0000000000..5349d434f1 --- /dev/null +++ b/docs/appledoc/Classes/ASInsetLayoutSpec.html @@ -0,0 +1,214 @@ + + + + + + ASInsetLayoutSpec Class Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASInsetLayoutSpec Class Reference

+ + +
+ + + + + + + +
Inherits fromASLayoutSpec : NSObject
Declared inASInsetLayoutSpec.h
+ + + + +
+ +

Overview

+

A layout spec that wraps another layoutElement child, applying insets around it.

+ +

If the child has a size specified as a fraction, the fraction is resolved against this spec’s parent +size after applying insets.

+ +

@example ASOuterLayoutSpec contains an ASInsetLayoutSpec with an ASInnerLayoutSpec. Suppose that: +- ASOuterLayoutSpec is 200pt wide. +- ASInnerLayoutSpec specifies its width as 100%. +- The ASInsetLayoutSpec has insets of 10pt on every side. +ASInnerLayoutSpec will have size 180pt, not 200pt, because it receives a parent size that has been adjusted for insets.

+ +

If you’re familiar with CSS: ASInsetLayoutSpec’s child behaves similarly to “box-sizing: border-box”.

+ +

An infinite inset is resolved as an inset equal to all remaining space after applying the other insets and child size. +@example An ASInsetLayoutSpec with an infinite left inset and 10px for all other edges will position it’s child 10px from the right edge.

+
+ + + + + +
+ + + + + + +
+
+ +

+ insetLayoutSpecWithInsets:child: +

+ +
+
+ +
+ + +
+

The amount of space to inset on each side.

+
+ + + +
+ (instancetype)insetLayoutSpecWithInsets:(UIEdgeInsets)insets child:(id<ASLayoutElement>)child
+ + + +
+

Parameters

+ + + + + + + + + + + + +
insets

The amount of space to inset on each side.

child

The wrapped child to inset.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASInsetLayoutSpec.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Classes/ASLayout.html b/docs/appledoc/Classes/ASLayout.html new file mode 100755 index 0000000000..98474e1041 --- /dev/null +++ b/docs/appledoc/Classes/ASLayout.html @@ -0,0 +1,762 @@ + + + + + + ASLayout Class Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASLayout Class Reference

+ + +
+ + + + + + + +
Inherits fromNSObject
Declared inASLayout.h
+ + + + +
+ +

Overview

+

A node in the layout tree that represents the size and position of the object that created it (ASLayoutElement).

+
+ + + + + +
+ + + + + + +
+
+ +

  layoutElement +

+ +
+
+ +
+ + +
+

The underlying object described by this layout

+
+ + + +
@property (nonatomic, weak, readonly) id<ASLayoutElement> layoutElement
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASLayout.h

+
+ + +
+
+
+ +

  type +

+ +
+
+ +
+ + +
+

The type of ASLayoutElement that created this layout

+
+ + + +
@property (nonatomic, assign, readonly) ASLayoutElementType type
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASLayout.h

+
+ + +
+
+
+ +

  size +

+ +
+
+ +
+ + +
+

Size of the current layout

+
+ + + +
@property (nonatomic, assign, readonly) CGSize size
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASLayout.h

+
+ + +
+
+
+ +

  position +

+ +
+
+ +
+ + +
+

Position in parent. Default to CGPointNull.

+
+ + + +
@property (nonatomic, assign, readonly) CGPoint position
+ + + + + + + + + +
+

Discussion

+

When being used as a sublayout, this property must not equal CGPointNull.

+
+ + + + + + + +
+

Declared In

+

ASLayout.h

+
+ + +
+
+
+ +

  sublayouts +

+ +
+
+ +
+ + +
+

Array of ASLayouts. Each must have a valid non-null position.

+
+ + + +
@property (nonatomic, copy, readonly) NSArray<ASLayout*> *sublayouts
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASLayout.h

+
+ + +
+
+
+ +

  frame +

+ +
+
+ +
+ + +
+

Returns a valid frame for the current layout computed with the size and position.

+
+ + + +
@property (nonatomic, assign, readonly) CGRect frame
+ + + + + + + + + +
+

Discussion

+

Clamps the layout’s origin or position to 0 if any of the calculated values are infinite.

+
+ + + + + + + +
+

Declared In

+

ASLayout.h

+
+ + +
+
+
+ +

– initWithLayoutElement:size:position:sublayouts: +

+ +
+
+ +
+ + +
+

Designated initializer

+
+ + + +
- (instancetype)initWithLayoutElement:(id<ASLayoutElement>)layoutElement size:(CGSize)size position:(CGPoint)position sublayouts:(nullable NSArray<ASLayout*> *)sublayouts
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASLayout.h

+
+ + +
+
+
+ +

+ layoutWithLayoutElement:size:position:sublayouts: +

+ +
+
+ +
+ + +
+

Convenience class initializer for layout construction.

+
+ + + +
+ (instancetype)layoutWithLayoutElement:(id<ASLayoutElement>)layoutElement size:(CGSize)size position:(CGPoint)position sublayouts:(nullable NSArray<ASLayout*> *)sublayouts
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
layoutElement

The backing ASLayoutElement object.

size

The size of this layout.

position

The position of this layout within its parent (if available).

sublayouts

Sublayouts belong to the new layout.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASLayout.h

+
+ + +
+
+
+ +

+ layoutWithLayoutElement:size:sublayouts: +

+ +
+
+ +
+ + +
+

Convenience initializer that has CGPointNull position. +Best used by ASDisplayNode subclasses that are manually creating a layout for -calculateLayoutThatFits:, +or for ASLayoutSpec subclasses that are referencing the “self” level in the layout tree, +or for creating a sublayout of which the position is yet to be determined.

+
+ + + +
+ (instancetype)layoutWithLayoutElement:(id<ASLayoutElement>)layoutElement size:(CGSize)size sublayouts:(nullable NSArray<ASLayout*> *)sublayouts
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + +
layoutElement

The backing ASLayoutElement object.

size

The size of this layout.

sublayouts

Sublayouts belong to the new layout.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASLayout.h

+
+ + +
+
+
+ +

+ layoutWithLayoutElement:size: +

+ +
+
+ +
+ + +
+

Convenience that has CGPointNull position and no sublayouts. +Best used for creating a layout that has no sublayouts, and is either a root one +or a sublayout of which the position is yet to be determined.

+
+ + + +
+ (instancetype)layoutWithLayoutElement:(id<ASLayoutElement>)layoutElement size:(CGSize)size
+ + + +
+

Parameters

+ + + + + + + + + + + + +
layoutElement

The backing ASLayoutElement object.

size

The size of this layout.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASLayout.h

+
+ + +
+
+
+ +

+ layoutWithLayout:position: +

+ +
+
+ +
+ + +
+

Convenience initializer that creates a layout based on the values of the given layout, with a new position

+
+ + + +
+ (instancetype)layoutWithLayout:(ASLayout *)layout position:(CGPoint)position
+ + + +
+

Parameters

+ + + + + + + + + + + + +
layout

The layout to use to create the new layout

position

The position of the new layout

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASLayout.h

+
+ + +
+
+
+ +

– filteredNodeLayoutTree +

+ +
+
+ +
+ + +
+

Traverses the existing layout tree and generates a new tree that represents only ASDisplayNode layouts

+
+ + + +
- (ASLayout *)filteredNodeLayoutTree
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASLayout.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Classes/ASLayoutElementStyle.html b/docs/appledoc/Classes/ASLayoutElementStyle.html new file mode 100755 index 0000000000..6a71f721ed --- /dev/null +++ b/docs/appledoc/Classes/ASLayoutElementStyle.html @@ -0,0 +1,796 @@ + + + + + + ASLayoutElementStyle Class Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASLayoutElementStyle Class Reference

+ + +
+ + + + + + + + + + +
Inherits fromNSObject
Conforms toASAbsoluteLayoutElement
ASStackLayoutElement
Declared inASLayoutElement.h
+ + + + + + +
+ + + + + + +
+
+ +

– initWithDelegate: +

+ +
+
+ +
+ + +
+

Initializes the layoutElement style with a specified delegate

+
+ + + +
- (instancetype)initWithDelegate:(id<ASLayoutElementStyleDelegate>)delegate
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASLayoutElement.h

+
+ + +
+
+
+ +

  delegate +

+ +
+
+ +
+ + +
+

The object that acts as the delegate of the style.

+
+ + + +
@property (nullable, nonatomic, weak, readonly) id<ASLayoutElementStyleDelegate> delegate
+ + + + + + + + + +
+

Discussion

+

The delegate must adopt the ASLayoutElementStyleDelegate protocol. The delegate is not retained.

+
+ + + + + + + +
+

Declared In

+

ASLayoutElement.h

+
+ + +
+
+
+ +

  width +

+ +
+
+ +
+ + +
+

The width property specifies the height of the content area of an ASLayoutElement. +The minWidth and maxWidth properties override width. +Defaults to ASDimensionAuto

+
+ + + +
@property (nonatomic, assign, readwrite) ASDimension width
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASLayoutElement.h

+
+ + +
+
+
+ +

  height +

+ +
+
+ +
+ + +
+

The height property specifies the height of the content area of an ASLayoutElement +The minHeight and maxHeight properties override height. +Defaults to ASDimensionAuto

+
+ + + +
@property (nonatomic, assign, readwrite) ASDimension height
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASLayoutElement.h

+
+ + +
+
+
+ +

  minHeight +

+ +
+
+ +
+ + +
+

The minHeight property is used to set the minimum height of a given element. It prevents the used value +of the height property from becoming smaller than the value specified for minHeight. +The value of minHeight overrides both maxHeight and height. +Defaults to ASDimensionAuto

+
+ + + +
@property (nonatomic, assign, readwrite) ASDimension minHeight
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASLayoutElement.h

+
+ + +
+
+
+ +

  maxHeight +

+ +
+
+ +
+ + +
+

The maxHeight property is used to set the maximum height of an element. It prevents the used value of the +height property from becoming larger than the value specified for maxHeight. +The value of maxHeight overrides height, but minHeight overrides maxHeight. +Defaults to ASDimensionAuto

+
+ + + +
@property (nonatomic, assign, readwrite) ASDimension maxHeight
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASLayoutElement.h

+
+ + +
+
+
+ +

  minWidth +

+ +
+
+ +
+ + +
+

The minWidth property is used to set the minimum width of a given element. It prevents the used value of +the width property from becoming smaller than the value specified for minWidth. +The value of minWidth overrides both maxWidth and width. +Defaults to ASDimensionAuto

+
+ + + +
@property (nonatomic, assign, readwrite) ASDimension minWidth
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASLayoutElement.h

+
+ + +
+
+
+ +

  maxWidth +

+ +
+
+ +
+ + +
+

The maxWidth property is used to set the maximum width of a given element. It prevents the used value of +the width property from becoming larger than the value specified for maxWidth. +The value of maxWidth overrides width, but minWidth overrides maxWidth. +Defaults to ASDimensionAuto

+
+ + + +
@property (nonatomic, assign, readwrite) ASDimension maxWidth
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASLayoutElement.h

+
+ + +
+
+
+ +

  preferredSize +

+ +
+
+ +
+ + +
+

Provides a suggested size for a layout element. If the optional minSize or maxSize are provided, +and the preferredSize exceeds these, the minSize or maxSize will be enforced. If this optional value is not +provided, the layout element’s size will default to it’s intrinsic content size provided calculateSizeThatFits:

+
+ + + +
@property (nonatomic, assign) CGSize preferredSize
+ + + + + + + + + +
+

Discussion

+

This method is optional, but one of either preferredSize or preferredLayoutSize is required +for nodes that either have no intrinsic content size or +should be laid out at a different size than its intrinsic content size. For example, this property could be +set on an ASImageNode to display at a size different from the underlying image size.

Warning: Calling the getter when the size’s width or height are relative will cause an assert.

+
+ + + + + + + +
+

Declared In

+

ASLayoutElement.h

+
+ + +
+
+
+ +

  minSize +

+ +
+
+ +
+ + +
+

An optional property that provides a minimum size bound for a layout element. If provided, this restriction will +always be enforced. If a parent layout element’s minimum size is smaller than its child’s minimum size, the child’s +minimum size will be enforced and its size will extend out of the layout spec’s.

+
+ + + +
@property (nonatomic, assign) CGSize minSize
+ + + + + + + + + +
+

Discussion

+

For example, if you set a preferred relative width of 50% and a minimum width of 200 points on an +element in a full screen container, this would result in a width of 160 points on an iPhone screen. However, +since 160 pts is lower than the minimum width of 200 pts, the minimum width would be used.

+
+ + + + + + + +
+

Declared In

+

ASLayoutElement.h

+
+ + +
+
+
+ +

  maxSize +

+ +
+
+ +
+ + +
+

An optional property that provides a maximum size bound for a layout element. If provided, this restriction will +always be enforced. If a child layout element’s maximum size is smaller than its parent, the child’s maximum size will +be enforced and its size will extend out of the layout spec’s.

+
+ + + +
@property (nonatomic, assign) CGSize maxSize
+ + + + + + + + + +
+

Discussion

+

For example, if you set a preferred relative width of 50% and a maximum width of 120 points on an +element in a full screen container, this would result in a width of 160 points on an iPhone screen. However, +since 160 pts is higher than the maximum width of 120 pts, the maximum width would be used.

+
+ + + + + + + +
+

Declared In

+

ASLayoutElement.h

+
+ + +
+
+
+ +

  preferredLayoutSize +

+ +
+
+ +
+ + +
+

Provides a suggested RELATIVE size for a layout element. An ASLayoutSize uses percentages rather +than points to specify layout. E.g. width should be 50% of the parent’s width. If the optional minLayoutSize or +maxLayoutSize are provided, and the preferredLayoutSize exceeds these, the minLayoutSize or maxLayoutSize +will be enforced. If this optional value is not provided, the layout element’s size will default to its intrinsic content size +provided calculateSizeThatFits:

+
+ + + +
@property (nonatomic, assign, readwrite) ASLayoutSize preferredLayoutSize
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASLayoutElement.h

+
+ + +
+
+
+ +

  minLayoutSize +

+ +
+
+ +
+ + +
+

An optional property that provides a minimum RELATIVE size bound for a layout element. If provided, this +restriction will always be enforced. If a parent layout element’s minimum relative size is smaller than its child’s minimum +relative size, the child’s minimum relative size will be enforced and its size will extend out of the layout spec’s.

+
+ + + +
@property (nonatomic, assign, readwrite) ASLayoutSize minLayoutSize
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASLayoutElement.h

+
+ + +
+
+
+ +

  maxLayoutSize +

+ +
+
+ +
+ + +
+

An optional property that provides a maximum RELATIVE size bound for a layout element. If provided, this +restriction will always be enforced. If a parent layout element’s maximum relative size is smaller than its child’s maximum +relative size, the child’s maximum relative size will be enforced and its size will extend out of the layout spec’s.

+
+ + + +
@property (nonatomic, assign, readwrite) ASLayoutSize maxLayoutSize
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASLayoutElement.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Classes/ASLayoutSpec.html b/docs/appledoc/Classes/ASLayoutSpec.html new file mode 100755 index 0000000000..3bb92fb29e --- /dev/null +++ b/docs/appledoc/Classes/ASLayoutSpec.html @@ -0,0 +1,363 @@ + + + + + + ASLayoutSpec Class Reference + + + + + + +
+
+ +

+ +

+ +

+ Texture +

+ +
+
+ + + +
+
+
+
+

ASLayoutSpec Class Reference

+ + +
+ + + + + + + + + + +
Inherits fromNSObject
Conforms toASLayoutElement
Declared inASLayoutSpec.h
+ + + + +
+ +

Overview

+

A layout spec is an immutable object that describes a layout, loosely inspired by React.

+
+ + + + + +
+ + + + + + +
+
+ +

  isMutable +

+ +
+
+ +
+ + +
+

Creation of a layout spec should only happen by a user in layoutSpecThatFits:. During that method, a +layout spec can be created and mutated. Once it is passed back to ASDK, the isMutable flag will be +set to NO and any further mutations will cause an assert.

+
+ + + +
@property (nonatomic, assign) BOOL isMutable
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASLayoutSpec.h

+
+ + +
+
+
+ +

  parent +

+ +
+
+ +
+ + +
+

Parent of the layout spec

+
+ + + +
@property (nullable, nonatomic, weak) id<ASLayoutElement> parent
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASLayoutSpec.h

+
+ + +
+
+
+ +

  child +

+ +
+
+ +
+ + +
+

Adds a child to this layout spec using a default identifier.

+
+ + + +
@property (nullable, strong, nonatomic) id<ASLayoutElement> child
+ + + +
+

Parameters

+ + + + + + + +
child

A child to be added.

+
+ + + + + + + +
+

Discussion

+

Every ASLayoutSpec must act on at least one child. The ASLayoutSpec base class takes the +responsibility of holding on to the spec children. Some layout specs, like ASInsetLayoutSpec, +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: +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:.

+
+ + + + + + + +
+

Declared In

+

ASLayoutSpec.h

+
+ + +
+
+
+ +

  children +

+ +
+
+ +
+ + +
+

Adds childen to this layout spec.

+
+ + + +
@property (nullable, strong, nonatomic) NSArray<id<ASLayoutElement> > *children
+ + + +
+

Parameters

+ + + + + + + +
children

An array of ASLayoutElement children to be added.

+
+ + + + + + + +
+

Discussion

+

Every ASLayoutSpec must act on at least one child. The ASLayoutSpec base class takes the +reponsibility of holding on to the spec children. Some layout specs, like ASStackLayoutSpec, +can take an unknown number of children. In this case, the this method should be used. +For good measure, in these layout specs it probably makes sense to define +setChild: and setChild:forIdentifier: methods to do something appropriate or to assert.

+
+ + + + + + + +
+

Declared In

+

ASLayoutSpec.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ + +
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Classes/ASMapNode.html b/docs/appledoc/Classes/ASMapNode.html new file mode 100755 index 0000000000..75a586bc13 --- /dev/null +++ b/docs/appledoc/Classes/ASMapNode.html @@ -0,0 +1,531 @@ + + + + + + ASMapNode Class Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASMapNode Class Reference

+ + +
+ + + + + + + +
Inherits fromASImageNode : ASControlNode : ASDisplayNode : ASDealloc2MainObject
Declared inASMapNode.h
+ + + + + + +
+ + + + + + +
+
+ +

  options +

+ +
+
+ +
+ + +
+

The current options of ASMapNode. This can be set at any time and ASMapNode will animate the change.

This property may be set from a background thread before the node is loaded, and will automatically be applied to define the behavior of the static snapshot (if .liveMap = NO) or the internal MKMapView (otherwise).

Changes to the region and camera options will only be animated when when the liveMap mode is enabled, otherwise these options will be applied statically to the new snapshot.

The options object is used to specify properties even when the liveMap mode is enabled, allowing seamless transitions between the snapshot and liveMap (as well as back to the snapshot).

+
+ + + +
@property (nonatomic, strong) MKMapSnapshotOptions *options
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASMapNode.h

+
+ + +
+
+
+ +

  region +

+ +
+
+ +
+ + +
+

The region is simply the sub-field on the options object. If the objects object is reset, + this will in effect be overwritten and become the value of the .region property on that object. + Defaults to MKCoordinateRegionForMapRect(MKMapRectWorld).

+
+ + + +
@property (nonatomic, assign) MKCoordinateRegion region
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASMapNode.h

+
+ + +
+
+
+ +

  mapView +

+ +
+
+ +
+ + +
+

This is the MKMapView that is the live map part of ASMapNode. This will be nil if .liveMap = NO. Note, MKMapView is not thread-safe.

+
+ + + +
@property (nullable, nonatomic, readonly) MKMapView *mapView
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASMapNode.h

+
+ + +
+
+
+ +

  liveMap +

+ +
+
+ +
+ + +
+

Set this to YES to turn the snapshot into an interactive MKMapView and vice versa. Defaults to NO. This property may be set on a background thread before the node is loaded, and will automatically be actioned, once the node is loaded.

+
+ + + +
@property (nonatomic, assign, getter=isLiveMap) BOOL liveMap
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASMapNode.h

+
+ + +
+
+
+ +

  needsMapReloadOnBoundsChange +

+ +
+
+ +
+ + +
+

Whether ASMapNode should automatically request a new map snapshot to correspond to the new node size. +@default Default value is YES.

+
+ + + +
@property (nonatomic, assign) BOOL needsMapReloadOnBoundsChange
+ + + + + + + + + +
+

Discussion

+

If mapSize is set then this will be set to NO, since the size will be the same in all orientations.

+
+ + + + + + + +
+

Declared In

+

ASMapNode.h

+
+ + +
+
+
+ +

  mapDelegate +

+ +
+
+ +
+ + +
+

Set the delegate of the MKMapView. This can be set even before mapView is created and will be set on the map in the case that the liveMap mode is engaged.

+
+ + + +
@property (nonatomic, weak) id<MKMapViewDelegate> mapDelegate
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASMapNode.h

+
+ + +
+
+
+ +

  annotations +

+ +
+
+ +
+ + +
+

The annotations to display on the map.

+
+ + + +
@property (nonatomic, copy) NSArray<id<MKAnnotation> > *annotations
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASMapNode.h

+
+ + +
+
+
+ +

  showAnnotationsOptions +

+ +
+
+ +
+ + +
+

This property specifies how to show the annotations. +@default Default value is ASMapNodeShowAnnotationsIgnored

+
+ + + +
@property (nonatomic, assign) ASMapNodeShowAnnotationsOptions showAnnotationsOptions
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASMapNode.h

+
+ + +
+
+
+ +

  imageForStaticMapAnnotationBlock +

+ +
+
+ +
+ + +
+

The block which should return annotation image for static map based on provided annotation.

+
+ + + +
@property (nonatomic, copy, nullable) UIImage *( ^ ) ( id<MKAnnotation> annotation , CGPoint *centerOffset ) imageForStaticMapAnnotationBlock
+ + + + + + + + + +
+

Discussion

+

This block is executed on an arbitrary serial queue. If this block is nil, standard pin is used.

+
+ + + + + + + +
+

Declared In

+

ASMapNode.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Classes/ASMultiplexImageNode.html b/docs/appledoc/Classes/ASMultiplexImageNode.html new file mode 100755 index 0000000000..bb6387c5e7 --- /dev/null +++ b/docs/appledoc/Classes/ASMultiplexImageNode.html @@ -0,0 +1,651 @@ + + + + + + ASMultiplexImageNode Class Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASMultiplexImageNode Class Reference

+ + +
+ + + + + + + +
Inherits fromASImageNode : ASControlNode : ASDisplayNode : ASDealloc2MainObject
Declared inASMultiplexImageNode.h
+ + + + +
+ +

Overview

+

ASMultiplexImageNode begins loading images when its resource can either return a UIImage directly, or a URL the image node should load.

+
+ + + + + +
+ + + + + + +
+
+ +

– initWithCache:downloader: +

+ +
+
+ +
+ + +
+

The designated initializer.

+
+ + + +
- (instancetype)initWithCache:(nullable id<ASImageCacheProtocol>)cache downloader:(nullable id<ASImageDownloaderProtocol>)downloader
+ + + +
+

Parameters

+ + + + + + + + + + + + +
cache

The object that implements a cache of images for the image node.

downloader

The object that implements image downloading for the image node.

+
+ + + +
+

Return Value

+

An initialized ASMultiplexImageNode.

+
+ + + + + +
+

Discussion

+

If cache is nil, the receiver will not attempt to retrieve images from a cache before downloading them.

+
+ + + + + + + +
+

Declared In

+

ASMultiplexImageNode.h

+
+ + +
+
+
+ +

  delegate +

+ +
+
+ +
+ + +
+

The delegate, which must conform to the ASMultiplexImageNodeDelegate protocol.

+
+ + + +
@property (nonatomic, readwrite, weak) id<ASMultiplexImageNodeDelegate> delegate
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASMultiplexImageNode.h

+
+ + +
+
+
+ +

  dataSource +

+ +
+
+ +
+ + +
+

The data source, which must conform to the ASMultiplexImageNodeDataSource protocol.

+
+ + + +
@property (nonatomic, readwrite, weak) id<ASMultiplexImageNodeDataSource> dataSource
+ + + + + + + + + +
+

Discussion

+

This value is required for ASMultiplexImageNode to load images.

+
+ + + + + + + +
+

Declared In

+

ASMultiplexImageNode.h

+
+ + +
+
+
+ +

  downloadsIntermediateImages +

+ +
+
+ +
+ + +
+

Whether the receiver should download more than just its highest-quality image. Defaults to NO.

+
+ + + +
@property (nonatomic, readwrite, assign) BOOL downloadsIntermediateImages
+ + + + + + + + + +
+

Discussion

+

ASMultiplexImageNode immediately loads and displays the first image specified in imageIdentifiers (its +highest-quality image). If that image is not immediately available or cached, the node can download and display +lesser-quality images. Set downloadsIntermediateImages to YES to enable this behaviour.

+
+ + + + + + + +
+

Declared In

+

ASMultiplexImageNode.h

+
+ + +
+
+
+ +

  imageIdentifiers +

+ +
+
+ +
+ + +
+

An array of identifiers representing various versions of an image for ASMultiplexImageNode to display.

+
+ + + +
@property (nonatomic, readwrite, copy) NSArray<ASImageIdentifier> *imageIdentifiers
+ + + + + + + + + +
+

Discussion

+

An identifier can be any object that conforms to NSObject and NSCopying. The array should be in +decreasing order of image quality – that is, the first identifier in the array represents the best version.

+
+ + + + + +
+

See Also

+ +
+ + + +
+

Declared In

+

ASMultiplexImageNode.h

+
+ + +
+
+
+ +

– reloadImageIdentifierSources +

+ +
+
+ +
+ + +
+

Notify the receiver SSAA that its data source has new UIImages or NSURLs available for imageIdentifiers.

+
+ + + +
- (void)reloadImageIdentifierSources
+ + + + + + + + + +
+

Discussion

+

If a higher-quality image than is currently displayed is now available, it will be loaded.

+
+ + + + + + + +
+

Declared In

+

ASMultiplexImageNode.h

+
+ + +
+
+
+ +

  loadedImageIdentifier +

+ +
+
+ +
+ + +
+

The identifier for the last image that the receiver loaded, or nil.

+
+ + + +
@property (nullable, nonatomic, readonly) ASImageIdentifier loadedImageIdentifier
+ + + + + + + + + +
+

Discussion

+

This value may differ from displayedImageIdentifier if the image hasn’t yet been displayed.

+
+ + + + + + + +
+

Declared In

+

ASMultiplexImageNode.h

+
+ + +
+
+
+ +

  displayedImageIdentifier +

+ +
+
+ +
+ + +
+

The identifier for the image that the receiver is currently displaying, or nil.

+
+ + + +
@property (nullable, nonatomic, readonly) ASImageIdentifier displayedImageIdentifier
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASMultiplexImageNode.h

+
+ + +
+
+
+ +

  shouldRenderProgressImages +

+ +
+
+ +
+ + +
+

If the downloader implements progressive image rendering and this value is YES progressive renders of the +image will be displayed as the image downloads. Regardless of this properties value, progress renders will +only occur when the node is visible. Defaults to YES.

+
+ + + +
@property (nonatomic, assign, readwrite) BOOL shouldRenderProgressImages
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASMultiplexImageNode.h

+
+ + +
+
+
+ +

  imageManager +

+ +
+
+ +
+ + +
+
    +
  • @abstract The image manager that this image node should use when requesting images from the Photos framework. If this is nil (the default), then PHImageManager.defaultManager is used.
  • +
+ +
+ + + +
@property (nullable, nonatomic, strong) PHImageManager *imageManager
+ + + + + + + + + +
+

Discussion

+
    +
  • @see +[NSURL URLWithAssetLocalIdentifier:targetSize:contentMode:options:] below.
  • +
+ +
+ + + + + + + +
+

Declared In

+

ASMultiplexImageNode.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Classes/ASNavigationController.html b/docs/appledoc/Classes/ASNavigationController.html new file mode 100755 index 0000000000..4dd2da497c --- /dev/null +++ b/docs/appledoc/Classes/ASNavigationController.html @@ -0,0 +1,127 @@ + + + + + + ASNavigationController Class Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASNavigationController Class Reference

+ + +
+ + + + + + + + + + +
Inherits fromUINavigationController
Conforms toASManagesChildVisibilityDepth
Declared inASNavigationController.h
+ + + + +
+ +

Overview

+

ASNavigationController

ASNavigationController is a drop in replacement for UINavigationController +which improves memory efficiency by implementing the @c ASManagesChildVisibilityDepth protocol. +You can use ASNavigationController with regular UIViewControllers, as well as ASViewControllers. +It is safe to subclass or use even where AsyncDisplayKit is not adopted.

+
+ + + + + + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Classes/ASNetworkImageNode.html b/docs/appledoc/Classes/ASNetworkImageNode.html new file mode 100755 index 0000000000..36b5f30476 --- /dev/null +++ b/docs/appledoc/Classes/ASNetworkImageNode.html @@ -0,0 +1,633 @@ + + + + + + ASNetworkImageNode Class Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASNetworkImageNode Class Reference

+ + +
+ + + + + + + +
Inherits fromASImageNode : ASControlNode : ASDisplayNode : ASDealloc2MainObject
Declared inASNetworkImageNode.h
+ + + + +
+ +

Overview

+

ASNetworkImageNode is a simple image node that can download and display an image from the network, with support for a +placeholder image (defaultImage). The currently-displayed image is always available in the inherited ASImageNode + property.

+
+ + + + + +
+ + + + + + +
+
+ +

– initWithCache:downloader: +

+ +
+
+ +
+ + +
+

The designated initializer. Cache and Downloader are WEAK references.

+
+ + + +
- (instancetype)initWithCache:(nullable id<ASImageCacheProtocol>)cache downloader:(id<ASImageDownloaderProtocol>)downloader
+ + + +
+

Parameters

+ + + + + + + + + + + + +
cache

The object that implements a cache of images for the image node. Weak reference.

downloader

The object that implements image downloading for the image node. Must not be nil. Weak reference.

+
+ + + +
+

Return Value

+

An initialized ASNetworkImageNode.

+
+ + + + + +
+

Discussion

+

If cache is nil, the receiver will not attempt to retrieve images from a cache before downloading them.

+
+ + + + + + + +
+

Declared In

+

ASNetworkImageNode.h

+
+ + +
+
+
+ +

– init +

+ +
+
+ +
+ + +
+

Convenience initialiser.

+
+ + + +
- (instancetype)init
+ + + + + +
+

Return Value

+

An ASNetworkImageNode configured to use the NSURLSession-powered ASBasicImageDownloader, and no extra cache.

+
+ + + + + + + + + + + +
+

Declared In

+

ASNetworkImageNode.h

+
+ + +
+
+
+ +

  delegate +

+ +
+
+ +
+ + +
+

The delegate, which must conform to the ASNetworkImageNodeDelegate protocol.

+
+ + + +
@property (nullable, nonatomic, weak, readwrite) id<ASNetworkImageNodeDelegate> delegate
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASNetworkImageNode.h

+
+ + +
+
+
+ +

  defaultImage +

+ +
+
+ +
+ + +
+

A placeholder image to display while the URL is loading.

+
+ + + +
@property (nullable, nonatomic, strong, readwrite) UIImage *defaultImage
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASNetworkImageNode.h

+
+ + +
+
+
+ +

  URL +

+ +
+
+ +
+ + +
+

The URL of a new image to download and display.

+
+ + + +
@property (nullable, nonatomic, strong, readwrite) NSURL *URL
+ + + + + + + + + +
+

Discussion

+

Changing this property will reset the displayed image to a placeholder (defaultImage) while loading.

+
+ + + + + + + +
+

Declared In

+

ASNetworkImageNode.h

+
+ + +
+
+
+ +

– setURL:resetToDefault: +

+ +
+
+ +
+ + +
+

Download and display a new image.

+
+ + + +
- (void)setURL:(nullable NSURL *)URL resetToDefault:(BOOL)reset
+ + + +
+

Parameters

+ + + + + + + + + + + + +
URL

The URL of a new image to download and display.

reset

Whether to display a placeholder (defaultImage) while loading the new image.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASNetworkImageNode.h

+
+ + +
+
+
+ +

  shouldCacheImage +

+ +
+
+ +
+ + +
+

If URL is a local file, set this property to YES to take advantage of UIKit’s image caching. Defaults to YES.

+
+ + + +
@property (nonatomic, assign, readwrite) BOOL shouldCacheImage
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASNetworkImageNode.h

+
+ + +
+
+
+ +

  shouldRenderProgressImages +

+ +
+
+ +
+ + +
+

If the downloader implements progressive image rendering and this value is YES progressive renders of the +image will be displayed as the image downloads. Regardless of this properties value, progress renders will +only occur when the node is visible. Defaults to YES.

+
+ + + +
@property (nonatomic, assign, readwrite) BOOL shouldRenderProgressImages
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASNetworkImageNode.h

+
+ + +
+
+
+ +

  currentImageQuality +

+ +
+
+ +
+ + +
+

The image quality of the current image. This is a number between 0 and 1 and can be used to track +progressive progress. Calculated by dividing number of bytes / expected number of total bytes.

+
+ + + +
@property (nonatomic, assign, readonly) CGFloat currentImageQuality
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASNetworkImageNode.h

+
+ + +
+
+
+ +

  renderedImageQuality +

+ +
+
+ +
+ + +
+

The image quality (value between 0 and 1) of the last image that completed displaying.

+
+ + + +
@property (nonatomic, assign, readonly) CGFloat renderedImageQuality
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASNetworkImageNode.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Classes/ASOverlayLayoutSpec.html b/docs/appledoc/Classes/ASOverlayLayoutSpec.html new file mode 100755 index 0000000000..8ebefaec69 --- /dev/null +++ b/docs/appledoc/Classes/ASOverlayLayoutSpec.html @@ -0,0 +1,247 @@ + + + + + + ASOverlayLayoutSpec Class Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASOverlayLayoutSpec Class Reference

+ + +
+ + + + + + + +
Inherits fromASLayoutSpec : NSObject
Declared inASOverlayLayoutSpec.h
+ + + + +
+ +

Overview

+

This layout spec lays out a single layoutElement child and then overlays a layoutElement object on top of it streched to its size

+
+ + + + + +
+ + + + + + +
+
+ +

  overlay +

+ +
+
+ +
+ + +
+

Overlay layoutElement of this layout spec

+
+ + + +
@property (nullable, nonatomic, strong) id<ASLayoutElement> overlay
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASOverlayLayoutSpec.h

+
+ + +
+
+
+ +

+ overlayLayoutSpecWithChild:overlay: +

+ +
+
+ +
+ + +
+

Creates and returns an ASOverlayLayoutSpec object with a given child and an layoutElement that act as overlay.

+
+ + + +
+ (instancetype)overlayLayoutSpecWithChild:(id<ASLayoutElement>)child overlay:(nullable id<ASLayoutElement>)overlay
+ + + +
+

Parameters

+ + + + + + + + + + + + +
child

A child that is laid out to determine the size of this spec.

overlay

A layoutElement object that is laid out over the child. If this is nil, the overlay is omitted.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASOverlayLayoutSpec.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Classes/ASPagerNode.html b/docs/appledoc/Classes/ASPagerNode.html new file mode 100755 index 0000000000..b6f529ae77 --- /dev/null +++ b/docs/appledoc/Classes/ASPagerNode.html @@ -0,0 +1,579 @@ + + + + + + ASPagerNode Class Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASPagerNode Class Reference

+ + +
+ + + + + + + +
Inherits fromASCollectionNode : ASDisplayNode : ASDealloc2MainObject
Declared inASPagerNode.h
+ + + + + + +
+ + + + + + +
+
+ +

– init +

+ +
+
+ +
+ + +
+

Configures a default horizontal, paging flow layout with 0 inter-item spacing.

+
+ + + +
- (instancetype)init
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASPagerNode.h

+
+ + +
+
+
+ +

– initWithCollectionViewLayout: +

+ +
+
+ +
+ + +
+

Initializer with custom-configured flow layout properties.

+
+ + + +
- (instancetype)initWithCollectionViewLayout:(ASPagerFlowLayout *)flowLayout
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASPagerNode.h

+
+ + +
+
+
+ +

– setDataSource: +

+ +
+
+ +
+ + +
+

Data Source is required, and uses a different protocol from ASCollectionNode.

+
+ + + +
- (void)setDataSource:(nullable id<ASPagerDataSource>)dataSource
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASPagerNode.h

+
+ + +
+
+
+ +

– dataSource +

+ +
+
+ +
+ + +
+

The object that acts as the asynchronous data source of the collection view

+
+ + + +
- (nullable id<ASPagerDataSource>)dataSource
+ + + + + + + + + +
+

Discussion

+

The datasource must adopt the ASCollectionDataSource protocol. The collection view maintains a weak reference to the datasource object.

+ +

The datasource object is responsible for providing nodes or node creation blocks to the collection view.

Note: This is a convenience method which sets the asyncDatasource on the collection node’s collection view.

+
+ + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

– setDelegate: +

+ +
+
+ +
+ + +
+

Delegate is optional. +This includes UIScrollViewDelegate as well as most methods from UICollectionViewDelegate, like willDisplay…

+
+ + + +
- (void)setDelegate:(nullable id<ASPagerDelegate>)delegate
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASPagerNode.h

+
+ + +
+
+
+ +

– delegate +

+ +
+
+ +
+ + +
+

The object that acts as the asynchronous delegate of the collection view

+
+ + + +
- (nullable id<ASPagerDelegate>)delegate
+ + + + + + + + + +
+

Discussion

+

The delegate must adopt the ASCollectionDelegate protocol. The collection view maintains a weak reference to the delegate object.

+ +

The delegate object is responsible for providing size constraints for nodes and indicating whether batch fetching should begin.

Note: This is a convenience method which sets the asyncDelegate on the collection node’s collection view.

+
+ + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

  view +

+ +
+
+ +
+ + +
+

The underlying ASCollectionView object.

+
+ + + +
@property (nonatomic, readonly) ASCollectionView *view
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASPagerNode.h

+
+ + +
+
+
+ +

  currentPageIndex +

+ +
+
+ +
+ + +
+

Returns the current page index

+
+ + + +
@property (nonatomic, assign, readonly) NSInteger currentPageIndex
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASPagerNode.h

+
+ + +
+
+
+ +

– scrollToPageAtIndex:animated: +

+ +
+
+ +
+ + +
+

Scroll the contents of the receiver to ensure that the page is visible

+
+ + + +
- (void)scrollToPageAtIndex:(NSInteger)index animated:(BOOL)animated
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASPagerNode.h

+
+ + +
+
+
+ +

– nodeForPageAtIndex: +

+ +
+
+ +
+ + +
+

Returns the node for the passed page index

+
+ + + +
- (ASCellNode *)nodeForPageAtIndex:(NSInteger)index
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASPagerNode.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Classes/ASRangeController.html b/docs/appledoc/Classes/ASRangeController.html new file mode 100755 index 0000000000..ad02f940ee --- /dev/null +++ b/docs/appledoc/Classes/ASRangeController.html @@ -0,0 +1,437 @@ + + + + + + ASRangeController Class Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASRangeController Class Reference

+ + +
+ + + + + + + + + + +
Inherits fromASDealloc2MainObject
Conforms toASDataControllerDelegate
Declared inASRangeController.h
+ + + + +
+ +

Overview

+

Working range controller.

+ +

Used internally by ASTableView and ASCollectionView. It is paired with ASDataController. +It is designed to support custom scrolling containers as well. Observes the visible range, maintains +“working ranges” to trigger network calls and rendering, and is responsible for driving asynchronous layout of cells. +This includes cancelling those asynchronous operations as cells fall outside of the working ranges.

+
+ + + + + +
+ + + + + + +
+
+ +

– setNeedsUpdate +

+ +
+
+ +
+ + +
+

Notify the range controller that the visible range has been updated. +This is the primary input call that drives updating the working ranges, and triggering their actions. +The ranges will be updated in the next turn of the main loop, or when -updateIfNeeded is called.

+
+ + + +
- (void)setNeedsUpdate
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASRangeController.h

+
+ + +
+
+
+ +

– updateIfNeeded +

+ +
+
+ +
+ + +
+

Update the ranges immediately, if -setNeedsUpdate has been called since the last update. +This is useful because the ranges must be updated immediately after a cell is added +into a table/collection to satisfy interface state API guarantees.

+
+ + + +
- (void)updateIfNeeded
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASRangeController.h

+
+ + +
+
+
+ +

– configureContentView:forCellNode: +

+ +
+
+ +
+ + +
+

Add the sized node for indexPath as a subview of contentView.

+
+ + + +
- (void)configureContentView:(UIView *)contentView forCellNode:(ASCellNode *)node
+ + + +
+

Parameters

+ + + + + + + + + + + + +
contentView

UIView to add a (sized) node’s view to.

node

The cell node to be added.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASRangeController.h

+
+ + +
+
+
+ +

  layoutController +

+ +
+
+ +
+ + +
+

An object that describes the layout behavior of the ranged component (table view, collection view, etc.)

+
+ + + +
@property (nonatomic, strong) id<ASLayoutController> layoutController
+ + + + + + + + + +
+

Discussion

+

Used primarily for providing the current range of index paths and identifying when the +range controller should invalidate its range.

+
+ + + + + + + +
+

Declared In

+

ASRangeController.h

+
+ + +
+
+
+ +

  dataSource +

+ +
+
+ +
+ + +
+

The underlying data source for the range controller

+
+ + + +
@property (nonatomic, weak) id<ASRangeControllerDataSource> dataSource
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASRangeController.h

+
+ + +
+
+
+ +

  delegate +

+ +
+
+ +
+ + +
+

Delegate for handling range controller events. Must not be nil.

+
+ + + +
@property (nonatomic, weak) id<ASRangeControllerDelegate> delegate
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASRangeController.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Classes/ASRatioLayoutSpec.html b/docs/appledoc/Classes/ASRatioLayoutSpec.html new file mode 100755 index 0000000000..cc159b5851 --- /dev/null +++ b/docs/appledoc/Classes/ASRatioLayoutSpec.html @@ -0,0 +1,139 @@ + + + + + + ASRatioLayoutSpec Class Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASRatioLayoutSpec Class Reference

+ + +
+ + + + + + + +
Inherits fromASLayoutSpec : NSObject
Declared inASRatioLayoutSpec.h
+ + + + +
+ +

Overview

+

Ratio layout spec +For when the content should respect a certain inherent ratio but can be scaled (think photos or videos) +The ratio passed is the ratio of height / width you expect

+ +

For a ratio 0.5, the spec will have a flat rectangle shape

+ +
+ +

| | +| _ _ |

+ +

For a ratio 2.0, the spec will be twice as tall as it is wide + _ _ +| | +| | +| | +| |

+ +

*

+
+ + + + + + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Classes/ASRelativeLayoutSpec.html b/docs/appledoc/Classes/ASRelativeLayoutSpec.html new file mode 100755 index 0000000000..ebcd558584 --- /dev/null +++ b/docs/appledoc/Classes/ASRelativeLayoutSpec.html @@ -0,0 +1,305 @@ + + + + + + ASRelativeLayoutSpec Class Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASRelativeLayoutSpec Class Reference

+ + +
+ + + + + + + +
Inherits fromASLayoutSpec : NSObject
Declared inASRelativeLayoutSpec.h
+ + + + +
+ +

Overview

+

Lays out a single layoutElement child and positions it within the layout bounds according to vertical and horizontal positional specifiers. +Can position the child at any of the 4 corners, or the middle of any of the 4 edges, as well as the center - similar to “9-part” image areas.

+
+ + + + + +
+ + + + + + +
+
+ +

+ relativePositionLayoutSpecWithHorizontalPosition:verticalPosition:sizingOption:child: +

+ +
+
+ +
+ + +
+

convenience constructor for a ASRelativeLayoutSpec

+
+ + + +
+ (instancetype)relativePositionLayoutSpecWithHorizontalPosition:(ASRelativeLayoutSpecPosition)horizontalPosition verticalPosition:(ASRelativeLayoutSpecPosition)verticalPosition sizingOption:(ASRelativeLayoutSpecSizingOption)sizingOption child:(id<ASLayoutElement>)child
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
horizontalPosition

how to position the item on the horizontal (x) axis

verticalPosition

how to position the item on the vertical (y) axis

sizingOption

how much size to take up

child

the child to layout

+
+ + + +
+

Return Value

+

a configured ASRelativeLayoutSpec

+
+ + + + + +
+

Discussion

+

convenience constructor for a ASRelativeLayoutSpec

+
+ + + + + + + +
+

Declared In

+

ASRelativeLayoutSpec.h

+
+ + +
+
+
+ +

– initWithHorizontalPosition:verticalPosition:sizingOption:child: +

+ +
+
+ +
+ + +
+

convenience initializer for a ASRelativeLayoutSpec

+
+ + + +
- (instancetype)initWithHorizontalPosition:(ASRelativeLayoutSpecPosition)horizontalPosition verticalPosition:(ASRelativeLayoutSpecPosition)verticalPosition sizingOption:(ASRelativeLayoutSpecSizingOption)sizingOption child:(id<ASLayoutElement>)child
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
horizontalPosition

how to position the item on the horizontal (x) axis

verticalPosition

how to position the item on the vertical (y) axis

sizingOption

how much size to take up

child

the child to layout

+
+ + + +
+

Return Value

+

a configured ASRelativeLayoutSpec

+
+ + + + + +
+

Discussion

+

convenience initializer for a ASRelativeLayoutSpec

+
+ + + + + + + +
+

Declared In

+

ASRelativeLayoutSpec.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Classes/ASScrollNode.html b/docs/appledoc/Classes/ASScrollNode.html new file mode 100755 index 0000000000..83843991b4 --- /dev/null +++ b/docs/appledoc/Classes/ASScrollNode.html @@ -0,0 +1,183 @@ + + + + + + ASScrollNode Class Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASScrollNode Class Reference

+ + +
+ + + + + + + +
Inherits fromASDisplayNode : ASDealloc2MainObject
Declared inASScrollNode.h
+ + + + +
+ +

Overview

+

Simple node that wraps UIScrollView.

+
+ + + + + +
+ + + + + + +
+
+ +

  view +

+ +
+
+ +
+ + +
+

The node’s UIScrollView.

+
+ + + +
@property (nonatomic, readonly, strong) UIScrollView *view
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASScrollNode.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Classes/ASStackLayoutSpec.html b/docs/appledoc/Classes/ASStackLayoutSpec.html new file mode 100755 index 0000000000..80de7f100c --- /dev/null +++ b/docs/appledoc/Classes/ASStackLayoutSpec.html @@ -0,0 +1,649 @@ + + + + + + ASStackLayoutSpec Class Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASStackLayoutSpec Class Reference

+ + +
+ + + + + + + +
Inherits fromASLayoutSpec : NSObject
Declared inASStackLayoutSpec.h
+ + + + +
+ +

Overview

+

A simple layout spec that stacks a list of children vertically or horizontally.

+ +
    +
  • All children are initially laid out with the an infinite available size in the stacking direction.
  • +
  • In the other direction, this spec’s constraint is passed.
  • +
  • The children’s sizes are summed in the stacking direction. + +
      +
    • If this sum is less than this spec’s minimum size in stacking direction, children with flexGrow are flexed.
    • +
    • If it is greater than this spec’s maximum size in the stacking direction, children with flexShrink are flexed.
    • +
    • If, even after flexing, the sum is still greater than this spec’s maximum size in the stacking direction, +justifyContent determines how children are laid out.
    • +
    +
  • +
+ + +

For example:

+ +
    +
  • Suppose stacking direction is Vertical, min-width=100, max-width=300, min-height=200, max-height=500.
  • +
  • All children are laid out with min-width=100, max-width=300, min-height=0, max-height=INFINITY.
  • +
  • If the sum of the childrens' heights is less than 200, children with flexGrow are flexed larger.
  • +
  • If the sum of the childrens' heights is greater than 500, children with flexShrink are flexed smaller. + Each child is shrunk by ((sum of heights) - 500)/(number of flexShrink-able children).
  • +
  • If the sum of the childrens' heights is greater than 500 even after flexShrink-able children are flexed, + justifyContent determines how children are laid out.
  • +
+ +
+ + + + + +
+ + + + + + +
+
+ +

  direction +

+ +
+
+ +
+ + +
+

Specifies the direction children are stacked in. If horizontalAlignment and verticalAlignment were set, +they will be resolved again, causing justifyContent and alignItems to be updated accordingly

+
+ + + +
@property (nonatomic, assign) ASStackLayoutDirection direction
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASStackLayoutSpec.h

+
+ + +
+
+
+ +

  spacing +

+ +
+
+ +
+ + +
+

The amount of space between each child.

+
+ + + +
@property (nonatomic, assign) CGFloat spacing
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASStackLayoutSpec.h

+
+ + +
+
+
+ +

  horizontalAlignment +

+ +
+
+ +
+ + +
+

Specifies how children are aligned horizontally. Depends on the stack direction, setting the alignment causes either +justifyContent or alignItems to be updated. The alignment will remain valid after future direction changes. +Thus, it is preferred to those properties

+
+ + + +
@property (nonatomic, assign) ASHorizontalAlignment horizontalAlignment
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASStackLayoutSpec.h

+
+ + +
+
+
+ +

  verticalAlignment +

+ +
+
+ +
+ + +
+

Specifies how children are aligned vertically. Depends on the stack direction, setting the alignment causes either +justifyContent or alignItems to be updated. The alignment will remain valid after future direction changes. +Thus, it is preferred to those properties

+
+ + + +
@property (nonatomic, assign) ASVerticalAlignment verticalAlignment
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASStackLayoutSpec.h

+
+ + +
+
+
+ +

  justifyContent +

+ +
+
+ +
+ + +
+

The amount of space between each child.

+
+ + + +
@property (nonatomic, assign) ASStackLayoutJustifyContent justifyContent
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASStackLayoutSpec.h

+
+ + +
+
+
+ +

  alignItems +

+ +
+
+ +
+ + +
+

Orientation of children along cross axis

+
+ + + +
@property (nonatomic, assign) ASStackLayoutAlignItems alignItems
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASStackLayoutSpec.h

+
+ + +
+
+
+ +

  baselineRelativeArrangement +

+ +
+
+ +
+ + +
+

If YES the vertical spacing between two views is measured from the last baseline of the top view to the top of the bottom view

+
+ + + +
@property (nonatomic, assign) BOOL baselineRelativeArrangement
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASStackLayoutSpec.h

+
+ + +
+
+
+ +

+ stackLayoutSpecWithDirection:spacing:justifyContent:alignItems:children: +

+ +
+
+ +
+ + +
+

The direction of the stack view (horizontal or vertical)

+
+ + + +
+ (instancetype)stackLayoutSpecWithDirection:(ASStackLayoutDirection)direction spacing:(CGFloat)spacing justifyContent:(ASStackLayoutJustifyContent)justifyContent alignItems:(ASStackLayoutAlignItems)alignItems children:(NSArray<id<ASLayoutElement> > *)children
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
direction

The direction of the stack view (horizontal or vertical)

spacing

The spacing between the children

justifyContent

If no children are flexible, this describes how to fill any extra space

alignItems

Orientation of the children along the cross axis

children

ASLayoutElement children to be positioned.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASStackLayoutSpec.h

+
+ + +
+
+
+ +

+ verticalStackLayoutSpec +

+ +
+
+ +
+ + +
+

A stack layout spec with direction of ASStackLayoutDirectionVertical

+
+ + + +
+ (instancetype)verticalStackLayoutSpec
+ + + + + +
+

Return Value

+

A stack layout spec with direction of ASStackLayoutDirectionVertical

+
+ + + + + + + + + + + +
+

Declared In

+

ASStackLayoutSpec.h

+
+ + +
+
+
+ +

+ horizontalStackLayoutSpec +

+ +
+
+ +
+ + +
+

A stack layout spec with direction of ASStackLayoutDirectionHorizontal

+
+ + + +
+ (instancetype)horizontalStackLayoutSpec
+ + + + + +
+

Return Value

+

A stack layout spec with direction of ASStackLayoutDirectionHorizontal

+
+ + + + + + + + + + + +
+

Declared In

+

ASStackLayoutSpec.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Classes/ASTabBarController.html b/docs/appledoc/Classes/ASTabBarController.html new file mode 100755 index 0000000000..851476ab61 --- /dev/null +++ b/docs/appledoc/Classes/ASTabBarController.html @@ -0,0 +1,125 @@ + + + + + + ASTabBarController Class Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASTabBarController Class Reference

+ + +
+ + + + + + + + + + +
Inherits fromUITabBarController
Conforms toASManagesChildVisibilityDepth
Declared inASTabBarController.h
+ + + + +
+ +

Overview

+

ASTabBarController

ASTabBarController is a drop in replacement for UITabBarController +which implements the memory efficiency improving @c ASManagesChildVisibilityDepth protocol.

+
+ + + + + + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Classes/ASTableNode.html b/docs/appledoc/Classes/ASTableNode.html new file mode 100755 index 0000000000..aa37fc25a1 --- /dev/null +++ b/docs/appledoc/Classes/ASTableNode.html @@ -0,0 +1,2128 @@ + + + + + + ASTableNode Class Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASTableNode Class Reference

+ + +
+ + + + + + + + + + +
Inherits fromASDisplayNode : ASDealloc2MainObject
Conforms toASRangeControllerUpdateRangeProtocol
Declared inASTableNode.h
+ + + + +
+ +

Overview

+

ASTableNode is a node based class that wraps an ASTableView. It can be used +as a subnode of another node, and provide room for many (great) features and improvements later on.

+
+ + + + + +
+ + + + + + +
+
+ +

– init +

+ +
+
+ +
+ + +
+

Designated initializer.

+
+ + + +
- (instancetype)init
+ + + + + +
+

Return Value

+

An ASDisplayNode instance whose view will be a subclass that enables asynchronous rendering, and passes +through -layout and touch handling methods.

+
+ + + + + + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+ +

  view +

+ +
+
+ +
+ + +
+

Returns a view.

+
+ + + +
@property (strong, nonatomic, readonly) ASTableView *view
+ + + + + + + + + +
+

Discussion

+

The view property is lazily initialized, similar to UIViewController. +To go the other direction, use ASViewToDisplayNode() in ASDisplayNodeExtras.h.

Warning: The first access to it must be on the main thread, and should only be used on the main thread thereafter as +well.

+
+ + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + +
+
+
+ +

– tuningParametersForRangeType: +

+ +
+
+ +
+ + +
+

Tuning parameters for a range type in full mode.

+
+ + + +
- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType
+ + + +
+

Parameters

+ + + + + + + +
rangeType

The range type to get the tuning parameters for.

+
+ + + +
+

Return Value

+

A tuning parameter value for the given range type in full mode.

+
+ + + + + + + + + + + +
+

Declared In

+

ASTableNode.h

+
+ + +
+
+
+ +

– setTuningParameters:forRangeType: +

+ +
+
+ +
+ + +
+

Set the tuning parameters for a range type in full mode.

+
+ + + +
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType
+ + + +
+

Parameters

+ + + + + + + + + + + + +
tuningParameters

The tuning parameters to store for a range type.

rangeType

The range type to set the tuning parameters for.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASTableNode.h

+
+ + +
+
+
+ +

– tuningParametersForRangeMode:rangeType: +

+ +
+
+ +
+ + +
+

Tuning parameters for a range type in the specified mode.

+
+ + + +
- (ASRangeTuningParameters)tuningParametersForRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType
+ + + +
+

Parameters

+ + + + + + + + + + + + +
rangeMode

The range mode to get the running parameters for.

rangeType

The range type to get the tuning parameters for.

+
+ + + +
+

Return Value

+

A tuning parameter value for the given range type in the given mode.

+
+ + + + + + + + + + + +
+

Declared In

+

ASTableNode.h

+
+ + +
+
+
+ +

– setTuningParameters:forRangeMode:rangeType: +

+ +
+
+ +
+ + +
+

Set the tuning parameters for a range type in the specified mode.

+
+ + + +
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + +
tuningParameters

The tuning parameters to store for a range type.

rangeMode

The range mode to set the running parameters for.

rangeType

The range type to set the tuning parameters for.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASTableNode.h

+
+ + +
+
+
+ +

– scrollToRowAtIndexPath:atScrollPosition:animated: +

+ +
+
+ +
+ + +
+

Scrolls the table to the given row.

+
+ + + +
- (void)scrollToRowAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + +
indexPath

The index path of the row.

scrollPosition

Where the row should end up after the scroll.

animated

Whether the scroll should be animated or not.

+ +

This method must be called on the main thread.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASTableNode.h

+
+ + +
+
+
+ +

– reloadDataWithCompletion: +

+ +
+
+ +
+ + +
+

Reload everything from scratch, destroying the working range and all cached nodes.

+
+ + + +
- (void)reloadDataWithCompletion:(nullable void ( ^ ) ( ))completion
+ + + +
+

Parameters

+ + + + + + + +
completion

block to run on completion of asynchronous loading or nil. If supplied, the block is run on +the main thread.

+
+ + + + + + + +
+

Discussion

+

Warning: This method is substantially more expensive than UITableView’s version.

+
+ + + + + + + +
+

Declared In

+

ASTableNode.h

+
+ + +
+
+
+ +

– reloadData +

+ +
+
+ +
+ + +
+

Reload everything from scratch, destroying the working range and all cached nodes.

+
+ + + +
- (void)reloadData
+ + + + + + + + + +
+

Discussion

+

Warning: This method is substantially more expensive than UITableView’s version.

+
+ + + + + + + +
+

Declared In

+

ASTableNode.h

+
+ + +
+
+
+ +

– relayoutItems +

+ +
+
+ +
+ + +
+

Triggers a relayout of all nodes.

+
+ + + +
- (void)relayoutItems
+ + + + + + + + + +
+

Discussion

+

This method invalidates and lays out every cell node in the table view.

+
+ + + + + + + +
+

Declared In

+

ASTableNode.h

+
+ + +
+
+
+ +

– performBatchAnimated:updates:completion: +

+ +
+
+ +
+ + +
+

Perform a batch of updates asynchronously, optionally disabling all animations in the batch. This method must be called from the main thread. +The data source must be updated to reflect the changes before the update block completes.

+
+ + + +
- (void)performBatchAnimated:(BOOL)animated updates:(nullable __attribute ( ( noescape ) ) void ( ^ ) ( ))updates completion:(nullable void ( ^ ) ( BOOL finished ))completion
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + +
animated

NO to disable animations for this batch

updates

The block that performs the relevant insert, delete, reload, or move operations.

completion

A completion handler block to execute when all of the operations are finished. This block takes a single +Boolean parameter that contains the value YES if all of the related animations completed successfully or +NO if they were interrupted. This parameter may be nil. If supplied, the block is run on the main thread.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASTableNode.h

+
+ + +
+
+
+ +

– performBatchUpdates:completion: +

+ +
+
+ +
+ + +
+

Perform a batch of updates asynchronously, optionally disabling all animations in the batch. This method must be called from the main thread. +The data source must be updated to reflect the changes before the update block completes.

+
+ + + +
- (void)performBatchUpdates:(nullable __attribute ( ( noescape ) ) void ( ^ ) ( ))updates completion:(nullable void ( ^ ) ( BOOL finished ))completion
+ + + +
+

Parameters

+ + + + + + + + + + + + +
updates

The block that performs the relevant insert, delete, reload, or move operations.

completion

A completion handler block to execute when all of the operations are finished. This block takes a single +Boolean parameter that contains the value YES if all of the related animations completed successfully or +NO if they were interrupted. This parameter may be nil. If supplied, the block is run on the main thread.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASTableNode.h

+
+ + +
+
+
+ +

– waitUntilAllUpdatesAreCommitted +

+ +
+
+ +
+ + +
+

Blocks execution of the main thread until all section and row updates are committed. This method must be called from the main thread.

+
+ + + +
- (void)waitUntilAllUpdatesAreCommitted
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASTableNode.h

+
+ + +
+
+
+ +

– insertSections:withRowAnimation: +

+ +
+
+ +
+ + +
+

Inserts one or more sections, with an option to animate the insertion.

+
+ + + +
- (void)insertSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation
+ + + +
+

Parameters

+ + + + + + + + + + + + +
sections

An index set that specifies the sections to insert.

animation

A constant that indicates how the insertion is to be animated. See UITableViewRowAnimation.

+
+ + + + + + + +
+

Discussion

+

This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes +before this method is called.

+
+ + + + + + + +
+

Declared In

+

ASTableNode.h

+
+ + +
+
+
+ +

– deleteSections:withRowAnimation: +

+ +
+
+ +
+ + +
+

Deletes one or more sections, with an option to animate the deletion.

+
+ + + +
- (void)deleteSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation
+ + + +
+

Parameters

+ + + + + + + + + + + + +
sections

An index set that specifies the sections to delete.

animation

A constant that indicates how the deletion is to be animated. See UITableViewRowAnimation.

+
+ + + + + + + +
+

Discussion

+

This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes +before this method is called.

+
+ + + + + + + +
+

Declared In

+

ASTableNode.h

+
+ + +
+
+
+ +

– reloadSections:withRowAnimation: +

+ +
+
+ +
+ + +
+

Reloads the specified sections using a given animation effect.

+
+ + + +
- (void)reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation
+ + + +
+

Parameters

+ + + + + + + + + + + + +
sections

An index set that specifies the sections to reload.

animation

A constant that indicates how the reloading is to be animated. See UITableViewRowAnimation.

+
+ + + + + + + +
+

Discussion

+

This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes +before this method is called.

+
+ + + + + + + +
+

Declared In

+

ASTableNode.h

+
+ + +
+
+
+ +

– moveSection:toSection: +

+ +
+
+ +
+ + +
+

Moves a section to a new location.

+
+ + + +
- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection
+ + + +
+

Parameters

+ + + + + + + + + + + + +
section

The index of the section to move.

newSection

The index that is the destination of the move for the section.

+
+ + + + + + + +
+

Discussion

+

This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes +before this method is called.

+
+ + + + + + + +
+

Declared In

+

ASTableNode.h

+
+ + +
+
+
+ +

– insertRowsAtIndexPaths:withRowAnimation: +

+ +
+
+ +
+ + +
+

Inserts rows at the locations identified by an array of index paths, with an option to animate the insertion.

+
+ + + +
- (void)insertRowsAtIndexPaths:(NSArray<NSIndexPath*> *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation
+ + + +
+

Parameters

+ + + + + + + + + + + + +
indexPaths

An array of NSIndexPath objects, each representing a row index and section index that together identify a row.

animation

A constant that indicates how the insertion is to be animated. See UITableViewRowAnimation.

+
+ + + + + + + +
+

Discussion

+

This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes +before this method is called.

+
+ + + + + + + +
+

Declared In

+

ASTableNode.h

+
+ + +
+
+
+ +

– deleteRowsAtIndexPaths:withRowAnimation: +

+ +
+
+ +
+ + +
+

Deletes the rows specified by an array of index paths, with an option to animate the deletion.

+
+ + + +
- (void)deleteRowsAtIndexPaths:(NSArray<NSIndexPath*> *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation
+ + + +
+

Parameters

+ + + + + + + + + + + + +
indexPaths

An array of NSIndexPath objects identifying the rows to delete.

animation

A constant that indicates how the deletion is to be animated. See UITableViewRowAnimation.

+
+ + + + + + + +
+

Discussion

+

This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes +before this method is called.

+
+ + + + + + + +
+

Declared In

+

ASTableNode.h

+
+ + +
+
+
+ +

– reloadRowsAtIndexPaths:withRowAnimation: +

+ +
+
+ +
+ + +
+

Reloads the specified rows using a given animation effect.

+
+ + + +
- (void)reloadRowsAtIndexPaths:(NSArray<NSIndexPath*> *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation
+ + + +
+

Parameters

+ + + + + + + + + + + + +
indexPaths

An array of NSIndexPath objects identifying the rows to reload.

animation

A constant that indicates how the reloading is to be animated. See UITableViewRowAnimation.

+
+ + + + + + + +
+

Discussion

+

This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes +before this method is called.

+
+ + + + + + + +
+

Declared In

+

ASTableNode.h

+
+ + +
+
+
+ +

– moveRowAtIndexPath:toIndexPath: +

+ +
+
+ +
+ + +
+

Moves the row at a specified location to a destination location.

+
+ + + +
- (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath
+ + + +
+

Parameters

+ + + + + + + + + + + + +
indexPath

The index path identifying the row to move.

newIndexPath

The index path that is the destination of the move for the row.

+
+ + + + + + + +
+

Discussion

+

This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes +before this method is called.

+
+ + + + + + + +
+

Declared In

+

ASTableNode.h

+
+ + +
+
+
+ +

– selectRowAtIndexPath:animated:scrollPosition: +

+ +
+
+ +
+ + +
+

Selects a row in the table view identified by index path, optionally scrolling the row to a location in the table view. +This method does not cause any selection-related delegate methods to be called.

+
+ + + +
- (void)selectRowAtIndexPath:(nullable NSIndexPath *)indexPath animated:(BOOL)animated scrollPosition:(UITableViewScrollPosition)scrollPosition
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + +
indexPath

An index path identifying a row in the table view.

animated

Specify YES to animate the change in the selection or NO to make the change without animating it.

scrollPosition

A constant that identifies a relative position in the table view (top, middle, bottom) for the row when scrolling concludes. See UITableViewScrollPosition for descriptions of valid constants.

+
+ + + + + + + +
+

Discussion

+

This method must be called from the main thread.

+
+ + + + + + + +
+

Declared In

+

ASTableNode.h

+
+ + +
+
+
+ +

– numberOfRowsInSection: +

+ +
+
+ +
+ + +
+

Retrieves the number of rows in the given section.

+
+ + + +
- (NSInteger)numberOfRowsInSection:(NSInteger)section
+ + + +
+

Parameters

+ + + + + + + +
section

The section.

+
+ + + +
+

Return Value

+

The number of rows.

+
+ + + + + + + + + + + +
+

Declared In

+

ASTableNode.h

+
+ + +
+
+
+ +

  numberOfSections +

+ +
+
+ +
+ + +
+

The number of sections in the table node.

+
+ + + +
@property (nonatomic, readonly) NSInteger numberOfSections
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASTableNode.h

+
+ + +
+
+
+ +

  visibleNodes +

+ +
+
+ +
+ + +
+

Similar to -visibleCells.

+
+ + + +
@property (nonatomic, readonly) NSArray<__kindofASCellNode*> *visibleNodes
+ + + + + +
+

Return Value

+

an array containing the nodes being displayed on screen. This must be called on the main thread.

+
+ + + + + + + + + + + +
+

Declared In

+

ASTableNode.h

+
+ + +
+
+
+ +

– nodeForRowAtIndexPath: +

+ +
+
+ +
+ + +
+

Retrieves the node for the row at the given index path.

+
+ + + +
- (nullable __kindof ASCellNode *)nodeForRowAtIndexPath:(NSIndexPath *)indexPath
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASTableNode.h

+
+ + +
+
+
+ +

– indexPathForNode: +

+ +
+
+ +
+ + +
+

Similar to -indexPathForCell:.

+
+ + + +
- (nullable NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode
+ + + +
+

Parameters

+ + + + + + + +
cellNode

a node for a row.

+
+ + + +
+

Return Value

+

The index path to this row, if it exists.

+
+ + + + + +
+

Discussion

+

This method will return @c nil for a node that is still being +displayed in the table view, if the data source has deleted the row. +That is, the node is visible but it no longer corresponds +to any item in the data source and will be removed soon.

+
+ + + + + + + +
+

Declared In

+

ASTableNode.h

+
+ + +
+
+
+ +

– rectForRowAtIndexPath: +

+ +
+
+ +
+ + +
+

Similar to -[UITableView rectForRowAtIndexPath:]

+
+ + + +
- (CGRect)rectForRowAtIndexPath:(NSIndexPath *)indexPath
+ + + +
+

Parameters

+ + + + + + + +
indexPath

An index path identifying a row in the table view.

+
+ + + +
+

Return Value

+

A rectangle defining the area in which the table view draws the row or CGRectZero if indexPath is invalid.

+
+ + + + + +
+

Discussion

+

This method must be called from the main thread.

+
+ + + + + + + +
+

Declared In

+

ASTableNode.h

+
+ + +
+
+
+ +

– cellForRowAtIndexPath: +

+ +
+
+ +
+ + +
+

Similar to -[UITableView cellForRowAtIndexPath:]

+
+ + + +
- (nullable __kindof UITableViewCell *)cellForRowAtIndexPath:(NSIndexPath *)indexPath
+ + + +
+

Parameters

+ + + + + + + +
indexPath

An index path identifying a row in the table view.

+
+ + + +
+

Return Value

+

An object representing a cell of the table, or nil if the cell is not visible or indexPath is out of range.

+
+ + + + + +
+

Discussion

+

This method must be called from the main thread.

+
+ + + + + + + +
+

Declared In

+

ASTableNode.h

+
+ + +
+
+
+ +

  indexPathForSelectedRow +

+ +
+
+ +
+ + +
+

Similar to UITableView.indexPathForSelectedRow

+
+ + + +
@property (nonatomic, readonly, nullable) NSIndexPath *indexPathForSelectedRow
+ + + + + +
+

Return Value

+

The value of this property is an index path identifying the row and section +indexes of the selected row, or nil if the index path is invalid. If there are multiple selections, +this property contains the first index-path object in the array of row selections; +this object has the lowest index values for section and row.

+
+ + + + + +
+

Discussion

+

This method must be called from the main thread.

+
+ + + + + + + +
+

Declared In

+

ASTableNode.h

+
+ + +
+
+
+ +

– indexPathForRowAtPoint: +

+ +
+
+ +
+ + +
+

Similar to -[UITableView indexPathForRowAtPoint:]

+
+ + + +
- (nullable NSIndexPath *)indexPathForRowAtPoint:(CGPoint)point
+ + + +
+

Parameters

+ + + + + + + +
point

A point in the local coordinate system of the table view (the table view’€™s bounds).

+
+ + + +
+

Return Value

+

An index path representing the row and section associated with point, +or nil if the point is out of the bounds of any row.

+
+ + + + + +
+

Discussion

+

This method must be called from the main thread.

+
+ + + + + + + +
+

Declared In

+

ASTableNode.h

+
+ + +
+
+
+ +

– indexPathsForRowsInRect: +

+ +
+
+ +
+ + +
+

Similar to -[UITableView indexPathsForRowsInRect:]

+
+ + + +
- (nullable NSArray<NSIndexPath*> *)indexPathsForRowsInRect:(CGRect)rect
+ + + +
+

Parameters

+ + + + + + + +
rect

A rectangle defining an area of the table view in local coordinates.

+
+ + + +
+

Return Value

+

An array of NSIndexPath objects each representing a row and section index identifying a row within rect. +Returns an empty array if there aren’t any rows to return.

+
+ + + + + +
+

Discussion

+

This method must be called from the main thread.

+
+ + + + + + + +
+

Declared In

+

ASTableNode.h

+
+ + +
+
+
+ +

– indexPathsForVisibleRows +

+ +
+
+ +
+ + +
+

Similar to -[UITableView indexPathsForVisibleRows]

+
+ + + +
- (NSArray<NSIndexPath*> *)indexPathsForVisibleRows
+ + + + + +
+

Return Value

+

The value of this property is an array of NSIndexPath objects each representing a row index and section index +that together identify a visible row in the table view. If no rows are visible, the value is nil.

+
+ + + + + +
+

Discussion

+

This method must be called from the main thread.

+
+ + + + + + + +
+

Declared In

+

ASTableNode.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Classes/ASTableView.html b/docs/appledoc/Classes/ASTableView.html new file mode 100755 index 0000000000..d1482c155a --- /dev/null +++ b/docs/appledoc/Classes/ASTableView.html @@ -0,0 +1,333 @@ + + + + + + ASTableView Class Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASTableView Class Reference

+ + +
+ + + + + + + +
Inherits fromUITableView
Declared inASTableView.h
+ + + + +
+ +

Overview

+

Asynchronous UITableView with Intelligent Preloading capabilities.

ASTableView is a true subclass of UITableView, meaning it is pointer-compatible with code that +currently uses UITableView

+ +

The main difference is that asyncDataSource expects -nodeForRowAtIndexPath, an ASCellNode, and +the heightForRowAtIndexPath: method is eliminated (as are the performance problems caused by it). +This is made possible because ASCellNodes can calculate their own size, and preload ahead of time.

Note: ASTableNode is strongly recommended over ASTableView. This class is provided for adoption convenience.

+
+ + + + + +
+ + + + + + +
+
+ +

  tableNode +

+ +
+
+ +
+ + +
+

The corresponding table node, or nil if one does not exist.

+
+ + + +
@property (nonatomic, weak, readonly) ASTableNode *tableNode
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASTableView.h

+
+ + +
+
+
+ +

– nodeForRowAtIndexPath: +

+ +
+
+ +
+ + +
+

Retrieves the node for the row at the given index path.

+
+ + + +
- (nullable ASCellNode *)nodeForRowAtIndexPath:(NSIndexPath *)indexPath
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASTableView.h

+
+ + +
+
+
+ +

  automaticallyAdjustsContentOffset +

+ +
+
+ +
+ + +
+

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.

+
+ + + +
@property (nonatomic) BOOL automaticallyAdjustsContentOffset
+ + + + + + + + + +
+

Discussion

+

default is NO.

+
+ + + + + + + +
+

Declared In

+

ASTableView.h

+
+ + +
+
+
+ +

  leadingScreensForBatching +

+ +
+
+ +
+ + +
+

The number of screens left to scroll before the delegate -tableView:beginBatchFetchingWithContext: is called.

+
+ + + +
@property (nonatomic, assign) CGFloat leadingScreensForBatching
+ + + + + + + + + +
+

Discussion

+

Defaults to two screenfuls.

+
+ + + + + + + +
+

Declared In

+

ASTableView.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Classes/ASTextCellNode.html b/docs/appledoc/Classes/ASTextCellNode.html new file mode 100755 index 0000000000..da3f0aa37d --- /dev/null +++ b/docs/appledoc/Classes/ASTextCellNode.html @@ -0,0 +1,316 @@ + + + + + + ASTextCellNode Class Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASTextCellNode Class Reference

+ + +
+ + + + + + + +
Inherits fromASCellNode : ASDisplayNode : ASDealloc2MainObject
Declared inASCellNode.h
+ + + + +
+ +

Overview

+

Simple label-style cell node. Read its source for an example of custom ASCellNodes.

+
+ + + + + +
+ + + + + + +
+
+ +

– initWithAttributes:insets: +

+ +
+
+ +
+ + +
+

Initializes a text cell with given text attributes and text insets

+
+ + + +
- (instancetype)initWithAttributes:(NSDictionary *)textAttributes insets:(UIEdgeInsets)textInsets
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASCellNode.h

+
+ + +
+
+
+ +

  text +

+ +
+
+ +
+ + +
+

Text to display.

+
+ + + +
@property (nonatomic, copy) NSString *text
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASCellNode.h

+
+ + +
+
+
+ +

  textAttributes +

+ +
+
+ +
+ + +
+

A dictionary containing key-value pairs for text attributes. You can specify the font, text color, text shadow color, and text shadow offset using the keys listed in NSString UIKit Additions Reference.

+
+ + + +
@property (nonatomic, copy) NSDictionary *textAttributes
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASCellNode.h

+
+ + +
+
+
+ +

  textInsets +

+ +
+
+ +
+ + +
+

The text inset or outset for each edge. The default value is 15.0 horizontal and 11.0 vertical padding.

+
+ + + +
@property (nonatomic, assign) UIEdgeInsets textInsets
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASCellNode.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Classes/ASTextNode.html b/docs/appledoc/Classes/ASTextNode.html new file mode 100755 index 0000000000..fab6645fc8 --- /dev/null +++ b/docs/appledoc/Classes/ASTextNode.html @@ -0,0 +1,1338 @@ + + + + + + ASTextNode Class Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASTextNode Class Reference

+ + +
+ + + + + + + +
Inherits fromASControlNode : ASDisplayNode : ASDealloc2MainObject
Declared inASTextNode.h
+ + + + +
+ +

Overview

+

Backed by TextKit.

+
+ + + + + +
+ + + + + + +
+
+ +

  attributedText +

+ +
+
+ +
+ + +
+

The styled text displayed by the node.

+
+ + + +
@property (nullable, nonatomic, copy) NSAttributedString *attributedText
+ + + + + + + + + +
+

Discussion

+

Defaults to nil, no text is shown. +For inline image attachments, add an attribute of key NSAttachmentAttributeName, with a value of an NSTextAttachment.

+
+ + + + + + + +
+

Declared In

+

ASTextNode.h

+
+ + +
+
+
+ +

  truncationAttributedText +

+ +
+
+ +
+ + +
+

The attributedText to use when the text must be truncated.

+
+ + + +
@property (nullable, nonatomic, copy) NSAttributedString *truncationAttributedText
+ + + + + + + + + +
+

Discussion

+

Defaults to a localized ellipsis character.

+
+ + + + + + + +
+

Declared In

+

ASTextNode.h

+
+ + +
+
+
+ +

  additionalTruncationMessage +

+ +
+
+ +
+ + +
+

@summary The second attributed string appended for truncation.

+
+ + + +
@property (nullable, nonatomic, copy) NSAttributedString *additionalTruncationMessage
+ + + + + + + + + +
+

Discussion

+

This string will be highlighted on touches. +@default nil

+
+ + + + + + + +
+

Declared In

+

ASTextNode.h

+
+ + +
+
+
+ +

  truncationMode +

+ +
+
+ +
+ + +
+

Determines how the text is truncated to fit within the receiver’s maximum size.

+
+ + + +
@property (nonatomic, assign) NSLineBreakMode truncationMode
+ + + + + + + + + +
+

Discussion

+

Defaults to NSLineBreakByWordWrapping.

Note: Setting a truncationMode in attributedString will override the truncation mode set here.

+
+ + + + + + + +
+

Declared In

+

ASTextNode.h

+
+ + +
+
+
+ +

  truncated +

+ +
+
+ +
+ + +
+

If the text node is truncated. Text must have been sized first.

+
+ + + +
@property (nonatomic, readonly, assign, getter=isTruncated) BOOL truncated
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASTextNode.h

+
+ + +
+
+
+ +

  maximumNumberOfLines +

+ +
+
+ +
+ + +
+

The maximum number of lines to render of the text before truncation. +@default 0 (No limit)

+
+ + + +
@property (nonatomic, assign) NSUInteger maximumNumberOfLines
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASTextNode.h

+
+ + +
+
+
+ +

  lineCount +

+ +
+
+ +
+ + +
+

The number of lines in the text. Text must have been sized first.

+
+ + + +
@property (nonatomic, readonly, assign) NSUInteger lineCount
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASTextNode.h

+
+ + +
+
+
+ +

  exclusionPaths +

+ +
+
+ +
+ + +
+

An array of path objects representing the regions where text should not be displayed.

+
+ + + +
@property (nullable, nonatomic, strong) NSArray<UIBezierPath*> *exclusionPaths
+ + + + + + + + + +
+

Discussion

+

The default value of this property is an empty array. You can +assign an array of UIBezierPath objects to exclude text from one or more regions in +the text node’s bounds. You can use this property to have text wrap around images, +shapes or other text like a fancy magazine.

+
+ + + + + + + +
+

Declared In

+

ASTextNode.h

+
+ + +
+
+
+ +

  placeholderEnabled +

+ +
+
+ +
+ + +
+

ASTextNode has a special placeholder behavior when placeholderEnabled is YES.

+
+ + + +
@property (nonatomic, assign) BOOL placeholderEnabled
+ + + + + + + + + +
+

Discussion

+

Defaults to NO. When YES, it draws rectangles for each line of text, +following the true shape of the text’s wrapping. This visually mirrors the overall +shape and weight of paragraphs, making the appearance of the finished text less jarring.

+
+ + + + + + + +
+

Declared In

+

ASTextNode.h

+
+ + +
+
+
+ +

  placeholderColor +

+ +
+
+ +
+ + +
+

The placeholder color.

+
+ + + +
@property (nullable, nonatomic, strong) UIColor *placeholderColor
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASTextNode.h

+
+ + +
+
+
+ +

  placeholderInsets +

+ +
+
+ +
+ + +
+

Inset each line of the placeholder.

+
+ + + +
@property (nonatomic, assign) UIEdgeInsets placeholderInsets
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASTextNode.h

+
+ + +
+
+
+ +

  shadowPadding +

+ +
+
+ +
+ + +
+

The number of pixels used for shadow padding on each side of the receiver.

+
+ + + +
@property (nonatomic, readonly, assign) UIEdgeInsets shadowPadding
+ + + + + + + + + +
+

Discussion

+

Each inset will be less than or equal to zero, so that applying +UIEdgeInsetsRect(boundingRectForText, shadowPadding) +will return a CGRect large enough to fit both the text and the appropriate shadow padding.

+
+ + + + + + + +
+

Declared In

+

ASTextNode.h

+
+ + +
+
+
+ +

– rectsForTextRange: +

+ +
+
+ +
+ + +
+

Returns an array of rects bounding the characters in a given text range.

+
+ + + +
- (NSArray<NSValue*> *)rectsForTextRange:(NSRange)textRange
+ + + +
+

Parameters

+ + + + + + + +
textRange

A range of text. Must be valid for the receiver’s string.

+
+ + + + + + + +
+

Discussion

+

Use this method to detect all the different rectangles a given range of text occupies. +The rects returned are not guaranteed to be contiguous (for example, if the given text range spans +a line break, the rects returned will be on opposite sides and different lines). The rects returned +are in the coordinate system of the receiver.

+
+ + + + + + + +
+

Declared In

+

ASTextNode.h

+
+ + +
+
+
+ +

– highlightRectsForTextRange: +

+ +
+
+ +
+ + +
+

Returns an array of rects used for highlighting the characters in a given text range.

+
+ + + +
- (NSArray<NSValue*> *)highlightRectsForTextRange:(NSRange)textRange
+ + + +
+

Parameters

+ + + + + + + +
textRange

A range of text. Must be valid for the receiver’s string.

+
+ + + + + + + +
+

Discussion

+

Use this method to detect all the different rectangles the highlights of a given range of text occupies. +The rects returned are not guaranteed to be contiguous (for example, if the given text range spans +a line break, the rects returned will be on opposite sides and different lines). The rects returned +are in the coordinate system of the receiver. This method is useful for visual coordination with a +highlighted range of text.

+
+ + + + + + + +
+

Declared In

+

ASTextNode.h

+
+ + +
+
+
+ +

– frameForTextRange: +

+ +
+
+ +
+ + +
+

Returns a bounding rect for the given text range.

+
+ + + +
- (CGRect)frameForTextRange:(NSRange)textRange
+ + + +
+

Parameters

+ + + + + + + +
textRange

A range of text. Must be valid for the receiver’s string.

+
+ + + + + + + +
+

Discussion

+

The height of the frame returned is that of the receiver’s line-height; adjustment for +cap-height and descenders is not performed. This method raises an exception if textRange is not +a valid substring range of the receiver’s string.

+
+ + + + + + + +
+

Declared In

+

ASTextNode.h

+
+ + +
+
+
+ +

– trailingRect +

+ +
+
+ +
+ + +
+

Returns the trailing rectangle of space in the receiver, after the final character.

+
+ + + +
- (CGRect)trailingRect
+ + + + + + + + + +
+

Discussion

+

Use this method to detect which portion of the receiver is not occupied by characters. +The rect returned is in the coordinate system of the receiver.

+
+ + + + + + + +
+

Declared In

+

ASTextNode.h

+
+ + +
+
+
+ +

  linkAttributeNames +

+ +
+
+ +
+ + +
+

The set of attribute names to consider links. Defaults to NSLinkAttributeName.

+
+ + + +
@property (nonatomic, copy) NSArray<NSString*> *linkAttributeNames
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASTextNode.h

+
+ + +
+
+
+ +

– linkAttributeValueAtPoint:attributeName:range: +

+ +
+
+ +
+ + +
+

Indicates whether the receiver has an entity at a given point.

+
+ + + +
- (nullable id)linkAttributeValueAtPoint:(CGPoint)point attributeName:(out NSString *_Nullable *_Nullable)attributeNameOut range:(out NSRange *_Nullable)rangeOut
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + +
point

The point, in the receiver’s coordinate system.

attributeNameOut

The name of the attribute at the point. Can be NULL.

rangeOut

The ultimate range of the found text. Can be NULL.

+
+ + + +
+

Return Value

+

YES if an entity exists at point; NO otherwise.

+
+ + + + + + + + + + + +
+

Declared In

+

ASTextNode.h

+
+ + +
+
+
+ +

  highlightStyle +

+ +
+
+ +
+ + +
+

The style to use when highlighting text.

+
+ + + +
@property (nonatomic, assign) ASTextNodeHighlightStyle highlightStyle
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASTextNode.h

+
+ + +
+
+
+ +

  highlightRange +

+ +
+
+ +
+ + +
+

The range of text highlighted by the receiver. Changes to this property are not animated by default.

+
+ + + +
@property (nonatomic, assign) NSRange highlightRange
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASTextNode.h

+
+ + +
+
+
+ +

– setHighlightRange:animated: +

+ +
+
+ +
+ + +
+

Set the range of text to highlight, with optional animation.

+
+ + + +
- (void)setHighlightRange:(NSRange)highlightRange animated:(BOOL)animated
+ + + +
+

Parameters

+ + + + + + + + + + + + +
highlightRange

The range of text to highlight.

animated

Whether the text should be highlighted with an animation.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASTextNode.h

+
+ + +
+
+
+ +

  delegate +

+ +
+
+ +
+ + +
+

Responds to actions from links in the text node.

+
+ + + +
@property (nonatomic, weak) id<ASTextNodeDelegate> delegate
+ + + + + + + + + +
+

Discussion

+

The delegate must be set before the node is loaded, and implement + textNode:longPressedLinkAttribute:value:atPoint:textRange: in order for + the long press gesture recognizer to be installed.

+
+ + + + + + + +
+

Declared In

+

ASTextNode.h

+
+ + +
+
+
+ +

  longPressCancelsTouches +

+ +
+
+ +
+ + +
+

If YES and a long press is recognized, touches are cancelled. Default is NO

+
+ + + +
@property (nonatomic, assign) BOOL longPressCancelsTouches
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASTextNode.h

+
+ + +
+
+
+ +

  passthroughNonlinkTouches +

+ +
+
+ +
+ + +
+

if YES will not intercept touches for non-link areas of the text. Default is NO.

+
+ + + +
@property (nonatomic, assign) BOOL passthroughNonlinkTouches
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASTextNode.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Classes/ASVideoNode.html b/docs/appledoc/Classes/ASVideoNode.html new file mode 100755 index 0000000000..f9b2b28315 --- /dev/null +++ b/docs/appledoc/Classes/ASVideoNode.html @@ -0,0 +1,265 @@ + + + + + + ASVideoNode Class Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASVideoNode Class Reference

+ + +
+ + + + + + + +
Inherits fromASNetworkImageNode : ASImageNode : ASControlNode : ASDisplayNode : ASDealloc2MainObject
Declared inASVideoNode.h
+ + + + + + +
+ + + + + + +
+
+ +

  assetURL +

+ +
+
+ +
+ + +
+
    +
  • @abstract The URL with which the asset was initialized.
  • +
  • @discussion Setting the URL will overwrite the current asset with a newly created AVURLAsset created from the given URL, and AVAsset *asset will point to that newly created AVURLAsset. Please don’t set both assetURL and asset.
  • +
  • @return Current URL the asset was initialized or nil if no URL was given.
  • +
+ +
+ + + +
@property (nullable, nonatomic, strong, readwrite) NSURL *assetURL
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASVideoNode.h

+
+ + +
+
+
+ +

  shouldAutoplay +

+ +
+
+ +
+ + +
+

When shouldAutoplay is set to true, a video node will play when it has both loaded and entered the “visible” interfaceState. +If it leaves the visible interfaceState it will pause but will resume once it has returned.

+
+ + + +
@property (nonatomic, assign, readwrite) BOOL shouldAutoplay
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASVideoNode.h

+
+ + +
+
+
+ +

  delegate +

+ +
+
+ +
+ + +
+

The delegate, which must conform to the ASNetworkImageNodeDelegate protocol.

+
+ + + +
@property (nullable, nonatomic, weak, readwrite) id<ASVideoNodeDelegate,ASNetworkImageNodeDelegate> delegate
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASNetworkImageNode.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Classes/ASVideoPlayerNode.html b/docs/appledoc/Classes/ASVideoPlayerNode.html new file mode 100755 index 0000000000..3d6645c850 --- /dev/null +++ b/docs/appledoc/Classes/ASVideoPlayerNode.html @@ -0,0 +1,174 @@ + + + + + + ASVideoPlayerNode Class Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASVideoPlayerNode Class Reference

+ + +
+ + + + + + + +
Inherits fromASDisplayNode : ASDealloc2MainObject
Declared inASVideoPlayerNode.h
+ + + + + + +
+ + + + + + +
+
+ +

  shouldAutoPlay +

+ +
+
+ +
+ + +
+

When shouldAutoplay is set to true, a video node will play when it has both loaded and entered the “visible” interfaceState. +If it leaves the visible interfaceState it will pause but will resume once it has returned.

+
+ + + +
@property (nonatomic, assign, readwrite) BOOL shouldAutoPlay
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASVideoPlayerNode.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Classes/ASViewController.html b/docs/appledoc/Classes/ASViewController.html new file mode 100755 index 0000000000..d29e08e034 --- /dev/null +++ b/docs/appledoc/Classes/ASViewController.html @@ -0,0 +1,450 @@ + + + + + + ASViewController Class Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASViewController Class Reference

+ + +
+ + + + + + + +
Conforms to*
:
ASDisplayNode
DisplayNodeType
__covariant
Declared inASViewController.h
+ + + + + + +
+ + + + + + +
+
+ +

– initWithNode: +

+ +
+
+ +
+ + +
+

ASViewController Designated initializer.

+
+ + + +
- (instancetype)initWithNode:(DisplayNodeType)node
+ + + +
+

Parameters

+ + + + + + + +
node

An ASDisplayNode which will provide the root view (self.view)

+
+ + + +
+

Return Value

+

An ASViewController instance whose root view will be backed by the provided ASDisplayNode.

+
+ + + + + +
+

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.

+
+ + + + + +
+

See Also

+ +
+ + + +
+

Declared In

+

ASViewController.h

+
+ + +
+
+
+ +

  node +

+ +
+
+ +
+ + +
+

node Returns the ASDisplayNode which provides the backing view to the view controller.

+
+ + + +
@property (nonatomic, strong, readonly) DisplayNodeType node
+ + + + + +
+

Return Value

+

node Returns the ASDisplayNode which provides the backing view to the view controller.

+
+ + + + + + + + + + + +
+

Declared In

+

ASViewController.h

+
+ + +
+
+
+ +

  overrideDisplayTraitsWithTraitCollection +

+ +
+
+ +
+ + +
+

Set this block to customize the ASDisplayTraits returned when the VC transitions to the given traitCollection.

+
+ + + +
@property (nonatomic, copy) ASDisplayTraitsForTraitCollectionBlock overrideDisplayTraitsWithTraitCollection
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASViewController.h

+
+ + +
+
+
+ +

  overrideDisplayTraitsWithWindowSize +

+ +
+
+ +
+ + +
+

Set this block to customize the ASDisplayTraits returned when the VC transitions to the given window size.

+
+ + + +
@property (nonatomic, copy) ASDisplayTraitsForTraitWindowSizeBlock overrideDisplayTraitsWithWindowSize
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASViewController.h

+
+ + +
+
+
+ +

  interfaceState +

+ +
+
+ +
+ + +
+

Passthrough property to the the .interfaceState of the node.

+
+ + + +
@property (nonatomic, readonly) ASInterfaceState interfaceState
+ + + + + +
+

Return Value

+

The current ASInterfaceState of the node, indicating whether it is visible and other situational properties.

+
+ + + + + + + + + +
+

See Also

+ +
+ + + +
+

Declared In

+

ASViewController.h

+
+ + +
+
+
+ +

– nodeConstrainedSize +

+ +
+
+ +
+ + +
+

The constrained size used to measure the backing node.

+
+ + + +
- (ASSizeRange)nodeConstrainedSize
+ + + + + + + + + +
+

Discussion

+

Defaults to providing a size range that uses the view controller view’s bounds as +both the min and max definitions. Override this method to provide a custom size range to the +backing node.

+
+ + + + + + + +
+

Declared In

+

ASViewController.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Classes/ASWrapperLayoutSpec.html b/docs/appledoc/Classes/ASWrapperLayoutSpec.html new file mode 100755 index 0000000000..844114f859 --- /dev/null +++ b/docs/appledoc/Classes/ASWrapperLayoutSpec.html @@ -0,0 +1,123 @@ + + + + + + ASWrapperLayoutSpec Class Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASWrapperLayoutSpec Class Reference

+ + +
+ + + + + + + +
Inherits fromASLayoutSpec : NSObject
Declared inASLayoutSpec.h
+ + + + +
+ +

Overview

+

An ASLayoutSpec subclass that can wrap one or more ASLayoutElement and calculates the layout based on the +sizes of the children. If multiple children are provided the size of the biggest child will be used to for +size of this layout spec.

+
+ + + + + + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Constants/ASAbsoluteLayoutSpecSizing.html b/docs/appledoc/Constants/ASAbsoluteLayoutSpecSizing.html new file mode 100755 index 0000000000..ae500f68a5 --- /dev/null +++ b/docs/appledoc/Constants/ASAbsoluteLayoutSpecSizing.html @@ -0,0 +1,175 @@ + + + + + + ASAbsoluteLayoutSpecSizing Constants Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASAbsoluteLayoutSpecSizing Constants Reference

+ + +
+ + + + +
Declared inASAbsoluteLayoutSpec.h
+ + + + + + + +

ASAbsoluteLayoutSpecSizing

+ + +
+

How much space the spec will take up.

+
+ + +
+ + +

Definition

+ typedef NS_ENUM(NSInteger, ASAbsoluteLayoutSpecSizing ) {
+ +    ASAbsoluteLayoutSpecSizingDefault,
+ +    ASAbsoluteLayoutSpecSizingSizeToFit,
+ + };
+ +
+ +
+

Constants

+
+ +
ASAbsoluteLayoutSpecSizingDefault
+
+ + +

The spec will take up the maximum size possible.

+ + + + + + +

+ Declared In ASAbsoluteLayoutSpec.h. +

+ +
+ +
ASAbsoluteLayoutSpecSizingSizeToFit
+
+ + +

Computes a size for the spec that is the union of all childrens' frames.

+ + + + + + +

+ Declared In ASAbsoluteLayoutSpec.h. +

+ +
+ +
+
+ + + + + + + + +
+

Declared In

+

ASAbsoluteLayoutSpec.h

+
+ + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Constants/ASButtonNodeImageAlignment.html b/docs/appledoc/Constants/ASButtonNodeImageAlignment.html new file mode 100755 index 0000000000..d03b419517 --- /dev/null +++ b/docs/appledoc/Constants/ASButtonNodeImageAlignment.html @@ -0,0 +1,175 @@ + + + + + + ASButtonNodeImageAlignment Constants Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASButtonNodeImageAlignment Constants Reference

+ + +
+ + + + +
Declared inASButtonNode.h
+ + + + + + + +

ASButtonNodeImageAlignment

+ + +
+

Image alignment defines where the image will be placed relative to the text.

+
+ + +
+ + +

Definition

+ typedef NS_ENUM(NSInteger, ASButtonNodeImageAlignment ) {
+ +    ASButtonNodeImageAlignmentBeginning,
+ +    ASButtonNodeImageAlignmentEnd,
+ + };
+ +
+ +
+

Constants

+
+ +
ASButtonNodeImageAlignmentBeginning
+
+ + +

Places the image before the text.

+ + + + + + +

+ Declared In ASButtonNode.h. +

+ +
+ +
ASButtonNodeImageAlignmentEnd
+
+ + +

Places the image after the text.

+ + + + + + +

+ Declared In ASButtonNode.h. +

+ +
+ +
+
+ + + + + + + + +
+

Declared In

+

ASButtonNode.h

+
+ + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Constants/ASCellNodeVisibilityEvent.html b/docs/appledoc/Constants/ASCellNodeVisibilityEvent.html new file mode 100755 index 0000000000..eefd793fcd --- /dev/null +++ b/docs/appledoc/Constants/ASCellNodeVisibilityEvent.html @@ -0,0 +1,218 @@ + + + + + + ASCellNodeVisibilityEvent Constants Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASCellNodeVisibilityEvent Constants Reference

+ + +
+ + + + +
Declared inASCellNode.h
+ + + + + + + +

ASCellNodeVisibilityEvent

+ +
+ + +

Definition

+ typedef NS_ENUM(NSUInteger, ASCellNodeVisibilityEvent ) {
+ +    ASCellNodeVisibilityEventVisible,
+ +    ASCellNodeVisibilityEventVisibleRectChanged,
+ +    ASCellNodeVisibilityEventInvisible,
+ +    ASCellNodeVisibilityEventWillBeginDragging,
+ +    ASCellNodeVisibilityEventDidEndDragging,
+ + };
+ +
+ +
+

Constants

+
+ +
ASCellNodeVisibilityEventVisible
+
+ + +

Indicates a cell has just became visible

+ + + + + + +

+ Declared In ASCellNode.h. +

+ +
+ +
ASCellNodeVisibilityEventVisibleRectChanged
+
+ + +

Its position (determined by scrollView.contentOffset) has changed while at least 1px remains visible. +It is possible that 100% of the cell is visible both before and after and only its position has changed, +or that the position change has resulted in more or less of the cell being visible. +Use CGRectIntersect between cellFrame and scrollView.bounds to get this rectangle

+ + + + + + +

+ Declared In ASCellNode.h. +

+ +
+ +
ASCellNodeVisibilityEventInvisible
+
+ + +

Indicates a cell is no longer visible

+ + + + + + +

+ Declared In ASCellNode.h. +

+ +
+ +
ASCellNodeVisibilityEventWillBeginDragging
+
+ + +

Indicates user has started dragging the visible cell

+ + + + + + +

+ Declared In ASCellNode.h. +

+ +
+ +
ASCellNodeVisibilityEventDidEndDragging
+
+ + +

Indicates user has ended dragging the visible cell

+ + + + + + +

+ Declared In ASCellNode.h. +

+ +
+ +
+
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Constants/ASCenterLayoutSpecCenteringOptions.html b/docs/appledoc/Constants/ASCenterLayoutSpecCenteringOptions.html new file mode 100755 index 0000000000..d99b9e76ed --- /dev/null +++ b/docs/appledoc/Constants/ASCenterLayoutSpecCenteringOptions.html @@ -0,0 +1,213 @@ + + + + + + ASCenterLayoutSpecCenteringOptions Constants Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASCenterLayoutSpecCenteringOptions Constants Reference

+ + +
+ + + + +
Declared inASCenterLayoutSpec.h
+ + + + + + + +

ASCenterLayoutSpecCenteringOptions

+ + +
+

How the child is centered within the spec.

+
+ + +
+ + +

Definition

+ typedef NS_OPTIONS(NSUInteger, ASCenterLayoutSpecCenteringOptions ) {
+ +    ASCenterLayoutSpecCenteringNone = 0,
+ +    ASCenterLayoutSpecCenteringX = 1 < < 0,
+ +    ASCenterLayoutSpecCenteringY = 1 < < 1,
+ +    ASCenterLayoutSpecCenteringXY = ASCenterLayoutSpecCenteringX | ASCenterLayoutSpecCenteringY,
+ + };
+ +
+ +
+

Constants

+
+ +
ASCenterLayoutSpecCenteringNone
+
+ + +

The child is positioned in {0,0} relatively to the layout bounds

+ + + + + + +

+ Declared In ASCenterLayoutSpec.h. +

+ +
+ +
ASCenterLayoutSpecCenteringX
+
+ + +

The child is centered along the X axis

+ + + + + + +

+ Declared In ASCenterLayoutSpec.h. +

+ +
+ +
ASCenterLayoutSpecCenteringY
+
+ + +

The child is centered along the Y axis

+ + + + + + +

+ Declared In ASCenterLayoutSpec.h. +

+ +
+ +
ASCenterLayoutSpecCenteringXY
+
+ + +

Convenience option to center both along the X and Y axis

+ + + + + + +

+ Declared In ASCenterLayoutSpec.h. +

+ +
+ +
+
+ + + + + + + + +
+

Declared In

+

ASCenterLayoutSpec.h

+
+ + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Constants/ASCenterLayoutSpecSizingOptions.html b/docs/appledoc/Constants/ASCenterLayoutSpecSizingOptions.html new file mode 100755 index 0000000000..f7dea9c8bc --- /dev/null +++ b/docs/appledoc/Constants/ASCenterLayoutSpecSizingOptions.html @@ -0,0 +1,213 @@ + + + + + + ASCenterLayoutSpecSizingOptions Constants Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASCenterLayoutSpecSizingOptions Constants Reference

+ + +
+ + + + +
Declared inASCenterLayoutSpec.h
+ + + + + + + +

ASCenterLayoutSpecSizingOptions

+ + +
+

How much space the spec will take up.

+
+ + +
+ + +

Definition

+ typedef NS_OPTIONS(NSUInteger, ASCenterLayoutSpecSizingOptions ) {
+ +    ASCenterLayoutSpecSizingOptionDefault = ASRelativeLayoutSpecSizingOptionDefault,
+ +    ASCenterLayoutSpecSizingOptionMinimumX = ASRelativeLayoutSpecSizingOptionMinimumWidth,
+ +    ASCenterLayoutSpecSizingOptionMinimumY = ASRelativeLayoutSpecSizingOptionMinimumHeight,
+ +    ASCenterLayoutSpecSizingOptionMinimumXY = ASRelativeLayoutSpecSizingOptionMinimumSize,
+ + };
+ +
+ +
+

Constants

+
+ +
ASCenterLayoutSpecSizingOptionDefault
+
+ + +

The spec will take up the maximum size possible

+ + + + + + +

+ Declared In ASCenterLayoutSpec.h. +

+ +
+ +
ASCenterLayoutSpecSizingOptionMinimumX
+
+ + +

The spec will take up the minimum size possible along the X axis

+ + + + + + +

+ Declared In ASCenterLayoutSpec.h. +

+ +
+ +
ASCenterLayoutSpecSizingOptionMinimumY
+
+ + +

The spec will take up the minimum size possible along the Y axis

+ + + + + + +

+ Declared In ASCenterLayoutSpec.h. +

+ +
+ +
ASCenterLayoutSpecSizingOptionMinimumXY
+
+ + +

Convenience option to take up the minimum size along both the X and Y axis

+ + + + + + +

+ Declared In ASCenterLayoutSpec.h. +

+ +
+ +
+
+ + + + + + + + +
+

Declared In

+

ASCenterLayoutSpec.h

+
+ + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Constants/ASControlNodeEvent.html b/docs/appledoc/Constants/ASControlNodeEvent.html new file mode 100755 index 0000000000..fba63ceab7 --- /dev/null +++ b/docs/appledoc/Constants/ASControlNodeEvent.html @@ -0,0 +1,308 @@ + + + + + + ASControlNodeEvent Constants Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASControlNodeEvent Constants Reference

+ + +
+ + + + +
Declared inASControlNode.h
+ + + + + + + +

ASControlNodeEvent

+ + +
+

These events are identical to their UIControl counterparts.

+
+ + +
+ + +

Definition

+ typedef NS_OPTIONS(NSUInteger, ASControlNodeEvent ) {
+ +    ASControlNodeEventTouchDown = 1 < < 0,
+ +    ASControlNodeEventTouchDownRepeat = 1 < < 1,
+ +    ASControlNodeEventTouchDragInside = 1 < < 2,
+ +    ASControlNodeEventTouchDragOutside = 1 < < 3,
+ +    ASControlNodeEventTouchUpInside = 1 < < 4,
+ +    ASControlNodeEventTouchUpOutside = 1 < < 5,
+ +    ASControlNodeEventTouchCancel = 1 < < 6,
+ +    ASControlNodeEventPrimaryActionTriggered = 1 < < 13,
+ +    ASControlNodeEventAllEvents = 0 xFFFFFFFF,
+ + };
+ +
+ +
+

Constants

+
+ +
ASControlNodeEventTouchDown
+
+ + +

A touch-down event in the control node.

+ + + + + + +

+ Declared In ASControlNode.h. +

+ +
+ +
ASControlNodeEventTouchDownRepeat
+
+ + +

A repeated touch-down event in the control node; for this event the value of the UITouch tapCount method is greater than one.

+ + + + + + +

+ Declared In ASControlNode.h. +

+ +
+ +
ASControlNodeEventTouchDragInside
+
+ + +

An event where a finger is dragged inside the bounds of the control node.

+ + + + + + +

+ Declared In ASControlNode.h. +

+ +
+ +
ASControlNodeEventTouchDragOutside
+
+ + +

An event where a finger is dragged just outside the bounds of the control.

+ + + + + + +

+ Declared In ASControlNode.h. +

+ +
+ +
ASControlNodeEventTouchUpInside
+
+ + +

A touch-up event in the control node where the finger is inside the bounds of the node.

+ + + + + + +

+ Declared In ASControlNode.h. +

+ +
+ +
ASControlNodeEventTouchUpOutside
+
+ + +

A touch-up event in the control node where the finger is outside the bounds of the node.

+ + + + + + +

+ Declared In ASControlNode.h. +

+ +
+ +
ASControlNodeEventTouchCancel
+
+ + +

A system event canceling the current touches for the control node.

+ + + + + + +

+ Declared In ASControlNode.h. +

+ +
+ +
ASControlNodeEventPrimaryActionTriggered
+
+ + +

A system event when the Play/Pause button on the Apple TV remote is pressed.

+ + + + + + +

+ Declared In ASControlNode.h. +

+ +
+ +
ASControlNodeEventAllEvents
+
+ + +

All events, including system events.

+ + + + + + +

+ Declared In ASControlNode.h. +

+ +
+ +
+
+ + + + + + + + +
+

Declared In

+

ASControlNode.h

+
+ + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Constants/ASControlState.html b/docs/appledoc/Constants/ASControlState.html new file mode 100755 index 0000000000..64744c12dc --- /dev/null +++ b/docs/appledoc/Constants/ASControlState.html @@ -0,0 +1,180 @@ + + + + + + ASControlState Constants Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASControlState Constants Reference

+ + +
+ + + + +
Declared inASControlNode.h
+ + + + + + + +

ASControlState

+ +
+ + +

Definition

+ typedef NS_OPTIONS(NSUInteger, ASControlState ) {
+ +    ASControlStateNormal = 0,
+ +    ASControlStateHighlighted = 1 < < 0,
+ +    ASControlStateDisabled = 1 < < 1,
+ +    ASControlStateSelected = 1 < < 2,
+ +    ASControlStateReserved = 0 xFF000000,
+ + };
+ +
+ +
+

Constants

+
+ +
ASControlStateNormal
+
+ + +

+ Declared In ASControlNode.h. +

+ +
+ +
ASControlStateHighlighted
+
+ + +

+ Declared In ASControlNode.h. +

+ +
+ +
ASControlStateDisabled
+
+ + +

+ Declared In ASControlNode.h. +

+ +
+ +
ASControlStateSelected
+
+ + +

+ Declared In ASControlNode.h. +

+ +
+ +
ASControlStateReserved
+
+ + +

+ Declared In ASControlNode.h. +

+ +
+ +
+
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Constants/ASDimensionUnit.html b/docs/appledoc/Constants/ASDimensionUnit.html new file mode 100755 index 0000000000..aabf7a9803 --- /dev/null +++ b/docs/appledoc/Constants/ASDimensionUnit.html @@ -0,0 +1,201 @@ + + + + + + ASDimensionUnit Constants Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASDimensionUnit Constants Reference

+ + +
+ + + + +
Declared inASDimension.h
+ + + + + + + +

ASDimensionUnit

+ + +
+

A dimension relative to constraints to be provided in the future. +A ASDimension can be one of three types:

+ +

“Auto” - This indicated “I have no opinion” and may be resolved in whatever way makes most sense given the circumstances.

+ +

“Points” - Just a number. It will always resolve to exactly this amount.

+ +

“Percent” - Multiplied to a provided parent amount to resolve a final amount.

+
+ + +
+ + +

Definition

+ typedef NS_ENUM(NSInteger, ASDimensionUnit ) {
+ +    ASDimensionUnitAuto,
+ +    ASDimensionUnitPoints,
+ +    ASDimensionUnitFraction,
+ + };
+ +
+ +
+

Constants

+
+ +
ASDimensionUnitAuto
+
+ + +

This indicates “I have no opinion” and may be resolved in whatever way makes most sense given the circumstances.

+ + + + + + +

+ Declared In ASDimension.h. +

+ +
+ +
ASDimensionUnitPoints
+
+ + +

Just a number. It will always resolve to exactly this amount. This is the default type.

+ + + + + + +

+ Declared In ASDimension.h. +

+ +
+ +
ASDimensionUnitFraction
+
+ + +

Multiplied to a provided parent amount to resolve a final amount.

+ + + + + + +

+ Declared In ASDimension.h. +

+ +
+ +
+
+ + + + + + + + +
+

Declared In

+

ASDimension.h

+
+ + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Constants/ASDisplayNodePerformanceMeasurementOptions.html b/docs/appledoc/Constants/ASDisplayNodePerformanceMeasurementOptions.html new file mode 100755 index 0000000000..ea8cb0d7ec --- /dev/null +++ b/docs/appledoc/Constants/ASDisplayNodePerformanceMeasurementOptions.html @@ -0,0 +1,175 @@ + + + + + + ASDisplayNodePerformanceMeasurementOptions Constants Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASDisplayNodePerformanceMeasurementOptions Constants Reference

+ + +
+ + + + +
Declared inASDisplayNode+Beta.h
+ + + + + + + +

ASDisplayNodePerformanceMeasurementOptions

+ + +
+

Bitmask to indicate what performance measurements the cell should record.

+
+ + +
+ + +

Definition

+ typedef NS_OPTIONS(NSUInteger, ASDisplayNodePerformanceMeasurementOptions ) {
+ +    ASDisplayNodePerformanceMeasurementOptionLayoutSpec = 1 < < 0,
+ +    ASDisplayNodePerformanceMeasurementOptionLayoutComputation = 1 < < 1,
+ + };
+ +
+ +
+

Constants

+
+ +
ASDisplayNodePerformanceMeasurementOptionLayoutSpec
+
+ + +

Bitmask to indicate what performance measurements the cell should record.

+ + + + + + +

+ Declared In ASDisplayNode+Beta.h. +

+ +
+ +
ASDisplayNodePerformanceMeasurementOptionLayoutComputation
+
+ + +

Bitmask to indicate what performance measurements the cell should record.

+ + + + + + +

+ Declared In ASDisplayNode+Beta.h. +

+ +
+ +
+
+ + + + + + + + +
+

Declared In

+

ASDisplayNode+Beta.h

+
+ + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Constants/ASHorizontalAlignment.html b/docs/appledoc/Constants/ASHorizontalAlignment.html new file mode 100755 index 0000000000..cfc38bf091 --- /dev/null +++ b/docs/appledoc/Constants/ASHorizontalAlignment.html @@ -0,0 +1,270 @@ + + + + + + ASHorizontalAlignment Constants Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASHorizontalAlignment Constants Reference

+ + +
+ + + + +
Declared inASStackLayoutDefines.h
+ + + + + + + +

ASHorizontalAlignment

+ + +
+

Orientation of children along horizontal axis

+
+ + +
+ + +

Definition

+ typedef NS_ENUM(NSUInteger, ASHorizontalAlignment ) {
+ +    ASHorizontalAlignmentNone,
+ +    ASHorizontalAlignmentLeft,
+ +    ASHorizontalAlignmentMiddle,
+ +    ASHorizontalAlignmentRight,
+ +    ASAlignmentLeft = ASHorizontalAlignmentLeft,
+ +    ASAlignmentMiddle = ASHorizontalAlignmentMiddle,
+ +    ASAlignmentRight = ASHorizontalAlignmentRight,
+ + };
+ +
+ +
+

Constants

+
+ +
ASHorizontalAlignmentNone
+
+ + +

No alignment specified. Default value

+ + + + + + +

+ Declared In ASStackLayoutDefines.h. +

+ +
+ +
ASHorizontalAlignmentLeft
+
+ + +

Left aligned

+ + + + + + +

+ Declared In ASStackLayoutDefines.h. +

+ +
+ +
ASHorizontalAlignmentMiddle
+
+ + +

Center aligned

+ + + + + + +

+ Declared In ASStackLayoutDefines.h. +

+ +
+ +
ASHorizontalAlignmentRight
+
+ + +

Right aligned

+ + + + + + +

+ Declared In ASStackLayoutDefines.h. +

+ +
+ +
ASAlignmentLeft
+
+ + +

Use ASHorizontalAlignmentLeft instead (Deprecated: Use ASHorizontalAlignmentLeft instead)

+ + + + + + +

+ Declared In ASStackLayoutDefines.h. +

+ +
+ +
ASAlignmentMiddle
+
+ + +

Use ASHorizontalAlignmentMiddle instead (Deprecated: Use ASHorizontalAlignmentMiddle instead)

+ + + + + + +

+ Declared In ASStackLayoutDefines.h. +

+ +
+ +
ASAlignmentRight
+
+ + +

Use ASHorizontalAlignmentRight instead (Deprecated: Use ASHorizontalAlignmentRight instead)

+ + + + + + +

+ Declared In ASStackLayoutDefines.h. +

+ +
+ +
+
+ + + + + + + + +
+

Declared In

+

ASStackLayoutDefines.h

+
+ + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Constants/ASInterfaceState.html b/docs/appledoc/Constants/ASInterfaceState.html new file mode 100755 index 0000000000..ab0706f83a --- /dev/null +++ b/docs/appledoc/Constants/ASInterfaceState.html @@ -0,0 +1,254 @@ + + + + + + ASInterfaceState Constants Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASInterfaceState Constants Reference

+ + +
+ + + + +
Declared inASDisplayNode.h
+ + + + + + + +

ASInterfaceState

+ + +
+

Interface state is available on ASDisplayNode and ASViewController, and +allows checking whether a node is in an interface situation where it is prudent to trigger certain +actions: measurement, data loading, display, and visibility (the latter for animations or other onscreen-only effects).

+
+ + +
+ + +

Definition

+ typedef NS_OPTIONS(NSUInteger, ASInterfaceState ) {
+ +    ASInterfaceStateNone = 0,
+ +    ASInterfaceStateMeasureLayout = 1 < < 0,
+ +    ASInterfaceStatePreload = 1 < < 1,
+ +    ASInterfaceStateDisplay = 1 < < 2,
+ +    ASInterfaceStateVisible = 1 < < 3,
+ +    ASInterfaceStateInHierarchy = ASInterfaceStateMeasureLayout | ASInterfaceStatePreload | ASInterfaceStateDisplay | ASInterfaceStateVisible,
+ + };
+ +
+ +
+

Constants

+
+ +
ASInterfaceStateNone
+
+ + +

The element is not predicted to be onscreen soon and preloading should not be performed

+ + + + + + +

+ Declared In ASDisplayNode.h. +

+ +
+ +
ASInterfaceStateMeasureLayout
+
+ + +

The element may be added to a view soon that could become visible. Measure the layout, including size calculation.

+ + + + + + +

+ Declared In ASDisplayNode.h. +

+ +
+ +
ASInterfaceStatePreload
+
+ + +

The element is likely enough to come onscreen that disk and/or network data required for display should be fetched.

+ + + + + + +

+ Declared In ASDisplayNode.h. +

+ +
+ +
ASInterfaceStateDisplay
+
+ + +

The element is very likely to become visible, and concurrent rendering should be executed for any -setNeedsDisplay.

+ + + + + + +

+ Declared In ASDisplayNode.h. +

+ +
+ +
ASInterfaceStateVisible
+
+ + +

The element is physically onscreen by at least 1 pixel. + In practice, all other bit fields should also be set when this flag is set.

+ + + + + + +

+ Declared In ASDisplayNode.h. +

+ +
+ +
ASInterfaceStateInHierarchy
+
+ + +

The node is not contained in a cell but it is in a window.

+ + + + + + +

+ Declared In ASDisplayNode.h. +

+ +
+ +
+
+ + + + + + + + +
+

Declared In

+

ASDisplayNode.h

+
+ + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Constants/ASLayoutElementType.html b/docs/appledoc/Constants/ASLayoutElementType.html new file mode 100755 index 0000000000..0cf6cc9244 --- /dev/null +++ b/docs/appledoc/Constants/ASLayoutElementType.html @@ -0,0 +1,175 @@ + + + + + + ASLayoutElementType Constants Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASLayoutElementType Constants Reference

+ + +
+ + + + +
Declared inASLayoutElement.h
+ + + + + + + +

ASLayoutElementType

+ + +
+

Type of ASLayoutElement

+
+ + +
+ + +

Definition

+ typedef NS_ENUM(NSUInteger, ASLayoutElementType ) {
+ +    ASLayoutElementTypeLayoutSpec,
+ +    ASLayoutElementTypeDisplayNode,
+ + };
+ +
+ +
+

Constants

+
+ +
ASLayoutElementTypeLayoutSpec
+
+ + +

Type of ASLayoutElement

+ + + + + + +

+ Declared In ASLayoutElement.h. +

+ +
+ +
ASLayoutElementTypeDisplayNode
+
+ + +

Type of ASLayoutElement

+ + + + + + +

+ Declared In ASLayoutElement.h. +

+ +
+ +
+
+ + + + + + + + +
+

Declared In

+

ASLayoutElement.h

+
+ + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Constants/ASMapNodeShowAnnotationsOptions.html b/docs/appledoc/Constants/ASMapNodeShowAnnotationsOptions.html new file mode 100755 index 0000000000..0f7414e76e --- /dev/null +++ b/docs/appledoc/Constants/ASMapNodeShowAnnotationsOptions.html @@ -0,0 +1,177 @@ + + + + + + ASMapNodeShowAnnotationsOptions Constants Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASMapNodeShowAnnotationsOptions Constants Reference

+ + +
+ + + + +
Declared inASMapNode.h
+ + + + + + + +

ASMapNodeShowAnnotationsOptions

+ +
+ + +

Definition

+ typedef NS_OPTIONS(NSUInteger, ASMapNodeShowAnnotationsOptions ) {
+ +    ASMapNodeShowAnnotationsOptionsIgnored = 0,
+ +    ASMapNodeShowAnnotationsOptionsZoomed = 1 < < 0,
+ +    ASMapNodeShowAnnotationsOptionsAnimated = 1 < < 1,
+ + };
+ +
+ +
+

Constants

+
+ +
ASMapNodeShowAnnotationsOptionsIgnored
+
+ + +

The annotations' positions are ignored, use the region or options specified instead.

+ + + + + + +

+ Declared In ASMapNode.h. +

+ +
+ +
ASMapNodeShowAnnotationsOptionsZoomed
+
+ + +

The annotations' positions are used to calculate the region to show in the map, equivalent to showAnnotations:animated.

+ + + + + + +

+ Declared In ASMapNode.h. +

+ +
+ +
ASMapNodeShowAnnotationsOptionsAnimated
+
+ + +

This will only have an effect if combined with the Zoomed state with liveMap turned on.

+ + + + + + +

+ Declared In ASMapNode.h. +

+ +
+ +
+
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Constants/ASMultiplexImageNodeErrorCode.html b/docs/appledoc/Constants/ASMultiplexImageNodeErrorCode.html new file mode 100755 index 0000000000..3843f6a12f --- /dev/null +++ b/docs/appledoc/Constants/ASMultiplexImageNodeErrorCode.html @@ -0,0 +1,217 @@ + + + + + + ASMultiplexImageNodeErrorCode Constants Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASMultiplexImageNodeErrorCode Constants Reference

+ + +
+ + + + +
Declared inASMultiplexImageNode.h
+ + + + + + + +

ASMultiplexImageNodeErrorCode

+ + +
+

ASMultiplexImageNode error codes.

+
+ + + + +
+

Constants

+
+ +
ASMultiplexImageNodeErrorCodeNoSourceForImage
+
+ + +

Indicates that the data source didn’t provide a source for an image identifier.

+ + + + + + +

+ Declared In ASMultiplexImageNode.h. +

+ +
+ +
ASMultiplexImageNodeErrorCodeBestImageIdentifierChanged
+
+ + +

Indicates that the best image identifier changed before a download for a worse identifier began.

+ + + + + + +

+ Declared In ASMultiplexImageNode.h. +

+ +
+ +
ASMultiplexImageNodeErrorCodePhotosImageManagerFailedWithoutError
+
+ + +

Indicates that the Photos framework returned no image and no error. +This may happen if the image is in iCloud and the user did not specify allowsNetworkAccess +in their image request.

+ + + + + + +

+ Declared In ASMultiplexImageNode.h. +

+ +
+ +
ASMultiplexImageNodeErrorCodePHAssetIsUnavailable
+
+ + +

Indicates that the image node could not retrieve the PHAsset for a given asset identifier. +This typically means that the user has not given Photos framework permissions yet or the asset +has been removed from the device.

+ + + + + + +

+ Declared In ASMultiplexImageNode.h. +

+ +
+ +
+
+ + + + + + + + +
+

Declared In

+

ASMultiplexImageNode.h

+
+ + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Constants/ASRelativeDimensionType.html b/docs/appledoc/Constants/ASRelativeDimensionType.html new file mode 100755 index 0000000000..f700916c7f --- /dev/null +++ b/docs/appledoc/Constants/ASRelativeDimensionType.html @@ -0,0 +1,201 @@ + + + + + + ASRelativeDimensionType Constants Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASRelativeDimensionType Constants Reference

+ + +
+ + + + +
Declared inASDimension.h
+ + + + + + + +

ASRelativeDimensionType

+ + +
+

A dimension relative to constraints to be provided in the future. +A ASDimension can be one of three types:

+ +

“Auto” - This indicated “I have no opinion” and may be resolved in whatever way makes most sense given the circumstances.

+ +

“Points” - Just a number. It will always resolve to exactly this amount.

+ +

“Percent” - Multiplied to a provided parent amount to resolve a final amount.

+
+ + +
+ + +

Definition

+ typedef NS_ENUM(NSInteger, ASRelativeDimensionType ) {
+ +    ASRelativeDimensionTypeAuto,
+ +    ASRelativeDimensionTypePoints,
+ +    ASRelativeDimensionTypeFraction,
+ + };
+ +
+ +
+

Constants

+
+ +
ASRelativeDimensionTypeAuto
+
+ + +

This indicates “I have no opinion” and may be resolved in whatever way makes most sense given the circumstances.

+ + + + + + +

+ Declared In ASDimension.h. +

+ +
+ +
ASRelativeDimensionTypePoints
+
+ + +

Just a number. It will always resolve to exactly this amount. This is the default type.

+ + + + + + +

+ Declared In ASDimension.h. +

+ +
+ +
ASRelativeDimensionTypeFraction
+
+ + +

Multiplied to a provided parent amount to resolve a final amount.

+ + + + + + +

+ Declared In ASDimension.h. +

+ +
+ +
+
+ + + + + + + + +
+

Declared In

+

ASDimension.h

+
+ + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Constants/ASRelativeLayoutSpecPosition.html b/docs/appledoc/Constants/ASRelativeLayoutSpecPosition.html new file mode 100755 index 0000000000..3f78b744af --- /dev/null +++ b/docs/appledoc/Constants/ASRelativeLayoutSpecPosition.html @@ -0,0 +1,213 @@ + + + + + + ASRelativeLayoutSpecPosition Constants Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASRelativeLayoutSpecPosition Constants Reference

+ + +
+ + + + +
Declared inASRelativeLayoutSpec.h
+ + + + + + + +

ASRelativeLayoutSpecPosition

+ + +
+

How the child is positioned within the spec.

+
+ + +
+ + +

Definition

+ typedef NS_ENUM(NSUInteger, ASRelativeLayoutSpecPosition ) {
+ +    ASRelativeLayoutSpecPositionNone = 0,
+ +    ASRelativeLayoutSpecPositionStart = 1,
+ +    ASRelativeLayoutSpecPositionCenter = 2,
+ +    ASRelativeLayoutSpecPositionEnd = 3,
+ + };
+ +
+ +
+

Constants

+
+ +
ASRelativeLayoutSpecPositionNone
+
+ + +

The child is positioned at point 0

+ + + + + + +

+ Declared In ASRelativeLayoutSpec.h. +

+ +
+ +
ASRelativeLayoutSpecPositionStart
+
+ + +

The child is positioned at point 0 relatively to the layout axis (ie left / top most)

+ + + + + + +

+ Declared In ASRelativeLayoutSpec.h. +

+ +
+ +
ASRelativeLayoutSpecPositionCenter
+
+ + +

The child is centered along the specified axis

+ + + + + + +

+ Declared In ASRelativeLayoutSpec.h. +

+ +
+ +
ASRelativeLayoutSpecPositionEnd
+
+ + +

The child is positioned at the maximum point of the layout axis (ie right / bottom most)

+ + + + + + +

+ Declared In ASRelativeLayoutSpec.h. +

+ +
+ +
+
+ + + + + + + + +
+

Declared In

+

ASRelativeLayoutSpec.h

+
+ + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Constants/ASRelativeLayoutSpecSizingOption.html b/docs/appledoc/Constants/ASRelativeLayoutSpecSizingOption.html new file mode 100755 index 0000000000..75d49d9737 --- /dev/null +++ b/docs/appledoc/Constants/ASRelativeLayoutSpecSizingOption.html @@ -0,0 +1,213 @@ + + + + + + ASRelativeLayoutSpecSizingOption Constants Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASRelativeLayoutSpecSizingOption Constants Reference

+ + +
+ + + + +
Declared inASRelativeLayoutSpec.h
+ + + + + + + +

ASRelativeLayoutSpecSizingOption

+ + +
+

How much space the spec will take up.

+
+ + +
+ + +

Definition

+ typedef NS_OPTIONS(NSUInteger, ASRelativeLayoutSpecSizingOption ) {
+ +    ASRelativeLayoutSpecSizingOptionDefault,
+ +    ASRelativeLayoutSpecSizingOptionMinimumWidth = 1 < < 0,
+ +    ASRelativeLayoutSpecSizingOptionMinimumHeight = 1 < < 1,
+ +    ASRelativeLayoutSpecSizingOptionMinimumSize = ASRelativeLayoutSpecSizingOptionMinimumWidth | ASRelativeLayoutSpecSizingOptionMinimumHeight,
+ + };
+ +
+ +
+

Constants

+
+ +
ASRelativeLayoutSpecSizingOptionDefault
+
+ + +

The spec will take up the maximum size possible

+ + + + + + +

+ Declared In ASRelativeLayoutSpec.h. +

+ +
+ +
ASRelativeLayoutSpecSizingOptionMinimumWidth
+
+ + +

The spec will take up the minimum size possible along the X axis

+ + + + + + +

+ Declared In ASRelativeLayoutSpec.h. +

+ +
+ +
ASRelativeLayoutSpecSizingOptionMinimumHeight
+
+ + +

The spec will take up the minimum size possible along the Y axis

+ + + + + + +

+ Declared In ASRelativeLayoutSpec.h. +

+ +
+ +
ASRelativeLayoutSpecSizingOptionMinimumSize
+
+ + +

Convenience option to take up the minimum size along both the X and Y axis

+ + + + + + +

+ Declared In ASRelativeLayoutSpec.h. +

+ +
+ +
+
+ + + + + + + + +
+

Declared In

+

ASRelativeLayoutSpec.h

+
+ + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Constants/ASStackLayoutAlignItems.html b/docs/appledoc/Constants/ASStackLayoutAlignItems.html new file mode 100755 index 0000000000..7a809bbe35 --- /dev/null +++ b/docs/appledoc/Constants/ASStackLayoutAlignItems.html @@ -0,0 +1,251 @@ + + + + + + ASStackLayoutAlignItems Constants Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASStackLayoutAlignItems Constants Reference

+ + +
+ + + + +
Declared inASStackLayoutDefines.h
+ + + + + + + +

ASStackLayoutAlignItems

+ + +
+

Orientation of children along cross axis

+
+ + +
+ + +

Definition

+ typedef NS_ENUM(NSUInteger, ASStackLayoutAlignItems ) {
+ +    ASStackLayoutAlignItemsStart,
+ +    ASStackLayoutAlignItemsEnd,
+ +    ASStackLayoutAlignItemsCenter,
+ +    ASStackLayoutAlignItemsStretch,
+ +    ASStackLayoutAlignItemsBaselineFirst,
+ +    ASStackLayoutAlignItemsBaselineLast,
+ + };
+ +
+ +
+

Constants

+
+ +
ASStackLayoutAlignItemsStart
+
+ + +

Align children to start of cross axis

+ + + + + + +

+ Declared In ASStackLayoutDefines.h. +

+ +
+ +
ASStackLayoutAlignItemsEnd
+
+ + +

Align children with end of cross axis

+ + + + + + +

+ Declared In ASStackLayoutDefines.h. +

+ +
+ +
ASStackLayoutAlignItemsCenter
+
+ + +

Center children on cross axis

+ + + + + + +

+ Declared In ASStackLayoutDefines.h. +

+ +
+ +
ASStackLayoutAlignItemsStretch
+
+ + +

Expand children to fill cross axis

+ + + + + + +

+ Declared In ASStackLayoutDefines.h. +

+ +
+ +
ASStackLayoutAlignItemsBaselineFirst
+
+ + +

Children align to their first baseline. Only available for horizontal stack spec

+ + + + + + +

+ Declared In ASStackLayoutDefines.h. +

+ +
+ +
ASStackLayoutAlignItemsBaselineLast
+
+ + +

Children align to their last baseline. Only available for horizontal stack spec

+ + + + + + +

+ Declared In ASStackLayoutDefines.h. +

+ +
+ +
+
+ + + + + + + + +
+

Declared In

+

ASStackLayoutDefines.h

+
+ + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Constants/ASStackLayoutAlignSelf.html b/docs/appledoc/Constants/ASStackLayoutAlignSelf.html new file mode 100755 index 0000000000..45606fd4db --- /dev/null +++ b/docs/appledoc/Constants/ASStackLayoutAlignSelf.html @@ -0,0 +1,244 @@ + + + + + + ASStackLayoutAlignSelf Constants Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASStackLayoutAlignSelf Constants Reference

+ + +
+ + + + + + + +
Declared inASStackLayoutDefines.h
ReferencesASStackLayoutAlignItems
+ + + + + + + +

ASStackLayoutAlignSelf

+ + +
+

Each child may override their parent stack’s cross axis alignment.

+
+ + +
+ + +

Definition

+ typedef NS_ENUM(NSUInteger, ASStackLayoutAlignSelf ) {
+ +    ASStackLayoutAlignSelfAuto,
+ +    ASStackLayoutAlignSelfStart,
+ +    ASStackLayoutAlignSelfEnd,
+ +    ASStackLayoutAlignSelfCenter,
+ +    ASStackLayoutAlignSelfStretch,
+ + };
+ +
+ +
+

Constants

+
+ +
ASStackLayoutAlignSelfAuto
+
+ + +

Inherit alignment value from containing stack.

+ + + + + + +

+ Declared In ASStackLayoutDefines.h. +

+ +
+ +
ASStackLayoutAlignSelfStart
+
+ + +

Align to start of cross axis

+ + + + + + +

+ Declared In ASStackLayoutDefines.h. +

+ +
+ +
ASStackLayoutAlignSelfEnd
+
+ + +

Align with end of cross axis

+ + + + + + +

+ Declared In ASStackLayoutDefines.h. +

+ +
+ +
ASStackLayoutAlignSelfCenter
+
+ + +

Center on cross axis

+ + + + + + +

+ Declared In ASStackLayoutDefines.h. +

+ +
+ +
ASStackLayoutAlignSelfStretch
+
+ + +

Expand to fill cross axis

+ + + + + + +

+ Declared In ASStackLayoutDefines.h. +

+ +
+ +
+
+ + + + + + +
+

See Also

+ +
+ + + +
+

Declared In

+

ASStackLayoutDefines.h

+
+ + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Constants/ASStackLayoutDirection.html b/docs/appledoc/Constants/ASStackLayoutDirection.html new file mode 100755 index 0000000000..ac22df4f8b --- /dev/null +++ b/docs/appledoc/Constants/ASStackLayoutDirection.html @@ -0,0 +1,175 @@ + + + + + + ASStackLayoutDirection Constants Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASStackLayoutDirection Constants Reference

+ + +
+ + + + +
Declared inASStackLayoutDefines.h
+ + + + + + + +

ASStackLayoutDirection

+ + +
+

The direction children are stacked in

+
+ + +
+ + +

Definition

+ typedef NS_ENUM(NSUInteger, ASStackLayoutDirection ) {
+ +    ASStackLayoutDirectionVertical,
+ +    ASStackLayoutDirectionHorizontal,
+ + };
+ +
+ +
+

Constants

+
+ +
ASStackLayoutDirectionVertical
+
+ + +

Children are stacked vertically

+ + + + + + +

+ Declared In ASStackLayoutDefines.h. +

+ +
+ +
ASStackLayoutDirectionHorizontal
+
+ + +

Children are stacked horizontally

+ + + + + + +

+ Declared In ASStackLayoutDefines.h. +

+ +
+ +
+
+ + + + + + + + +
+

Declared In

+

ASStackLayoutDefines.h

+
+ + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Constants/ASStackLayoutJustifyContent.html b/docs/appledoc/Constants/ASStackLayoutJustifyContent.html new file mode 100755 index 0000000000..946d43e0dd --- /dev/null +++ b/docs/appledoc/Constants/ASStackLayoutJustifyContent.html @@ -0,0 +1,242 @@ + + + + + + ASStackLayoutJustifyContent Constants Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASStackLayoutJustifyContent Constants Reference

+ + +
+ + + + +
Declared inASStackLayoutDefines.h
+ + + + + + + +

ASStackLayoutJustifyContent

+ + +
+

If no children are flexible, how should this spec justify its children in the available space?

+
+ + +
+ + +

Definition

+ typedef NS_ENUM(NSUInteger, ASStackLayoutJustifyContent ) {
+ +    ASStackLayoutJustifyContentStart,
+ +    ASStackLayoutJustifyContentCenter,
+ +    ASStackLayoutJustifyContentEnd,
+ +    ASStackLayoutJustifyContentSpaceBetween,
+ +    ASStackLayoutJustifyContentSpaceAround,
+ + };
+ +
+ +
+

Constants

+
+ +
ASStackLayoutJustifyContentStart
+
+ + +

On overflow, children overflow out of this spec’s bounds on the right/bottom side. + On underflow, children are left/top-aligned within this spec’s bounds.

+ + + + + + +

+ Declared In ASStackLayoutDefines.h. +

+ +
+ +
ASStackLayoutJustifyContentCenter
+
+ + +

On overflow, children are centered and overflow on both sides. + On underflow, children are centered within this spec’s bounds in the stacking direction.

+ + + + + + +

+ Declared In ASStackLayoutDefines.h. +

+ +
+ +
ASStackLayoutJustifyContentEnd
+
+ + +

On overflow, children overflow out of this spec’s bounds on the left/top side. + On underflow, children are right/bottom-aligned within this spec’s bounds.

+ + + + + + +

+ Declared In ASStackLayoutDefines.h. +

+ +
+ +
ASStackLayoutJustifyContentSpaceBetween
+
+ + +

On overflow or if the stack has only 1 child, this value is identical to ASStackLayoutJustifyContentStart. + Otherwise, the starting edge of the first child is at the starting edge of the stack, + the ending edge of the last child is at the ending edge of the stack, and the remaining children + are distributed so that the spacing between any two adjacent ones is the same. + If there is a remaining space after spacing division, it is combined with the last spacing (i.e the one between the last 2 children).

+ + + + + + +

+ Declared In ASStackLayoutDefines.h. +

+ +
+ +
ASStackLayoutJustifyContentSpaceAround
+
+ + +

On overflow or if the stack has only 1 child, this value is identical to ASStackLayoutJustifyContentCenter. + Otherwise, children are distributed such that the spacing between any two adjacent ones is the same, + and the spacing between the first/last child and the stack edges is half the size of the spacing between children. + If there is a remaining space after spacing division, it is combined with the last spacing (i.e the one between the last child and the stack ending edge).

+ + + + + + +

+ Declared In ASStackLayoutDefines.h. +

+ +
+ +
+
+ + + + + + + + +
+

Declared In

+

ASStackLayoutDefines.h

+
+ + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Constants/ASTextNodeHighlightStyle.html b/docs/appledoc/Constants/ASTextNodeHighlightStyle.html new file mode 100755 index 0000000000..e47cd046cf --- /dev/null +++ b/docs/appledoc/Constants/ASTextNodeHighlightStyle.html @@ -0,0 +1,175 @@ + + + + + + ASTextNodeHighlightStyle Constants Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASTextNodeHighlightStyle Constants Reference

+ + +
+ + + + +
Declared inASTextNode.h
+ + + + + + + +

ASTextNodeHighlightStyle

+ + +
+

Highlight styles.

+
+ + +
+ + +

Definition

+ typedef NS_ENUM(NSUInteger, ASTextNodeHighlightStyle ) {
+ +    ASTextNodeHighlightStyleLight,
+ +    ASTextNodeHighlightStyleDark,
+ + };
+ +
+ +
+

Constants

+
+ +
ASTextNodeHighlightStyleLight
+
+ + +

Highlight style for text on a light background.

+ + + + + + +

+ Declared In ASTextNode.h. +

+ +
+ +
ASTextNodeHighlightStyleDark
+
+ + +

Highlight style for text on a dark background.

+ + + + + + +

+ Declared In ASTextNode.h. +

+ +
+ +
+
+ + + + + + + + +
+

Declared In

+

ASTextNode.h

+
+ + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Constants/ASVerticalAlignment.html b/docs/appledoc/Constants/ASVerticalAlignment.html new file mode 100755 index 0000000000..640a47dc9b --- /dev/null +++ b/docs/appledoc/Constants/ASVerticalAlignment.html @@ -0,0 +1,270 @@ + + + + + + ASVerticalAlignment Constants Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASVerticalAlignment Constants Reference

+ + +
+ + + + +
Declared inASStackLayoutDefines.h
+ + + + + + + +

ASVerticalAlignment

+ + +
+

Orientation of children along vertical axis

+
+ + +
+ + +

Definition

+ typedef NS_ENUM(NSUInteger, ASVerticalAlignment ) {
+ +    ASVerticalAlignmentNone,
+ +    ASVerticalAlignmentTop,
+ +    ASVerticalAlignmentCenter,
+ +    ASVerticalAlignmentBottom,
+ +    ASAlignmentTop = ASVerticalAlignmentTop,
+ +    ASAlignmentCenter = ASVerticalAlignmentCenter,
+ +    ASAlignmentBottom = ASVerticalAlignmentBottom,
+ + };
+ +
+ +
+

Constants

+
+ +
ASVerticalAlignmentNone
+
+ + +

No alignment specified. Default value

+ + + + + + +

+ Declared In ASStackLayoutDefines.h. +

+ +
+ +
ASVerticalAlignmentTop
+
+ + +

Top aligned

+ + + + + + +

+ Declared In ASStackLayoutDefines.h. +

+ +
+ +
ASVerticalAlignmentCenter
+
+ + +

Center aligned

+ + + + + + +

+ Declared In ASStackLayoutDefines.h. +

+ +
+ +
ASVerticalAlignmentBottom
+
+ + +

Bottom aligned

+ + + + + + +

+ Declared In ASStackLayoutDefines.h. +

+ +
+ +
ASAlignmentTop
+
+ + +

Use ASVerticalAlignmentTop instead (Deprecated: Use ASVerticalAlignmentTop instead)

+ + + + + + +

+ Declared In ASStackLayoutDefines.h. +

+ +
+ +
ASAlignmentCenter
+
+ + +

Use ASVerticalAlignmentCenter instead (Deprecated: Use ASVerticalAlignmentCenter instead)

+ + + + + + +

+ Declared In ASStackLayoutDefines.h. +

+ +
+ +
ASAlignmentBottom
+
+ + +

Use ASVerticalAlignmentBottom instead (Deprecated: Use ASVerticalAlignmentBottom instead)

+ + + + + + +

+ Declared In ASStackLayoutDefines.h. +

+ +
+ +
+
+ + + + + + + + +
+

Declared In

+

ASStackLayoutDefines.h

+
+ + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Protocols/ASAbsoluteLayoutElement.html b/docs/appledoc/Protocols/ASAbsoluteLayoutElement.html new file mode 100755 index 0000000000..ecaff9596c --- /dev/null +++ b/docs/appledoc/Protocols/ASAbsoluteLayoutElement.html @@ -0,0 +1,180 @@ + + + + + + ASAbsoluteLayoutElement Protocol Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASAbsoluteLayoutElement Protocol Reference

+ + +
+ + + + +
Declared inASAbsoluteLayoutElement.h
+ + + + +
+ +

Overview

+

Layout options that can be defined for an ASLayoutElement being added to a ASAbsoluteLayoutSpec.

+
+ + + + + +
+ + + + + + +
+
+ +

  layoutPosition +required method

+ +
+
+ +
+ + +
+

The position of this object within its parent spec.

+
+ + + +
@property (nonatomic, assign) CGPoint layoutPosition
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASAbsoluteLayoutElement.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Protocols/ASCellNodeInteractionDelegate.html b/docs/appledoc/Protocols/ASCellNodeInteractionDelegate.html new file mode 100755 index 0000000000..70d2a92642 --- /dev/null +++ b/docs/appledoc/Protocols/ASCellNodeInteractionDelegate.html @@ -0,0 +1,198 @@ + + + + + + ASCellNodeInteractionDelegate Protocol Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASCellNodeInteractionDelegate Protocol Reference

+ + +
+ + + + + + + +
Conforms toNSObject
Declared inASCellNode+Internal.h
+ + + + + + +
+ + + + + + +
+
+ +

– nodeDidRelayout:sizeChanged: +required method

+ +
+
+ +
+ + +
+

Notifies the delegate that the specified cell node has done a relayout. +The notification is done on main thread.

+
+ + + +
- (void)nodeDidRelayout:(ASCellNode *)node sizeChanged:(BOOL)sizeChanged
+ + + +
+

Parameters

+ + + + + + + + + + + + +
node

A node informing the delegate about the relayout.

sizeChanged

YES if the node’s calculatedSize changed during the relayout, NO otherwise.

+
+ + + + + + + +
+

Discussion

+

This will not be called due to measurement passes before the node has loaded +its view, even if triggered by -setNeedsLayout, as it is assumed these are +not relevant to UIKit. Indeed, these calls can cause consistency issues.

+
+ + + + + + + +
+

Declared In

+

ASCellNode+Internal.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Protocols/ASCollectionDataSource.html b/docs/appledoc/Protocols/ASCollectionDataSource.html new file mode 100755 index 0000000000..1e4b1ec080 --- /dev/null +++ b/docs/appledoc/Protocols/ASCollectionDataSource.html @@ -0,0 +1,807 @@ + + + + + + ASCollectionDataSource Protocol Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASCollectionDataSource Protocol Reference

+ + +
+ + + + + + + +
Conforms toASCommonCollectionDataSource
Declared inASCollectionNode.h
+ + + + +
+ +

Overview

+

This is a node-based UICollectionViewDataSource.

+
+ + + + + +
+ + + + + + +
+
+ +

– collectionNode:numberOfItemsInSection: +

+ +
+
+ +
+ + +
+

Asks the data source for the number of items in the given section of the collection node.

+
+ + + +
- (NSInteger)collectionNode:(ASCollectionNode *)collectionNode numberOfItemsInSection:(NSInteger)section
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

– numberOfSectionsInCollectionNode: +

+ +
+
+ +
+ + +
+

Asks the data source for the number of sections in the collection node.

+
+ + + +
- (NSInteger)numberOfSectionsInCollectionNode:(ASCollectionNode *)collectionNode
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

– collectionNode:nodeBlockForItemAtIndexPath: +

+ +
+
+ +
+ + +
+

Similar to -collectionNode:nodeForItemAtIndexPath: +This method takes precedence over collectionNode:nodeForItemAtIndexPath: if implemented.

+
+ + + +
- (ASCellNodeBlock)collectionNode:(ASCollectionNode *)collectionNode nodeBlockForItemAtIndexPath:(NSIndexPath *)indexPath
+ + + +
+

Parameters

+ + + + + + + + + + + + +
collectionNode

The sender.

indexPath

The index path of the item.

+
+ + + +
+

Return Value

+

a block that creates the node for display for this item. +Must be thread-safe (can be called on the main thread or a background +queue) and should not implement reuse (it will be called once per row).

+
+ + + + + + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

– collectionNode:nodeForItemAtIndexPath: +

+ +
+
+ +
+ + +
+

Similar to -collectionView:cellForItemAtIndexPath:.

+
+ + + +
- (ASCellNode *)collectionNode:(ASCollectionNode *)collectionNode nodeForItemAtIndexPath:(NSIndexPath *)indexPath
+ + + +
+

Parameters

+ + + + + + + + + + + + +
indexPath

The index path of the item.

collectionView

The sender.

+
+ + + +
+

Return Value

+

A node to display for the given item. This will be called on the main thread and should +not implement reuse (it will be called once per item). Unlike UICollectionView’s version, +this method is not called when the item is about to display.

+
+ + + + + + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

– collectionNode:nodeForSupplementaryElementOfKind:atIndexPath: +

+ +
+
+ +
+ + +
+

Asks the data source to provide a node to display for the given supplementary element in the collection view.

+
+ + + +
- (ASCellNode *)collectionNode:(ASCollectionNode *)collectionNode nodeForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + +
collectionNode

The sender.

kind

The kind of supplementary element.

indexPath

The index path of the supplementary element.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

– collectionNode:contextForSection: +

+ +
+
+ +
+ + +
+

Asks the data source to provide a context object for the given section. This object +can later be retrieved by calling @c contextForSection: and is useful when implementing +custom @c UICollectionViewLayout subclasses. The context object is ret

+
+ + + +
- (nullable id<ASSectionContext>)collectionNode:(ASCollectionNode *)collectionNode contextForSection:(NSInteger)section
+ + + +
+

Parameters

+ + + + + + + + + + + + +
collectionNode

The sender.

section

The index of the section to provide context for.

+
+ + + +
+

Return Value

+

A context object to assign to the given section, or @c nil.

+
+ + + + + + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

– collectionView:nodeForItemAtIndexPath: +

+ +
+
+ +
+ + +
+

Similar to -collectionView:cellForItemAtIndexPath:.

+
+ + + +
- (ASCellNode *)collectionView:(ASCollectionView *)collectionView nodeForItemAtIndexPath:(NSIndexPath *)indexPath
+ + + +
+

Parameters

+ + + + + + + + + + + + +
collectionView

The sender.

indexPath

The index path of the requested node.

+
+ + + +
+

Return Value

+

a node for display at this indexpath. This will be called on the main thread and should +not implement reuse (it will be called once per row). Unlike UICollectionView’s version, +this method is not called when the row is about to display.

+
+ + + + + + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

– collectionView:nodeBlockForItemAtIndexPath: +

+ +
+
+ +
+ + +
+

Similar to -collectionView:nodeForItemAtIndexPath: +This method takes precedence over collectionView:nodeForItemAtIndexPath: if implemented.

+
+ + + +
- (ASCellNodeBlock)collectionView:(ASCollectionView *)collectionView nodeBlockForItemAtIndexPath:(NSIndexPath *)indexPath
+ + + +
+

Parameters

+ + + + + + + + + + + + +
collectionView

The sender.

indexPath

The index path of the requested node.

+
+ + + +
+

Return Value

+

a block that creates the node for display at this indexpath. +Must be thread-safe (can be called on the main thread or a background +queue) and should not implement reuse (it will be called once per row).

+
+ + + + + + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

– collectionView:nodeForSupplementaryElementOfKind:atIndexPath: +

+ +
+
+ +
+ + +
+

Asks the collection view to provide a supplementary node to display in the collection view.

+
+ + + +
- (ASCellNode *)collectionView:(ASCollectionView *)collectionView nodeForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + +
collectionView

An object representing the collection view requesting this information.

kind

The kind of supplementary node to provide.

indexPath

The index path that specifies the location of the new supplementary node.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

– collectionViewLockDataSource: +

+ +
+
+ +
+ + +
+

Indicator to lock the data source for data fetching in async mode. +We should not update the data source until the data source has been unlocked. Otherwise, it will incur data inconsistency or exception +due to the data access in async mode. (Deprecated: The data source is always accessed on the main thread, and this method will not be called.)

+
+ + + +
- (void)collectionViewLockDataSource:(ASCollectionView *)collectionView
+ + + +
+

Parameters

+ + + + + + + +
collectionView

The sender.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

– collectionViewUnlockDataSource: +

+ +
+
+ +
+ + +
+

Indicator to unlock the data source for data fetching in async mode. +We should not update the data source until the data source has been unlocked. Otherwise, it will incur data inconsistency or exception +due to the data access in async mode. (Deprecated: The data source is always accessed on the main thread, and this method will not be called.)

+
+ + + +
- (void)collectionViewUnlockDataSource:(ASCollectionView *)collectionView
+ + + +
+

Parameters

+ + + + + + + +
collectionView

The sender.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Protocols/ASCollectionDelegate.html b/docs/appledoc/Protocols/ASCollectionDelegate.html new file mode 100755 index 0000000000..8b40171190 --- /dev/null +++ b/docs/appledoc/Protocols/ASCollectionDelegate.html @@ -0,0 +1,685 @@ + + + + + + ASCollectionDelegate Protocol Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASCollectionDelegate Protocol Reference

+ + +
+ + + + + + + +
Conforms toASCommonCollectionDelegate
NSObject
Declared inASCollectionNode.h
+ + + + +
+ +

Overview

+

This is a node-based UICollectionViewDelegate.

+
+ + + + + +
+ + + + + + +
+
+ +

– collectionNode:constrainedSizeForItemAtIndexPath: +

+ +
+
+ +
+ + +
+

Provides the constrained size range for measuring the given item.

+
+ + + +
- (ASSizeRange)collectionNode:(ASCollectionNode *)collectionNode constrainedSizeForItemAtIndexPath:(NSIndexPath *)indexPath
+ + + +
+

Parameters

+ + + + + + + + + + + + +
collectionNode

The sender.

indexPath

The index path of the item.

+
+ + + +
+

Return Value

+

A constrained size range for layout for the item at this index path.

+
+ + + + + + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

– collectionNode:willBeginBatchFetchWithContext: +

+ +
+
+ +
+ + +
+

Receive a message that the collection node is near the end of its data set and more data should be fetched if +necessary.

+
+ + + +
- (void)collectionNode:(ASCollectionNode *)collectionNode willBeginBatchFetchWithContext:(ASBatchContext *)context
+ + + +
+

Parameters

+ + + + + + + + + + + + +
collectionNode

The sender.

context

A context object that must be notified when the batch fetch is completed.

+
+ + + + + + + +
+

Discussion

+

You must eventually call -completeBatchFetching: with an argument of YES in order to receive future +notifications to do batch fetches. This method is called on a background queue.

+ +

ASCollectionNode currently only supports batch events for tail loads. If you require a head load, consider +implementing a UIRefreshControl.

+
+ + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

– shouldBatchFetchForCollectionNode: +

+ +
+
+ +
+ + +
+

Tell the collection node if batch fetching should begin.

+
+ + + +
- (BOOL)shouldBatchFetchForCollectionNode:(ASCollectionNode *)collectionNode
+ + + +
+

Parameters

+ + + + + + + +
collectionNode

The sender.

+
+ + + + + + + +
+

Discussion

+

Use this method to conditionally fetch batches. Example use cases are: limiting the total number of +objects that can be fetched or no network connection.

+ +

If not implemented, the collection node assumes that it should notify its asyncDelegate when batch fetching +should occur.

+
+ + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

– collectionView:constrainedSizeForNodeAtIndexPath: +

+ +
+
+ +
+ + +
+

Provides the constrained size range for measuring the node at the index path.

+
+ + + +
- (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath
+ + + +
+

Parameters

+ + + + + + + + + + + + +
collectionView

The sender.

indexPath

The index path of the node.

+
+ + + +
+

Return Value

+

A constrained size range for layout the node at this index path.

+
+ + + + + + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

– collectionView:willDisplayNode:forItemAtIndexPath: +

+ +
+
+ +
+ + +
+

Informs the delegate that the collection view will add the given node +at the given index path to the view hierarchy.

+
+ + + +
- (void)collectionView:(ASCollectionView *)collectionView willDisplayNode:(ASCellNode *)node forItemAtIndexPath:(NSIndexPath *)indexPath
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + +
collectionView

The sender.

node

The node that will be displayed.

indexPath

The index path of the item that will be displayed.

+
+ + + + + + + +
+

Discussion

+

Warning: AsyncDisplayKit processes collection view edits asynchronously. The index path +passed into this method may not correspond to the same item in your data source +if your data source has been updated since the last edit was processed.

+
+ + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

– collectionView:didEndDisplayingNode:forItemAtIndexPath: +

+ +
+
+ +
+ + +
+

Informs the delegate that the collection view did remove the provided node from the view hierarchy. +This may be caused by the node scrolling out of view, or by deleting the item +or its containing section with @c deleteItemsAtIndexPaths: or @c deleteSections: .

+
+ + + +
- (void)collectionView:(ASCollectionView *)collectionView didEndDisplayingNode:(ASCellNode *)node forItemAtIndexPath:(NSIndexPath *)indexPath
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + +
collectionView

The sender.

node

The node which was removed from the view hierarchy.

indexPath

The index path at which the node was located before it was removed.

+
+ + + + + + + +
+

Discussion

+

Warning: AsyncDisplayKit processes collection view edits asynchronously. The index path +passed into this method may not correspond to the same item in your data source +if your data source has been updated since the last edit was processed.

+
+ + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

– shouldBatchFetchForCollectionView: +

+ +
+
+ +
+ + +
+

Tell the collectionView if batch fetching should begin.

+
+ + + +
- (BOOL)shouldBatchFetchForCollectionView:(ASCollectionView *)collectionView
+ + + +
+

Parameters

+ + + + + + + +
collectionView

The sender.

+
+ + + + + + + +
+

Discussion

+

Use this method to conditionally fetch batches. Example use cases are: limiting the total number of +objects that can be fetched or no network connection.

+ +

If not implemented, the collectionView assumes that it should notify its asyncDelegate when batch fetching +should occur.

+
+ + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+ +

– collectionView:willDisplayNodeForItemAtIndexPath: +

+ +
+
+ +
+ + +
+

Informs the delegate that the collection view will add the node +at the given index path to the view hierarchy.

+
+ + + +
- (void)collectionView:(ASCollectionView *)collectionView willDisplayNodeForItemAtIndexPath:(NSIndexPath *)indexPath
+ + + +
+

Parameters

+ + + + + + + + + + + + +
collectionView

The sender.

indexPath

The index path of the item that will be displayed.

+
+ + + + + + + +
+

Discussion

+

Warning: AsyncDisplayKit processes collection view edits asynchronously. The index path +passed into this method may not correspond to the same item in your data source +if your data source has been updated since the last edit was processed.

+ +

This method is deprecated. Use @c collectionView:willDisplayNode:forItemAtIndexPath: instead.

+
+ + + + + + + +
+

Declared In

+

ASCollectionNode.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Protocols/ASCollectionViewDelegateFlowLayout.html b/docs/appledoc/Protocols/ASCollectionViewDelegateFlowLayout.html new file mode 100755 index 0000000000..2185f480bd --- /dev/null +++ b/docs/appledoc/Protocols/ASCollectionViewDelegateFlowLayout.html @@ -0,0 +1,286 @@ + + + + + + ASCollectionViewDelegateFlowLayout Protocol Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASCollectionViewDelegateFlowLayout Protocol Reference

+ + +
+ + + + + + + +
Conforms toASCollectionDelegate
Declared inASCollectionView.h
+ + + + +
+ +

Overview

+

Defines methods that let you coordinate with a UICollectionViewFlowLayout in combination with an ASCollectionView.

+
+ + + + + +
+ + + + + + +
+
+ +

– collectionView:layout:insetForSectionAtIndex: +

+ +
+
+ +
+ + +
+

This method is deprecated and does nothing from 1.9.7 and up +Previously it applies the section inset to every cells within the corresponding section. +The expected behavior is to apply the section inset to the whole section rather than +shrinking each cell individually. +If you want this behavior, you can integrate your insets calculation into +constrainedSizeForNodeAtIndexPath +please file a github issue if you would like this to be restored.

+
+ + + +
- (UIEdgeInsets)collectionView:(ASCollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section
+ + + + + + + + + +
+

Discussion

+

This method is deprecated and does nothing from 1.9.7 and up +Previously it applies the section inset to every cells within the corresponding section. +The expected behavior is to apply the section inset to the whole section rather than +shrinking each cell individually. +If you want this behavior, you can integrate your insets calculation into +constrainedSizeForNodeAtIndexPath +please file a github issue if you would like this to be restored.

+
+ + + + + + + +
+

Declared In

+

ASCollectionView.h

+
+ + +
+
+
+ +

– collectionView:layout:referenceSizeForHeaderInSection: +

+ +
+
+ +
+ + +
+

Asks the delegate for the size of the header in the specified section.

+
+ + + +
- (CGSize)collectionView:(ASCollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASCollectionView.h

+
+ + +
+
+
+ +

– collectionView:layout:referenceSizeForFooterInSection: +

+ +
+
+ +
+ + +
+

Asks the delegate for the size of the footer in the specified section.

+
+ + + +
- (CGSize)collectionView:(ASCollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASCollectionView.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Protocols/ASCollectionViewLayoutFacilitatorProtocol.html b/docs/appledoc/Protocols/ASCollectionViewLayoutFacilitatorProtocol.html new file mode 100755 index 0000000000..946ec7bdcd --- /dev/null +++ b/docs/appledoc/Protocols/ASCollectionViewLayoutFacilitatorProtocol.html @@ -0,0 +1,308 @@ + + + + + + ASCollectionViewLayoutFacilitatorProtocol Protocol Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASCollectionViewLayoutFacilitatorProtocol Protocol Reference

+ + +
+ + + + + + + +
Conforms toNSObject
Declared inASCollectionViewLayoutFacilitatorProtocol.h
+ + + + +
+ +

Overview

+

This facilitator protocol is intended to help Layout to better +gel with the CollectionView

+
+ + + + + +
+ + + + + + +
+
+ +

– collectionViewWillEditCellsAtIndexPaths:batched: +required method

+ +
+
+ +
+ + +
+

Inform that the collectionView is editing the cells at a list of indexPaths

+
+ + + +
- (void)collectionViewWillEditCellsAtIndexPaths:(NSArray *)indexPaths batched:(BOOL)isBatched
+ + + +
+

Parameters

+ + + + + + + + + + + + +
indexPaths

an array of NSIndexPath objects of cells being/will be edited.

isBatched

indicates whether the editing operation will be batched by the collectionView

+ +

NOTE: when isBatched, used in combination with -collectionViewWillPerformBatchUpdates

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASCollectionViewLayoutFacilitatorProtocol.h

+
+ + +
+
+
+ +

– collectionViewWillEditSectionsAtIndexSet:batched: +required method

+ +
+
+ +
+ + +
+

Inform that the collectionView is editing the sections at a set of indexes

+
+ + + +
- (void)collectionViewWillEditSectionsAtIndexSet:(NSIndexSet *)indexes batched:(BOOL)batched
+ + + +
+

Parameters

+ + + + + + + + + + + + +
indexes

an NSIndexSet of section indexes being/will be edited.

batched

indicates whether the editing operation will be batched by the collectionView

+ +

NOTE: when batched, used in combination with -collectionViewWillPerformBatchUpdates

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASCollectionViewLayoutFacilitatorProtocol.h

+
+ + +
+
+
+ +

– collectionViewWillPerformBatchUpdates +required method

+ +
+
+ +
+ + +
+

Informs the delegate that the collectionView is about to call performBatchUpdates

+
+ + + +
- (void)collectionViewWillPerformBatchUpdates
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASCollectionViewLayoutFacilitatorProtocol.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Protocols/ASCommonCollectionDataSource.html b/docs/appledoc/Protocols/ASCommonCollectionDataSource.html new file mode 100755 index 0000000000..0fef7a7747 --- /dev/null +++ b/docs/appledoc/Protocols/ASCommonCollectionDataSource.html @@ -0,0 +1,121 @@ + + + + + + ASCommonCollectionDataSource Protocol Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASCommonCollectionDataSource Protocol Reference

+ + +
+ + + + + + + +
Conforms toNSObject
Declared inASCollectionViewProtocols.h
+ + + + +
+ +

Overview

+

This is a subset of UICollectionViewDataSource.

+
+ + + + + + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Protocols/ASCommonCollectionDelegate.html b/docs/appledoc/Protocols/ASCommonCollectionDelegate.html new file mode 100755 index 0000000000..8901487e47 --- /dev/null +++ b/docs/appledoc/Protocols/ASCommonCollectionDelegate.html @@ -0,0 +1,121 @@ + + + + + + ASCommonCollectionDelegate Protocol Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASCommonCollectionDelegate Protocol Reference

+ + +
+ + + + + + + +
Conforms toNSObject
UIScrollViewDelegate
Declared inASCollectionViewProtocols.h
+ + + + +
+ +

Overview

+

This is a subset of UICollectionViewDelegate.

+
+ + + + + + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Protocols/ASCommonTableDataSource.html b/docs/appledoc/Protocols/ASCommonTableDataSource.html new file mode 100755 index 0000000000..9068e3ecab --- /dev/null +++ b/docs/appledoc/Protocols/ASCommonTableDataSource.html @@ -0,0 +1,121 @@ + + + + + + ASCommonTableDataSource Protocol Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASCommonTableDataSource Protocol Reference

+ + +
+ + + + + + + +
Conforms toNSObject
Declared inASTableViewProtocols.h
+ + + + +
+ +

Overview

+

This is a subset of UITableViewDataSource.

+
+ + + + + + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Protocols/ASCommonTableViewDelegate.html b/docs/appledoc/Protocols/ASCommonTableViewDelegate.html new file mode 100755 index 0000000000..4032159da3 --- /dev/null +++ b/docs/appledoc/Protocols/ASCommonTableViewDelegate.html @@ -0,0 +1,121 @@ + + + + + + ASCommonTableViewDelegate Protocol Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASCommonTableViewDelegate Protocol Reference

+ + +
+ + + + + + + +
Conforms toNSObject
UIScrollViewDelegate
Declared inASTableViewProtocols.h
+ + + + +
+ +

Overview

+

This is a subset of UITableViewDelegate.

+
+ + + + + + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Protocols/ASContextTransitioning.html b/docs/appledoc/Protocols/ASContextTransitioning.html new file mode 100755 index 0000000000..dde53dd1e5 --- /dev/null +++ b/docs/appledoc/Protocols/ASContextTransitioning.html @@ -0,0 +1,532 @@ + + + + + + ASContextTransitioning Protocol Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASContextTransitioning Protocol Reference

+ + +
+ + + + + + + +
Conforms toNSObject
Declared inASContextTransitioning.h
+ + + + + + +
+ + + + + + +
+
+ +

– isAnimated +required method

+ +
+
+ +
+ + +
+

Defines if the given transition is animated

+
+ + + +
- (BOOL)isAnimated
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASContextTransitioning.h

+
+ + +
+
+
+ +

– layoutForKey: +required method

+ +
+
+ +
+ + +
+

Retrieve either the “from” or “to” layout

+
+ + + +
- (nullable ASLayout *)layoutForKey:(NSString *)key
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASContextTransitioning.h

+
+ + +
+
+
+ +

– constrainedSizeForKey: +required method

+ +
+
+ +
+ + +
+

Retrieve either the “from” or “to” constrainedSize

+
+ + + +
- (ASSizeRange)constrainedSizeForKey:(NSString *)key
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASContextTransitioning.h

+
+ + +
+
+
+ +

– subnodesForKey: +required method

+ +
+
+ +
+ + +
+

Retrieve the subnodes from either the “from” or “to” layout

+
+ + + +
- (NSArray<ASDisplayNode*> *)subnodesForKey:(NSString *)key
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASContextTransitioning.h

+
+ + +
+
+
+ +

– insertedSubnodes +required method

+ +
+
+ +
+ + +
+

Subnodes that have been inserted in the layout transition

+
+ + + +
- (NSArray<ASDisplayNode*> *)insertedSubnodes
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASContextTransitioning.h

+
+ + +
+
+
+ +

– removedSubnodes +required method

+ +
+
+ +
+ + +
+

Subnodes that will be removed in the layout transition

+
+ + + +
- (NSArray<ASDisplayNode*> *)removedSubnodes
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASContextTransitioning.h

+
+ + +
+
+
+ +

– initialFrameForNode: +required method

+ +
+
+ +
+ + +
+

The frame for the given node before the transition began.

+
+ + + +
- (CGRect)initialFrameForNode:(ASDisplayNode *)node
+ + + + + + + + + +
+

Discussion

+

Returns CGRectNull if the node was not in the hierarchy before the transition.

+
+ + + + + + + +
+

Declared In

+

ASContextTransitioning.h

+
+ + +
+
+
+ +

– finalFrameForNode: +required method

+ +
+
+ +
+ + +
+

The frame for the given node when the transition completes.

+
+ + + +
- (CGRect)finalFrameForNode:(ASDisplayNode *)node
+ + + + + + + + + +
+

Discussion

+

Returns CGRectNull if the node is no longer in the hierarchy after the transition.

+
+ + + + + + + +
+

Declared In

+

ASContextTransitioning.h

+
+ + +
+
+
+ +

– completeTransition: +required method

+ +
+
+ +
+ + +
+

Invoke this method when the transition is completed in animateLayoutTransition:

+
+ + + +
- (void)completeTransition:(BOOL)didComplete
+ + + + + + + + + +
+

Discussion

+

Passing NO to didComplete will set the original layout as the new layout.

+
+ + + + + + + +
+

Declared In

+

ASContextTransitioning.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Protocols/ASEditableTextNodeDelegate.html b/docs/appledoc/Protocols/ASEditableTextNodeDelegate.html new file mode 100755 index 0000000000..9fed893533 --- /dev/null +++ b/docs/appledoc/Protocols/ASEditableTextNodeDelegate.html @@ -0,0 +1,472 @@ + + + + + + ASEditableTextNodeDelegate Protocol Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASEditableTextNodeDelegate Protocol Reference

+ + +
+ + + + + + + +
Conforms toNSObject
Declared inASEditableTextNode.h
+ + + + +
+ +

Overview

+

The methods declared by the ASEditableTextNodeDelegate protocol allow the adopting delegate to +respond to notifications such as began and finished editing, selection changed and text updated; +and manage whether a specified text should be replaced.

+
+ + + + + +
+ + + + + + +
+
+ +

– editableTextNodeDidBeginEditing: +

+ +
+
+ +
+ + +
+

Indicates to the delegate that the text node began editing.

+
+ + + +
- (void)editableTextNodeDidBeginEditing:(ASEditableTextNode *)editableTextNode
+ + + +
+

Parameters

+ + + + + + + +
editableTextNode

An editable text node.

+
+ + + + + + + +
+

Discussion

+

The invocation of this method coincides with the keyboard animating to become visible.

+
+ + + + + + + +
+

Declared In

+

ASEditableTextNode.h

+
+ + +
+
+
+ +

– editableTextNode:shouldChangeTextInRange:replacementText: +

+ +
+
+ +
+ + +
+

Asks the delegate whether the specified text should be replaced in the editable text node.

+
+ + + +
- (BOOL)editableTextNode:(ASEditableTextNode *)editableTextNode shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + +
editableTextNode

An editable text node.

range

The current selection range. If the length of the range is 0, range reflects the current insertion point. If the user presses the Delete key, the length of the range is 1 and an empty string object replaces that single character.

text

The text to insert.

+
+ + + +
+

Return Value

+

The text node calls this method whenever the user types a new character or deletes an existing character. Implementation of this method is optional – the default implementation returns YES.

+
+ + + + + +
+

Discussion

+

YES if the old text should be replaced by the new text; NO if the replacement operation should be aborted.

+
+ + + + + + + +
+

Declared In

+

ASEditableTextNode.h

+
+ + +
+
+
+ +

– editableTextNodeDidChangeSelection:fromSelectedRange:toSelectedRange:dueToEditing: +

+ +
+
+ +
+ + +
+

Indicates to the delegate that the text node’s selection has changed.

+
+ + + +
- (void)editableTextNodeDidChangeSelection:(ASEditableTextNode *)editableTextNode fromSelectedRange:(NSRange)fromSelectedRange toSelectedRange:(NSRange)toSelectedRange dueToEditing:(BOOL)dueToEditing
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
editableTextNode

An editable text node.

fromSelectedRange

The previously selected range.

toSelectedRange

The current selected range. Equivalent to the property.

dueToEditing

YES if the selection change was due to editing; NO otherwise.

+
+ + + + + + + +
+

Discussion

+

You can access the selection of the receiver via .

+
+ + + + + + + +
+

Declared In

+

ASEditableTextNode.h

+
+ + +
+
+
+ +

– editableTextNodeDidUpdateText: +

+ +
+
+ +
+ + +
+

Indicates to the delegate that the text node’s text was updated.

+
+ + + +
- (void)editableTextNodeDidUpdateText:(ASEditableTextNode *)editableTextNode
+ + + +
+

Parameters

+ + + + + + + +
editableTextNode

An editable text node.

+
+ + + + + + + +
+

Discussion

+

This method is called each time the user updated the text node’s text. It is not called for programmatic changes made to the text via the property.

+
+ + + + + + + +
+

Declared In

+

ASEditableTextNode.h

+
+ + +
+
+
+ +

– editableTextNodeDidFinishEditing: +

+ +
+
+ +
+ + +
+

Indicates to the delegate that teh text node has finished editing.

+
+ + + +
- (void)editableTextNodeDidFinishEditing:(ASEditableTextNode *)editableTextNode
+ + + +
+

Parameters

+ + + + + + + +
editableTextNode

An editable text node.

+
+ + + + + + + +
+

Discussion

+

The invocation of this method coincides with the keyboard animating to become hidden.

+
+ + + + + + + +
+

Declared In

+

ASEditableTextNode.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Protocols/ASLayoutElement.html b/docs/appledoc/Protocols/ASLayoutElement.html new file mode 100755 index 0000000000..fc275151ba --- /dev/null +++ b/docs/appledoc/Protocols/ASLayoutElement.html @@ -0,0 +1,657 @@ + + + + + + ASLayoutElement Protocol Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASLayoutElement Protocol Reference

+ + +
+ + + + + + + +
Conforms toASEnvironment
ASLayoutElementExtensibility
ASLayoutElementPrivate
ASLayoutElementStylability
NSFastEnumeration
Declared inASLayoutElement.h
+ + + + +
+ +

Overview

+

The ASLayoutElement protocol declares a method for measuring the layout of an object. A layout +is defined by an ASLayout return value, and must specify 1) the size (but not position) of the +layoutElement object, and 2) the size and position of all of its immediate child objects. The tree +recursion is driven by parents requesting layouts from their children in order to determine their +size, followed by the parents setting the position of the children once the size is known

+ +

The protocol also implements a “family” of LayoutElement protocols. These protocols contain layout +options that can be used for specific layout specs. For example, ASStackLayoutSpec has options +defining how a layoutElement should shrink or grow based upon available space.

+ +

These layout options are all stored in an ASLayoutOptions class (that is defined in ASLayoutElementPrivate). +Generally you needn’t worry about the layout options class, as the layoutElement protocols allow all direct +access to the options via convenience properties. If you are creating custom layout spec, then you can +extend the backing layout options class to accommodate any new layout options.

+
+ + + + + +
+ + + + + + +
+
+ +

  layoutElementType +required method

+ +
+
+ +
+ + +
+

Returns type of layoutElement

+
+ + + +
@property (nonatomic, assign, readonly) ASLayoutElementType layoutElementType
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASLayoutElement.h

+
+ + +
+
+
+ +

  canLayoutAsynchronous +required method

+ +
+
+ +
+ + +
+

Returns if the layoutElement can be used to layout in an asynchronous way on a background thread.

+
+ + + +
@property (nonatomic, assign, readonly) BOOL canLayoutAsynchronous
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASLayoutElement.h

+
+ + +
+
+
+ +

  style +required method

+ +
+
+ +
+ + +
+

A size constraint that should apply to this ASLayoutElement.

+
+ + + +
@property (nonatomic, assign, readonly) ASLayoutElementStyle *style
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASLayoutElement.h

+
+ + +
+
+
+ +

  debugName +required method

+ +
+
+ +
+ + +
+

Optional name that is printed by ascii art string and displayed in description.

+
+ + + +
@property (nullable, nonatomic, copy) NSString *debugName
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASLayoutElement.h

+
+ + +
+
+
+ +

– layoutThatFits: +required method

+ +
+
+ +
+ + +
+

Asks the node to return a layout based on given size range.

+
+ + + +
- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize
+ + + +
+

Parameters

+ + + + + + + +
constrainedSize

The minimum and maximum sizes the receiver should fit in.

+
+ + + +
+

Return Value

+

An ASLayout instance defining the layout of the receiver (and its children, if the box layout model is used).

+
+ + + + + +
+

Discussion

+

Though this method does not set the bounds of the view, it does have side effects–caching both the +constraint and the result.

Warning: Subclasses must not override this; it caches results from -calculateLayoutThatFits:. Calling this method may +be expensive if result is not cached.

+
+ + + + + + + + + +
+

Declared In

+

ASLayoutElement.h

+
+ + +
+
+
+ +

– layoutThatFits:parentSize: +required method

+ +
+
+ +
+ + +
+

Call this on children layoutElements to compute their layouts within your implementation of -calculateLayoutThatFits:.

+
+ + + +
- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize parentSize:(CGSize)parentSize
+ + + +
+

Parameters

+ + + + + + + + + + + + +
constrainedSize

Specifies a minimum and maximum size. The receiver must choose a size that is in this range.

parentSize

The parent node’s size. If the parent component does not have a final size in a given dimension, +then it should be passed as ASLayoutElementParentDimensionUndefined (for example, if the parent’s width +depends on the child’s size).

+
+ + + +
+

Return Value

+

An ASLayout instance defining the layout of the receiver (and its children, if the box layout model is used).

+
+ + + + + +
+

Discussion

+

Warning: You may not override this method. Override -calculateLayoutThatFits: instead.

Warning: In almost all cases, prefer the use of ASCalculateLayout in ASLayout

Though this method does not set the bounds of the view, it does have side effects–caching both the +constraint and the result.

+
+ + + + + + + +
+

Declared In

+

ASLayoutElement.h

+
+ + +
+
+
+ +

– calculateLayoutThatFits: +required method

+ +
+
+ +
+ + +
+

Override this method to compute your layoutElement’s layout.

+
+ + + +
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
+ + + +
+

Parameters

+ + + + + + + +
constrainedSize

A min and max size. This is computed as described in the description. The ASLayout you +return MUST have a size between these two sizes.

+
+ + + + + + + +
+

Discussion

+

Why do you need to override -calculateLayoutThatFits: instead of -layoutThatFits:parentSize:? +The base implementation of -layoutThatFits:parentSize: does the following for you: +1. First, it uses the parentSize parameter to resolve the nodes’s size (the one assigned to the size property). +2. Then, it intersects the resolved size with the constrainedSize parameter. If the two don’t intersect, +constrainedSize wins. This allows a component to always override its childrens' sizes when computing its layout. +(The analogy for UIView: you might return a certain size from -sizeThatFits:, but a parent view can always override +that size and set your frame to any size.) +3. It caches it result for reuse

+
+ + + + + + + +
+

Declared In

+

ASLayoutElement.h

+
+ + +
+
+
+ +

– calculateLayoutThatFits:restrictedToSize:relativeToParentSize: +required method

+ +
+
+ +
+ + +
+

In certain advanced cases, you may want to override this method. Overriding this method allows you to receive the +layoutElement’s size, parentSize, and constrained size. With these values you could calculate the final constrained size +and call -calculateLayoutThatFits: with the result.

+
+ + + +
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize restrictedToSize:(ASLayoutElementSize)size relativeToParentSize:(CGSize)parentSize
+ + + + + + + + + +
+

Discussion

+

Warning: Overriding this method should be done VERY rarely.

+
+ + + + + + + +
+

Declared In

+

ASLayoutElement.h

+
+ + +
+
+
+ +

– measureWithSizeRange: +required method

+ +
+
+ +
+ + +
+

Calculate a layout based on given size range. (Deprecated: Deprecated in version 2.0: Use layoutThatFits: or layoutThatFits:parentSize: if used in +ASLayoutSpec subclasses)

+
+ + + +
- (nonnull ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
+ + + +
+

Parameters

+ + + + + + + +
constrainedSize

The minimum and maximum sizes the receiver should fit in.

+
+ + + +
+

Return Value

+

An ASLayout instance defining the layout of the receiver and its children.

+
+ + + + + + + + + + + +
+

Declared In

+

ASLayoutElement.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Protocols/ASLayoutElementAsciiArtProtocol.html b/docs/appledoc/Protocols/ASLayoutElementAsciiArtProtocol.html new file mode 100755 index 0000000000..c17c1fe76c --- /dev/null +++ b/docs/appledoc/Protocols/ASLayoutElementAsciiArtProtocol.html @@ -0,0 +1,225 @@ + + + + + + ASLayoutElementAsciiArtProtocol Protocol Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASLayoutElementAsciiArtProtocol Protocol Reference

+ + +
+ + + + + + + +
Conforms toNSObject
Declared inASAsciiArtBoxCreator.h
+ + + + + + +
+ + + + + + +
+
+ +

– asciiArtString +required method

+ +
+
+ +
+ + +
+

Returns an ascii-art representation of this object and its children. +For example, an ASInsetSpec may return something like this:

+
+ + + +
- (NSString *)asciiArtString
+ + + + + + + + + +
+

Discussion

+

–ASInsetLayoutSpec–

+ +

| ASTextNode |

+
+ + + + + + + +
+

Declared In

+

ASAsciiArtBoxCreator.h

+
+ + +
+
+
+ +

– asciiArtName +required method

+ +
+
+ +
+ + +
+

returns the name of this object that will display in the ascii art. Usually this can +simply be NSStringFromClass([self class]).

+
+ + + +
- (NSString *)asciiArtName
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASAsciiArtBoxCreator.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Protocols/ASLayoutElementPrivate.html b/docs/appledoc/Protocols/ASLayoutElementPrivate.html new file mode 100755 index 0000000000..01647a4d78 --- /dev/null +++ b/docs/appledoc/Protocols/ASLayoutElementPrivate.html @@ -0,0 +1,243 @@ + + + + + + ASLayoutElementPrivate Protocol Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASLayoutElementPrivate Protocol Reference

+ + +
+ + + + + + + +
Conforms toNSObject
Declared inASLayoutElementPrivate.h
+ + + + +
+ +

Overview

+

The base protocol for ASLayoutElement. Generally the methods/properties in this class do not need to be +called by the end user and are only called internally. However, there may be a case where the methods are useful.

+
+ + + + + +
+ + + + + + +
+
+ +

– finalLayoutElement +required method

+ +
+
+ +
+ + +
+

This method can be used to give the user a chance to wrap an ASLayoutElement in an ASLayoutSpec +just before it is added to a parent ASLayoutSpec. For example, if you wanted an ASTextNode that was always +inside of an ASInsetLayoutSpec, you could subclass ASTextNode and implement finalLayoutElement so that it wraps +itself in an inset spec.

+ +

Note that any ASLayoutElement other than self that is returned MUST set isFinalLayoutElement to YES. Make sure +to do this BEFORE adding a child to the ASLayoutElement.

+
+ + + +
- (id<ASLayoutElement>)finalLayoutElement
+ + + + + +
+

Return Value

+

The layoutElement that will be added to the parent layout spec. Defaults to self.

+
+ + + + + + + + + + + +
+

Declared In

+

ASLayoutElementPrivate.h

+
+ + +
+
+
+ +

  isFinalLayoutElement +required method

+ +
+
+ +
+ + +
+

A flag to indicate that this ASLayoutElement was created in finalLayoutElement. This MUST be set to YES +before adding a child to this layoutElement.

+
+ + + +
@property (nonatomic, assign) BOOL isFinalLayoutElement
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASLayoutElementPrivate.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Protocols/ASManagesChildVisibilityDepth.html b/docs/appledoc/Protocols/ASManagesChildVisibilityDepth.html new file mode 100755 index 0000000000..817097a2bd --- /dev/null +++ b/docs/appledoc/Protocols/ASManagesChildVisibilityDepth.html @@ -0,0 +1,200 @@ + + + + + + ASManagesChildVisibilityDepth Protocol Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASManagesChildVisibilityDepth Protocol Reference

+ + +
+ + + + + + + +
Conforms toASVisibilityDepth
Declared inASVisibilityProtocols.h
+ + + + +
+ +

Overview

+

ASManagesChildVisibilityDepth

A protocol which should be implemented by container view controllers to allow proper +propagation of visibility depth

+
+ + + + + +
+ + + + + + +
+
+ +

– visibilityDepthOfChildViewController: +required method

+ +
+
+ +
+ + +
+

Container view controllers should adopt this protocol to indicate that they will manage their child’s +visibilityDepth. For example, ASNavigationController adopts this protocol and manages its childrens visibility +depth.

+ +

If you adopt this protocol, you must also emit visibilityDepthDidChange messages to child view controllers.

+
+ + + +
- (NSInteger)visibilityDepthOfChildViewController:(UIViewController *)childViewController
+ + + +
+

Parameters

+ + + + + + + +
childViewController

Expected to return the visibility depth of the child view controller.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASVisibilityProtocols.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Protocols/ASMultiplexImageNodeDataSource.html b/docs/appledoc/Protocols/ASMultiplexImageNodeDataSource.html new file mode 100755 index 0000000000..b979e068fd --- /dev/null +++ b/docs/appledoc/Protocols/ASMultiplexImageNodeDataSource.html @@ -0,0 +1,364 @@ + + + + + + ASMultiplexImageNodeDataSource Protocol Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASMultiplexImageNodeDataSource Protocol Reference

+ + +
+ + + + + + + +
Conforms toNSObject
Declared inASMultiplexImageNode.h
+ + + + +
+ +

Overview

+

The ASMultiplexImageNodeDataSource protocol is adopted by an object that provides the multiplex image node, +for each image identifier, an image or a URL the image node should load.

+
+ + + + + +
+ + + + + + +
+
+ +

– multiplexImageNode:imageForImageIdentifier: +

+ +
+
+ +
+ + +
+

An image for the specified identifier.

+
+ + + +
- (nullable UIImage *)multiplexImageNode:(ASMultiplexImageNode *)imageNode imageForImageIdentifier:(ASImageIdentifier)imageIdentifier
+ + + +
+

Parameters

+ + + + + + + + + + + + +
imageNode

The sender.

imageIdentifier

The identifier for the image that should be returned.

+
+ + + +
+

Return Value

+

A UIImage corresponding to imageIdentifier, or nil if none is available.

+
+ + + + + +
+

Discussion

+

If the image is already available to the data source, this method should be used in lieu of providing the +URL to the image via -multiplexImageNode:URLForImageIdentifier:.

+
+ + + + + + + +
+

Declared In

+

ASMultiplexImageNode.h

+
+ + +
+
+
+ +

– multiplexImageNode:URLForImageIdentifier: +

+ +
+
+ +
+ + +
+

An image URL for the specified identifier.

+
+ + + +
- (nullable NSURL *)multiplexImageNode:(ASMultiplexImageNode *)imageNode URLForImageIdentifier:(ASImageIdentifier)imageIdentifier
+ + + +
+

Parameters

+ + + + + + + + + + + + +
imageNode

The sender.

imageIdentifier

The identifier for the image that will be downloaded.

+
+ + + +
+

Return Value

+

An NSURL for the image identified by imageIdentifier, or nil if none is available.

+
+ + + + + +
+

Discussion

+

Supported URLs include HTTP, HTTPS, AssetsLibrary, and FTP URLs as well as Photos framework URLs (see note).

+ +

If the image is already available to the data source, it should be provided via [ASMultiplexImageNodeDataSource multiplexImageNode:imageForImageIdentifier:] instead.

+
+ + + + + +
+

See Also

+
    + +
  • [NSURL URLWithAssetLocalIdentifier:targetSize:contentMode:options:] below.

  • + +
+
+ + + +
+

Declared In

+

ASMultiplexImageNode.h

+
+ + +
+
+
+ +

– multiplexImageNode:assetForLocalIdentifier: +

+ +
+
+ +
+ + +
+

A PHAsset for the specific asset local identifier

+
+ + + +
- (nullable PHAsset *)multiplexImageNode:(ASMultiplexImageNode *)imageNode assetForLocalIdentifier:(NSString *)assetLocalIdentifier
+ + + +
+

Parameters

+ + + + + + + + + + + + +
imageNode

The sender.

assetLocalIdentifier

The local identifier for a PHAsset that this image node is loading.

+
+ + + +
+

Return Value

+

A PHAsset corresponding to assetLocalIdentifier, or nil if none is available.

+
+ + + + + +
+

Discussion

+

This optional method can improve image performance if your data source already has the PHAsset available. +If this method is not implemented, or returns nil, the image node will request the asset from the Photos framework.

Note: This method may be called from any thread.

+
+ + + + + + + +
+

Declared In

+

ASMultiplexImageNode.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Protocols/ASMultiplexImageNodeDelegate.html b/docs/appledoc/Protocols/ASMultiplexImageNodeDelegate.html new file mode 100755 index 0000000000..100ffd6038 --- /dev/null +++ b/docs/appledoc/Protocols/ASMultiplexImageNodeDelegate.html @@ -0,0 +1,550 @@ + + + + + + ASMultiplexImageNodeDelegate Protocol Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASMultiplexImageNodeDelegate Protocol Reference

+ + +
+ + + + + + + +
Conforms toNSObject
Declared inASMultiplexImageNode.h
+ + + + +
+ +

Overview

+

The methods declared by the ASMultiplexImageNodeDelegate protocol allow the adopting delegate to respond to +notifications such as began, progressed and finished downloading, updated and displayed an image.

+
+ + + + + +
+ + + + + + +
+
+ +

– multiplexImageNode:didStartDownloadOfImageWithIdentifier: +

+ +
+
+ +
+ + +
+

Notification that the image node began downloading an image.

+
+ + + +
- (void)multiplexImageNode:(ASMultiplexImageNode *)imageNode didStartDownloadOfImageWithIdentifier:(id)imageIdentifier
+ + + +
+

Parameters

+ + + + + + + + + + + + +
imageNode

The sender.

imageIdentifier

The identifier for the image that is downloading.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASMultiplexImageNode.h

+
+ + +
+
+
+ +

– multiplexImageNode:didUpdateDownloadProgress:forImageWithIdentifier: +

+ +
+
+ +
+ + +
+

Notification that the image node’s download progressed.

+
+ + + +
- (void)multiplexImageNode:(ASMultiplexImageNode *)imageNode didUpdateDownloadProgress:(CGFloat)downloadProgress forImageWithIdentifier:(ASImageIdentifier)imageIdentifier
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + +
imageNode

The sender.

downloadProgress

The progress of the download. Value is between 0.0 and 1.0.

imageIdentifier

The identifier for the image that is downloading.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASMultiplexImageNode.h

+
+ + +
+
+
+ +

– multiplexImageNode:didFinishDownloadingImageWithIdentifier:error: +

+ +
+
+ +
+ + +
+

Notification that the image node’s download has finished.

+
+ + + +
- (void)multiplexImageNode:(ASMultiplexImageNode *)imageNode didFinishDownloadingImageWithIdentifier:(ASImageIdentifier)imageIdentifier error:(nullable NSError *)error
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + +
imageNode

The sender.

imageIdentifier

The identifier for the image that finished downloading.

error

The error that occurred while downloading, if one occurred; nil otherwise.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASMultiplexImageNode.h

+
+ + +
+
+
+ +

– multiplexImageNode:didUpdateImage:withIdentifier:fromImage:withIdentifier: +

+ +
+
+ +
+ + +
+

Notification that the image node’s image was updated.

+
+ + + +
- (void)multiplexImageNode:(ASMultiplexImageNode *)imageNode didUpdateImage:(nullable UIImage *)image withIdentifier:(nullable ASImageIdentifier)imageIdentifier fromImage:(nullable UIImage *)previousImage withIdentifier:(nullable ASImageIdentifier)previousImageIdentifier
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
imageNode

The sender.

image

The new image, ready for display.

imageIdentifier

The identifier for image.

previousImage

The old, previously-loaded image.

previousImageIdentifier

The identifier for previousImage.

+
+ + + + + + + +
+

Discussion

+

Note: This method does not indicate that image has been displayed.

+
+ + + + + + + + + +
+

Declared In

+

ASMultiplexImageNode.h

+
+ + +
+
+
+ +

– multiplexImageNode:didDisplayUpdatedImage:withIdentifier: +

+ +
+
+ +
+ + +
+

Notification that the image node displayed a new image.

+
+ + + +
- (void)multiplexImageNode:(ASMultiplexImageNode *)imageNode didDisplayUpdatedImage:(nullable UIImage *)image withIdentifier:(nullable ASImageIdentifier)imageIdentifier
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + +
imageNode

The sender.

image

The new image, now being displayed.

imageIdentifier

The identifier for image.

+
+ + + + + + + +
+

Discussion

+

This method is only called when image changes, and not on subsequent redisplays of the same image.

+
+ + + + + + + +
+

Declared In

+

ASMultiplexImageNode.h

+
+ + +
+
+
+ +

– multiplexImageNodeDidFinishDisplay: +

+ +
+
+ +
+ + +
+

Notification that the image node finished displaying an image.

+
+ + + +
- (void)multiplexImageNodeDidFinishDisplay:(ASMultiplexImageNode *)imageNode
+ + + +
+

Parameters

+ + + + + + + +
imageNode

The sender.

+
+ + + + + + + +
+

Discussion

+

This method is called every time an image is displayed, whether or not it has changed.

+
+ + + + + + + +
+

Declared In

+

ASMultiplexImageNode.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Protocols/ASNetworkImageNodeDelegate.html b/docs/appledoc/Protocols/ASNetworkImageNodeDelegate.html new file mode 100755 index 0000000000..993331c3f7 --- /dev/null +++ b/docs/appledoc/Protocols/ASNetworkImageNodeDelegate.html @@ -0,0 +1,386 @@ + + + + + + ASNetworkImageNodeDelegate Protocol Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASNetworkImageNodeDelegate Protocol Reference

+ + +
+ + + + + + + +
Conforms toNSObject
Declared inASNetworkImageNode.h
+ + + + +
+ +

Overview

+

The methods declared by the ASNetworkImageNodeDelegate protocol allow the adopting delegate to respond to +notifications such as finished decoding and downloading an image.

+
+ + + + + +
+ + + + + + +
+
+ +

– imageNode:didLoadImage: +

+ +
+
+ +
+ + +
+

Notification that the image node finished downloading an image.

+
+ + + +
- (void)imageNode:(ASNetworkImageNode *)imageNode didLoadImage:(UIImage *)image
+ + + +
+

Parameters

+ + + + + + + + + + + + +
imageNode

The sender.

image

The newly-loaded image.

+
+ + + + + + + +
+

Discussion

+

Called on a background queue.

+
+ + + + + + + +
+

Declared In

+

ASNetworkImageNode.h

+
+ + +
+
+
+ +

– imageNodeDidStartFetchingData: +

+ +
+
+ +
+ + +
+

Notification that the image node started to load

+
+ + + +
- (void)imageNodeDidStartFetchingData:(ASNetworkImageNode *)imageNode
+ + + +
+

Parameters

+ + + + + + + +
imageNode

The sender.

+
+ + + + + + + +
+

Discussion

+

Called on a background queue.

+
+ + + + + + + +
+

Declared In

+

ASNetworkImageNode.h

+
+ + +
+
+
+ +

– imageNode:didFailWithError: +

+ +
+
+ +
+ + +
+

Notification that the image node failed to download the image.

+
+ + + +
- (void)imageNode:(ASNetworkImageNode *)imageNode didFailWithError:(NSError *)error
+ + + +
+

Parameters

+ + + + + + + + + + + + +
imageNode

The sender.

error

The error with details.

+
+ + + + + + + +
+

Discussion

+

Called on a background queue.

+
+ + + + + + + +
+

Declared In

+

ASNetworkImageNode.h

+
+ + +
+
+
+ +

– imageNodeDidFinishDecoding: +

+ +
+
+ +
+ + +
+

Notification that the image node finished decoding an image.

+
+ + + +
- (void)imageNodeDidFinishDecoding:(ASNetworkImageNode *)imageNode
+ + + +
+

Parameters

+ + + + + + + +
imageNode

The sender.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASNetworkImageNode.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Protocols/ASPagerDataSource.html b/docs/appledoc/Protocols/ASPagerDataSource.html new file mode 100755 index 0000000000..6cea3bee54 --- /dev/null +++ b/docs/appledoc/Protocols/ASPagerDataSource.html @@ -0,0 +1,325 @@ + + + + + + ASPagerDataSource Protocol Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASPagerDataSource Protocol Reference

+ + +
+ + + + + + + +
Conforms toNSObject
Declared inASPagerNode.h
+ + + + + + +
+ + + + + + +
+
+ +

– numberOfPagesInPagerNode: +required method

+ +
+
+ +
+ + +
+

This method replaces -collectionView:numberOfItemsInSection:

+
+ + + +
- (NSInteger)numberOfPagesInPagerNode:(ASPagerNode *)pagerNode
+ + + +
+

Parameters

+ + + + + + + +
pagerNode

The sender.

+
+ + + +
+

Return Value

+

The total number of pages that can display in the pagerNode.

+
+ + + + + + + + + + + +
+

Declared In

+

ASPagerNode.h

+
+ + +
+
+
+ +

– pagerNode:nodeAtIndex: +

+ +
+
+ +
+ + +
+

This method replaces -collectionView:nodeForItemAtIndexPath:

+
+ + + +
- (ASCellNode *)pagerNode:(ASPagerNode *)pagerNode nodeAtIndex:(NSInteger)index
+ + + +
+

Parameters

+ + + + + + + + + + + + +
pagerNode

The sender.

index

The index of the requested node.

+
+ + + +
+

Return Value

+

a node for display at this index. This will be called on the main thread and should +not implement reuse (it will be called once per row). Unlike UICollectionView’s version, +this method is not called when the row is about to display.

+
+ + + + + + + + + + + +
+

Declared In

+

ASPagerNode.h

+
+ + +
+
+
+ +

– pagerNode:nodeBlockAtIndex: +

+ +
+
+ +
+ + +
+

This method replaces -collectionView:nodeBlockForItemAtIndexPath: +This method takes precedence over pagerNode:nodeAtIndex: if implemented.

+
+ + + +
- (ASCellNodeBlock)pagerNode:(ASPagerNode *)pagerNode nodeBlockAtIndex:(NSInteger)index
+ + + +
+

Parameters

+ + + + + + + + + + + + +
pagerNode

The sender.

index

The index of the requested node.

+
+ + + +
+

Return Value

+

a block that creates the node for display at this index. +Must be thread-safe (can be called on the main thread or a background +queue) and should not implement reuse (it will be called once per row).

+
+ + + + + + + + + + + +
+

Declared In

+

ASPagerNode.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Protocols/ASPagerDelegate.html b/docs/appledoc/Protocols/ASPagerDelegate.html new file mode 100755 index 0000000000..6f7e47f1f7 --- /dev/null +++ b/docs/appledoc/Protocols/ASPagerDelegate.html @@ -0,0 +1,195 @@ + + + + + + ASPagerDelegate Protocol Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASPagerDelegate Protocol Reference

+ + +
+ + + + + + + +
Conforms toASCollectionDelegate
Declared inASPagerNode.h
+ + + + + + +
+ + + + + + +
+
+ +

– pagerNode:constrainedSizeForNodeAtIndex: +

+ +
+
+ +
+ + +
+

Provides the constrained size range for measuring the node at the index.

+
+ + + +
- (ASSizeRange)pagerNode:(ASPagerNode *)pagerNode constrainedSizeForNodeAtIndex:(NSInteger)index
+ + + +
+

Parameters

+ + + + + + + + + + + + +
pagerNode

The sender.

index

The index of the node.

+
+ + + +
+

Return Value

+

A constrained size range for layout the node at this index.

+
+ + + + + + + + + + + +
+

Declared In

+

ASPagerNode.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Protocols/ASRangeControllerDataSource.html b/docs/appledoc/Protocols/ASRangeControllerDataSource.html new file mode 100755 index 0000000000..2aa1157e93 --- /dev/null +++ b/docs/appledoc/Protocols/ASRangeControllerDataSource.html @@ -0,0 +1,385 @@ + + + + + + ASRangeControllerDataSource Protocol Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASRangeControllerDataSource Protocol Reference

+ + +
+ + + + + + + +
Conforms toNSObject
Declared inASRangeController.h
+ + + + +
+ +

Overview

+

Data source for ASRangeController.

+ +

Allows the range controller to perform external queries on the range. +Ex. range nodes, visible index paths, and viewport size.

+
+ + + + + +
+ + + + + + +
+
+ +

– visibleNodeIndexPathsForRangeController: +required method

+ +
+
+ +
+ + +
+

Sender.

+
+ + + +
- (NSArray<NSIndexPath*> *)visibleNodeIndexPathsForRangeController:(ASRangeController *)rangeController
+ + + +
+

Parameters

+ + + + + + + +
rangeController

Sender.

+
+ + + +
+

Return Value

+

an array of index paths corresponding to the nodes currently visible onscreen (i.e., the visible range).

+
+ + + + + + + + + + + +
+

Declared In

+

ASRangeController.h

+
+ + +
+
+
+ +

– scrollDirectionForRangeController: +required method

+ +
+
+ +
+ + +
+

Sender.

+
+ + + +
- (ASScrollDirection)scrollDirectionForRangeController:(ASRangeController *)rangeController
+ + + +
+

Parameters

+ + + + + + + +
rangeController

Sender.

+
+ + + +
+

Return Value

+

the current scroll direction of the view using this range controller.

+
+ + + + + + + + + + + +
+

Declared In

+

ASRangeController.h

+
+ + +
+
+
+ +

– viewportSizeForRangeController: +required method

+ +
+
+ +
+ + +
+

Sender.

+
+ + + +
- (CGSize)viewportSizeForRangeController:(ASRangeController *)rangeController
+ + + +
+

Parameters

+ + + + + + + +
rangeController

Sender.

+
+ + + +
+

Return Value

+

the receiver’s viewport size (i.e., the screen space occupied by the visible range).

+
+ + + + + + + + + + + +
+

Declared In

+

ASRangeController.h

+
+ + +
+
+
+ +

– interfaceStateForRangeController: +required method

+ +
+
+ +
+ + +
+

Sender.

+
+ + + +
- (ASInterfaceState)interfaceStateForRangeController:(ASRangeController *)rangeController
+ + + +
+

Parameters

+ + + + + + + +
rangeController

Sender.

+
+ + + +
+

Return Value

+

the ASInterfaceState of the node that this controller is powering. This allows nested range controllers +to collaborate with one another, as an outer controller may set bits in .interfaceState such as Visible. +If this controller is an orthogonally scrolling element, it waits until it is visible to preload outside the viewport.

+
+ + + + + + + + + + + +
+

Declared In

+

ASRangeController.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Protocols/ASRangeControllerDelegate.html b/docs/appledoc/Protocols/ASRangeControllerDelegate.html new file mode 100755 index 0000000000..e97a5829f0 --- /dev/null +++ b/docs/appledoc/Protocols/ASRangeControllerDelegate.html @@ -0,0 +1,585 @@ + + + + + + ASRangeControllerDelegate Protocol Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASRangeControllerDelegate Protocol Reference

+ + +
+ + + + + + + +
Conforms toNSObject
Declared inASRangeController.h
+ + + + +
+ +

Overview

+

Delegate for ASRangeController.

+
+ + + + + +
+ + + + + + +
+
+ +

– didBeginUpdatesInRangeController: +required method

+ +
+
+ +
+ + +
+

Begin updates.

+
+ + + +
- (void)didBeginUpdatesInRangeController:(ASRangeController *)rangeController
+ + + +
+

Parameters

+ + + + + + + +
rangeController

Sender.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASRangeController.h

+
+ + +
+
+
+ +

– rangeController:didEndUpdatesAnimated:completion: +required method

+ +
+
+ +
+ + +
+

End updates.

+
+ + + +
- (void)rangeController:(ASRangeController *)rangeController didEndUpdatesAnimated:(BOOL)animated completion:(void ( ^ ) ( BOOL ))completion
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + +
rangeController

Sender.

animated

NO if all animations are disabled. YES otherwise.

completion

Completion block.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASRangeController.h

+
+ + +
+
+
+ +

– didCompleteUpdatesInRangeController: +required method

+ +
+
+ +
+ + +
+

Completed updates to cell node addition and removal.

+
+ + + +
- (void)didCompleteUpdatesInRangeController:(ASRangeController *)rangeController
+ + + +
+

Parameters

+ + + + + + + +
rangeController

Sender.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASRangeController.h

+
+ + +
+
+
+ +

– rangeController:didInsertNodes:atIndexPaths:withAnimationOptions: +required method

+ +
+
+ +
+ + +
+

Called for nodes insertion.

+
+ + + +
- (void)rangeController:(ASRangeController *)rangeController didInsertNodes:(NSArray<ASCellNode*> *)nodes atIndexPaths:(NSArray<NSIndexPath*> *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
rangeController

Sender.

nodes

Inserted nodes.

indexPaths

Index path of inserted nodes.

animationOptions

Animation options. See ASDataControllerAnimationOptions.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASRangeController.h

+
+ + +
+
+
+ +

– rangeController:didDeleteNodes:atIndexPaths:withAnimationOptions: +required method

+ +
+
+ +
+ + +
+

Called for nodes deletion.

+
+ + + +
- (void)rangeController:(ASRangeController *)rangeController didDeleteNodes:(NSArray<ASCellNode*> *)nodes atIndexPaths:(NSArray<NSIndexPath*> *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
rangeController

Sender.

nodes

Deleted nodes.

indexPaths

Index path of deleted nodes.

animationOptions

Animation options. See ASDataControllerAnimationOptions.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASRangeController.h

+
+ + +
+
+
+ +

– rangeController:didInsertSectionsAtIndexSet:withAnimationOptions: +required method

+ +
+
+ +
+ + +
+

Called for section insertion.

+
+ + + +
- (void)rangeController:(ASRangeController *)rangeController didInsertSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + +
rangeController

Sender.

indexSet

Index set of inserted sections.

animationOptions

Animation options. See ASDataControllerAnimationOptions.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASRangeController.h

+
+ + +
+
+
+ +

– rangeController:didDeleteSectionsAtIndexSet:withAnimationOptions: +required method

+ +
+
+ +
+ + +
+

Called for section deletion.

+
+ + + +
- (void)rangeController:(ASRangeController *)rangeController didDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + +
rangeController

Sender.

indexSet

Index set of deleted sections.

animationOptions

Animation options. See ASDataControllerAnimationOptions.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASRangeController.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Protocols/ASStackLayoutElement.html b/docs/appledoc/Protocols/ASStackLayoutElement.html new file mode 100755 index 0000000000..97a44dd6a6 --- /dev/null +++ b/docs/appledoc/Protocols/ASStackLayoutElement.html @@ -0,0 +1,493 @@ + + + + + + ASStackLayoutElement Protocol Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASStackLayoutElement Protocol Reference

+ + +
+ + + + + + + +
Conforms toNSObject
Declared inASStackLayoutElement.h
+ + + + +
+ +

Overview

+

Layout options that can be defined for an ASLayoutElement being added to a ASStackLayoutSpec.

+
+ + + + + +
+ + + + + + +
+
+ +

  spacingBefore +required method

+ +
+
+ +
+ + +
+

Additional space to place before this object in the stacking direction. +Used when attached to a stack layout.

+
+ + + +
@property (nonatomic, readwrite) CGFloat spacingBefore
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASStackLayoutElement.h

+
+ + +
+
+
+ +

  spacingAfter +required method

+ +
+
+ +
+ + +
+

Additional space to place after this object in the stacking direction. +Used when attached to a stack layout.

+
+ + + +
@property (nonatomic, readwrite) CGFloat spacingAfter
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASStackLayoutElement.h

+
+ + +
+
+
+ +

  flexGrow +required method

+ +
+
+ +
+ + +
+

If the sum of childrens' stack dimensions is less than the minimum size, how much should this component grow? +This value represents the “flex grow factor” and determines how much this component should grow in relation to any +other flexible children.

+
+ + + +
@property (nonatomic, readwrite) CGFloat flexGrow
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASStackLayoutElement.h

+
+ + +
+
+
+ +

  flexShrink +required method

+ +
+
+ +
+ + +
+

If the sum of childrens' stack dimensions is greater than the maximum size, how much should this component shrink? +This value represents the “flex shrink factor” and determines how much this component should shink in relation to +other flexible children.

+
+ + + +
@property (nonatomic, readwrite) CGFloat flexShrink
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASStackLayoutElement.h

+
+ + +
+
+
+ +

  flexBasis +required method

+ +
+
+ +
+ + +
+

Specifies the initial size in the stack dimension for this object. +Default to ASDimensionAuto +Used when attached to a stack layout.

+
+ + + +
@property (nonatomic, readwrite) ASDimension flexBasis
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASStackLayoutElement.h

+
+ + +
+
+
+ +

  alignSelf +required method

+ +
+
+ +
+ + +
+

Orientation of the object along cross axis, overriding alignItems +Used when attached to a stack layout.

+
+ + + +
@property (nonatomic, readwrite) ASStackLayoutAlignSelf alignSelf
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASStackLayoutElement.h

+
+ + +
+
+
+ +

  ascender +required method

+ +
+
+ +
+ + +
+

Used for baseline alignment. The distance from the top of the object to its baseline.

+
+ + + +
@property (nonatomic, readwrite) CGFloat ascender
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASStackLayoutElement.h

+
+ + +
+
+
+ +

  descender +required method

+ +
+
+ +
+ + +
+

Used for baseline alignment. The distance from the baseline of the object to its bottom.

+
+ + + +
@property (nonatomic, readwrite) CGFloat descender
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASStackLayoutElement.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Protocols/ASTableDataSource.html b/docs/appledoc/Protocols/ASTableDataSource.html new file mode 100755 index 0000000000..c6c4cc407c --- /dev/null +++ b/docs/appledoc/Protocols/ASTableDataSource.html @@ -0,0 +1,614 @@ + + + + + + ASTableDataSource Protocol Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASTableDataSource Protocol Reference

+ + +
+ + + + + + + +
Conforms toASCommonTableDataSource
NSObject
Declared inASTableNode.h
+ + + + +
+ +

Overview

+

This is a node-based UITableViewDataSource.

+
+ + + + + +
+ + + + + + +
+
+ +

– numberOfSectionsInTableNode: +

+ +
+
+ +
+ + +
+

Asks the data source for the number of sections in the table node.

+
+ + + +
- (NSInteger)numberOfSectionsInTableNode:(ASTableNode *)tableNode
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASTableNode.h

+
+ + +
+
+
+ +

– tableNode:numberOfRowsInSection: +

+ +
+
+ +
+ + +
+

Asks the data source for the number of rows in the given section of the table node.

+
+ + + +
- (NSInteger)tableNode:(ASTableNode *)tableNode numberOfRowsInSection:(NSInteger)section
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASTableNode.h

+
+ + +
+
+
+ +

– tableNode:nodeBlockForRowAtIndexPath: +

+ +
+
+ +
+ + +
+

Asks the data source for a block to create a node to represent the row at the given index path. +The block will be run by the table node concurrently in the background before the row is inserted +into the table view.

+
+ + + +
- (ASCellNodeBlock)tableNode:(ASTableNode *)tableNode nodeBlockForRowAtIndexPath:(NSIndexPath *)indexPath
+ + + +
+

Parameters

+ + + + + + + + + + + + +
tableNode

The sender.

indexPath

The index path of the row.

+
+ + + +
+

Return Value

+

a block that creates the node for display at this indexpath. +Must be thread-safe (can be called on the main thread or a background +queue) and should not implement reuse (it will be called once per row).

+
+ + + + + +
+

Discussion

+

Note: This method takes precedence over tableNode:nodeForRowAtIndexPath: if implemented.

+
+ + + + + + + +
+

Declared In

+

ASTableNode.h

+
+ + +
+
+
+ +

– tableNode:nodeForRowAtIndexPath: +

+ +
+
+ +
+ + +
+

Asks the data source for a node to represent the row at the given index path.

+
+ + + +
- (ASCellNode *)tableNode:(ASTableNode *)tableNode nodeForRowAtIndexPath:(NSIndexPath *)indexPath
+ + + +
+

Parameters

+ + + + + + + + + + + + +
tableNode

The sender.

indexPath

The index path of the row.

+
+ + + +
+

Return Value

+

a node to display for this row. This will be called on the main thread and should not implement reuse (it will be called once per row). Unlike UITableView’s version, this method +is not called when the row is about to display.

+
+ + + + + + + + + + + +
+

Declared In

+

ASTableNode.h

+
+ + +
+
+
+ +

– tableView:nodeForRowAtIndexPath: +

+ +
+
+ +
+ + +
+

Similar to -tableView:cellForRowAtIndexPath:.

+
+ + + +
- (ASCellNode *)tableView:(ASTableView *)tableView nodeForRowAtIndexPath:(NSIndexPath *)indexPath
+ + + +
+

Parameters

+ + + + + + + + + + + + +
indexPath

The index path of the requested node.

tableNode

The sender.

+
+ + + +
+

Return Value

+

a node for display at this indexpath. This will be called on the main thread and should not implement reuse (it will be called once per row). Unlike UITableView’s version, this method +is not called when the row is about to display.

+
+ + + + + + + + + + + +
+

Declared In

+

ASTableNode.h

+
+ + +
+
+
+ +

– tableView:nodeBlockForRowAtIndexPath: +

+ +
+
+ +
+ + +
+

Similar to -tableView:nodeForRowAtIndexPath: +This method takes precedence over tableView:nodeForRowAtIndexPath: if implemented.

+
+ + + +
- (ASCellNodeBlock)tableView:(ASTableView *)tableView nodeBlockForRowAtIndexPath:(NSIndexPath *)indexPath
+ + + +
+

Parameters

+ + + + + + + + + + + + +
tableView

The sender.

indexPath

The index path of the requested node.

+
+ + + +
+

Return Value

+

a block that creates the node for display at this indexpath. +Must be thread-safe (can be called on the main thread or a background +queue) and should not implement reuse (it will be called once per row).

+
+ + + + + + + + + + + +
+

Declared In

+

ASTableNode.h

+
+ + +
+
+
+ +

– tableViewLockDataSource: +

+ +
+
+ +
+ + +
+

Indicator to lock the data source for data fetching in async mode. +We should not update the data source until the data source has been unlocked. Otherwise, it will incur data inconsistency or exception +due to the data access in async mode. (Deprecated: The data source is always accessed on the main thread, and this method will not be called.)

+
+ + + +
- (void)tableViewLockDataSource:(ASTableView *)tableView
+ + + +
+

Parameters

+ + + + + + + +
tableView

The sender.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASTableNode.h

+
+ + +
+
+
+ +

– tableViewUnlockDataSource: +

+ +
+
+ +
+ + +
+

Indicator to unlock the data source for data fetching in asyn mode. +We should not update the data source until the data source has been unlocked. Otherwise, it will incur data inconsistency or exception +due to the data access in async mode. (Deprecated: The data source is always accessed on the main thread, and this method will not be called.)

+
+ + + +
- (void)tableViewUnlockDataSource:(ASTableView *)tableView
+ + + +
+

Parameters

+ + + + + + + +
tableView

The sender.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASTableNode.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Protocols/ASTableDelegate.html b/docs/appledoc/Protocols/ASTableDelegate.html new file mode 100755 index 0000000000..993e7cac16 --- /dev/null +++ b/docs/appledoc/Protocols/ASTableDelegate.html @@ -0,0 +1,758 @@ + + + + + + ASTableDelegate Protocol Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASTableDelegate Protocol Reference

+ + +
+ + + + + + + +
Conforms toASCommonTableViewDelegate
NSObject
Declared inASTableNode.h
+ + + + +
+ +

Overview

+

This is a node-based UITableViewDelegate.

+ +

Note that -tableView:heightForRowAtIndexPath: has been removed; instead, your custom ASCellNode subclasses are +responsible for deciding their preferred onscreen height in -calculateSizeThatFits:.

+
+ + + + + +
+ + + + + + +
+
+ +

– tableNode:constrainedSizeForRowAtIndexPath: +

+ +
+
+ +
+ + +
+

Provides the constrained size range for measuring the row at the index path. +Note: the widths in the returned size range are ignored!

+
+ + + +
- (ASSizeRange)tableNode:(ASTableNode *)tableNode constrainedSizeForRowAtIndexPath:(NSIndexPath *)indexPath
+ + + +
+

Parameters

+ + + + + + + + + + + + +
tableNode

The sender.

indexPath

The index path of the node.

+
+ + + +
+

Return Value

+

A constrained size range for layout the node at this index path.

+
+ + + + + + + + + + + +
+

Declared In

+

ASTableNode.h

+
+ + +
+
+
+ +

– tableNode:willBeginBatchFetchWithContext: +

+ +
+
+ +
+ + +
+

Receive a message that the tableView is near the end of its data set and more data should be fetched if necessary.

+
+ + + +
- (void)tableNode:(ASTableNode *)tableNode willBeginBatchFetchWithContext:(ASBatchContext *)context
+ + + +
+

Parameters

+ + + + + + + + + + + + +
context

A context object that must be notified when the batch fetch is completed.

tableView

The sender.

+
+ + + + + + + +
+

Discussion

+

You must eventually call -completeBatchFetching: with an argument of YES in order to receive future +notifications to do batch fetches. This method is called on a background queue.

+ +

ASTableView currently only supports batch events for tail loads. If you require a head load, consider implementing a +UIRefreshControl.

+
+ + + + + + + +
+

Declared In

+

ASTableNode.h

+
+ + +
+
+
+ +

– shouldBatchFetchForTableNode: +

+ +
+
+ +
+ + +
+

Tell the tableView if batch fetching should begin.

+
+ + + +
- (BOOL)shouldBatchFetchForTableNode:(ASTableNode *)tableNode
+ + + +
+

Parameters

+ + + + + + + +
tableView

The sender.

+
+ + + + + + + +
+

Discussion

+

Use this method to conditionally fetch batches. Example use cases are: limiting the total number of +objects that can be fetched or no network connection.

+ +

If not implemented, the tableView assumes that it should notify its asyncDelegate when batch fetching +should occur.

+
+ + + + + + + +
+

Declared In

+

ASTableNode.h

+
+ + +
+
+
+ +

– tableView:willDisplayNode:forRowAtIndexPath: +

+ +
+
+ +
+ + +
+

Informs the delegate that the table view will add the given node +at the given index path to the view hierarchy.

+
+ + + +
- (void)tableView:(ASTableView *)tableView willDisplayNode:(ASCellNode *)node forRowAtIndexPath:(NSIndexPath *)indexPath
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + +
tableView

The sender.

node

The node that will be displayed.

indexPath

The index path of the row that will be displayed.

+
+ + + + + + + +
+

Discussion

+

Warning: AsyncDisplayKit processes table view edits asynchronously. The index path +passed into this method may not correspond to the same item in your data source +if your data source has been updated since the last edit was processed.

+
+ + + + + + + +
+

Declared In

+

ASTableNode.h

+
+ + +
+
+
+ +

– tableView:didEndDisplayingNode:forRowAtIndexPath: +

+ +
+
+ +
+ + +
+

Informs the delegate that the table view did remove the provided node from the view hierarchy. +This may be caused by the node scrolling out of view, or by deleting the row +or its containing section with @c deleteRowsAtIndexPaths:withRowAnimation: or @c deleteSections:withRowAnimation: .

+
+ + + +
- (void)tableView:(ASTableView *)tableView didEndDisplayingNode:(ASCellNode *)node forRowAtIndexPath:(NSIndexPath *)indexPath
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + +
tableView

The sender.

node

The node which was removed from the view hierarchy.

indexPath

The index path at which the node was located before the removal.

+
+ + + + + + + +
+

Discussion

+

Warning: AsyncDisplayKit processes table view edits asynchronously. The index path +passed into this method may not correspond to the same item in your data source +if your data source has been updated since the last edit was processed.

+
+ + + + + + + +
+

Declared In

+

ASTableNode.h

+
+ + +
+
+
+ +

– tableView:willBeginBatchFetchWithContext: +

+ +
+
+ +
+ + +
+

Receive a message that the tableView is near the end of its data set and more data should be fetched if necessary.

+
+ + + +
- (void)tableView:(ASTableView *)tableView willBeginBatchFetchWithContext:(ASBatchContext *)context
+ + + +
+

Parameters

+ + + + + + + + + + + + +
tableView

The sender.

context

A context object that must be notified when the batch fetch is completed.

+
+ + + + + + + +
+

Discussion

+

You must eventually call -completeBatchFetching: with an argument of YES in order to receive future +notifications to do batch fetches. This method is called on a background queue.

+ +

ASTableView currently only supports batch events for tail loads. If you require a head load, consider implementing a +UIRefreshControl.

+
+ + + + + + + +
+

Declared In

+

ASTableNode.h

+
+ + +
+
+
+ +

– shouldBatchFetchForTableView: +

+ +
+
+ +
+ + +
+

Tell the tableView if batch fetching should begin.

+
+ + + +
- (BOOL)shouldBatchFetchForTableView:(ASTableView *)tableView
+ + + +
+

Parameters

+ + + + + + + +
tableView

The sender.

+
+ + + + + + + +
+

Discussion

+

Use this method to conditionally fetch batches. Example use cases are: limiting the total number of +objects that can be fetched or no network connection.

+ +

If not implemented, the tableView assumes that it should notify its asyncDelegate when batch fetching +should occur.

+
+ + + + + + + +
+

Declared In

+

ASTableNode.h

+
+ + +
+
+
+ +

– tableView:constrainedSizeForRowAtIndexPath: +

+ +
+
+ +
+ + +
+

Provides the constrained size range for measuring the row at the index path. +Note: the widths in the returned size range are ignored!

+
+ + + +
- (ASSizeRange)tableView:(ASTableView *)tableView constrainedSizeForRowAtIndexPath:(NSIndexPath *)indexPath
+ + + +
+

Parameters

+ + + + + + + + + + + + +
tableView

The sender.

indexPath

The index path of the node.

+
+ + + +
+

Return Value

+

A constrained size range for layout the node at this index path.

+
+ + + + + + + + + + + +
+

Declared In

+

ASTableNode.h

+
+ + +
+
+
+ +

– tableView:willDisplayNodeForRowAtIndexPath: +

+ +
+
+ +
+ + +
+

Informs the delegate that the table view will add the node +at the given index path to the view hierarchy.

+
+ + + +
- (void)tableView:(ASTableView *)tableView willDisplayNodeForRowAtIndexPath:(NSIndexPath *)indexPath
+ + + +
+

Parameters

+ + + + + + + + + + + + +
tableView

The sender.

indexPath

The index path of the row that will be displayed.

+
+ + + + + + + +
+

Discussion

+

Warning: AsyncDisplayKit processes table view edits asynchronously. The index path +passed into this method may not correspond to the same item in your data source +if your data source has been updated since the last edit was processed.

+ +

This method is deprecated. Use @c tableView:willDisplayNode:forRowAtIndexPath: instead.

+
+ + + + + + + +
+

Declared In

+

ASTableNode.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Protocols/ASTextNodeDelegate.html b/docs/appledoc/Protocols/ASTextNodeDelegate.html new file mode 100755 index 0000000000..a5945ed623 --- /dev/null +++ b/docs/appledoc/Protocols/ASTextNodeDelegate.html @@ -0,0 +1,450 @@ + + + + + + ASTextNodeDelegate Protocol Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASTextNodeDelegate Protocol Reference

+ + +
+ + + + + + + +
Conforms toNSObject
Declared inASTextNode.h
+ + + + + + + + +
+ + + + + + +
+
+ +

– textNode:tappedLinkAttribute:value:atPoint:textRange: +

+ +
+
+ +
+ + +
+

Indicates to the delegate that a link was tapped within a text node.

+
+ + + +
- (void)textNode:(ASTextNode *)textNode tappedLinkAttribute:(NSString *)attribute value:(id)value atPoint:(CGPoint)point textRange:(NSRange)textRange
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
textNode

The ASTextNode containing the link that was tapped.

attribute

The attribute that was tapped. Will not be nil.

value

The value of the tapped attribute.

point

The point within textNode, in textNode’s coordinate system, that was tapped.

textRange

The range of highlighted text.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASTextNode.h

+
+ + +
+
+
+ +

– textNode:longPressedLinkAttribute:value:atPoint:textRange: +

+ +
+
+ +
+ + +
+

Indicates to the delegate that a link was tapped within a text node.

+
+ + + +
- (void)textNode:(ASTextNode *)textNode longPressedLinkAttribute:(NSString *)attribute value:(id)value atPoint:(CGPoint)point textRange:(NSRange)textRange
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
textNode

The ASTextNode containing the link that was tapped.

attribute

The attribute that was tapped. Will not be nil.

value

The value of the tapped attribute.

point

The point within textNode, in textNode’s coordinate system, that was tapped.

textRange

The range of highlighted text.

+
+ + + + + + + +
+

Discussion

+

In addition to implementing this method, the delegate must be set on the text + node before it is loaded (the recognizer is created in -didLoad)

+
+ + + + + + + +
+

Declared In

+

ASTextNode.h

+
+ + +
+
+
+ +

– textNode:shouldHighlightLinkAttribute:value:atPoint: +

+ +
+
+ +
+ + +
+

Indicates to the text node if an attribute should be considered a link.

+
+ + + +
- (BOOL)textNode:(ASTextNode *)textNode shouldHighlightLinkAttribute:(NSString *)attribute value:(id)value atPoint:(CGPoint)point
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
textNode

The text node containing the entity attribute.

attribute

The attribute that was tapped. Will not be nil.

value

The value of the tapped attribute.

point

The point within textNode, in textNode’s coordinate system, that was touched to trigger a highlight.

+
+ + + +
+

Return Value

+

YES if the entity attribute should be a link, NO otherwise.

+
+ + + + + +
+

Discussion

+

If not implemented, the default value is YES.

+
+ + + + + + + +
+

Declared In

+

ASTextNode.h

+
+ + +
+
+
+ +

– textNode:shouldLongPressLinkAttribute:value:atPoint: +

+ +
+
+ +
+ + +
+

Indicates to the text node if an attribute is a valid long-press target

+
+ + + +
- (BOOL)textNode:(ASTextNode *)textNode shouldLongPressLinkAttribute:(NSString *)attribute value:(id)value atPoint:(CGPoint)point
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
textNode

The text node containing the entity attribute.

attribute

The attribute that was tapped. Will not be nil.

value

The value of the tapped attribute.

point

The point within textNode, in textNode’s coordinate system, that was long-pressed.

+
+ + + +
+

Return Value

+

YES if the entity attribute should be treated as a long-press target, NO otherwise.

+
+ + + + + +
+

Discussion

+

If not implemented, the default value is NO.

+
+ + + + + + + +
+

Declared In

+

ASTextNode.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Protocols/ASVideoNodeDelegate.html b/docs/appledoc/Protocols/ASVideoNodeDelegate.html new file mode 100755 index 0000000000..10b8f8505f --- /dev/null +++ b/docs/appledoc/Protocols/ASVideoNodeDelegate.html @@ -0,0 +1,729 @@ + + + + + + ASVideoNodeDelegate Protocol Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASVideoNodeDelegate Protocol Reference

+ + +
+ + + + + + + +
Conforms toASNetworkImageNodeDelegate
Declared inASVideoNode.h
+ + + + + + +
+ + + + + + +
+
+ +

– videoDidPlayToEnd: +

+ +
+
+ +
+ + +
+

Delegate method invoked when the node’s video has played to its end time.

+
+ + + +
- (void)videoDidPlayToEnd:(ASVideoNode *)videoNode
+ + + +
+

Parameters

+ + + + + + + +
videoNode

The video node has played to its end time.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASVideoNode.h

+
+ + +
+
+
+ +

– didTapVideoNode: +

+ +
+
+ +
+ + +
+

Delegate method invoked the node is tapped.

+
+ + + +
- (void)didTapVideoNode:(ASVideoNode *)videoNode
+ + + +
+

Parameters

+ + + + + + + +
videoNode

The video node that was tapped.

+
+ + + + + + + +
+

Discussion

+

The video’s play state is toggled if this method is not implemented.

+
+ + + + + + + +
+

Declared In

+

ASVideoNode.h

+
+ + +
+
+
+ +

– videoNode:willChangePlayerState:toState: +

+ +
+
+ +
+ + +
+

Delegate method invoked when player changes state.

+
+ + + +
- (void)videoNode:(ASVideoNode *)videoNode willChangePlayerState:(ASVideoNodePlayerState)state toState:(ASVideoNodePlayerState)toState
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + +
videoNode

The video node.

state

player state before this change.

toState

player new state.

+
+ + + + + + + +
+

Discussion

+

This method is called after each state change

+
+ + + + + + + +
+

Declared In

+

ASVideoNode.h

+
+ + +
+
+
+ +

– videoNode:shouldChangePlayerStateTo: +

+ +
+
+ +
+ + +
+

Ssks delegate if state change is allowed +ASVideoNodePlayerStatePlaying or ASVideoNodePlayerStatePaused. +asks delegate if state change is allowed.

+
+ + + +
- (BOOL)videoNode:(ASVideoNode *)videoNode shouldChangePlayerStateTo:(ASVideoNodePlayerState)state
+ + + +
+

Parameters

+ + + + + + + + + + + + +
videoNode

The video node.

state

player state that is going to be set.

+
+ + + + + + + +
+

Discussion

+

Delegate method invoked when player changes it’s state to +ASVideoNodePlayerStatePlaying or ASVideoNodePlayerStatePaused +and asks delegate if state change is valid

+
+ + + + + + + +
+

Declared In

+

ASVideoNode.h

+
+ + +
+
+
+ +

– videoNode:didPlayToTimeInterval: +

+ +
+
+ +
+ + +
+

Delegate method invoked when player playback time is updated.

+
+ + + +
- (void)videoNode:(ASVideoNode *)videoNode didPlayToTimeInterval:(NSTimeInterval)timeInterval
+ + + +
+

Parameters

+ + + + + + + + + + + + +
videoNode

The video node.

second

current playback time in seconds.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASVideoNode.h

+
+ + +
+
+
+ +

– videoNode:didStallAtTimeInterval: +

+ +
+
+ +
+ + +
+

Delegate method invoked when the video player stalls.

+
+ + + +
- (void)videoNode:(ASVideoNode *)videoNode didStallAtTimeInterval:(NSTimeInterval)timeInterval
+ + + +
+

Parameters

+ + + + + + + + + + + + +
videoNode

The video node that has experienced the stall

second

Current playback time when the stall happens

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASVideoNode.h

+
+ + +
+
+
+ +

– videoNodeDidStartInitialLoading: +

+ +
+
+ +
+ + +
+

Delegate method invoked when the video player starts the inital asset loading

+
+ + + +
- (void)videoNodeDidStartInitialLoading:(ASVideoNode *)videoNode
+ + + +
+

Parameters

+ + + + + + + +
videoNode

The videoNode

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASVideoNode.h

+
+ + +
+
+
+ +

– videoNodeDidFinishInitialLoading: +

+ +
+
+ +
+ + +
+

Delegate method invoked when the video is done loading the asset and can start the playback

+
+ + + +
- (void)videoNodeDidFinishInitialLoading:(ASVideoNode *)videoNode
+ + + +
+

Parameters

+ + + + + + + +
videoNode

The videoNode

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASVideoNode.h

+
+ + +
+
+
+ +

– videoNode:didSetCurrentItem: +

+ +
+
+ +
+ + +
+

Delegate method invoked when the AVPlayerItem for the asset has been set up and can be accessed throught currentItem.

+
+ + + +
- (void)videoNode:(ASVideoNode *)videoNode didSetCurrentItem:(AVPlayerItem *)currentItem
+ + + +
+

Parameters

+ + + + + + + + + + + + +
videoNode

The videoNode.

currentItem

The AVPlayerItem that was constructed from the asset.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASVideoNode.h

+
+ + +
+
+
+ +

– videoNodeDidRecoverFromStall: +

+ +
+
+ +
+ + +
+

Delegate method invoked when the video node has recovered from the stall

+
+ + + +
- (void)videoNodeDidRecoverFromStall:(ASVideoNode *)videoNode
+ + + +
+

Parameters

+ + + + + + + +
videoNode

The videoNode

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASVideoNode.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Protocols/ASVideoPlayerNodeDelegate.html b/docs/appledoc/Protocols/ASVideoPlayerNodeDelegate.html new file mode 100755 index 0000000000..89faa40314 --- /dev/null +++ b/docs/appledoc/Protocols/ASVideoPlayerNodeDelegate.html @@ -0,0 +1,940 @@ + + + + + + ASVideoPlayerNodeDelegate Protocol Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

ASVideoPlayerNodeDelegate Protocol Reference

+ + +
+ + + + + + + +
Conforms toNSObject
Declared inASVideoPlayerNode.h
+ + + + + + +
+ + + + + + +
+
+ +

– videoPlayerNodeNeededDefaultControls: +

+ +
+
+ +
+ + +
+

Delegate method invoked before creating controlbar controls +@param videoPlayer

+
+ + + +
- (NSArray *)videoPlayerNodeNeededDefaultControls:(ASVideoPlayerNode *)videoPlayer
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASVideoPlayerNode.h

+
+ + +
+
+
+ +

– videoPlayerNodeCustomControls: +

+ +
+
+ +
+ + +
+

Delegate method invoked before creating default controls, asks delegate for custom controls dictionary. +This dictionary must constain only ASDisplayNode subclass objects. +@param videoPlayer

+
+ + + +
- (NSDictionary *)videoPlayerNodeCustomControls:(ASVideoPlayerNode *)videoPlayer
+ + + + + + + + + +
+

Discussion

+
    +
  • This method is invoked only when developer implements videoPlayerNodeLayoutSpec:forControls:forMaximumSize: +and gives ability to add custom constrols to ASVideoPlayerNode, for example mute button.
  • +
+ +
+ + + + + + + +
+

Declared In

+

ASVideoPlayerNode.h

+
+ + +
+
+
+ +

– videoPlayerNodeLayoutSpec:forControls:forMaximumSize: +

+ +
+
+ +
+ + +
+

Delegate method invoked in layoutSpecThatFits: +@param videoPlayer

+
+ + + +
- (ASLayoutSpec *)videoPlayerNodeLayoutSpec:(ASVideoPlayerNode *)videoPlayer forControls:(NSDictionary *)controls forMaximumSize:(CGSize)maxSize
+ + + +
+

Parameters

+ + + + + + + + + + + + +
controls
    +
  • Dictionary of controls which are used in videoPlayer; Dictionary keys are ASVideoPlayerNodeControlType
  • +
+
maxSize
    +
  • Maximum size for ASVideoPlayerNode
  • +
+
+
+ + + + + + + +
+

Discussion

+
    +
  • Developer can layout whole ASVideoPlayerNode as he wants. ASVideoNode is locked and it can’t be changed
  • +
+ +
+ + + + + + + +
+

Declared In

+

ASVideoPlayerNode.h

+
+ + +
+
+
+ +

– videoPlayerNodeTimeLabelAttributes:timeLabelType: +

+ +
+
+ +
+ + +
+

Delegate method invoked before creating ASVideoPlayerNodeControlTypeElapsedText and ASVideoPlayerNodeControlTypeDurationText +@param videoPlayer +@param timeLabelType

+
+ + + +
- (NSDictionary *)videoPlayerNodeTimeLabelAttributes:(ASVideoPlayerNode *)videoPlayerNode timeLabelType:(ASVideoPlayerNodeControlType)timeLabelType
+ + + + + + + + + + + + + + + +
+

Declared In

+

ASVideoPlayerNode.h

+
+ + +
+
+
+ +

– didTapVideoPlayerNode: +

+ +
+
+ +
+ + +
+

Delegate method invoked when ASVideoPlayerNode is taped.

+
+ + + +
- (void)didTapVideoPlayerNode:(ASVideoPlayerNode *)videoPlayer
+ + + +
+

Parameters

+ + + + + + + +
videoPlayerNode

The ASVideoPlayerNode that was tapped.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASVideoPlayerNode.h

+
+ + +
+
+
+ +

– videoPlayerNode:didPlayToTime: +

+ +
+
+ +
+ + +
+

Delegate method invoked when ASVideoNode playback time is updated.

+
+ + + +
- (void)videoPlayerNode:(ASVideoPlayerNode *)videoPlayer didPlayToTime:(CMTime)time
+ + + +
+

Parameters

+ + + + + + + + + + + + +
second

current playback time.

videoPlayerNode

The video player node

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASVideoPlayerNode.h

+
+ + +
+
+
+ +

– videoPlayerNode:willChangeVideoNodeState:toVideoNodeState: +

+ +
+
+ +
+ + +
+

Delegate method invoked when ASVideoNode changes state.

+
+ + + +
- (void)videoPlayerNode:(ASVideoPlayerNode *)videoPlayer willChangeVideoNodeState:(ASVideoNodePlayerState)state toVideoNodeState:(ASVideoNodePlayerState)toState
+ + + +
+

Parameters

+ + + + + + + + + + + + + + + + + +
state

ASVideoNode state before this change.

videoPlayerNode

The ASVideoPlayerNode whose ASVideoNode is changing state.

toSate

ASVideoNode new state.

+
+ + + + + + + +
+

Discussion

+

This method is called after each state change

+
+ + + + + + + +
+

Declared In

+

ASVideoPlayerNode.h

+
+ + +
+
+
+ +

– videoPlayerNode:shouldChangeVideoNodeStateTo: +

+ +
+
+ +
+ + +
+

Delegate method is invoked when ASVideoNode decides to change state.

+
+ + + +
- (BOOL)videoPlayerNode:(ASVideoPlayerNode *)videoPlayer shouldChangeVideoNodeStateTo:(ASVideoNodePlayerState)state
+ + + +
+

Parameters

+ + + + + + + + + + + + +
state

ASVideoNode that is going to be set.

videoPlayerNode

The ASVideoPlayerNode whose ASVideoNode is changing state.

+
+ + + + + + + +
+

Discussion

+

Delegate method invoked when player changes it’s state to +ASVideoNodePlayerStatePlaying or ASVideoNodePlayerStatePaused +and asks delegate if state change is valid

+
+ + + + + + + +
+

Declared In

+

ASVideoPlayerNode.h

+
+ + +
+
+
+ +

– videoPlayerNodeDidPlayToEnd: +

+ +
+
+ +
+ + +
+

Delegate method invoked when the ASVideoNode has played to its end time.

+
+ + + +
- (void)videoPlayerNodeDidPlayToEnd:(ASVideoPlayerNode *)videoPlayer
+ + + +
+

Parameters

+ + + + + + + +
videoPlayer

The video node has played to its end time.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASVideoPlayerNode.h

+
+ + +
+
+
+ +

– videoPlayerNode:didSetCurrentItem: +

+ +
+
+ +
+ + +
+

Delegate method invoked when the ASVideoNode has constructed its AVPlayerItem for the asset.

+
+ + + +
- (void)videoPlayerNode:(ASVideoPlayerNode *)videoPlayer didSetCurrentItem:(AVPlayerItem *)currentItem
+ + + +
+

Parameters

+ + + + + + + + + + + + +
videoPlayer

The video player node.

currentItem

The AVPlayerItem that was constructed from the asset.

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASVideoPlayerNode.h

+
+ + +
+
+
+ +

– videoPlayerNode:didStallAtTimeInterval: +

+ +
+
+ +
+ + +
+

Delegate method invoked when the ASVideoNode stalls.

+
+ + + +
- (void)videoPlayerNode:(ASVideoPlayerNode *)videoPlayer didStallAtTimeInterval:(NSTimeInterval)timeInterval
+ + + +
+

Parameters

+ + + + + + + + + + + + +
videoPlayer

The video player node that has experienced the stall

second

Current playback time when the stall happens

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASVideoPlayerNode.h

+
+ + +
+
+
+ +

– videoPlayerNodeDidStartInitialLoading: +

+ +
+
+ +
+ + +
+

Delegate method invoked when the ASVideoNode starts the inital asset loading

+
+ + + +
- (void)videoPlayerNodeDidStartInitialLoading:(ASVideoPlayerNode *)videoPlayer
+ + + +
+

Parameters

+ + + + + + + +
videoPlayer

The videoPlayer

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASVideoPlayerNode.h

+
+ + +
+
+
+ +

– videoPlayerNodeDidFinishInitialLoading: +

+ +
+
+ +
+ + +
+

Delegate method invoked when the ASVideoNode is done loading the asset and can start the playback

+
+ + + +
- (void)videoPlayerNodeDidFinishInitialLoading:(ASVideoPlayerNode *)videoPlayer
+ + + +
+

Parameters

+ + + + + + + +
videoPlayer

The videoPlayer

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASVideoPlayerNode.h

+
+ + +
+
+
+ +

– videoPlayerNodeDidRecoverFromStall: +

+ +
+
+ +
+ + +
+

Delegate method invoked when the ASVideoNode has recovered from the stall

+
+ + + +
- (void)videoPlayerNodeDidRecoverFromStall:(ASVideoPlayerNode *)videoPlayer
+ + + +
+

Parameters

+ + + + + + + +
videoPlayer

The videoplayer

+
+ + + + + + + + + + + + + +
+

Declared In

+

ASVideoPlayerNode.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/Protocols/ASVisibilityDepth.html b/docs/appledoc/Protocols/ASVisibilityDepth.html new file mode 100755 index 0000000000..29597c887f --- /dev/null +++ b/docs/appledoc/Protocols/ASVisibilityDepth.html @@ -0,0 +1,269 @@ + + + + + + ASVisibilityDepth Protocol Reference + + + + + + +
+
+ +

+ +

+ +

+ Texture +

+ +
+
+ + + +
+
+
+
+

ASVisibilityDepth Protocol Reference

+ + +
+ + + + + + + +
Conforms toNSObject
Declared inASVisibilityProtocols.h
+ + + + +
+ +

Overview

+

ASVisibilityDepth

“Visibility Depth” represents the number of user actions required to make an ASDisplayNode or +ASViewController visibile. Texture uses this information to intelligently manage memory and focus +resources where they are most visible to the user.

+ +

The ASVisibilityDepth protocol describes how custom view controllers can integrate with this system.

+ +

Parent view controllers should also implement @c ASManagesChildVisibilityDepth

+
+ + + + + +
+ + + + + + +
+
+ +

– visibilityDepth +required method

+ +
+
+ +
+ + +
+

Visibility depth

+
+ + + +
- (NSInteger)visibilityDepth
+ + + + + + + + + +
+

Discussion

+

Represents the number of user actions necessary to reach the view controller. An increased visibility +depth indicates a higher number of user interactions for the view controller to be visible again. For example, +an onscreen navigation controller’s top view controller should have a visibility depth of 0. The view controller +one from the top should have a visibility deptch of 1 as should the root view controller in the stack (because +the user can hold the back button to pop to the root view controller).

+ +

Visibility depth is used to automatically adjust ranges on range controllers (and thus free up memory) and can +be used to reduce memory usage of other items as well.

+
+ + + + + + + +
+

Declared In

+

ASVisibilityProtocols.h

+
+ + +
+
+
+ +

– visibilityDepthDidChange +required method

+ +
+
+ +
+ + +
+

Called when visibility depth changes

+
+ + + +
- (void)visibilityDepthDidChange
+ + + + + + + + + +
+

Discussion

+

@c visibilityDepthDidChange is called whenever the visibility depth of the represented view controller +has changed.

+ +

If implemented by a view controller container, use this method to notify child view controllers that their view +depth has changed @see ASNavigationController.m

+ +

If implemented on an ASViewController, use this method to reduce or increase the resources that your +view controller uses. A higher visibility depth view controller should decrease it’s resource usage, a lower +visibility depth controller should pre-warm resources in preperation for a display at 0 depth.

+ +

ASViewController implements this method and reduces / increases range mode of supporting nodes (such as ASCollectionNode +and ASTableNode).

+
+ + + + + +
+

See Also

+ +
+ + + +
+

Declared In

+

ASVisibilityProtocols.h

+
+ + +
+
+
+
+ +
+ + + + + + +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/css/scss/_index.scss b/docs/appledoc/css/scss/_index.scss new file mode 100755 index 0000000000..6a57ec5dc3 --- /dev/null +++ b/docs/appledoc/css/scss/_index.scss @@ -0,0 +1,17 @@ +.index-container { + -webkit-flex-direction: column; + flex-direction: column; + + @media (min-width: $desktop-min-width) { + display: flex; + -webkit-flex-direction: row; + flex-direction: row; + -webkit-flex-wrap: wrap; + flex-wrap: wrap; + } + + .index-column { + -webkit-flex: 1 1 33%; + flex: 1 1 33%; + } +} diff --git a/docs/appledoc/css/scss/_layout.scss b/docs/appledoc/css/scss/_layout.scss new file mode 100755 index 0000000000..da46aef079 --- /dev/null +++ b/docs/appledoc/css/scss/_layout.scss @@ -0,0 +1,302 @@ +* { + box-sizing: border-box; +} + +.clear { + clear: both; +} + +.clearfix { + &:before, &:after { + clear: both; + display: table; + content: ""; + } +} + +.xcode .hide-in-xcode { + display: none; +} + +body { + font: 62.5% $body-font; + background: $body-background; + + @media (max-width: $mobile-max-width) { + background-color: $content-background; + } +} + +h1, h2, h3 { + font-weight: 300; + color: #808080; +} + +h1 { + font-size: 2em; + color: #000; +} + +h4 { + font-size: 13px; + line-height: 1.5; + margin: 21px 0 0 0; +} + +a { + color: $tint-color; + text-decoration: none; +} + +pre, code { + font-family: $code-font; + word-wrap: break-word; +} + +pre > code, .method-declaration code { + display: inline-block; + font-size: .85em; + padding: 4px 0 4px 10px; + border-left: 5px solid rgba(0, 155, 51, .2); + + &:before { + content: "Objective-C"; + display: block; + + font: 9px/1 $body-font; + color: #009b33; + text-transform: uppercase; + letter-spacing: 2px; + padding-bottom: 6px; + } +} + +pre > code { + font-size: inherit; +} + +table, th, td { + border: 1px solid #e9e9e9; +} + +table { + width: 100%; +} + +th, td { + padding: 7px; + + > :first-child { + margin-top: 0; + } + + > :last-child { + margin-bottom: 0; + } +} + +.container { + @extend .clearfix; + + max-width: 980px; + padding: 0 10px; + margin: 0 auto; + + @media (max-width: $mobile-max-width) { + padding: 0; + } +} + +header { + position: fixed; + top: 0; + left: 0; + width: 100%; + z-index: 2; + + background: #414141; + color: #fff; + font-size: 1.1em; + line-height: 25px; + letter-spacing: .05em; + + #library-title { + float: left; + } + + #developer-home { + float: right; + } + + h1 { + font-size: inherit; + font-weight: inherit; + margin: 0; + } + + p { + margin: 0; + } + + h1, a { + color: inherit; + } + + @media (max-width: $mobile-max-width) { + .container { + padding: 0 10px; + } + } +} + +aside { + position: fixed; + top: 25px; + left: 0; + width: 100%; + height: 25px; + z-index: 2; + + font-size: 1.1em; + + #header-buttons { + background: rgba(255, 255, 255, .8); + margin: 0 1px; + padding: 0; + list-style: none; + text-align: right; + line-height: 32px; + + li { + display: inline-block; + cursor: pointer; + padding: 0 10px; + } + + label, select { + cursor: inherit; + } + + #on-this-page { + position: relative; + + .chevron { + display: inline-block; + width: 14px; + height: 4px; + position: relative; + + .chevy { + background: #878787; + height: 2px; + position: absolute; + width: 10px; + + &.chevron-left { + left: 0; + transform: rotateZ(45deg) scale(0.6); + } + + &.chevron-right { + right: 0; + transform: rotateZ(-45deg) scale(0.6); + } + } + } + + #jump-to { + opacity: 0; + font-size: 16px; + + position: absolute; + top: 5px; + left: 0; + width: 100%; + height: 100%; + } + } + } +} + +article { + margin-top: 25px; + + #content { + @extend .clearfix; + + background: $content-background; + border: 1px solid $content-border; + padding: 15px 25px 30px 25px; + + font-size: 1.4em; + line-height: 1.45; + + position: relative; + + @media (max-width: $mobile-max-width) { + padding: 15px 10px 20px 10px; + border: none; + } + + .navigation-top { + position: absolute; + top: 15px; + right: 25px; + } + + .title { + margin: 21px 0 0 0; + padding: 15px 0; + } + + p { + color: #414141; + margin: 0 0 15px 0; + } + + th, td { + p:last-child { + margin-bottom: 0; + } + } + + main { + ul { + list-style: none; + margin-left: 24px; + margin-bottom: 12px; + padding: 0; + + li { + position: relative; + padding-left: 1.3em; + + &:before { + content: "\02022"; + + color: #414141; + font-size: 1.08em; + line-height: 1; + + position: absolute; + left: 0; + padding-top: 2px; + } + } + } + } + + footer { + @extend .clearfix; + + .footer-copyright { + margin: 70px 25px 10px 0; + } + + p { + font-size: .71em; + color: #a0a0a0; + } + } + } +} diff --git a/docs/appledoc/css/scss/_normalize.scss b/docs/appledoc/css/scss/_normalize.scss new file mode 100755 index 0000000000..9b8848a5cf --- /dev/null +++ b/docs/appledoc/css/scss/_normalize.scss @@ -0,0 +1,581 @@ +/* ========================================================================== + Normalize.scss settings + ========================================================================== */ +/** + * Includes legacy browser support IE6/7 + * + * Set to false if you want to drop support for IE6 and IE7 + */ + +$legacy_browser_support: false !default; + +/* Base + ========================================================================== */ + +/** + * 1. Set default font family to sans-serif. + * 2. Prevent iOS text size adjust after orientation change, without disabling + * user zoom. + * 3. Corrects text resizing oddly in IE 6/7 when body `font-size` is set using + * `em` units. + */ + +html { + font-family: sans-serif; /* 1 */ + -ms-text-size-adjust: 100%; /* 2 */ + -webkit-text-size-adjust: 100%; /* 2 */ + @if $legacy_browser_support { + *font-size: 100%; /* 3 */ + } +} + +/** + * Remove default margin. + */ + +body { + margin: 0; +} + +/* HTML5 display definitions + ========================================================================== */ + +/** + * Correct `block` display not defined for any HTML5 element in IE 8/9. + * Correct `block` display not defined for `details` or `summary` in IE 10/11 + * and Firefox. + * Correct `block` display not defined for `main` in IE 11. + */ + +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +main, +menu, +nav, +section, +summary { + display: block; +} + +/** + * 1. Correct `inline-block` display not defined in IE 6/7/8/9 and Firefox 3. + * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. + */ + +audio, +canvas, +progress, +video { + display: inline-block; /* 1 */ + vertical-align: baseline; /* 2 */ + @if $legacy_browser_support { + *display: inline; + *zoom: 1; + } +} + +/** + * Prevents modern browsers from displaying `audio` without controls. + * Remove excess height in iOS 5 devices. + */ + +audio:not([controls]) { + display: none; + height: 0; +} + +/** + * Address `[hidden]` styling not present in IE 8/9/10. + * Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22. + */ + +[hidden], +template { + display: none; +} + +/* Links + ========================================================================== */ + +/** + * Remove the gray background color from active links in IE 10. + */ + +a { + background-color: transparent; +} + +/** + * Improve readability when focused and also mouse hovered in all browsers. + */ + +a { + &:active, &:hover { + outline: 0; + }; +} + +/* Text-level semantics + ========================================================================== */ + +/** + * Address styling not present in IE 8/9/10/11, Safari, and Chrome. + */ + +abbr[title] { + border-bottom: 1px dotted; +} + +/** + * Address style set to `bolder` in Firefox 4+, Safari, and Chrome. + */ + +b, +strong { + font-weight: bold; +} + +@if $legacy_browser_support { + blockquote { + margin: 1em 40px; + } +} + +/** + * Address styling not present in Safari and Chrome. + */ + +dfn { + font-style: italic; +} + +/** + * Address variable `h1` font-size and margin within `section` and `article` + * contexts in Firefox 4+, Safari, and Chrome. + */ + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +@if $legacy_browser_support { + h2 { + font-size: 1.5em; + margin: 0.83em 0; + } + + h3 { + font-size: 1.17em; + margin: 1em 0; + } + + h4 { + font-size: 1em; + margin: 1.33em 0; + } + + h5 { + font-size: 0.83em; + margin: 1.67em 0; + } + + h6 { + font-size: 0.67em; + margin: 2.33em 0; + } +} + +/** + * Addresses styling not present in IE 8/9. + */ + +mark { + background: #ff0; + color: #000; +} + +@if $legacy_browser_support { + + /** + * Addresses margins set differently in IE 6/7. + */ + + p, + pre { + *margin: 1em 0; + } + + /* + * Addresses CSS quotes not supported in IE 6/7. + */ + + q { + *quotes: none; + } + + /* + * Addresses `quotes` property not supported in Safari 4. + */ + + q:before, + q:after { + content: ''; + content: none; + } +} + +/** + * Address inconsistent and variable font size in all browsers. + */ + +small { + font-size: 80%; +} + +/** + * Prevent `sub` and `sup` affecting `line-height` in all browsers. + */ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sup { + top: -0.5em; +} + +sub { + bottom: -0.25em; +} + +@if $legacy_browser_support { + + /* ========================================================================== + Lists + ========================================================================== */ + + /* + * Addresses margins set differently in IE 6/7. + */ + + dl, + menu, + ol, + ul { + *margin: 1em 0; + } + + dd { + *margin: 0 0 0 40px; + } + + /* + * Addresses paddings set differently in IE 6/7. + */ + + menu, + ol, + ul { + *padding: 0 0 0 40px; + } + + /* + * Corrects list images handled incorrectly in IE 7. + */ + + nav ul, + nav ol { + *list-style: none; + *list-style-image: none; + } + +} + +/* Embedded content + ========================================================================== */ + +/** + * 1. Remove border when inside `a` element in IE 8/9/10. + * 2. Improves image quality when scaled in IE 7. + */ + +img { + border: 0; + @if $legacy_browser_support { + *-ms-interpolation-mode: bicubic; /* 2 */ + } +} + +/** + * Correct overflow not hidden in IE 9/10/11. + */ + +svg:not(:root) { + overflow: hidden; +} + +/* Grouping content + ========================================================================== */ + +/** + * Address margin not present in IE 8/9 and Safari. + */ + +figure { + margin: 1em 40px; +} + +/** + * Address differences between Firefox and other browsers. + */ + +hr { + -moz-box-sizing: content-box; + box-sizing: content-box; + height: 0; +} + +/** + * Contain overflow in all browsers. + */ + +pre { + overflow: auto; +} + +/** + * Address odd `em`-unit font size rendering in all browsers. + * Correct font family set oddly in IE 6, Safari 4/5, and Chrome. + */ + +code, +kbd, +pre, +samp { + font-family: monospace, monospace; + @if $legacy_browser_support { + _font-family: 'courier new', monospace; + } + font-size: 1em; +} + +/* Forms + ========================================================================== */ + +/** + * Known limitation: by default, Chrome and Safari on OS X allow very limited + * styling of `select`, unless a `border` property is set. + */ + +/** + * 1. Correct color not being inherited. + * Known issue: affects color of disabled elements. + * 2. Correct font properties not being inherited. + * 3. Address margins set differently in Firefox 4+, Safari, and Chrome. + * 4. Improves appearance and consistency in all browsers. + */ + +button, +input, +optgroup, +select, +textarea { + color: inherit; /* 1 */ + font: inherit; /* 2 */ + margin: 0; /* 3 */ + @if $legacy_browser_support { + vertical-align: baseline; /* 3 */ + *vertical-align: middle; /* 3 */ + } +} + +/** + * Address `overflow` set to `hidden` in IE 8/9/10/11. + */ + +button { + overflow: visible; +} + +/** + * Address inconsistent `text-transform` inheritance for `button` and `select`. + * All other form control elements do not inherit `text-transform` values. + * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera. + * Correct `select` style inheritance in Firefox. + */ + +button, +select { + text-transform: none; +} + +/** + * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` + * and `video` controls. + * 2. Correct inability to style clickable `input` types in iOS. + * 3. Improve usability and consistency of cursor style between image-type + * `input` and others. + * 4. Removes inner spacing in IE 7 without affecting normal text inputs. + * Known issue: inner spacing remains in IE 6. + */ + +button, +html input[type="button"], /* 1 */ +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; /* 2 */ + cursor: pointer; /* 3 */ + @if $legacy_browser_support { + *overflow: visible; /* 4 */ + } +} + +/** + * Re-set default cursor for disabled elements. + */ + +button[disabled], +html input[disabled] { + cursor: default; +} + +/** + * Remove inner padding and border in Firefox 4+. + */ + +button::-moz-focus-inner, +input::-moz-focus-inner { + border: 0; + padding: 0; +} + +/** + * Address Firefox 4+ setting `line-height` on `input` using `!important` in + * the UA stylesheet. + */ + +input { + line-height: normal; +} + +/** + * 1. Address box sizing set to `content-box` in IE 8/9/10. + * 2. Remove excess padding in IE 8/9/10. + * Known issue: excess padding remains in IE 6. + */ + +input[type="checkbox"], +input[type="radio"] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ + @if $legacy_browser_support { + *height: 13px; /* 3 */ + *width: 13px; /* 3 */ + } +} + +/** + * Fix the cursor style for Chrome's increment/decrement buttons. For certain + * `font-size` values of the `input`, it causes the cursor style of the + * decrement button to change from `default` to `text`. + */ + +input[type="number"]::-webkit-inner-spin-button, +input[type="number"]::-webkit-outer-spin-button { + height: auto; +} + +/** + * 1. Address `appearance` set to `searchfield` in Safari and Chrome. + * 2. Address `box-sizing` set to `border-box` in Safari and Chrome + * (include `-moz` to future-proof). + */ + +input[type="search"] { + -webkit-appearance: textfield; /* 1 */ + -moz-box-sizing: content-box; + -webkit-box-sizing: content-box; /* 2 */ + box-sizing: content-box; +} + +/** + * Remove inner padding and search cancel button in Safari and Chrome on OS X. + * Safari (but not Chrome) clips the cancel button when the search input has + * padding (and `textfield` appearance). + */ + +input[type="search"]::-webkit-search-cancel-button, +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +/** + * Define consistent border, margin, and padding. + */ + +fieldset { + border: 1px solid #c0c0c0; + margin: 0 2px; + padding: 0.35em 0.625em 0.75em; +} + +/** + * 1. Correct `color` not being inherited in IE 8/9/10/11. + * 2. Remove padding so people aren't caught out if they zero out fieldsets. + * 3. Corrects text not wrapping in Firefox 3. + * 4. Corrects alignment displayed oddly in IE 6/7. + */ + +legend { + border: 0; /* 1 */ + padding: 0; /* 2 */ + @if $legacy_browser_support { + white-space: normal; /* 3 */ + *margin-left: -7px; /* 4 */ + } +} + +/** + * Remove default vertical scrollbar in IE 8/9/10/11. + */ + +textarea { + overflow: auto; +} + +/** + * Don't inherit the `font-weight` (applied by a rule above). + * NOTE: the default cannot safely be changed in Chrome and Safari on OS X. + */ + +optgroup { + font-weight: bold; +} + +/* Tables + ========================================================================== */ + +/** + * Remove most spacing between table cells. + */ + +table { + border-collapse: collapse; + border-spacing: 0; +} + +td, +th { + padding: 0; +} diff --git a/docs/appledoc/css/scss/_object.scss b/docs/appledoc/css/scss/_object.scss new file mode 100755 index 0000000000..22eebd87d0 --- /dev/null +++ b/docs/appledoc/css/scss/_object.scss @@ -0,0 +1,89 @@ +.section-specification { + table { + width: auto; + + th { + text-align: left; + } + } +} + +.method-title { + margin-left: -15px; + margin-bottom: 8px; + transition: margin-left .3s ease-out; + + .section-method.hide & { + margin-left: 0; + } + + code { + font-weight: 400; + font-size: .85em; + } +} + +.method-info { + background: $object-background; + border-bottom: 1px solid $object-border; + margin: 0 -25px; + padding: 20px 25px 0 25px; + transition: height .3s ease-out; + + position: relative; + + .pointy-thing { + background: $content-background; + height: 10px; + border-bottom: 1px solid $object-border; + margin: -20px -25px 16px -25px; + + &:before { + display: inline-block; + content: ""; + + background: $object-background; + border: 1px solid $object-border; + border-bottom: 0; + border-right: 0; + + position: absolute; + left: 21px; + top: 3px; + width: 12px; + height: 12px; + transform: rotate(45deg); + } + } + + .method-subsection { + margin-bottom: 15px; + + .argument-name { + width: 1px; + text-align: right; + + code { + color: #808080; + font-style: italic; + font-weight: 400; + } + } + } +} + +.section-method { + &.hide .method-info { + height: 0 !important; + overflow: hidden; + display: none; + } + + &.hide.animating .method-info { + display: block; + } + + &.animating .method-info { + overflow: hidden; + } +} diff --git a/docs/appledoc/css/scss/_print.scss b/docs/appledoc/css/scss/_print.scss new file mode 100755 index 0000000000..61bdf99f86 --- /dev/null +++ b/docs/appledoc/css/scss/_print.scss @@ -0,0 +1,42 @@ +@media print { + body { + background: #fff; + padding: 8px; + } + + header { + position: static; + background: #fff; + color: #000; + } + + aside { + display: none; + } + + .container { + max-width: none; + padding: 0; + } + + article { + margin-top: 0; + + #content { + border: 0; + background: #fff; + padding: 15px 0 0 0; + + .title { + margin-top: 0; + padding-top: 0; + } + } + } + + .method-info { + &, & .pointy-thing { + background: #fff; + } + } +} diff --git a/docs/appledoc/css/scss/_variables.scss b/docs/appledoc/css/scss/_variables.scss new file mode 100755 index 0000000000..38e072d310 --- /dev/null +++ b/docs/appledoc/css/scss/_variables.scss @@ -0,0 +1,12 @@ +$body-font: -apple-system-font, "Helvetica Neue", Helvetica, sans-serif; +$code-font: "Source Code Pro", Monaco, Menlo, Consolas, monospace; + +$body-background: #f2f2f2; +$content-background: #fff; +$content-border: #e9e9e9; +$tint-color: #08c; +$object-background: #f9f9f9; +$object-border: #e9e9e9; + +$mobile-max-width: 650px; +$desktop-min-width: 768px; \ No newline at end of file diff --git a/docs/appledoc/css/scss/_xcode.scss b/docs/appledoc/css/scss/_xcode.scss new file mode 100755 index 0000000000..340b1f6b80 --- /dev/null +++ b/docs/appledoc/css/scss/_xcode.scss @@ -0,0 +1,29 @@ +.xcode { + header, aside { + display: none; + } + + .container { + padding: 0; + } + + article { + margin-top: 0; + + #content { + border: 0; + margin: 0; + } + } + + .method-info { + &, .section-method.hide & { + max-height: auto; + overflow: visible; + + &.hiding { + display: block; + } + } + } +} diff --git a/docs/appledoc/css/scss/style.scss b/docs/appledoc/css/scss/style.scss new file mode 100755 index 0000000000..648a6086ba --- /dev/null +++ b/docs/appledoc/css/scss/style.scss @@ -0,0 +1 @@ +@import "variables", "normalize", "layout", "index", "object", "print", "xcode"; diff --git a/docs/appledoc/css/style.css b/docs/appledoc/css/style.css new file mode 100755 index 0000000000..d9d59dd080 --- /dev/null +++ b/docs/appledoc/css/style.css @@ -0,0 +1,2 @@ +html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}*{box-sizing:border-box}.clear{clear:both}.clearfix:before,.container:before,article #content:before,article #content footer:before,.clearfix:after,.container:after,article #content:after,article #content footer:after{clear:both;display:table;content:""}.xcode .hide-in-xcode{display:none}body{font:62.5% -apple-system-font,"Helvetica Neue",Helvetica,sans-serif;background:#f2f2f2}@media (max-width: 650px){body{background-color:#fff}}h1,h2,h3{font-weight:300;color:#808080}h1{font-size:2em;color:#000}h4{font-size:13px;line-height:1.5;margin:21px 0 0 0}a{color:#08c;text-decoration:none}pre,code{font-family:"Source Code Pro",Monaco,Menlo,Consolas,monospace;word-wrap:break-word}pre>code,.method-declaration code{display:inline-block;font-size:.85em;padding:4px 0 4px 10px;border-left:5px solid rgba(0,155,51,0.2)}pre>code:before,.method-declaration code:before{content:"Objective-C";display:block;font:9px/1 -apple-system-font,"Helvetica Neue",Helvetica,sans-serif;color:#009b33;text-transform:uppercase;letter-spacing:2px;padding-bottom:6px}pre>code{font-size:inherit}table,th,td{border:1px solid #e9e9e9}table{width:100%}th,td{padding:7px}th>:first-child,td>:first-child{margin-top:0}th>:last-child,td>:last-child{margin-bottom:0}.container{max-width:980px;padding:0 10px;margin:0 auto}@media (max-width: 650px){.container{padding:0}}header{position:fixed;top:0;left:0;width:100%;z-index:2;background:#414141;color:#fff;font-size:1.1em;line-height:25px;letter-spacing:.05em}header #library-title{float:left}header #developer-home{float:right}header h1{font-size:inherit;font-weight:inherit;margin:0}header p{margin:0}header h1,header a{color:inherit}@media (max-width: 650px){header .container{padding:0 10px}}aside{position:fixed;top:25px;left:0;width:100%;height:25px;z-index:2;font-size:1.1em}aside #header-buttons{background:rgba(255,255,255,0.8);margin:0 1px;padding:0;list-style:none;text-align:right;line-height:32px}aside #header-buttons li{display:inline-block;cursor:pointer;padding:0 10px}aside #header-buttons label,aside #header-buttons select{cursor:inherit}aside #header-buttons #on-this-page{position:relative}aside #header-buttons #on-this-page .chevron{display:inline-block;width:14px;height:4px;position:relative}aside #header-buttons #on-this-page .chevron .chevy{background:#878787;height:2px;position:absolute;width:10px}aside #header-buttons #on-this-page .chevron .chevy.chevron-left{left:0;transform:rotateZ(45deg) scale(0.6)}aside #header-buttons #on-this-page .chevron .chevy.chevron-right{right:0;transform:rotateZ(-45deg) scale(0.6)}aside #header-buttons #on-this-page #jump-to{opacity:0;font-size:16px;position:absolute;top:5px;left:0;width:100%;height:100%}article{margin-top:25px}article #content{background:#fff;border:1px solid #e9e9e9;padding:15px 25px 30px 25px;font-size:1.4em;line-height:1.45;position:relative}@media (max-width: 650px){article #content{padding:15px 10px 20px 10px;border:none}}article #content .navigation-top{position:absolute;top:15px;right:25px}article #content .title{margin:21px 0 0 0;padding:15px 0}article #content p{color:#414141;margin:0 0 15px 0}article #content th p:last-child,article #content td p:last-child{margin-bottom:0}article #content main ul{list-style:none;margin-left:24px;margin-bottom:12px;padding:0}article #content main ul li{position:relative;padding-left:1.3em}article #content main ul li:before{content:"\02022";color:#414141;font-size:1.08em;line-height:1;position:absolute;left:0;padding-top:2px}article #content footer .footer-copyright{margin:70px 25px 10px 0}article #content footer p{font-size:.71em;color:#a0a0a0}.index-container{-webkit-flex-direction:column;flex-direction:column}@media (min-width: 768px){.index-container{display:flex;-webkit-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;flex-wrap:wrap}}.index-container .index-column{-webkit-flex:1 1 33%;flex:1 1 33%}.section-specification table{width:auto}.section-specification table th{text-align:left}.method-title{margin-left:-15px;margin-bottom:8px;transition:margin-left .3s ease-out}.section-method.hide .method-title{margin-left:0}.method-title code{font-weight:400;font-size:.85em}.method-info{background:#f9f9f9;border-bottom:1px solid #e9e9e9;margin:0 -25px;padding:20px 25px 0 25px;transition:height .3s ease-out;position:relative}.method-info .pointy-thing{background:#fff;height:10px;border-bottom:1px solid #e9e9e9;margin:-20px -25px 16px -25px}.method-info .pointy-thing:before{display:inline-block;content:"";background:#f9f9f9;border:1px solid #e9e9e9;border-bottom:0;border-right:0;position:absolute;left:21px;top:3px;width:12px;height:12px;-webkit-transform:rotate(45deg);transform:rotate(45deg) }.method-info .method-subsection{margin-bottom:15px}.method-info .method-subsection .argument-name{width:1px;text-align:right}.method-info .method-subsection .argument-name code{color:#808080;font-style:italic;font-weight:400}.section-method.hide .method-info{height:0 !important;overflow:hidden;display:none}.section-method.hide.animating .method-info{display:block}.section-method.animating .method-info{overflow:hidden}@media print{body{background:#fff;padding:8px}header{position:static;background:#fff;color:#000}aside{display:none}.container{max-width:none;padding:0}article{margin-top:0}article #content{border:0;background:#fff;padding:15px 0 0 0}article #content .title{margin-top:0;padding-top:0}.method-info,.method-info .pointy-thing{background:#fff}}.xcode header,.xcode aside{display:none}.xcode .container{padding:0}.xcode article{margin-top:0}.xcode article #content{border:0;margin:0}.xcode .method-info,.section-method.hide .xcode .method-info{max-height:auto;overflow:visible}.xcode .method-info.hiding,.section-method.hide .xcode .method-info.hiding{display:block} + diff --git a/docs/appledoc/hierarchy.html b/docs/appledoc/hierarchy.html new file mode 100755 index 0000000000..f9eb61138c --- /dev/null +++ b/docs/appledoc/hierarchy.html @@ -0,0 +1,402 @@ + + + + + + Hierarchy + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

Hierarchy

+ + +
+

Class Hierarchy

+ + + +
+ + + +
+ +

Protocol References

+ + + +

Constant References

+ + + +

Category References

+ + +
+ + +
+ +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/img/button_bar_background.png b/docs/appledoc/img/button_bar_background.png new file mode 100755 index 0000000000..71d1019bc0 Binary files /dev/null and b/docs/appledoc/img/button_bar_background.png differ diff --git a/docs/appledoc/img/disclosure.png b/docs/appledoc/img/disclosure.png new file mode 100755 index 0000000000..4c5cbf4456 Binary files /dev/null and b/docs/appledoc/img/disclosure.png differ diff --git a/docs/appledoc/img/disclosure_open.png b/docs/appledoc/img/disclosure_open.png new file mode 100755 index 0000000000..82396fed29 Binary files /dev/null and b/docs/appledoc/img/disclosure_open.png differ diff --git a/docs/appledoc/img/library_background.png b/docs/appledoc/img/library_background.png new file mode 100755 index 0000000000..3006248afe Binary files /dev/null and b/docs/appledoc/img/library_background.png differ diff --git a/docs/appledoc/img/title_background.png b/docs/appledoc/img/title_background.png new file mode 100755 index 0000000000..846e4968d0 Binary files /dev/null and b/docs/appledoc/img/title_background.png differ diff --git a/docs/appledoc/index.html b/docs/appledoc/index.html new file mode 100755 index 0000000000..27076d4fb6 --- /dev/null +++ b/docs/appledoc/index.html @@ -0,0 +1,340 @@ + + + + + + Reference + + + + + + +
+
+ +

+ +

+ +

+ AsyncDisplayKit +

+ +
+
+ + + +
+
+
+
+

Reference

+ + + +
+ + + + + + + +
+ +

Protocol References

+ + + + +

Constant References

+ + + + +

Category References

+ + +
+ +
+ +
+ +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/appledoc/js/script.js b/docs/appledoc/js/script.js new file mode 100755 index 0000000000..4074361c44 --- /dev/null +++ b/docs/appledoc/js/script.js @@ -0,0 +1,59 @@ +function $() { + return document.querySelector.apply(document, arguments); +} + +if (navigator.userAgent.indexOf("Xcode") != -1) { + document.documentElement.classList.add("xcode"); +} + +var jumpTo = $("#jump-to"); + +if (jumpTo) { + jumpTo.addEventListener("change", function(e) { + location.hash = this.options[this.selectedIndex].value; + }); +} + +function hashChanged() { + if (/^#\/\/api\//.test(location.hash)) { + var element = document.querySelector("a[name='" + location.hash.substring(1) + "']"); + + if (!element) { + return; + } + + element = element.parentNode; + + element.classList.remove("hide"); + fixScrollPosition(element); + } +} + +function fixScrollPosition(element) { + var scrollTop = element.offsetTop - 150; + document.documentElement.scrollTop = scrollTop; + document.body.scrollTop = scrollTop; +} + +[].forEach.call(document.querySelectorAll(".section-method"), function(element) { + element.classList.add("hide"); + + element.querySelector(".method-title a").addEventListener("click", function(e) { + var info = element.querySelector(".method-info"), + infoContainer = element.querySelector(".method-info-container"); + + element.classList.add("animating"); + info.style.height = (infoContainer.clientHeight + 40) + "px"; + fixScrollPosition(element); + element.classList.toggle("hide"); + if (element.classList.contains("hide")) { + e.preventDefault(); + } + setTimeout(function() { + element.classList.remove("animating"); + }, 300); + }); +}); + +window.addEventListener("hashchange", hashChanged); +hashChanged(); diff --git a/docs/appledocs.md b/docs/appledocs.md new file mode 100755 index 0000000000..541ea17cfc --- /dev/null +++ b/docs/appledocs.md @@ -0,0 +1,7 @@ +--- +title: api +layout: appledocs +permalink: /appledocs.html +--- + + diff --git a/docs/index.md b/docs/index.md new file mode 100755 index 0000000000..ad660777f4 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,23 @@ +--- +layout: default +title: A UI Framework for Effortless Responsiveness +id: home +--- + +
+
+
Keeps the most complex iOS user interfaces smooth and responsive.
+ Get Started + View on GitHub +
+
+ +
+
+

Texture is an iOS framework built on top of UIKit that keeps even the most complex user interfaces smooth and responsive. It was originally built to make Facebook's Paper possible, and goes hand-in-hand with pop's physics-based animations — but it's just as powerful with UIKit Dynamics and conventional app designs. More recently, it was used to power Pinterest's app rewrite.

+ +

As the framework has grown, many features have been added that can save developers tons of time by eliminating common boilerplate style structures common in modern iOS apps. If you've ever dealt with cell reuse bugs, tried to performantly preload data for a page or scroll style interface or even just tried to keep your app from dropping too many frames you can benefit from integrating Texture.

+ +

To learn more, check out our docs!

+
+
diff --git a/docs/showcase.md b/docs/showcase.md new file mode 100755 index 0000000000..ab6c061239 --- /dev/null +++ b/docs/showcase.md @@ -0,0 +1,200 @@ +--- +title: Showcase +layout: default +permalink: /showcase.html +--- + +
+ +
+

Who's using Texture?

+

If you're curious to see what can be accomplished with Texture, check out these apps.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ Facebook +
+ Introducing AsyncDisplayKit: For smooth and responsive apps on iOS +
+ +
+ Pinterest +
+ Re-architecting Pinterest's iOS app +
+ +
+ Buffer +
+ Smooth Scrolling in Buffer for iOS: How (and Why) We Implemented AsyncDisplayKit +
+ +
+ Auxy +
+ 2016 Apple Design Award Winner +
+ +
+ NYT +
+ +
+ NFL +
+ +
+ Yahoo +
+ +
+ Kwaver Music +
+ +
+ This is Money +
+ +
+ JSwipe +
+ +
+ Peloton Cycle +
+ +
+ 即刻 - 不错过你惦记的每一件小事 +
+ +
+ Tripstr +
+ +
+ Fyuse +
+ +
+ ClassDojo +
+ +
+ GitBucket +
+ +
+ Roposo +
+ +
+ Mishu +
+ +
+ InstaBuy +
+ +
+ Eniro +
+ +
+ Kayako +
+ +
+ Yep +
+ +
+ HakkerJobs +
+ +
+

If you built an app using Texture, we'd love to have your app in this showcase!

+

If you would like to have your app added or do not want your app featured on this page, please email textureframework@gmail.com.

+ +
+ +
diff --git a/docs/slack.md b/docs/slack.md new file mode 100755 index 0000000000..121dc4d51e --- /dev/null +++ b/docs/slack.md @@ -0,0 +1,10 @@ +--- +title: slack +layout: slack +permalink: /slack.html +--- + + + + +If the auto-invite link above does not work for you, please email textureframework@gmail.com for an invite. diff --git a/docs/static/images/1-shuffle-crop.png b/docs/static/images/1-shuffle-crop.png new file mode 100755 index 0000000000..d1e0a83b0c Binary files /dev/null and b/docs/static/images/1-shuffle-crop.png differ diff --git a/docs/static/images/Texturelogo.png b/docs/static/images/Texturelogo.png new file mode 100644 index 0000000000..cf669bef16 Binary files /dev/null and b/docs/static/images/Texturelogo.png differ diff --git a/docs/static/images/Texturelogo@2x.png b/docs/static/images/Texturelogo@2x.png new file mode 100644 index 0000000000..3ad259d962 Binary files /dev/null and b/docs/static/images/Texturelogo@2x.png differ diff --git a/docs/static/images/basicMap.png b/docs/static/images/basicMap.png new file mode 100755 index 0000000000..b387adc082 Binary files /dev/null and b/docs/static/images/basicMap.png differ diff --git a/docs/static/images/catsButt.png b/docs/static/images/catsButt.png new file mode 100755 index 0000000000..0d442faf4d Binary files /dev/null and b/docs/static/images/catsButt.png differ diff --git a/docs/static/images/catsFace.png b/docs/static/images/catsFace.png new file mode 100755 index 0000000000..4be93671f9 Binary files /dev/null and b/docs/static/images/catsFace.png differ diff --git a/docs/static/images/catsMiddle.png b/docs/static/images/catsMiddle.png new file mode 100755 index 0000000000..f6526eebef Binary files /dev/null and b/docs/static/images/catsMiddle.png differ diff --git a/docs/static/images/clip-corners.png b/docs/static/images/clip-corners.png new file mode 100755 index 0000000000..cc43a0a974 Binary files /dev/null and b/docs/static/images/clip-corners.png differ diff --git a/docs/static/images/component-controllers.png b/docs/static/images/component-controllers.png new file mode 100755 index 0000000000..e882cd1f7e Binary files /dev/null and b/docs/static/images/component-controllers.png differ diff --git a/docs/static/images/corner-rounding-flowchart-v2.png b/docs/static/images/corner-rounding-flowchart-v2.png new file mode 100755 index 0000000000..71b50848a1 Binary files /dev/null and b/docs/static/images/corner-rounding-flowchart-v2.png differ diff --git a/docs/static/images/corner-rounding-movement.png b/docs/static/images/corner-rounding-movement.png new file mode 100755 index 0000000000..1749d6683c Binary files /dev/null and b/docs/static/images/corner-rounding-movement.png differ diff --git a/docs/static/images/corner-rounding-overlap.png b/docs/static/images/corner-rounding-overlap.png new file mode 100755 index 0000000000..b69b3825e6 Binary files /dev/null and b/docs/static/images/corner-rounding-overlap.png differ diff --git a/docs/static/images/corner-rounding-scrolling.png b/docs/static/images/corner-rounding-scrolling.png new file mode 100755 index 0000000000..ef29151707 Binary files /dev/null and b/docs/static/images/corner-rounding-scrolling.png differ diff --git a/docs/static/images/example-app-screenshots/ASCollectionView.png b/docs/static/images/example-app-screenshots/ASCollectionView.png new file mode 100755 index 0000000000..3aaff368e5 Binary files /dev/null and b/docs/static/images/example-app-screenshots/ASCollectionView.png differ diff --git a/docs/static/images/example-app-screenshots/ASDKLayoutTransition.gif b/docs/static/images/example-app-screenshots/ASDKLayoutTransition.gif new file mode 100755 index 0000000000..c75467164d Binary files /dev/null and b/docs/static/images/example-app-screenshots/ASDKLayoutTransition.gif differ diff --git a/docs/static/images/example-app-screenshots/ASDKTube.gif b/docs/static/images/example-app-screenshots/ASDKTube.gif new file mode 100755 index 0000000000..956a5c0440 Binary files /dev/null and b/docs/static/images/example-app-screenshots/ASDKTube.gif differ diff --git a/docs/static/images/example-app-screenshots/ASDKgram.png b/docs/static/images/example-app-screenshots/ASDKgram.png new file mode 100755 index 0000000000..358dae3805 Binary files /dev/null and b/docs/static/images/example-app-screenshots/ASDKgram.png differ diff --git a/docs/static/images/example-app-screenshots/ASMapNode.png b/docs/static/images/example-app-screenshots/ASMapNode.png new file mode 100755 index 0000000000..aa605349fe Binary files /dev/null and b/docs/static/images/example-app-screenshots/ASMapNode.png differ diff --git a/docs/static/images/example-app-screenshots/ASTableViewStressTest.png b/docs/static/images/example-app-screenshots/ASTableViewStressTest.png new file mode 100755 index 0000000000..cd7aae2d9d Binary files /dev/null and b/docs/static/images/example-app-screenshots/ASTableViewStressTest.png differ diff --git a/docs/static/images/example-app-screenshots/ASViewController.png b/docs/static/images/example-app-screenshots/ASViewController.png new file mode 100755 index 0000000000..545f3c8d63 Binary files /dev/null and b/docs/static/images/example-app-screenshots/ASViewController.png differ diff --git a/docs/static/images/example-app-screenshots/AsyncDisplayKitOverview.png b/docs/static/images/example-app-screenshots/AsyncDisplayKitOverview.png new file mode 100755 index 0000000000..4941292c4b Binary files /dev/null and b/docs/static/images/example-app-screenshots/AsyncDisplayKitOverview.png differ diff --git a/docs/static/images/example-app-screenshots/BackgroundPropertySetting.gif b/docs/static/images/example-app-screenshots/BackgroundPropertySetting.gif new file mode 100755 index 0000000000..e2655ef235 Binary files /dev/null and b/docs/static/images/example-app-screenshots/BackgroundPropertySetting.gif differ diff --git a/docs/static/images/example-app-screenshots/CatDealsCollectionView.png b/docs/static/images/example-app-screenshots/CatDealsCollectionView.png new file mode 100755 index 0000000000..72f9179e94 Binary files /dev/null and b/docs/static/images/example-app-screenshots/CatDealsCollectionView.png differ diff --git a/docs/static/images/example-app-screenshots/CollectionViewWithViewControllerCells.png b/docs/static/images/example-app-screenshots/CollectionViewWithViewControllerCells.png new file mode 100755 index 0000000000..078b7b945f Binary files /dev/null and b/docs/static/images/example-app-screenshots/CollectionViewWithViewControllerCells.png differ diff --git a/docs/static/images/example-app-screenshots/CustomCollectionView.gif b/docs/static/images/example-app-screenshots/CustomCollectionView.gif new file mode 100755 index 0000000000..8d8a2aed1e Binary files /dev/null and b/docs/static/images/example-app-screenshots/CustomCollectionView.gif differ diff --git a/docs/static/images/example-app-screenshots/EditableText.png b/docs/static/images/example-app-screenshots/EditableText.png new file mode 100755 index 0000000000..1aa8d6db9b Binary files /dev/null and b/docs/static/images/example-app-screenshots/EditableText.png differ diff --git a/docs/static/images/example-app-screenshots/HorizontalwithinVerticalScrolling.gif b/docs/static/images/example-app-screenshots/HorizontalwithinVerticalScrolling.gif new file mode 100755 index 0000000000..fe722a308d Binary files /dev/null and b/docs/static/images/example-app-screenshots/HorizontalwithinVerticalScrolling.gif differ diff --git a/docs/static/images/example-app-screenshots/Kittens.png b/docs/static/images/example-app-screenshots/Kittens.png new file mode 100755 index 0000000000..91d9bf36fa Binary files /dev/null and b/docs/static/images/example-app-screenshots/Kittens.png differ diff --git a/docs/static/images/example-app-screenshots/LayoutSpecPlayground.png b/docs/static/images/example-app-screenshots/LayoutSpecPlayground.png new file mode 100755 index 0000000000..9ed4db7c55 Binary files /dev/null and b/docs/static/images/example-app-screenshots/LayoutSpecPlayground.png differ diff --git a/docs/static/images/example-app-screenshots/Multiplex.gif b/docs/static/images/example-app-screenshots/Multiplex.gif new file mode 100755 index 0000000000..fcb98cad62 Binary files /dev/null and b/docs/static/images/example-app-screenshots/Multiplex.gif differ diff --git a/docs/static/images/example-app-screenshots/PagerNode.gif b/docs/static/images/example-app-screenshots/PagerNode.gif new file mode 100755 index 0000000000..8c13a0faea Binary files /dev/null and b/docs/static/images/example-app-screenshots/PagerNode.gif differ diff --git a/docs/static/images/example-app-screenshots/SocialAppLayout.png b/docs/static/images/example-app-screenshots/SocialAppLayout.png new file mode 100755 index 0000000000..33d6672102 Binary files /dev/null and b/docs/static/images/example-app-screenshots/SocialAppLayout.png differ diff --git a/docs/static/images/example-app-screenshots/Swift.png b/docs/static/images/example-app-screenshots/Swift.png new file mode 100755 index 0000000000..b099a17229 Binary files /dev/null and b/docs/static/images/example-app-screenshots/Swift.png differ diff --git a/docs/static/images/example-app-screenshots/SynchronousConcurrency.png b/docs/static/images/example-app-screenshots/SynchronousConcurrency.png new file mode 100755 index 0000000000..e0eac3788e Binary files /dev/null and b/docs/static/images/example-app-screenshots/SynchronousConcurrency.png differ diff --git a/docs/static/images/example-app-screenshots/VerticalWithinHorizontalScrolling.gif b/docs/static/images/example-app-screenshots/VerticalWithinHorizontalScrolling.gif new file mode 100755 index 0000000000..c50474b19b Binary files /dev/null and b/docs/static/images/example-app-screenshots/VerticalWithinHorizontalScrolling.gif differ diff --git a/docs/static/images/example-app-screenshots/VideoTableView.png b/docs/static/images/example-app-screenshots/VideoTableView.png new file mode 100755 index 0000000000..ddeefdc773 Binary files /dev/null and b/docs/static/images/example-app-screenshots/VideoTableView.png differ diff --git a/docs/static/images/example-app-screenshots/Videos.gif b/docs/static/images/example-app-screenshots/Videos.gif new file mode 100755 index 0000000000..5e5c5f2ca9 Binary files /dev/null and b/docs/static/images/example-app-screenshots/Videos.gif differ diff --git a/docs/static/images/favicon/android-icon-144x144.png b/docs/static/images/favicon/android-icon-144x144.png new file mode 100755 index 0000000000..68d4423f04 Binary files /dev/null and b/docs/static/images/favicon/android-icon-144x144.png differ diff --git a/docs/static/images/favicon/android-icon-192x192.png b/docs/static/images/favicon/android-icon-192x192.png new file mode 100755 index 0000000000..c7e107a2e3 Binary files /dev/null and b/docs/static/images/favicon/android-icon-192x192.png differ diff --git a/docs/static/images/favicon/android-icon-36x36.png b/docs/static/images/favicon/android-icon-36x36.png new file mode 100755 index 0000000000..b0da36b5b0 Binary files /dev/null and b/docs/static/images/favicon/android-icon-36x36.png differ diff --git a/docs/static/images/favicon/android-icon-48x48.png b/docs/static/images/favicon/android-icon-48x48.png new file mode 100755 index 0000000000..ed53510513 Binary files /dev/null and b/docs/static/images/favicon/android-icon-48x48.png differ diff --git a/docs/static/images/favicon/android-icon-72x72.png b/docs/static/images/favicon/android-icon-72x72.png new file mode 100755 index 0000000000..97a2a20792 Binary files /dev/null and b/docs/static/images/favicon/android-icon-72x72.png differ diff --git a/docs/static/images/favicon/android-icon-96x96.png b/docs/static/images/favicon/android-icon-96x96.png new file mode 100755 index 0000000000..1ea7ba4148 Binary files /dev/null and b/docs/static/images/favicon/android-icon-96x96.png differ diff --git a/docs/static/images/favicon/apple-icon-114x114.png b/docs/static/images/favicon/apple-icon-114x114.png new file mode 100755 index 0000000000..c4f63ca5cd Binary files /dev/null and b/docs/static/images/favicon/apple-icon-114x114.png differ diff --git a/docs/static/images/favicon/apple-icon-120x120.png b/docs/static/images/favicon/apple-icon-120x120.png new file mode 100755 index 0000000000..5e1cf1afe5 Binary files /dev/null and b/docs/static/images/favicon/apple-icon-120x120.png differ diff --git a/docs/static/images/favicon/apple-icon-144x144.png b/docs/static/images/favicon/apple-icon-144x144.png new file mode 100755 index 0000000000..68d4423f04 Binary files /dev/null and b/docs/static/images/favicon/apple-icon-144x144.png differ diff --git a/docs/static/images/favicon/apple-icon-152x152.png b/docs/static/images/favicon/apple-icon-152x152.png new file mode 100755 index 0000000000..d9a225aaa4 Binary files /dev/null and b/docs/static/images/favicon/apple-icon-152x152.png differ diff --git a/docs/static/images/favicon/apple-icon-180x180.png b/docs/static/images/favicon/apple-icon-180x180.png new file mode 100755 index 0000000000..6c40cd7b1c Binary files /dev/null and b/docs/static/images/favicon/apple-icon-180x180.png differ diff --git a/docs/static/images/favicon/apple-icon-57x57.png b/docs/static/images/favicon/apple-icon-57x57.png new file mode 100755 index 0000000000..c9d74b6b30 Binary files /dev/null and b/docs/static/images/favicon/apple-icon-57x57.png differ diff --git a/docs/static/images/favicon/apple-icon-60x60.png b/docs/static/images/favicon/apple-icon-60x60.png new file mode 100755 index 0000000000..7b440c6049 Binary files /dev/null and b/docs/static/images/favicon/apple-icon-60x60.png differ diff --git a/docs/static/images/favicon/apple-icon-72x72.png b/docs/static/images/favicon/apple-icon-72x72.png new file mode 100755 index 0000000000..97a2a20792 Binary files /dev/null and b/docs/static/images/favicon/apple-icon-72x72.png differ diff --git a/docs/static/images/favicon/apple-icon-76x76.png b/docs/static/images/favicon/apple-icon-76x76.png new file mode 100755 index 0000000000..0d5dd5f9f2 Binary files /dev/null and b/docs/static/images/favicon/apple-icon-76x76.png differ diff --git a/docs/static/images/favicon/apple-icon-precomposed.png b/docs/static/images/favicon/apple-icon-precomposed.png new file mode 100755 index 0000000000..43549d71c3 Binary files /dev/null and b/docs/static/images/favicon/apple-icon-precomposed.png differ diff --git a/docs/static/images/favicon/apple-icon.png b/docs/static/images/favicon/apple-icon.png new file mode 100755 index 0000000000..43549d71c3 Binary files /dev/null and b/docs/static/images/favicon/apple-icon.png differ diff --git a/docs/static/images/favicon/browserconfig.xml b/docs/static/images/favicon/browserconfig.xml new file mode 100755 index 0000000000..c554148223 --- /dev/null +++ b/docs/static/images/favicon/browserconfig.xml @@ -0,0 +1,2 @@ + +#ffffff \ No newline at end of file diff --git a/docs/static/images/favicon/favicon-16x16.png b/docs/static/images/favicon/favicon-16x16.png new file mode 100755 index 0000000000..4dc8bcf4ae Binary files /dev/null and b/docs/static/images/favicon/favicon-16x16.png differ diff --git a/docs/static/images/favicon/favicon-32x32.png b/docs/static/images/favicon/favicon-32x32.png new file mode 100755 index 0000000000..3ebcbef1d8 Binary files /dev/null and b/docs/static/images/favicon/favicon-32x32.png differ diff --git a/docs/static/images/favicon/favicon-96x96.png b/docs/static/images/favicon/favicon-96x96.png new file mode 100755 index 0000000000..1ea7ba4148 Binary files /dev/null and b/docs/static/images/favicon/favicon-96x96.png differ diff --git a/docs/static/images/favicon/favicon.ico b/docs/static/images/favicon/favicon.ico new file mode 100755 index 0000000000..ecafdea634 Binary files /dev/null and b/docs/static/images/favicon/favicon.ico differ diff --git a/docs/static/images/favicon/manifest.json b/docs/static/images/favicon/manifest.json new file mode 100755 index 0000000000..013d4a6a53 --- /dev/null +++ b/docs/static/images/favicon/manifest.json @@ -0,0 +1,41 @@ +{ + "name": "App", + "icons": [ + { + "src": "\/android-icon-36x36.png", + "sizes": "36x36", + "type": "image\/png", + "density": "0.75" + }, + { + "src": "\/android-icon-48x48.png", + "sizes": "48x48", + "type": "image\/png", + "density": "1.0" + }, + { + "src": "\/android-icon-72x72.png", + "sizes": "72x72", + "type": "image\/png", + "density": "1.5" + }, + { + "src": "\/android-icon-96x96.png", + "sizes": "96x96", + "type": "image\/png", + "density": "2.0" + }, + { + "src": "\/android-icon-144x144.png", + "sizes": "144x144", + "type": "image\/png", + "density": "3.0" + }, + { + "src": "\/android-icon-192x192.png", + "sizes": "192x192", + "type": "image\/png", + "density": "4.0" + } + ] +} \ No newline at end of file diff --git a/docs/static/images/favicon/ms-icon-144x144.png b/docs/static/images/favicon/ms-icon-144x144.png new file mode 100755 index 0000000000..68d4423f04 Binary files /dev/null and b/docs/static/images/favicon/ms-icon-144x144.png differ diff --git a/docs/static/images/favicon/ms-icon-150x150.png b/docs/static/images/favicon/ms-icon-150x150.png new file mode 100755 index 0000000000..8c1fc25b26 Binary files /dev/null and b/docs/static/images/favicon/ms-icon-150x150.png differ diff --git a/docs/static/images/favicon/ms-icon-310x310.png b/docs/static/images/favicon/ms-icon-310x310.png new file mode 100755 index 0000000000..8d43edcdc8 Binary files /dev/null and b/docs/static/images/favicon/ms-icon-310x310.png differ diff --git a/docs/static/images/favicon/ms-icon-70x70.png b/docs/static/images/favicon/ms-icon-70x70.png new file mode 100755 index 0000000000..498e41ba17 Binary files /dev/null and b/docs/static/images/favicon/ms-icon-70x70.png differ diff --git a/docs/static/images/flexbasis.png b/docs/static/images/flexbasis.png new file mode 100755 index 0000000000..28a9ad0b8b Binary files /dev/null and b/docs/static/images/flexbasis.png differ diff --git a/docs/static/images/intelligent-preloading-ranges-screenfuls.png b/docs/static/images/intelligent-preloading-ranges-screenfuls.png new file mode 100755 index 0000000000..07b08f25c6 Binary files /dev/null and b/docs/static/images/intelligent-preloading-ranges-screenfuls.png differ diff --git a/docs/static/images/intelligent-preloading-ranges-with-names.png b/docs/static/images/intelligent-preloading-ranges-with-names.png new file mode 100755 index 0000000000..b2ef62a767 Binary files /dev/null and b/docs/static/images/intelligent-preloading-ranges-with-names.png differ diff --git a/docs/static/images/intelligent-preloading-ranges.png b/docs/static/images/intelligent-preloading-ranges.png new file mode 100755 index 0000000000..de884a1390 Binary files /dev/null and b/docs/static/images/intelligent-preloading-ranges.png differ diff --git a/docs/static/images/intelligent-preloading.png b/docs/static/images/intelligent-preloading.png new file mode 100755 index 0000000000..a24f31c8ab Binary files /dev/null and b/docs/static/images/intelligent-preloading.png differ diff --git a/docs/static/images/kittenLink.png b/docs/static/images/kittenLink.png new file mode 100755 index 0000000000..1c46390e5e Binary files /dev/null and b/docs/static/images/kittenLink.png differ diff --git a/docs/static/images/layout-api-sizing-1.png b/docs/static/images/layout-api-sizing-1.png new file mode 100755 index 0000000000..dd004c3a42 Binary files /dev/null and b/docs/static/images/layout-api-sizing-1.png differ diff --git a/docs/static/images/layout-api-sizing-2.png b/docs/static/images/layout-api-sizing-2.png new file mode 100755 index 0000000000..20814d8425 Binary files /dev/null and b/docs/static/images/layout-api-sizing-2.png differ diff --git a/docs/static/images/layout-example-1.png b/docs/static/images/layout-example-1.png new file mode 100755 index 0000000000..aac85798e5 Binary files /dev/null and b/docs/static/images/layout-example-1.png differ diff --git a/docs/static/images/layout-example-2.png b/docs/static/images/layout-example-2.png new file mode 100755 index 0000000000..673c16721b Binary files /dev/null and b/docs/static/images/layout-example-2.png differ diff --git a/docs/static/images/layout-example-3.png b/docs/static/images/layout-example-3.png new file mode 100755 index 0000000000..72f9179e94 Binary files /dev/null and b/docs/static/images/layout-example-3.png differ diff --git a/docs/static/images/layout-examples-photo-with-inset-text-overlay-diagram.png b/docs/static/images/layout-examples-photo-with-inset-text-overlay-diagram.png new file mode 100755 index 0000000000..dbf95847ed Binary files /dev/null and b/docs/static/images/layout-examples-photo-with-inset-text-overlay-diagram.png differ diff --git a/docs/static/images/layout-examples-photo-with-inset-text-overlay-photo.png b/docs/static/images/layout-examples-photo-with-inset-text-overlay-photo.png new file mode 100755 index 0000000000..2465aaa3cb Binary files /dev/null and b/docs/static/images/layout-examples-photo-with-inset-text-overlay-photo.png differ diff --git a/docs/static/images/layout-examples-photo-with-inset-text-overlay.png b/docs/static/images/layout-examples-photo-with-inset-text-overlay.png new file mode 100755 index 0000000000..020685ae10 Binary files /dev/null and b/docs/static/images/layout-examples-photo-with-inset-text-overlay.png differ diff --git a/docs/static/images/layout-examples-photo-with-outset-icon-overlay-icon.png b/docs/static/images/layout-examples-photo-with-outset-icon-overlay-icon.png new file mode 100755 index 0000000000..22a1652b3f Binary files /dev/null and b/docs/static/images/layout-examples-photo-with-outset-icon-overlay-icon.png differ diff --git a/docs/static/images/layout-examples-photo-with-outset-icon-overlay-photo.png b/docs/static/images/layout-examples-photo-with-outset-icon-overlay-photo.png new file mode 100755 index 0000000000..7d7bef7e13 Binary files /dev/null and b/docs/static/images/layout-examples-photo-with-outset-icon-overlay-photo.png differ diff --git a/docs/static/images/layout-examples-photo-with-outset-icon-overlay.png b/docs/static/images/layout-examples-photo-with-outset-icon-overlay.png new file mode 100755 index 0000000000..46669c860a Binary files /dev/null and b/docs/static/images/layout-examples-photo-with-outset-icon-overlay.png differ diff --git a/docs/static/images/layout-examples-simple-header-with-left-right-justified-text-diagram.png b/docs/static/images/layout-examples-simple-header-with-left-right-justified-text-diagram.png new file mode 100755 index 0000000000..c81e6f5122 Binary files /dev/null and b/docs/static/images/layout-examples-simple-header-with-left-right-justified-text-diagram.png differ diff --git a/docs/static/images/layout-examples-simple-header-with-left-right-justified-text.png b/docs/static/images/layout-examples-simple-header-with-left-right-justified-text.png new file mode 100755 index 0000000000..1296620146 Binary files /dev/null and b/docs/static/images/layout-examples-simple-header-with-left-right-justified-text.png differ diff --git a/docs/static/images/layout-examples-simple-inset-text-cell-closeup.png b/docs/static/images/layout-examples-simple-inset-text-cell-closeup.png new file mode 100755 index 0000000000..1b5b0d55a3 Binary files /dev/null and b/docs/static/images/layout-examples-simple-inset-text-cell-closeup.png differ diff --git a/docs/static/images/layout-examples-simple-inset-text-cell.png b/docs/static/images/layout-examples-simple-inset-text-cell.png new file mode 100755 index 0000000000..b161af9eca Binary files /dev/null and b/docs/static/images/layout-examples-simple-inset-text-cell.png differ diff --git a/docs/static/images/layout-examples-top-bottom-separator-line-diagram.png b/docs/static/images/layout-examples-top-bottom-separator-line-diagram.png new file mode 100755 index 0000000000..eac57973ce Binary files /dev/null and b/docs/static/images/layout-examples-top-bottom-separator-line-diagram.png differ diff --git a/docs/static/images/layout-examples-top-bottom-separator-line.png b/docs/static/images/layout-examples-top-bottom-separator-line.png new file mode 100755 index 0000000000..38f1fd9564 Binary files /dev/null and b/docs/static/images/layout-examples-top-bottom-separator-line.png differ diff --git a/docs/static/images/layout-spec-relationship-1.png b/docs/static/images/layout-spec-relationship-1.png new file mode 100755 index 0000000000..a6ca2945c8 Binary files /dev/null and b/docs/static/images/layout-spec-relationship-1.png differ diff --git a/docs/static/images/layout-spec-relationship-2.png b/docs/static/images/layout-spec-relationship-2.png new file mode 100755 index 0000000000..a21f052429 Binary files /dev/null and b/docs/static/images/layout-spec-relationship-2.png differ diff --git a/docs/static/images/layout2-api-sizing.png b/docs/static/images/layout2-api-sizing.png new file mode 100755 index 0000000000..0a7f054365 Binary files /dev/null and b/docs/static/images/layout2-api-sizing.png differ diff --git a/docs/static/images/layoutSpec-examples/layout-example-inset-overlay.png b/docs/static/images/layoutSpec-examples/layout-example-inset-overlay.png new file mode 100755 index 0000000000..f10f8f0fe4 Binary files /dev/null and b/docs/static/images/layoutSpec-examples/layout-example-inset-overlay.png differ diff --git a/docs/static/images/layoutSpec-types/ASBackgroundLayoutSpec-diagram.png b/docs/static/images/layoutSpec-types/ASBackgroundLayoutSpec-diagram.png new file mode 100755 index 0000000000..5b365c0211 Binary files /dev/null and b/docs/static/images/layoutSpec-types/ASBackgroundLayoutSpec-diagram.png differ diff --git a/docs/static/images/layoutSpec-types/ASCenterLayoutSpec-diagram-text.png b/docs/static/images/layoutSpec-types/ASCenterLayoutSpec-diagram-text.png new file mode 100755 index 0000000000..7559e73d08 Binary files /dev/null and b/docs/static/images/layoutSpec-types/ASCenterLayoutSpec-diagram-text.png differ diff --git a/docs/static/images/layoutSpec-types/ASCenterLayoutSpec-diagram.png b/docs/static/images/layoutSpec-types/ASCenterLayoutSpec-diagram.png new file mode 100755 index 0000000000..040b5ce86e Binary files /dev/null and b/docs/static/images/layoutSpec-types/ASCenterLayoutSpec-diagram.png differ diff --git a/docs/static/images/layoutSpec-types/ASInsetLayoutSpec-diagram-text.png b/docs/static/images/layoutSpec-types/ASInsetLayoutSpec-diagram-text.png new file mode 100755 index 0000000000..f4e3810af4 Binary files /dev/null and b/docs/static/images/layoutSpec-types/ASInsetLayoutSpec-diagram-text.png differ diff --git a/docs/static/images/layoutSpec-types/ASInsetLayoutSpec-diagram.png b/docs/static/images/layoutSpec-types/ASInsetLayoutSpec-diagram.png new file mode 100755 index 0000000000..6ee3642fb3 Binary files /dev/null and b/docs/static/images/layoutSpec-types/ASInsetLayoutSpec-diagram.png differ diff --git a/docs/static/images/layoutSpec-types/ASInsetLayoutSpec-example-complex.png b/docs/static/images/layoutSpec-types/ASInsetLayoutSpec-example-complex.png new file mode 100755 index 0000000000..ca08d0c9ec Binary files /dev/null and b/docs/static/images/layoutSpec-types/ASInsetLayoutSpec-example-complex.png differ diff --git a/docs/static/images/layoutSpec-types/ASOverlayLayouSpec-diagram.png b/docs/static/images/layoutSpec-types/ASOverlayLayouSpec-diagram.png new file mode 100755 index 0000000000..7bba2cf067 Binary files /dev/null and b/docs/static/images/layoutSpec-types/ASOverlayLayouSpec-diagram.png differ diff --git a/docs/static/images/layoutSpec-types/ASOverlayLayoutSpec-example-diagram.png b/docs/static/images/layoutSpec-types/ASOverlayLayoutSpec-example-diagram.png new file mode 100755 index 0000000000..deda779f3f Binary files /dev/null and b/docs/static/images/layoutSpec-types/ASOverlayLayoutSpec-example-diagram.png differ diff --git a/docs/static/images/layoutSpec-types/ASRatioLayoutSpec-diagram.png b/docs/static/images/layoutSpec-types/ASRatioLayoutSpec-diagram.png new file mode 100755 index 0000000000..eac1fd3b1f Binary files /dev/null and b/docs/static/images/layoutSpec-types/ASRatioLayoutSpec-diagram.png differ diff --git a/docs/static/images/layoutable-types.png b/docs/static/images/layoutable-types.png new file mode 100755 index 0000000000..6ac3e9d11d Binary files /dev/null and b/docs/static/images/layoutable-types.png differ diff --git a/docs/static/images/liveMap.gif b/docs/static/images/liveMap.gif new file mode 100755 index 0000000000..633d1fb344 Binary files /dev/null and b/docs/static/images/liveMap.gif differ diff --git a/docs/static/images/logo.png b/docs/static/images/logo.png new file mode 100644 index 0000000000..d1e84d62c7 Binary files /dev/null and b/docs/static/images/logo.png differ diff --git a/docs/static/images/logo.svg b/docs/static/images/logo.svg new file mode 100644 index 0000000000..bc30fe6cdc --- /dev/null +++ b/docs/static/images/logo.svg @@ -0,0 +1,24 @@ + + + + Texture Logo + Created with Sketch. + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/static/images/mapWithAnnotation.png b/docs/static/images/mapWithAnnotation.png new file mode 100755 index 0000000000..e6316293d1 Binary files /dev/null and b/docs/static/images/mapWithAnnotation.png differ diff --git a/docs/static/images/node-hierarchy.png b/docs/static/images/node-hierarchy.png new file mode 100755 index 0000000000..8f0c021b62 Binary files /dev/null and b/docs/static/images/node-hierarchy.png differ diff --git a/docs/static/images/node-view-layer.png b/docs/static/images/node-view-layer.png new file mode 100755 index 0000000000..544294af8f Binary files /dev/null and b/docs/static/images/node-view-layer.png differ diff --git a/docs/static/images/overlay-vs-inset-spec.png b/docs/static/images/overlay-vs-inset-spec.png new file mode 100755 index 0000000000..2da1be2d80 Binary files /dev/null and b/docs/static/images/overlay-vs-inset-spec.png differ diff --git a/docs/static/images/satelliteMap.png b/docs/static/images/satelliteMap.png new file mode 100755 index 0000000000..702f12b248 Binary files /dev/null and b/docs/static/images/satelliteMap.png differ diff --git a/docs/static/images/synchronous-concurrency.jpg b/docs/static/images/synchronous-concurrency.jpg new file mode 100644 index 0000000000..b49ea5d4c9 Binary files /dev/null and b/docs/static/images/synchronous-concurrency.jpg differ diff --git a/docs/static/images/textNodeTruncation.png b/docs/static/images/textNodeTruncation.png new file mode 100755 index 0000000000..a5125a8143 Binary files /dev/null and b/docs/static/images/textNodeTruncation.png differ diff --git a/docs/static/images/video.gif b/docs/static/images/video.gif new file mode 100755 index 0000000000..dd9bab6a78 Binary files /dev/null and b/docs/static/images/video.gif differ diff --git a/docs/static/linkify.js b/docs/static/linkify.js new file mode 100755 index 0000000000..b6adfbd5f9 --- /dev/null +++ b/docs/static/linkify.js @@ -0,0 +1,22 @@ +[].slice.apply( + document.querySelectorAll( + 'article h2, article h3, article h4' + ) +).forEach(function(header) { + var slug = header.innerText + .toLowerCase() + .replace(/[^a-z0-9]/g, '-') + .replace(/-+/g, '-') + .replace(/^-|-$/g, ''); + + var hashref = document.createElement('a'); + hashref.id = slug; + hashref.className = 'hashref'; + header.appendChild(hashref); + + var hash = document.createElement('a'); + hash.className = 'hash'; + hash.href = '#' + slug; + hash.innerText = '#'; + header.appendChild(hash); +}); diff --git a/docs/static/talks/10_3_2016_ASCollectionNode_Sequence_Diagrams.pdf b/docs/static/talks/10_3_2016_ASCollectionNode_Sequence_Diagrams.pdf new file mode 100755 index 0000000000..ce202e60be Binary files /dev/null and b/docs/static/talks/10_3_2016_ASCollectionNode_Sequence_Diagrams.pdf differ diff --git a/docs/static/talks/ASCollectionView.pdf b/docs/static/talks/ASCollectionView.pdf new file mode 100755 index 0000000000..43d834f1d9 Binary files /dev/null and b/docs/static/talks/ASCollectionView.pdf differ diff --git a/docs/static/talks/UICollectionView.pdf b/docs/static/talks/UICollectionView.pdf new file mode 100755 index 0000000000..da3691d049 Binary files /dev/null and b/docs/static/talks/UICollectionView.pdf differ diff --git a/docs/static/toggle.js b/docs/static/toggle.js new file mode 100755 index 0000000000..65b9b5e2bd --- /dev/null +++ b/docs/static/toggle.js @@ -0,0 +1,27 @@ +document.addEventListener("DOMContentLoaded", function() { + var swiftButtons = document.getElementsByClassName("swiftButton"); + var objectiveCButons = document.getElementsByClassName("objcButton"); + var objcCodes = document.getElementsByClassName("objcCode"); + var swiftCodes = document.getElementsByClassName("swiftCode"); + + var totalCodeSections = swiftButtons.length; + for(var i = 0; i < totalCodeSections; i++) { + swiftButtons[i].onclick = function () { + for (var i = 0; i < totalCodeSections; i++) { + swiftCodes[i].classList.remove("hidden"); + objcCodes[i].classList.add("hidden"); + objectiveCButons[i].classList.remove("active"); + swiftButtons[i].classList.add("active"); + }; + } + + objectiveCButons[i].onclick = function () { + for (var i = 0; i < totalCodeSections; i++) { + swiftCodes[i].classList.add("hidden"); + objcCodes[i].classList.remove("hidden"); + objectiveCButons[i].classList.add("active"); + swiftButtons[i].classList.remove("active"); + }; + } + } +}); \ No newline at end of file diff --git a/docs/stylesheets/main.scss b/docs/stylesheets/main.scss new file mode 100755 index 0000000000..ee270fa770 --- /dev/null +++ b/docs/stylesheets/main.scss @@ -0,0 +1,338 @@ +--- +--- + +@charset "utf-8"; + +* { + padding: 0; + margin: 0; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + -webkit-text-size-adjust: 100%; +} + +html { + -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; + background-color: #FFFFFF; +} + +body { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: proxima-nova, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: 400; + color: #444; + margin: 0; + line-height: 22px; +} + +h1, h2, h3, h4 { + font-weight: normal; + margin: 1.3em 0 0.2em; + line-height: 1.4em; + position: relative; +} + +td { + vertical-align: top; +} + +h1:first-child, h2:first-child, h3:first-child, h4:first-child { + margin-top: 0; +} + +h1 { + font-size: 2em; + font-weight: bold; +} + +h2 { + font-size: 1.5em; +} + +h3 { + font-size: 1.3em; +} + +h4 { + font-size: 1.1em; +} + +p, ul, ol { + margin: 0 0 1em 0; +} + +a { + color: #0484f2; + text-decoration: none; +} + +.btn { + padding: 10px 20px; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; + background: #0f70dd; + color: #fff; + display: inline-block; + text-decoration: none; + font-weight: bold; +} + +.container { + width: 90%; + margin: 0 auto; + clear: both; + + &:after { content: ""; display: table; clear: both; } +} + +header { + padding: 30px 0 0 0; + background: #f8f8f8; + + h1 { + position: relative; + top: -5px; + } + + nav { + font-size: 18px; + + ul { + list-style: none; + + li { + float: left; + margin-right: 20px; + + a { + color: #0484f2; + text-decoration: none; + } + } + } + } +} + +#logo { + margin: 0; +} + +.hero { + background: #0f70dd; + margin-bottom: 30px; + padding: 30px 0; + + .hero-title { + font-size: 45px; + line-height: 110%; + color: #fff; + margin-bottom: 20px; + } + + .btn { + background: #fff; + color: #0f70dd; + border: 2px solid #fff; + margin-right: 10px; + } + + .btn.btn-outlined { + background: none; + border: 2px solid #fff; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; + color: #fff; + } +} + +.container-margin { + margin-top: 30px; +} + +.container .sidebar { + font-size: 14px; + + ul { + list-style: none; + + li { + list-style: none; + } + } + + a.active { + font-weight: bold; + } +} + +.container tr > td { + padding-bottom: 30px; +} + +.container tr td img { + display: inline-block; + margin-bottom: 5px; +} + +.container tr td b { + padding-bottom: 5px; + display: inline-block; +} + +.roundrect { + border: 1px solid #f4f4f4; + -webkit-border-radius: 20px; + -moz-border-radius: 20px; + border-radius: 20px; +} + +.container .content ul, .container .content ol { + margin-left: 20px; + margin-bottom: 30px; +} + +.content p img { + display: block; + margin: 1em auto; + max-width: 100%; +} + +.edit-page-link { + font-size: 14px; +} + +@import "code"; + +.language-toggle { + border-bottom: 2px #0484f2 solid; + background: white; + display: block; + box-sizing: border-box; + font-size: 125%; + -webkit-font-smoothing: antialiased; + line-height: 1.5; + padding-right: 10px; + + a { + cursor: pointer; + display: block; + float: right; + padding-left: 1em; + + color: #a3a39e !important; + text-decoration: none; + transition: color 0.1s linear; + + &.active { + color: #222220 !important; + } + } + + &:after { + content: ""; + display: table; + clear: both; + } +} + +.highlight-group { + font-family: 'Inconsolata' !important; + margin: 0; + margin-top: 20px; + margin-bottom: 20px; + background: #f8f7f5; +} + +.hidden { + display: none; +} +.code { + padding: 15px; + overflow: auto; +} +pre { + font-family: 'Inconsolata' !important; + font-weight: 500; + color: #333333; +} + +.note { + padding: 10px; + border-radius: 3px; + background-color: #EFF7FF; + border: 1px solid #CCDDFF; + margin-bottom: 20px; + + p:before { + content: "NOTE:"; + font-weight: bold; + } + + p { + margin-bottom: 0px; + } +} + +.note-important { + padding: 10px; + border-radius: 3px; + background-color: #FFCCCC; + border: 1px solid #FF8899; + margin-bottom: 20px; + + p:before { + content: "IMPORTANT:"; + font-weight: bold; + } + + p { + margin-bottom: 0px; + } +} + +.right { + float: right; +} + +footer { + padding: 20px 0; + + p { + text-align: center; + } +} + +/** Banner */ +.texture-banner { + padding: 20px 0 20px 0; + width: 100%; + background-color: #0e1e28; + box-shadow: inset 0 -2px 20px 0 rgba(0,0,0,0.5); + text-align: center; + + .announcement { + font-stretch: normal; + font-size: 16px; + line-height: 1.75; + letter-spacing: 0.1px; + text-align: center; + color: #d7e2e9; + font-weight: bold; + margin: 0; + } + + .learn-more-link { + font-size: 12px; + font-weight: bold; + font-style: normal; + font-stretch: normal; + line-height: 1.17; + letter-spacing: 0.5px; + color: #0484f2; + margin-left: 10px; + } +} diff --git a/docs/stylesheets/media.css b/docs/stylesheets/media.css new file mode 100755 index 0000000000..37235a7ceb --- /dev/null +++ b/docs/stylesheets/media.css @@ -0,0 +1,50 @@ +/* Media queries */ +@media (min-width: 600px) { + header { + display: block; + } + + header nav { + line-height: 55px; + } + + header ul { + } + + header nav ul { + display: inline-block; + float: right; + margin: 0 auto; + } + + #logo { + float: left; + } + + .container .sidebar { + width: 19%; + float: left; + } + + .container .content { + width: 75%; + float: right; + } + +} + +@media (min-width: 950px) { + .container { + width: 900px; + } + + .container .sidebar { + width: 19%; + float: left; + } + + .container .content { + width: 75%; + float: right; + } +} diff --git a/docs/stylesheets/pygments.css b/docs/stylesheets/pygments.css new file mode 100755 index 0000000000..91f27c87dd --- /dev/null +++ b/docs/stylesheets/pygments.css @@ -0,0 +1,61 @@ +.hll { background-color: #ffffcc } +.c { color: #999988; font-style: italic } /* Comment */ +.err { color: #a61717; background-color: #e3d2d2 } /* Error */ +.k { color: #000000; font-weight: bold } /* Keyword */ +.o { color: #000000; } /* Operator */ +.cm { color: #666666; font-weight: bold; font-style: italic } /* Comment.Multiline */ +.cp { color: #666666; font-weight: bold; font-style: italic } /* Comment.Preproc */ +.c1 { color: #666666; font-weight: bold; font-style: italic } /* Comment.Single */ +.cs { color: #666666; font-weight: bold; font-style: italic } /* Comment.Special */ +.gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ +.ge { color: #000000; font-style: italic } /* Generic.Emph */ +.gr { color: #aa0000 } /* Generic.Error */ +.gh { color: #999999 } /* Generic.Heading */ +.gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ +.go { color: #888888 } /* Generic.Output */ +.gp { color: #555555 } /* Generic.Prompt */ +.gs { font-weight: bold } /* Generic.Strong */ +.gu { color: #aaaaaa } /* Generic.Subheading */ +.gt { color: #aa0000 } /* Generic.Traceback */ +.kc { color: #000000; font-weight: bold } /* Keyword.Constant */ +.kd { color: #000000; font-weight: bold } /* Keyword.Declaration */ +.kn { color: #000000; font-weight: bold } /* Keyword.Namespace */ +.kp { color: #000000; font-weight: bold } /* Keyword.Pseudo */ +.kr { color: #000000; font-weight: bold } /* Keyword.Reserved */ +.kt { color: #445588; font-weight: bold } /* Keyword.Type */ +.m { color: #009999 } /* Literal.Number */ +.s { color: #d01040 } /* Literal.String */ +.na { color: #008080 } /* Name.Attribute */ +.nb { } /* Name.Builtin */ +.nc { color: #445588; font-weight: bold } /* Name.Class */ +.no { color: #008080 } /* Name.Constant */ +.nd { color: #3c5d5d; font-weight: bold } /* Name.Decorator */ +.ni { color: #800080 } /* Name.Entity */ +.ne { color: #990000; font-weight: bold } /* Name.Exception */ +.nf { color: #990000; font-weight: bold } /* Name.Function */ +.nl { color: #990000; font-weight: bold } /* Name.Label */ +.nn { color: #555555 } /* Name.Namespace */ +.nt { color: #000080 } /* Name.Tag */ +.nv { color: #008080 } /* Name.Variable */ +.ow { color: #000000; font-weight: bold } /* Operator.Word */ +.w { color: #bbbbbb } /* Text.Whitespace */ +.mf { color: #009999 } /* Literal.Number.Float */ +.mh { color: #009999 } /* Literal.Number.Hex */ +.mi { color: #009999 } /* Literal.Number.Integer */ +.mo { color: #009999 } /* Literal.Number.Oct */ +.sb { color: #d01040 } /* Literal.String.Backtick */ +.sc { color: #d01040 } /* Literal.String.Char */ +.sd { color: #d01040 } /* Literal.String.Doc */ +.s2 { color: #d01040 } /* Literal.String.Double */ +.se { color: #d01040 } /* Literal.String.Escape */ +.sh { color: #d01040 } /* Literal.String.Heredoc */ +.si { color: #d01040 } /* Literal.String.Interpol */ +.sx { color: #d01040 } /* Literal.String.Other */ +.sr { color: #009926 } /* Literal.String.Regex */ +.s1 { color: #d01040 } /* Literal.String.Single */ +.ss { color: #990073 } /* Literal.String.Symbol */ +.bp { color: #999999 } /* Name.Builtin.Pseudo */ +.vc { color: #008080 } /* Name.Variable.Class */ +.vg { color: #008080 } /* Name.Variable.Global */ +.vi { color: #008080 } /* Name.Variable.Instance */ +.il { color: #009999 } /* Literal.Number.Integer.Long */ diff --git a/examples/ASCollectionView/Podfile b/examples/ASCollectionView/Podfile index 7a8d8c1a00..922ff50ec1 100644 --- a/examples/ASCollectionView/Podfile +++ b/examples/ASCollectionView/Podfile @@ -1,6 +1,6 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' target 'Sample' do - pod 'AsyncDisplayKit', :path => '../..' + pod 'Texture', :path => '../..' end diff --git a/examples/ASCollectionView/Sample.xcodeproj/project.pbxproj b/examples/ASCollectionView/Sample.xcodeproj/project.pbxproj index 75b2986947..634184297c 100644 --- a/examples/ASCollectionView/Sample.xcodeproj/project.pbxproj +++ b/examples/ASCollectionView/Sample.xcodeproj/project.pbxproj @@ -127,12 +127,12 @@ isa = PBXNativeTarget; buildConfigurationList = AC3C4A811A11F47200143C57 /* Build configuration list for PBXNativeTarget "Sample" */; buildPhases = ( - F868CFBB21824CC9521B6588 /* 📦 Check Pods Manifest.lock */, + F868CFBB21824CC9521B6588 /* [CP] Check Pods Manifest.lock */, AC3C4A5A1A11F47200143C57 /* Sources */, AC3C4A5B1A11F47200143C57 /* Frameworks */, AC3C4A5C1A11F47200143C57 /* Resources */, - A6902C454C7661D0D277AC62 /* 📦 Copy Pods Resources */, - 79E97C651F7D1ECDBE6B4793 /* 📦 Embed Pods Frameworks */, + A6902C454C7661D0D277AC62 /* [CP] Copy Pods Resources */, + 79E97C651F7D1ECDBE6B4793 /* [CP] Embed Pods Frameworks */, 8C4782EECEE7F1205007D6DB /* Embed Pods Frameworks */, ); buildRules = ( @@ -189,14 +189,14 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 79E97C651F7D1ECDBE6B4793 /* 📦 Embed Pods Frameworks */ = { + 79E97C651F7D1ECDBE6B4793 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "📦 Embed Pods Frameworks"; + name = "[CP] Embed Pods Frameworks"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; @@ -219,14 +219,14 @@ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Sample/Pods-Sample-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - A6902C454C7661D0D277AC62 /* 📦 Copy Pods Resources */ = { + A6902C454C7661D0D277AC62 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "📦 Copy Pods Resources"; + name = "[CP] Copy Pods Resources"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; @@ -234,19 +234,19 @@ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Sample/Pods-Sample-resources.sh\"\n"; showEnvVarsInLog = 0; }; - F868CFBB21824CC9521B6588 /* 📦 Check Pods Manifest.lock */ = { + F868CFBB21824CC9521B6588 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "📦 Check Pods Manifest.lock"; + 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 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"; + 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 */ diff --git a/examples/ASCollectionView/Sample.xcworkspace/contents.xcworkspacedata b/examples/ASCollectionView/Sample.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000000..7b5a2f3050 --- /dev/null +++ b/examples/ASCollectionView/Sample.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/examples/ASCollectionView/Sample/ViewController.m b/examples/ASCollectionView/Sample/ViewController.m index d1718e2f9f..13bfe64c72 100644 --- a/examples/ASCollectionView/Sample/ViewController.m +++ b/examples/ASCollectionView/Sample/ViewController.m @@ -1,18 +1,18 @@ // // ViewController.m -// Sample +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. // -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "ViewController.h" @@ -21,6 +21,8 @@ #import "SupplementaryNode.h" #import "ItemNode.h" +#define ASYNC_COLLECTION_LAYOUT 0 + @interface ViewController () @property (nonatomic, strong) ASCollectionNode *collectionNode; @@ -43,26 +45,26 @@ - (void)viewDidLoad { [super viewDidLoad]; - + +#if ASYNC_COLLECTION_LAYOUT + id layoutDelegate = [[ASCollectionGalleryLayoutDelegate alloc] initWithScrollableDirections:ASScrollDirectionVerticalDirections + itemSize:CGSizeMake(180, 90)]; + self.collectionNode = [[ASCollectionNode alloc] initWithLayoutDelegate:layoutDelegate layoutFacilitator:nil]; +#else UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; layout.headerReferenceSize = CGSizeMake(50.0, 50.0); layout.footerReferenceSize = CGSizeMake(50.0, 50.0); - - // This method is deprecated because we reccommend using ASCollectionNode instead of ASCollectionView. - // This functionality & example project remains for users who insist on using ASCollectionView. self.collectionNode = [[ASCollectionNode alloc] initWithFrame:self.view.bounds collectionViewLayout:layout]; +#endif + self.collectionNode.dataSource = self; self.collectionNode.delegate = self; self.collectionNode.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; self.collectionNode.backgroundColor = [UIColor whiteColor]; - // This method is deprecated because we reccommend using ASCollectionNode instead of ASCollectionView. - // This functionality & example project remains for users who insist on using ASCollectionView. - [self.collectionNode registerSupplementaryNodeOfKind:UICollectionElementKindSectionHeader]; - [self.collectionNode registerSupplementaryNodeOfKind:UICollectionElementKindSectionFooter]; - [self.view addSubnode:self.collectionNode]; + self.collectionNode.frame = self.view.bounds; #if !SIMULATE_WEB_RESPONSE self.navigationItem.leftItemsSupplementBackButton = YES; diff --git a/examples/ASDKLayoutTransition/Podfile b/examples/ASDKLayoutTransition/Podfile index 32b4c3336e..90c6ad7ea8 100644 --- a/examples/ASDKLayoutTransition/Podfile +++ b/examples/ASDKLayoutTransition/Podfile @@ -1,6 +1,6 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' target 'Sample' do - pod 'AsyncDisplayKit', :path => '../..' - pod 'AsyncDisplayKit/Yoga', :path => '../..' + pod 'Texture', :path => '../..' + pod 'Texture/Yoga', :path => '../..' end diff --git a/examples/ASDKLayoutTransition/Sample.xcodeproj/project.pbxproj b/examples/ASDKLayoutTransition/Sample.xcodeproj/project.pbxproj index 5de473f6a0..3b4f1e6f1d 100644 --- a/examples/ASDKLayoutTransition/Sample.xcodeproj/project.pbxproj +++ b/examples/ASDKLayoutTransition/Sample.xcodeproj/project.pbxproj @@ -202,7 +202,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - 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"; + 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; }; F012A6F39E0149F18F564F50 /* [CP] Copy Pods Resources */ = { diff --git a/examples/ASDKTube/Podfile b/examples/ASDKTube/Podfile index defaf55058..b75e492fab 100644 --- a/examples/ASDKTube/Podfile +++ b/examples/ASDKTube/Podfile @@ -1,5 +1,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' target 'Sample' do - pod 'AsyncDisplayKit', :path => '../..' + pod 'Texture', :path => '../..' end diff --git a/examples/ASDKTube/Sample.xcodeproj/project.pbxproj b/examples/ASDKTube/Sample.xcodeproj/project.pbxproj index 2e142cc317..0e6c890fa4 100644 --- a/examples/ASDKTube/Sample.xcodeproj/project.pbxproj +++ b/examples/ASDKTube/Sample.xcodeproj/project.pbxproj @@ -188,12 +188,12 @@ isa = PBXNativeTarget; buildConfigurationList = 05E212A419D4DB510098F589 /* Build configuration list for PBXNativeTarget "Sample" */; buildPhases = ( - E080B80F89C34A25B3488E26 /* 📦 Check Pods Manifest.lock */, + E080B80F89C34A25B3488E26 /* [CP] Check Pods Manifest.lock */, 05E2127D19D4DB510098F589 /* Sources */, 05E2127E19D4DB510098F589 /* Frameworks */, 05E2127F19D4DB510098F589 /* Resources */, - F012A6F39E0149F18F564F50 /* 📦 Copy Pods Resources */, - 93B7780A33739EF25F20366B /* 📦 Embed Pods Frameworks */, + F012A6F39E0149F18F564F50 /* [CP] Copy Pods Resources */, + 93B7780A33739EF25F20366B /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -256,14 +256,14 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 93B7780A33739EF25F20366B /* 📦 Embed Pods Frameworks */ = { + 93B7780A33739EF25F20366B /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "📦 Embed Pods Frameworks"; + name = "[CP] Embed Pods Frameworks"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; @@ -271,29 +271,29 @@ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Sample/Pods-Sample-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - E080B80F89C34A25B3488E26 /* 📦 Check Pods Manifest.lock */ = { + E080B80F89C34A25B3488E26 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "📦 Check Pods Manifest.lock"; + 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 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"; + 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; }; - F012A6F39E0149F18F564F50 /* 📦 Copy Pods Resources */ = { + F012A6F39E0149F18F564F50 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "📦 Copy Pods Resources"; + name = "[CP] Copy Pods Resources"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; diff --git a/examples/ASDKTube/Sample/Nodes/VideoContentCell.m b/examples/ASDKTube/Sample/Nodes/VideoContentCell.m index 8f57bea3cd..dac4ce77ce 100644 --- a/examples/ASDKTube/Sample/Nodes/VideoContentCell.m +++ b/examples/ASDKTube/Sample/Nodes/VideoContentCell.m @@ -18,7 +18,9 @@ // #import "VideoContentCell.h" -#import "ASVideoPlayerNode.h" + +#import + #import "Utilities.h" #define AVATAR_IMAGE_HEIGHT 30 diff --git a/examples/ASDKgram/Podfile b/examples/ASDKgram/Podfile index c401cd001c..eb879f3f12 100644 --- a/examples/ASDKgram/Podfile +++ b/examples/ASDKgram/Podfile @@ -1,6 +1,8 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' target 'Sample' do - pod 'AsyncDisplayKit/IGListKit', :path => '../..' - pod 'AsyncDisplayKit/PINRemoteImage', :path => '../..' + pod 'Texture/IGListKit', :path => '../..' + pod 'Texture/PINRemoteImage', :path => '../..' + pod 'Texture/Yoga', :path => '../..' + pod 'Weaver' end diff --git a/examples/ASDKgram/Sample.xcodeproj/project.pbxproj b/examples/ASDKgram/Sample.xcodeproj/project.pbxproj index aab6d55e44..9cfb0e254d 100644 --- a/examples/ASDKgram/Sample.xcodeproj/project.pbxproj +++ b/examples/ASDKgram/Sample.xcodeproj/project.pbxproj @@ -387,7 +387,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - 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"; + 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; }; F012A6F39E0149F18F564F50 /* [CP] Copy Pods Resources */ = { diff --git a/examples/ASDKgram/Sample/ASCollectionSectionController.m b/examples/ASDKgram/Sample/ASCollectionSectionController.m index 27284a1e99..2e5b657773 100644 --- a/examples/ASDKgram/Sample/ASCollectionSectionController.m +++ b/examples/ASDKgram/Sample/ASCollectionSectionController.m @@ -52,25 +52,19 @@ return; } - BOOL wasEmpty = (self.items.count == 0); - dispatch_async(self.diffingQueue, ^{ IGListIndexSetResult *result = IGListDiff(self.pendingItems, newItems, IGListDiffPointerPersonality); self.pendingItems = newItems; dispatch_async(dispatch_get_main_queue(), ^{ id ctx = self.collectionContext; - [ctx performBatchAnimated:animated updates:^{ - [ctx insertInSectionController:(id)self atIndexes:result.inserts]; - [ctx deleteInSectionController:(id)self atIndexes:result.deletes]; + [ctx performBatchAnimated:animated updates:^(id _Nonnull batchContext) { + [batchContext insertInSectionController:(id)self atIndexes:result.inserts]; + [batchContext deleteInSectionController:(id)self atIndexes:result.deletes]; _items = newItems; } completion:^(BOOL finished) { if (completion) { completion(); } - // WORKAROUND for https://github.com/Instagram/IGListKit/issues/378 - if (wasEmpty) { - [(IGListAdapter *)ctx performUpdatesAnimated:NO completion:nil]; - } }]; }); }); diff --git a/examples/ASDKgram/Sample/AppDelegate.m b/examples/ASDKgram/Sample/AppDelegate.m index ce3c4a332c..16484467f3 100644 --- a/examples/ASDKgram/Sample/AppDelegate.m +++ b/examples/ASDKgram/Sample/AppDelegate.m @@ -1,20 +1,18 @@ // // AppDelegate.m -// Sample -// -// Created by Hannah Troisi on 2/16/16. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. // -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "AppDelegate.h" @@ -24,6 +22,12 @@ #import "WindowWithStatusBarUnderlay.h" #import "Utilities.h" +#define WEAVER 0 + +#if WEAVER +#import +#endif + @interface AppDelegate () @end @@ -75,7 +79,13 @@ // iOS8 hides the status bar in landscape orientation, this forces the status bar hidden status to NO [application setStatusBarHidden:YES withAnimation:UIStatusBarAnimationNone]; [application setStatusBarHidden:NO withAnimation:UIStatusBarAnimationNone]; - + +#if WEAVER + WVDebugger *debugger = [WVDebugger defaultInstance]; + [debugger enableLayoutElementDebuggingWithApplication:application]; + [debugger autoConnect]; +#endif + return YES; } diff --git a/examples/ASDKgram/Sample/ImageURLModel.h b/examples/ASDKgram/Sample/ImageURLModel.h index 9c167fed2f..88381fde8f 100644 --- a/examples/ASDKgram/Sample/ImageURLModel.h +++ b/examples/ASDKgram/Sample/ImageURLModel.h @@ -9,12 +9,12 @@ // 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. +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // @interface ImageURLModel : NSObject diff --git a/examples/ASDKgram/Sample/ImageURLModel.m b/examples/ASDKgram/Sample/ImageURLModel.m index 776ee31777..78c4b43714 100644 --- a/examples/ASDKgram/Sample/ImageURLModel.m +++ b/examples/ASDKgram/Sample/ImageURLModel.m @@ -9,12 +9,12 @@ // 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. +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "ImageURLModel.h" @@ -25,12 +25,12 @@ { BOOL squareImageRequested = (size.width == size.height) ? YES : NO; - NSUInteger imageParameterID; if (squareImageRequested) { - imageParameterID = [self imageParameterForSquareCroppedSize:size]; + NSUInteger imageParameterID = [self imageParameterForSquareCroppedSize:size]; + return [NSString stringWithFormat:@"&image_size=%lu", (long)imageParameterID]; + } else { + return @""; } - - return [NSString stringWithFormat:@"&image_size=%lu", (long)imageParameterID]; } // 500px standard cropped image sizes diff --git a/examples/ASDKgram/Sample/PhotoCellNode.m b/examples/ASDKgram/Sample/PhotoCellNode.m index a0b13fc04b..d424587c60 100644 --- a/examples/ASDKgram/Sample/PhotoCellNode.m +++ b/examples/ASDKgram/Sample/PhotoCellNode.m @@ -18,15 +18,18 @@ // #import "PhotoCellNode.h" + +#import +#import + #import "Utilities.h" -#import "AsyncDisplayKit.h" -#import "ASDisplayNode+Beta.h" #import "CommentsNode.h" #import "PINImageView+PINRemoteImage.h" #import "PINButton+PINRemoteImage.h" // There are many ways to format ASLayoutSpec code. In this example, we offer two different formats: // A flatter, more ordinary Objective-C style; or a more structured, "visually" declarative style. +#define YOGA_LAYOUT 0 #define FLAT_LAYOUT 0 #define DEBUG_PHOTOCELL_LAYOUT 0 @@ -104,6 +107,8 @@ // instead of adding everything addSubnode: self.automaticallyManagesSubnodes = YES; + + [self setupYogaLayoutIfNeeded]; #if DEBUG_PHOTOCELL_LAYOUT _userAvatarImageNode.backgroundColor = [UIColor greenColor]; @@ -119,6 +124,7 @@ return self; } +#if !YOGA_LAYOUT - (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize { // There are many ways to format ASLayoutSpec code. In this example, we offer two different formats: @@ -265,6 +271,7 @@ ]]; } } +#endif #pragma mark - Instance Methods @@ -296,4 +303,66 @@ } } +- (void)setupYogaLayoutIfNeeded +{ +#if YOGA_LAYOUT + [self.style yogaNodeCreateIfNeeded]; + [_userAvatarImageNode.style yogaNodeCreateIfNeeded]; + [_userNameLabel.style yogaNodeCreateIfNeeded]; + [_photoImageNode.style yogaNodeCreateIfNeeded]; + [_photoCommentsNode.style yogaNodeCreateIfNeeded]; + [_photoLikesLabel.style yogaNodeCreateIfNeeded]; + [_photoDescriptionLabel.style yogaNodeCreateIfNeeded]; + [_photoLocationLabel.style yogaNodeCreateIfNeeded]; + [_photoTimeIntervalSincePostLabel.style yogaNodeCreateIfNeeded]; + + ASDisplayNode *headerStack = [ASDisplayNode yogaHorizontalStack]; + headerStack.style.margin = ASEdgeInsetsMake(InsetForHeader); + headerStack.style.alignItems = ASStackLayoutAlignItemsCenter; + headerStack.style.flexGrow = 1.0; + + // Avatar Image, with inset - first thing in the header stack. + _userAvatarImageNode.style.preferredSize = CGSizeMake(USER_IMAGE_HEIGHT, USER_IMAGE_HEIGHT); + _userAvatarImageNode.style.margin = ASEdgeInsetsMake(InsetForAvatar); + [headerStack addYogaChild:_userAvatarImageNode]; + + // User Name and Photo Location stack is next + ASDisplayNode *userPhotoLocationStack = [ASDisplayNode yogaVerticalStack]; + userPhotoLocationStack.style.flexShrink = 1.0; + [headerStack addYogaChild:userPhotoLocationStack]; + + // Setup the inside of the User Name and Photo Location stack. + _userNameLabel.style.flexShrink = 1.0; + [userPhotoLocationStack addYogaChild:_userNameLabel]; + + if (_photoLocationLabel.attributedText) { + _photoLocationLabel.style.flexShrink = 1.0; + [userPhotoLocationStack addYogaChild:_photoLocationLabel]; + } + + // Add a spacer to allow a flexible space between the User Name / Location stack, and the Timestamp. + [headerStack addYogaChild:[ASDisplayNode yogaSpacerNode]]; + + // Photo Timestamp Label. + _photoTimeIntervalSincePostLabel.style.spacingBefore = HORIZONTAL_BUFFER; + [headerStack addYogaChild:_photoTimeIntervalSincePostLabel]; + + // Create the last stack before assembling everything: the Footer Stack contains the description and comments. + ASDisplayNode *footerStack = [ASDisplayNode yogaVerticalStack]; + footerStack.style.margin = ASEdgeInsetsMake(InsetForFooter); + footerStack.style.padding = ASEdgeInsetsMake(UIEdgeInsetsMake(0.0, 0.0, VERTICAL_BUFFER, 0.0)); + footerStack.yogaChildren = @[_photoLikesLabel, _photoDescriptionLabel, _photoCommentsNode]; + + // Main Vertical Stack: contains header, large main photo with fixed aspect ratio, and footer. + _photoImageNode.style.aspectRatio = 1.0; + + ASDisplayNode *verticalStack = self; + self.style.flexDirection = ASStackLayoutDirectionVertical; + + [verticalStack addYogaChild:headerStack]; + [verticalStack addYogaChild:_photoImageNode]; + [verticalStack addYogaChild:footerStack]; +#endif +} + @end diff --git a/examples/ASDKgram/Sample/PhotoFeedListKitViewController.m b/examples/ASDKgram/Sample/PhotoFeedListKitViewController.m index 5aa50732b7..f01f8c8eea 100644 --- a/examples/ASDKgram/Sample/PhotoFeedListKitViewController.m +++ b/examples/ASDKgram/Sample/PhotoFeedListKitViewController.m @@ -65,7 +65,7 @@ { // Ask the first section controller to do the refreshing. id secCtrl = [self.listAdapter sectionControllerForObject:self.photoFeed]; - if ([secCtrl conformsToProtocol:@protocol(RefreshingSectionControllerType)]) { + if ([(NSObject*)secCtrl conformsToProtocol:@protocol(RefreshingSectionControllerType)]) { [secCtrl refreshContentWithCompletion:^{ [self.refreshCtrl endRefreshing]; }]; @@ -93,7 +93,7 @@ return self.spinner; } -- (IGListSectionController *)listAdapter:(IGListAdapter *)listAdapter sectionControllerForObject:(id)object +- (IGListSectionController *)listAdapter:(IGListAdapter *)listAdapter sectionControllerForObject:(id)object { if ([object isKindOfClass:[PhotoFeedModel class]]) { return [[PhotoFeedSectionController alloc] init]; diff --git a/examples/ASDKgram/Sample/PhotoFeedNodeController.m b/examples/ASDKgram/Sample/PhotoFeedNodeController.m index 1e22925e6d..7396094e73 100644 --- a/examples/ASDKgram/Sample/PhotoFeedNodeController.m +++ b/examples/ASDKgram/Sample/PhotoFeedNodeController.m @@ -9,12 +9,12 @@ // 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. +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "PhotoFeedNodeController.h" @@ -27,12 +27,10 @@ #define AUTO_TAIL_LOADING_NUM_SCREENFULS 2.5 @interface PhotoFeedNodeController () +@property (nonatomic, strong) ASTableNode *tableNode; @end @implementation PhotoFeedNodeController -{ - ASTableNode *_tableNode; -} #pragma mark - Lifecycle @@ -42,15 +40,15 @@ { _tableNode = [[ASTableNode alloc] init]; self = [super initWithNode:_tableNode]; - + if (self) { self.navigationItem.title = @"ASDK"; [self.navigationController setNavigationBarHidden:YES]; - + _tableNode.dataSource = self; _tableNode.delegate = self; } - + return self; } @@ -59,14 +57,14 @@ - (void)loadView { [super loadView]; - - _tableNode.view.leadingScreensForBatching = AUTO_TAIL_LOADING_NUM_SCREENFULS; // overriding default of 2.0 + + self.tableNode.leadingScreensForBatching = AUTO_TAIL_LOADING_NUM_SCREENFULS; // overriding default of 2.0 } - (void)loadPageWithContext:(ASBatchContext *)context { [self.photoFeed requestPageWithCompletionBlock:^(NSArray *newPhotos){ - + [self insertNewRows:newPhotos]; [self requestCommentsForPhotos:newPhotos]; if (context) { @@ -107,7 +105,7 @@ PhotoCellNode *cellNode = [[PhotoCellNode alloc] initWithPhotoObject:photoModel]; return cellNode; }; - + return ASCellNodeBlock; } diff --git a/examples/ASDKgram/Sample/PhotoFeedSectionController.h b/examples/ASDKgram/Sample/PhotoFeedSectionController.h index 37ca7a8f92..0da1836a68 100644 --- a/examples/ASDKgram/Sample/PhotoFeedSectionController.h +++ b/examples/ASDKgram/Sample/PhotoFeedSectionController.h @@ -15,7 +15,7 @@ NS_ASSUME_NONNULL_BEGIN -@interface PhotoFeedSectionController : ASCollectionSectionController +@interface PhotoFeedSectionController : ASCollectionSectionController @property (nonatomic, strong, nullable) PhotoFeedModel *photoFeed; diff --git a/examples/ASDKgram/Sample/RefreshingSectionControllerType.h b/examples/ASDKgram/Sample/RefreshingSectionControllerType.h index d2ed2ee712..892a3a32ce 100644 --- a/examples/ASDKgram/Sample/RefreshingSectionControllerType.h +++ b/examples/ASDKgram/Sample/RefreshingSectionControllerType.h @@ -10,7 +10,7 @@ NS_ASSUME_NONNULL_BEGIN -@protocol RefreshingSectionControllerType +@protocol RefreshingSectionControllerType - (void)refreshContentWithCompletion:(nullable void(^)())completion; diff --git a/examples/ASMapNode/Podfile b/examples/ASMapNode/Podfile index 7a8d8c1a00..922ff50ec1 100644 --- a/examples/ASMapNode/Podfile +++ b/examples/ASMapNode/Podfile @@ -1,6 +1,6 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' target 'Sample' do - pod 'AsyncDisplayKit', :path => '../..' + pod 'Texture', :path => '../..' end diff --git a/examples/ASMapNode/Sample/MapHandlerNode.h b/examples/ASMapNode/Sample/MapHandlerNode.h index f51924419f..29c77c7131 100644 --- a/examples/ASMapNode/Sample/MapHandlerNode.h +++ b/examples/ASMapNode/Sample/MapHandlerNode.h @@ -1,18 +1,18 @@ // // MapHandlerNode.h -// Sample +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. // -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/examples/ASMapNode/Sample/MapHandlerNode.m b/examples/ASMapNode/Sample/MapHandlerNode.m index 3d7173783b..0f6f4a0e27 100644 --- a/examples/ASMapNode/Sample/MapHandlerNode.m +++ b/examples/ASMapNode/Sample/MapHandlerNode.m @@ -1,18 +1,18 @@ // // MapHandlerNode.m -// Sample +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. // -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "MapHandlerNode.h" @@ -112,22 +112,24 @@ } /** - * ---------------------------------ASStackLayoutSpec-------------------------------- - * | ------------------------------ASInsetLayoutSpec----------------------------- | - * | | ---------------------------ASStackLayoutSpec-------------------------- | | - * | | | -----------------ASStackLayoutSpec---------------- | | | - * | | | | --------------ASStackLayoutSpec------------- | | | | - * | | | | | ASEditableTextNode ASEditableTextNode | | | | | - * | | | | -------------------------------------------- | | | | - * | | | | --------------ASStackLayoutSpec------------- | ASButtonNode | | | - * | | | | | ASEditableTextNode ASEditableTextNode | | | | | - * | | | | -------------------------------------------- | | | | - * | | | -------------------------------------------------- | | | - * | | ---------------------------------------------------------------------- | | - * | ---------------------------------------------------------------------------- | - * | ASButtonNode | - * | ASMapNode | - * ---------------------------------------------------------------------------------- + * ------------------------------------ASStackLayoutSpec----------------------------------- + * | ---------------------------------ASInsetLayoutSpec-------------------------------- | + * | | ------------------------------ASStackLayoutSpec----------------------------- | | + * | | | ---------------------------ASStackLayoutSpec-------------------------- | | | + * | | | | -----------------ASStackLayoutSpec---------------- | | | | + * | | | | | --------------ASStackLayoutSpec------------- | | | | | + * | | | | | | ASEditableTextNode ASEditableTextNode | | | | | | + * | | | | | -------------------------------------------- | | | | | + * | | | | | --------------ASStackLayoutSpec------------- | ASButtonNode | | | | + * | | | | | | ASEditableTextNode ASEditableTextNode | | | | | | + * | | | | | -------------------------------------------- | | | | | + * | | | | -------------------------------------------------- | | | | + * | | | ---------------------------------------------------------------------- | | | + * | | | ASButtonNode | | | + * | | ---------------------------------------------------------------------------- | | + * | ---------------------------------------------------------------------------------- | + * | ASMapNode | + * ---------------------------------------------------------------------------------------- * * This diagram was created by setting a breakpoint on the returned `layoutSpec` * and calling "po [layoutSpec asciiArtString]" in the debugger. diff --git a/examples/ASViewController/Podfile b/examples/ASViewController/Podfile index 7a8d8c1a00..922ff50ec1 100644 --- a/examples/ASViewController/Podfile +++ b/examples/ASViewController/Podfile @@ -1,6 +1,6 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' target 'Sample' do - pod 'AsyncDisplayKit', :path => '../..' + pod 'Texture', :path => '../..' end diff --git a/examples/ASViewController/Sample.xcodeproj/project.pbxproj b/examples/ASViewController/Sample.xcodeproj/project.pbxproj index 5343c32679..200b3c2255 100644 --- a/examples/ASViewController/Sample.xcodeproj/project.pbxproj +++ b/examples/ASViewController/Sample.xcodeproj/project.pbxproj @@ -122,12 +122,12 @@ isa = PBXNativeTarget; buildConfigurationList = 694993E41C8B334F00491CA5 /* Build configuration list for PBXNativeTarget "Sample" */; buildPhases = ( - 80035273449C25F4B2E1454F /* Check Pods Manifest.lock */, + 80035273449C25F4B2E1454F /* [CP] Check Pods Manifest.lock */, 694993C91C8B334F00491CA5 /* Sources */, 694993CA1C8B334F00491CA5 /* Frameworks */, 694993CB1C8B334F00491CA5 /* Resources */, - 06EE2E0ABEB6289D4775A867 /* Copy Pods Resources */, - 23FC03B282CBD9014D868DF6 /* Embed Pods Frameworks */, + 06EE2E0ABEB6289D4775A867 /* [CP] Copy Pods Resources */, + 23FC03B282CBD9014D868DF6 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -183,14 +183,14 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 06EE2E0ABEB6289D4775A867 /* Copy Pods Resources */ = { + 06EE2E0ABEB6289D4775A867 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Copy Pods Resources"; + name = "[CP] Copy Pods Resources"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; @@ -198,14 +198,14 @@ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Sample/Pods-Sample-resources.sh\"\n"; showEnvVarsInLog = 0; }; - 23FC03B282CBD9014D868DF6 /* Embed Pods Frameworks */ = { + 23FC03B282CBD9014D868DF6 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Embed Pods Frameworks"; + name = "[CP] Embed Pods Frameworks"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; @@ -213,19 +213,19 @@ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Sample/Pods-Sample-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - 80035273449C25F4B2E1454F /* Check Pods Manifest.lock */ = { + 80035273449C25F4B2E1454F /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Check Pods Manifest.lock"; + 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 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"; + 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 */ diff --git a/examples/ASViewController/Sample/DetailCellNode.h b/examples/ASViewController/Sample/DetailCellNode.h index 4331025c34..e80b6dc6cb 100644 --- a/examples/ASViewController/Sample/DetailCellNode.h +++ b/examples/ASViewController/Sample/DetailCellNode.h @@ -15,7 +15,7 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // -#import "ASCellNode.h" +#import @class ASNetworkImageNode; diff --git a/examples/ASViewController/Sample/DetailRootNode.h b/examples/ASViewController/Sample/DetailRootNode.h index fb34ae0ae4..a60ce8007b 100644 --- a/examples/ASViewController/Sample/DetailRootNode.h +++ b/examples/ASViewController/Sample/DetailRootNode.h @@ -15,7 +15,7 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // -#import "ASDisplayNode.h" +#import @class ASCollectionNode; diff --git a/examples/ASViewController/Sample/DetailViewController.h b/examples/ASViewController/Sample/DetailViewController.h index 9ad5a4a58b..f11bfaf42c 100644 --- a/examples/ASViewController/Sample/DetailViewController.h +++ b/examples/ASViewController/Sample/DetailViewController.h @@ -15,7 +15,7 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // -#import "ASViewController.h" +#import #import "DetailRootNode.h" @interface DetailViewController : ASViewController diff --git a/examples/AnimatedGIF/Podfile b/examples/AnimatedGIF/Podfile index 7a8d8c1a00..922ff50ec1 100644 --- a/examples/AnimatedGIF/Podfile +++ b/examples/AnimatedGIF/Podfile @@ -1,6 +1,6 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' target 'Sample' do - pod 'AsyncDisplayKit', :path => '../..' + pod 'Texture', :path => '../..' end diff --git a/examples/AnimatedGIF/Sample.xcodeproj/project.pbxproj b/examples/AnimatedGIF/Sample.xcodeproj/project.pbxproj index c413c9b029..7498a38dde 100644 --- a/examples/AnimatedGIF/Sample.xcodeproj/project.pbxproj +++ b/examples/AnimatedGIF/Sample.xcodeproj/project.pbxproj @@ -1,794 +1,391 @@ - - - - - archiveVersion - 1 - classes - - objectVersion - 46 - objects - - 26A96BEEF893B1FA39F144CF - - buildActionMask - 2147483647 - files - - inputPaths - - isa - PBXShellScriptBuildPhase - name - Embed Pods Frameworks - outputPaths - - runOnlyForDeploymentPostprocessing - 0 - shellPath - /bin/sh - shellScript - "${SRCROOT}/Pods/Target Support Files/Pods-Sample/Pods-Sample-frameworks.sh" - - showEnvVarsInLog - 0 - - 26E3F7C63D79A1F47BEF1AA2 - - explicitFileType - archive.ar - includeInIndex - 0 - isa - PBXFileReference - path - libPods-Sample.a - sourceTree - BUILT_PRODUCTS_DIR - - 2ADE0E7B5309A9CD043DDB3E - - buildActionMask - 2147483647 - files - - inputPaths - - isa - PBXShellScriptBuildPhase - name - Copy Pods Resources - outputPaths - - runOnlyForDeploymentPostprocessing - 0 - shellPath - /bin/sh - shellScript - "${SRCROOT}/Pods/Target Support Files/Pods-Sample/Pods-Sample-resources.sh" - - showEnvVarsInLog - 0 - - 683ADB951CA19883005863A4 - - children - - 683ADBA01CA19883005863A4 - 683ADB9F1CA19883005863A4 - 71A772B0DB9B7760CE330DD9 - 8C6AC07DE55B51935C632F56 - - isa - PBXGroup - sourceTree - <group> - - 683ADB961CA19883005863A4 - - attributes - - LastUpgradeCheck - 0720 - ORGANIZATIONNAME - Facebook, Inc. - TargetAttributes - - 683ADB9D1CA19883005863A4 - - CreatedOnToolsVersion - 7.2 - - - - buildConfigurationList - 683ADB991CA19883005863A4 - compatibilityVersion - Xcode 3.2 - developmentRegion - English - hasScannedForEncodings - 0 - isa - PBXProject - knownRegions - - en - Base - - mainGroup - 683ADB951CA19883005863A4 - productRefGroup - 683ADB9F1CA19883005863A4 - projectDirPath - - projectReferences - - projectRoot - - targets - - 683ADB9D1CA19883005863A4 - - - 683ADB991CA19883005863A4 - - buildConfigurations - - 683ADBB31CA19883005863A4 - 683ADBB41CA19883005863A4 - - defaultConfigurationIsVisible - 0 - defaultConfigurationName - Release - isa - XCConfigurationList - - 683ADB9A1CA19883005863A4 - - buildActionMask - 2147483647 - files - - 683ADBA91CA19883005863A4 - 683ADBA61CA19883005863A4 - 683ADBA31CA19883005863A4 - - isa - PBXSourcesBuildPhase - runOnlyForDeploymentPostprocessing - 0 - - 683ADB9B1CA19883005863A4 - - buildActionMask - 2147483647 - files - - 9AE987532BDC7AC1FD693515 - - isa - PBXFrameworksBuildPhase - runOnlyForDeploymentPostprocessing - 0 - - 683ADB9C1CA19883005863A4 - - buildActionMask - 2147483647 - files - - 683ADBB11CA19883005863A4 - 683ADBAE1CA19883005863A4 - 683ADBAC1CA19883005863A4 - DE5187DA1CD9AA2A00EC11DE - DE5187D91CD9AA2A00EC11DE - - isa - PBXResourcesBuildPhase - runOnlyForDeploymentPostprocessing - 0 - - 683ADB9D1CA19883005863A4 - - buildConfigurationList - 683ADBB51CA19883005863A4 - buildPhases - - 694B306B43ED1C3916B0D909 - 683ADB9A1CA19883005863A4 - 683ADB9B1CA19883005863A4 - 683ADB9C1CA19883005863A4 - 26A96BEEF893B1FA39F144CF - 2ADE0E7B5309A9CD043DDB3E - - buildRules - - dependencies - - isa - PBXNativeTarget - name - Sample - productName - ASAnimatedImage - productReference - 683ADB9E1CA19883005863A4 - productType - com.apple.product-type.application - - 683ADB9E1CA19883005863A4 - - explicitFileType - wrapper.application - includeInIndex - 0 - isa - PBXFileReference - path - Sample.app - sourceTree - BUILT_PRODUCTS_DIR - - 683ADB9F1CA19883005863A4 - - children - - 683ADB9E1CA19883005863A4 - - isa - PBXGroup - name - Products - sourceTree - <group> - - 683ADBA01CA19883005863A4 - - children - - 683ADBA41CA19883005863A4 - 683ADBA51CA19883005863A4 - 683ADBA71CA19883005863A4 - 683ADBA81CA19883005863A4 - 683ADBAA1CA19883005863A4 - 683ADBAD1CA19883005863A4 - 683ADBAF1CA19883005863A4 - 683ADBB21CA19883005863A4 - 683ADBA11CA19883005863A4 - - isa - PBXGroup - path - ASAnimatedImage - sourceTree - <group> - - 683ADBA11CA19883005863A4 - - children - - 683ADBA21CA19883005863A4 - - isa - PBXGroup - name - Supporting Files - sourceTree - <group> - - 683ADBA21CA19883005863A4 - - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - path - main.m - sourceTree - <group> - - 683ADBA31CA19883005863A4 - - fileRef - 683ADBA21CA19883005863A4 - isa - PBXBuildFile - - 683ADBA41CA19883005863A4 - - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - path - AppDelegate.h - sourceTree - <group> - - 683ADBA51CA19883005863A4 - - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - path - AppDelegate.m - sourceTree - <group> - - 683ADBA61CA19883005863A4 - - fileRef - 683ADBA51CA19883005863A4 - isa - PBXBuildFile - - 683ADBA71CA19883005863A4 - - isa - PBXFileReference - lastKnownFileType - sourcecode.c.h - path - ViewController.h - sourceTree - <group> - - 683ADBA81CA19883005863A4 - - isa - PBXFileReference - lastKnownFileType - sourcecode.c.objc - path - ViewController.m - sourceTree - <group> - - 683ADBA91CA19883005863A4 - - fileRef - 683ADBA81CA19883005863A4 - isa - PBXBuildFile - - 683ADBAA1CA19883005863A4 - - children - - 683ADBAB1CA19883005863A4 - - isa - PBXVariantGroup - name - Main.storyboard - sourceTree - <group> - - 683ADBAB1CA19883005863A4 - - isa - PBXFileReference - lastKnownFileType - file.storyboard - name - Base - path - Base.lproj/Main.storyboard - sourceTree - <group> - - 683ADBAC1CA19883005863A4 - - fileRef - 683ADBAA1CA19883005863A4 - isa - PBXBuildFile - - 683ADBAD1CA19883005863A4 - - isa - PBXFileReference - lastKnownFileType - folder.assetcatalog - path - Assets.xcassets - sourceTree - <group> - - 683ADBAE1CA19883005863A4 - - fileRef - 683ADBAD1CA19883005863A4 - isa - PBXBuildFile - - 683ADBAF1CA19883005863A4 - - children - - 683ADBB01CA19883005863A4 - - isa - PBXVariantGroup - name - LaunchScreen.storyboard - sourceTree - <group> - - 683ADBB01CA19883005863A4 - - isa - PBXFileReference - lastKnownFileType - file.storyboard - name - Base - path - Base.lproj/LaunchScreen.storyboard - sourceTree - <group> - - 683ADBB11CA19883005863A4 - - fileRef - 683ADBAF1CA19883005863A4 - isa - PBXBuildFile - - 683ADBB21CA19883005863A4 - - isa - PBXFileReference - lastKnownFileType - text.plist.xml - path - Info.plist - sourceTree - <group> - - 683ADBB31CA19883005863A4 - - buildSettings - - ALWAYS_SEARCH_USER_PATHS - NO - 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_EMPTY_BODY - YES - CLANG_WARN_ENUM_CONVERSION - YES - CLANG_WARN_INT_CONVERSION - YES - CLANG_WARN_OBJC_ROOT_CLASS - YES_ERROR - 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 - 9.2 - MTL_ENABLE_DEBUG_INFO - YES - ONLY_ACTIVE_ARCH - YES - SDKROOT - iphoneos - TARGETED_DEVICE_FAMILY - 1,2 - - isa - XCBuildConfiguration - name - Debug - - 683ADBB41CA19883005863A4 - - buildSettings - - ALWAYS_SEARCH_USER_PATHS - NO - 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_EMPTY_BODY - YES - CLANG_WARN_ENUM_CONVERSION - YES - CLANG_WARN_INT_CONVERSION - YES - CLANG_WARN_OBJC_ROOT_CLASS - YES_ERROR - 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 - 9.2 - MTL_ENABLE_DEBUG_INFO - NO - SDKROOT - iphoneos - TARGETED_DEVICE_FAMILY - 1,2 - VALIDATE_PRODUCT - YES - - isa - XCBuildConfiguration - name - Release - - 683ADBB51CA19883005863A4 - - buildConfigurations - - 683ADBB61CA19883005863A4 - 683ADBB71CA19883005863A4 - - defaultConfigurationIsVisible - 0 - defaultConfigurationName - Release - isa - XCConfigurationList - - 683ADBB61CA19883005863A4 - - baseConfigurationReference - DE5187D71CD9AA2A00EC11DE - buildSettings - - ASSETCATALOG_COMPILER_APPICON_NAME - AppIcon - INFOPLIST_FILE - ASAnimatedImage/Info.plist - LD_RUNPATH_SEARCH_PATHS - $(inherited) @executable_path/Frameworks - PRODUCT_BUNDLE_IDENTIFIER - com.facebook.AsyncDisplayKit.Sample - PRODUCT_NAME - $(TARGET_NAME) - - isa - XCBuildConfiguration - name - Debug - - 683ADBB71CA19883005863A4 - - baseConfigurationReference - DE5187D81CD9AA2A00EC11DE - buildSettings - - ASSETCATALOG_COMPILER_APPICON_NAME - AppIcon - INFOPLIST_FILE - ASAnimatedImage/Info.plist - LD_RUNPATH_SEARCH_PATHS - $(inherited) @executable_path/Frameworks - PRODUCT_BUNDLE_IDENTIFIER - com.facebook.AsyncDisplayKit.Sample - PRODUCT_NAME - $(TARGET_NAME) - - isa - XCBuildConfiguration - name - Release - - 694B306B43ED1C3916B0D909 - - buildActionMask - 2147483647 - files - - inputPaths - - isa - PBXShellScriptBuildPhase - name - Check Pods Manifest.lock - outputPaths - - runOnlyForDeploymentPostprocessing - 0 - shellPath - /bin/sh - shellScript - diff "${PODS_ROOT}/../Podfile.lock" "${PODS_ROOT}/Manifest.lock" > /dev/null -if [[ $? != 0 ]] ; then - cat << EOM -error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation. -EOM - exit 1 -fi - - showEnvVarsInLog - 0 - - 71A772B0DB9B7760CE330DD9 - - children - - DE5187D71CD9AA2A00EC11DE - DE5187D81CD9AA2A00EC11DE - - isa - PBXGroup - name - Pods - sourceTree - <group> - - 8C6AC07DE55B51935C632F56 - - children - - A6F2399FA1A86586D9BDAE05 - 26E3F7C63D79A1F47BEF1AA2 - - isa - PBXGroup - name - Frameworks - sourceTree - <group> - - 9AE987532BDC7AC1FD693515 - - fileRef - 26E3F7C63D79A1F47BEF1AA2 - isa - PBXBuildFile - - A6F2399FA1A86586D9BDAE05 - - explicitFileType - archive.ar - includeInIndex - 0 - isa - PBXFileReference - path - libPods.a - sourceTree - BUILT_PRODUCTS_DIR - - DE5187D71CD9AA2A00EC11DE - - fileEncoding - 4 - isa - PBXFileReference - lastKnownFileType - text.xcconfig - name - Pods-Sample.debug.xcconfig - path - Pods/Target Support Files/Pods-Sample/Pods-Sample.debug.xcconfig - sourceTree - <group> - - DE5187D81CD9AA2A00EC11DE - - fileEncoding - 4 - isa - PBXFileReference - lastKnownFileType - text.xcconfig - name - Pods-Sample.release.xcconfig - path - Pods/Target Support Files/Pods-Sample/Pods-Sample.release.xcconfig - sourceTree - <group> - - DE5187D91CD9AA2A00EC11DE - - fileRef - DE5187D71CD9AA2A00EC11DE - isa - PBXBuildFile - - DE5187DA1CD9AA2A00EC11DE - - fileRef - DE5187D81CD9AA2A00EC11DE - isa - PBXBuildFile - - - rootObject - 683ADB961CA19883005863A4 - - +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 683ADBA31CA19883005863A4 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 683ADBA21CA19883005863A4 /* main.m */; }; + 683ADBA61CA19883005863A4 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 683ADBA51CA19883005863A4 /* AppDelegate.m */; }; + 683ADBA91CA19883005863A4 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 683ADBA81CA19883005863A4 /* ViewController.m */; }; + 683ADBAC1CA19883005863A4 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 683ADBAA1CA19883005863A4 /* Main.storyboard */; }; + 683ADBAE1CA19883005863A4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 683ADBAD1CA19883005863A4 /* Assets.xcassets */; }; + 683ADBB11CA19883005863A4 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 683ADBAF1CA19883005863A4 /* LaunchScreen.storyboard */; }; + 9AE987532BDC7AC1FD693515 /* libPods-Sample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 26E3F7C63D79A1F47BEF1AA2 /* libPods-Sample.a */; }; + DE5187D91CD9AA2A00EC11DE /* Pods-Sample.debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = DE5187D71CD9AA2A00EC11DE /* Pods-Sample.debug.xcconfig */; }; + DE5187DA1CD9AA2A00EC11DE /* Pods-Sample.release.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = DE5187D81CD9AA2A00EC11DE /* Pods-Sample.release.xcconfig */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 26E3F7C63D79A1F47BEF1AA2 /* libPods-Sample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Sample.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 683ADB9E1CA19883005863A4 /* Sample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Sample.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 683ADBA21CA19883005863A4 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 683ADBA41CA19883005863A4 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 683ADBA51CA19883005863A4 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 683ADBA71CA19883005863A4 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; + 683ADBA81CA19883005863A4 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; + 683ADBAB1CA19883005863A4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 683ADBAD1CA19883005863A4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 683ADBB01CA19883005863A4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 683ADBB21CA19883005863A4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + A6F2399FA1A86586D9BDAE05 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; }; + DE5187D71CD9AA2A00EC11DE /* Pods-Sample.debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = "Pods-Sample.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Sample/Pods-Sample.debug.xcconfig"; sourceTree = ""; }; + DE5187D81CD9AA2A00EC11DE /* Pods-Sample.release.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = "Pods-Sample.release.xcconfig"; path = "Pods/Target Support Files/Pods-Sample/Pods-Sample.release.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 683ADB9B1CA19883005863A4 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 9AE987532BDC7AC1FD693515 /* libPods-Sample.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 683ADB951CA19883005863A4 = { + isa = PBXGroup; + children = ( + 683ADBA01CA19883005863A4 /* ASAnimatedImage */, + 683ADB9F1CA19883005863A4 /* Products */, + 71A772B0DB9B7760CE330DD9 /* Pods */, + 8C6AC07DE55B51935C632F56 /* Frameworks */, + ); + sourceTree = ""; + }; + 683ADB9F1CA19883005863A4 /* Products */ = { + isa = PBXGroup; + children = ( + 683ADB9E1CA19883005863A4 /* Sample.app */, + ); + name = Products; + sourceTree = ""; + }; + 683ADBA01CA19883005863A4 /* ASAnimatedImage */ = { + isa = PBXGroup; + children = ( + 683ADBA41CA19883005863A4 /* AppDelegate.h */, + 683ADBA51CA19883005863A4 /* AppDelegate.m */, + 683ADBA71CA19883005863A4 /* ViewController.h */, + 683ADBA81CA19883005863A4 /* ViewController.m */, + 683ADBAA1CA19883005863A4 /* Main.storyboard */, + 683ADBAD1CA19883005863A4 /* Assets.xcassets */, + 683ADBAF1CA19883005863A4 /* LaunchScreen.storyboard */, + 683ADBB21CA19883005863A4 /* Info.plist */, + 683ADBA11CA19883005863A4 /* Supporting Files */, + ); + path = ASAnimatedImage; + sourceTree = ""; + }; + 683ADBA11CA19883005863A4 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 683ADBA21CA19883005863A4 /* main.m */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 71A772B0DB9B7760CE330DD9 /* Pods */ = { + isa = PBXGroup; + children = ( + DE5187D71CD9AA2A00EC11DE /* Pods-Sample.debug.xcconfig */, + DE5187D81CD9AA2A00EC11DE /* Pods-Sample.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; + 8C6AC07DE55B51935C632F56 /* Frameworks */ = { + isa = PBXGroup; + children = ( + A6F2399FA1A86586D9BDAE05 /* libPods.a */, + 26E3F7C63D79A1F47BEF1AA2 /* libPods-Sample.a */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 683ADB9D1CA19883005863A4 /* Sample */ = { + isa = PBXNativeTarget; + buildConfigurationList = 683ADBB51CA19883005863A4 /* Build configuration list for PBXNativeTarget "Sample" */; + buildPhases = ( + 694B306B43ED1C3916B0D909 /* [CP] Check Pods Manifest.lock */, + 683ADB9A1CA19883005863A4 /* Sources */, + 683ADB9B1CA19883005863A4 /* Frameworks */, + 683ADB9C1CA19883005863A4 /* Resources */, + 26A96BEEF893B1FA39F144CF /* [CP] Embed Pods Frameworks */, + 2ADE0E7B5309A9CD043DDB3E /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Sample; + productName = ASAnimatedImage; + productReference = 683ADB9E1CA19883005863A4 /* Sample.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 683ADB961CA19883005863A4 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0720; + ORGANIZATIONNAME = "Facebook, Inc."; + TargetAttributes = { + 683ADB9D1CA19883005863A4 = { + CreatedOnToolsVersion = 7.2; + }; + }; + }; + buildConfigurationList = 683ADB991CA19883005863A4 /* Build configuration list for PBXProject "Sample" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 683ADB951CA19883005863A4; + productRefGroup = 683ADB9F1CA19883005863A4 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 683ADB9D1CA19883005863A4 /* Sample */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 683ADB9C1CA19883005863A4 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 683ADBB11CA19883005863A4 /* LaunchScreen.storyboard in Resources */, + 683ADBAE1CA19883005863A4 /* Assets.xcassets in Resources */, + 683ADBAC1CA19883005863A4 /* Main.storyboard in Resources */, + DE5187DA1CD9AA2A00EC11DE /* Pods-Sample.release.xcconfig in Resources */, + DE5187D91CD9AA2A00EC11DE /* Pods-Sample.debug.xcconfig in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 26A96BEEF893B1FA39F144CF /* [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-Sample/Pods-Sample-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 2ADE0E7B5309A9CD043DDB3E /* [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-Sample/Pods-Sample-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + 694B306B43ED1C3916B0D909 /* [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 */ + 683ADB9A1CA19883005863A4 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 683ADBA91CA19883005863A4 /* ViewController.m in Sources */, + 683ADBA61CA19883005863A4 /* AppDelegate.m in Sources */, + 683ADBA31CA19883005863A4 /* main.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 683ADBAA1CA19883005863A4 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 683ADBAB1CA19883005863A4 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 683ADBAF1CA19883005863A4 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 683ADBB01CA19883005863A4 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 683ADBB31CA19883005863A4 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + 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_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + 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 = 9.2; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 683ADBB41CA19883005863A4 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + 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_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + 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 = 9.2; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 683ADBB61CA19883005863A4 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = DE5187D71CD9AA2A00EC11DE /* Pods-Sample.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = ASAnimatedImage/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.facebook.AsyncDisplayKit.Sample; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 683ADBB71CA19883005863A4 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = DE5187D81CD9AA2A00EC11DE /* Pods-Sample.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = ASAnimatedImage/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.facebook.AsyncDisplayKit.Sample; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 683ADB991CA19883005863A4 /* Build configuration list for PBXProject "Sample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 683ADBB31CA19883005863A4 /* Debug */, + 683ADBB41CA19883005863A4 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 683ADBB51CA19883005863A4 /* Build configuration list for PBXNativeTarget "Sample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 683ADBB61CA19883005863A4 /* Debug */, + 683ADBB71CA19883005863A4 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 683ADB961CA19883005863A4 /* Project object */; +} diff --git a/examples/AsyncDisplayKitOverview/Podfile b/examples/AsyncDisplayKitOverview/Podfile index 95777ae876..84d7dee82e 100644 --- a/examples/AsyncDisplayKitOverview/Podfile +++ b/examples/AsyncDisplayKitOverview/Podfile @@ -5,6 +5,6 @@ platform :ios, '8.0' # use_frameworks! target 'Sample' do - pod 'AsyncDisplayKit', :path => '../..' + pod 'Texture', :path => '../..' end diff --git a/examples/AsyncDisplayKitOverview/Sample.xcodeproj/project.pbxproj b/examples/AsyncDisplayKitOverview/Sample.xcodeproj/project.pbxproj index da40db4660..8d5abb4bbc 100644 --- a/examples/AsyncDisplayKitOverview/Sample.xcodeproj/project.pbxproj +++ b/examples/AsyncDisplayKitOverview/Sample.xcodeproj/project.pbxproj @@ -135,12 +135,12 @@ isa = PBXNativeTarget; buildConfigurationList = 697216471CCD8FB300122312 /* Build configuration list for PBXNativeTarget "Sample" */; buildPhases = ( - 78A0D09A94A74B3737920EA7 /* Check Pods Manifest.lock */, + 78A0D09A94A74B3737920EA7 /* [CP] Check Pods Manifest.lock */, 6972162C1CCD8FB300122312 /* Sources */, 6972162D1CCD8FB300122312 /* Frameworks */, 6972162E1CCD8FB300122312 /* Resources */, - 267658CA53A0F4A2D24A8438 /* Embed Pods Frameworks */, - 84F93825AFB1CA7FBB116BA4 /* Copy Pods Resources */, + 267658CA53A0F4A2D24A8438 /* [CP] Embed Pods Frameworks */, + 84F93825AFB1CA7FBB116BA4 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -196,14 +196,14 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 267658CA53A0F4A2D24A8438 /* Embed Pods Frameworks */ = { + 267658CA53A0F4A2D24A8438 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Embed Pods Frameworks"; + name = "[CP] Embed Pods Frameworks"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; @@ -211,29 +211,29 @@ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Sample/Pods-Sample-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - 78A0D09A94A74B3737920EA7 /* Check Pods Manifest.lock */ = { + 78A0D09A94A74B3737920EA7 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Check Pods Manifest.lock"; + 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 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"; + 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; }; - 84F93825AFB1CA7FBB116BA4 /* Copy Pods Resources */ = { + 84F93825AFB1CA7FBB116BA4 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Copy Pods Resources"; + name = "[CP] Copy Pods Resources"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; diff --git a/examples/AsyncDisplayKitOverview/Sample/Node Containers/OverviewASCollectionNode.h b/examples/AsyncDisplayKitOverview/Sample/Node Containers/OverviewASCollectionNode.h index 93c2778dac..6be9dfaa7a 100644 --- a/examples/AsyncDisplayKitOverview/Sample/Node Containers/OverviewASCollectionNode.h +++ b/examples/AsyncDisplayKitOverview/Sample/Node Containers/OverviewASCollectionNode.h @@ -17,7 +17,7 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // -#import "ASDisplayNode.h" +#import @interface OverviewASCollectionNode : ASDisplayNode diff --git a/examples/CatDealsCollectionView/Podfile b/examples/CatDealsCollectionView/Podfile index defaf55058..b75e492fab 100644 --- a/examples/CatDealsCollectionView/Podfile +++ b/examples/CatDealsCollectionView/Podfile @@ -1,5 +1,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' target 'Sample' do - pod 'AsyncDisplayKit', :path => '../..' + pod 'Texture', :path => '../..' end diff --git a/examples/CatDealsCollectionView/Sample.xcodeproj/project.pbxproj b/examples/CatDealsCollectionView/Sample.xcodeproj/project.pbxproj index 8f4214a411..5a9485def8 100644 --- a/examples/CatDealsCollectionView/Sample.xcodeproj/project.pbxproj +++ b/examples/CatDealsCollectionView/Sample.xcodeproj/project.pbxproj @@ -145,12 +145,12 @@ isa = PBXNativeTarget; buildConfigurationList = AC3C4A811A11F47200143C57 /* Build configuration list for PBXNativeTarget "Sample" */; buildPhases = ( - F868CFBB21824CC9521B6588 /* Check Pods Manifest.lock */, + F868CFBB21824CC9521B6588 /* [CP] Check Pods Manifest.lock */, AC3C4A5A1A11F47200143C57 /* Sources */, AC3C4A5B1A11F47200143C57 /* Frameworks */, AC3C4A5C1A11F47200143C57 /* Resources */, - A6902C454C7661D0D277AC62 /* Copy Pods Resources */, - B4CD33E927E6F4EE5DD6CCF0 /* Embed Pods Frameworks */, + A6902C454C7661D0D277AC62 /* [CP] Copy Pods Resources */, + B4CD33E927E6F4EE5DD6CCF0 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -206,14 +206,14 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - A6902C454C7661D0D277AC62 /* Copy Pods Resources */ = { + A6902C454C7661D0D277AC62 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Copy Pods Resources"; + name = "[CP] Copy Pods Resources"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; @@ -221,14 +221,14 @@ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Sample/Pods-Sample-resources.sh\"\n"; showEnvVarsInLog = 0; }; - B4CD33E927E6F4EE5DD6CCF0 /* Embed Pods Frameworks */ = { + B4CD33E927E6F4EE5DD6CCF0 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Embed Pods Frameworks"; + name = "[CP] Embed Pods Frameworks"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; @@ -236,19 +236,19 @@ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Sample/Pods-Sample-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - F868CFBB21824CC9521B6588 /* Check Pods Manifest.lock */ = { + F868CFBB21824CC9521B6588 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Check Pods Manifest.lock"; + 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 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"; + 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 */ diff --git a/examples/CatDealsCollectionView/Sample/ItemNode.h b/examples/CatDealsCollectionView/Sample/ItemNode.h index 7fc1dedc13..678327e111 100644 --- a/examples/CatDealsCollectionView/Sample/ItemNode.h +++ b/examples/CatDealsCollectionView/Sample/ItemNode.h @@ -1,18 +1,18 @@ // // ItemNode.h -// Sample +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. // -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/examples/CatDealsCollectionView/Sample/ItemNode.m b/examples/CatDealsCollectionView/Sample/ItemNode.m index 79d8c2ff21..400f5b79e8 100644 --- a/examples/CatDealsCollectionView/Sample/ItemNode.m +++ b/examples/CatDealsCollectionView/Sample/ItemNode.m @@ -1,23 +1,24 @@ // // ItemNode.m -// Sample +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. // -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "ItemNode.h" #import "ItemStyles.h" #import "PlaceholderNetworkImageNode.h" +#import const CGFloat kFixedLabelsAreaHeight = 96.0; const CGFloat kDesignWidth = 320.0; @@ -45,16 +46,19 @@ const CGFloat kSoldOutGBHeight = 50.0; @end @implementation ItemNode +@dynamic viewModel; - (instancetype)initWithViewModel:(ItemViewModel *)viewModel { self = [super init]; if (self != nil) { - _viewModel = viewModel; + self.viewModel = viewModel; [self setup]; [self updateLabels]; [self updateBackgroundColor]; + ASSetDebugName(self, @"Item #%zd", viewModel.identifier); + self.accessibilityIdentifier = viewModel.titleText; } return self; } diff --git a/examples/CatDealsCollectionView/Sample/ItemViewModel.h b/examples/CatDealsCollectionView/Sample/ItemViewModel.h index 4f73f3afe1..621b4060a9 100644 --- a/examples/CatDealsCollectionView/Sample/ItemViewModel.h +++ b/examples/CatDealsCollectionView/Sample/ItemViewModel.h @@ -1,20 +1,18 @@ // // ItemViewModel.h -// Sample -// -// Created by Samuel Stow on 12/29/15. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. // -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -22,8 +20,9 @@ @interface ItemViewModel : NSObject -+ (instancetype)randomItem; ++ (ItemViewModel *)randomItem; +@property (nonatomic, readonly) NSInteger identifier; @property (nonatomic, copy) NSString *titleText; @property (nonatomic, copy) NSString *firstInfoText; @property (nonatomic, copy) NSString *secondInfoText; diff --git a/examples/CatDealsCollectionView/Sample/ItemViewModel.m b/examples/CatDealsCollectionView/Sample/ItemViewModel.m index c975a798f4..4cf925e5d6 100644 --- a/examples/CatDealsCollectionView/Sample/ItemViewModel.m +++ b/examples/CatDealsCollectionView/Sample/ItemViewModel.m @@ -1,23 +1,22 @@ // // ItemViewModel.m -// Sample -// -// Created by Samuel Stow on 12/29/15. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. // -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "ItemViewModel.h" +#import NSArray *titles; NSArray *firstInfos; @@ -32,28 +31,29 @@ NSArray *badges; @implementation ItemViewModel -+ (instancetype)randomItem { ++ (ItemViewModel *)randomItem { return [[ItemViewModel alloc] init]; } - (instancetype)init { self = [super init]; if (self) { - _titleText = [self randomObjectFromArray:titles]; - _firstInfoText = [self randomObjectFromArray:firstInfos]; - _secondInfoText = [NSString stringWithFormat:@"%zd+ bought", [self randomNumberInRange:5 to:6000]]; - _originalPriceText = [NSString stringWithFormat:@"$%zd", [self randomNumberInRange:40 to:90]]; - _finalPriceText = [NSString stringWithFormat:@"$%zd", [self randomNumberInRange:5 to:30]]; - BOOL isSoldOut = arc4random() % 5 == 0; - _soldOutText = isSoldOut ? @"SOLD OUT" : nil; - _distanceLabelText = [NSString stringWithFormat:@"%zd mi", [self randomNumberInRange:1 to:20]]; - BOOL isBadged = arc4random() % 2 == 0; - if (isBadged) { - _badgeText = [self randomObjectFromArray:badges]; - } - _catNumber = [self randomNumberInRange:1 to:10]; - _labelNumber = [self randomNumberInRange:1 to:10000]; - + static _Atomic(NSInteger) nextID = ATOMIC_VAR_INIT(1); + _identifier = atomic_fetch_add(&nextID, 1); + _titleText = [self randomObjectFromArray:titles]; + _firstInfoText = [self randomObjectFromArray:firstInfos]; + _secondInfoText = [NSString stringWithFormat:@"%zd+ bought", [self randomNumberInRange:5 to:6000]]; + _originalPriceText = [NSString stringWithFormat:@"$%zd", [self randomNumberInRange:40 to:90]]; + _finalPriceText = [NSString stringWithFormat:@"$%zd", [self randomNumberInRange:5 to:30]]; + BOOL isSoldOut = arc4random() % 5 == 0; + _soldOutText = isSoldOut ? @"SOLD OUT" : nil; + _distanceLabelText = [NSString stringWithFormat:@"%zd mi", [self randomNumberInRange:1 to:20]]; + BOOL isBadged = arc4random() % 2 == 0; + if (isBadged) { + _badgeText = [self randomObjectFromArray:badges]; + } + _catNumber = [self randomNumberInRange:1 to:10]; + _labelNumber = [self randomNumberInRange:1 to:10000]; } return self; } diff --git a/examples/CatDealsCollectionView/Sample/ViewController.m b/examples/CatDealsCollectionView/Sample/ViewController.m index 6d693f690c..6e63dabe09 100644 --- a/examples/CatDealsCollectionView/Sample/ViewController.m +++ b/examples/CatDealsCollectionView/Sample/ViewController.m @@ -1,18 +1,18 @@ // // ViewController.m -// Sample +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. // -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "ViewController.h" @@ -56,6 +56,7 @@ static const CGFloat kHorizontalSectionPadding = 10.0f; _collectionNode.dataSource = self; _collectionNode.delegate = self; _collectionNode.backgroundColor = [UIColor grayColor]; + _collectionNode.accessibilityIdentifier = @"Cat deals list"; ASRangeTuningParameters preloadTuning; preloadTuning.leadingBufferScreenfuls = 2; @@ -84,7 +85,7 @@ static const CGFloat kHorizontalSectionPadding = 10.0f; [super viewDidLoad]; // set any collectionView properties here (once the node's backing view is loaded) - _collectionNode.view.leadingScreensForBatching = 2; + _collectionNode.leadingScreensForBatching = 2; [self fetchMoreCatsWithCompletion:nil]; } @@ -96,12 +97,10 @@ static const CGFloat kHorizontalSectionPadding = 10.0f; ViewController *strongSelf = weakSelf; if (strongSelf != nil) { - NSLog(@"ViewController is not nil"); [strongSelf appendMoreItems:kBatchSize completion:completion]; - NSLog(@"ViewController finished updating collectionView"); } else { - NSLog(@"ViewController is nil - won't update collectionView"); + NSLog(@"ViewController is nil - won't update collection"); } }; @@ -113,13 +112,11 @@ static const CGFloat kHorizontalSectionPadding = 10.0f; - (void)appendMoreItems:(NSInteger)numberOfNewItems completion:(void (^)(BOOL))completion { NSArray *newData = [self getMoreData:numberOfNewItems]; - dispatch_async(dispatch_get_main_queue(), ^{ - [_collectionNode performBatchAnimated:YES updates:^{ - [_data addObjectsFromArray:newData]; - NSArray *addedIndexPaths = [self indexPathsForObjects:newData]; - [_collectionNode insertItemsAtIndexPaths:addedIndexPaths]; - } completion:completion]; - }); + [_collectionNode performBatchAnimated:YES updates:^{ + [_data addObjectsFromArray:newData]; + NSArray *addedIndexPaths = [self indexPathsForObjects:newData]; + [_collectionNode insertItemsAtIndexPaths:addedIndexPaths]; + } completion:completion]; } - (NSArray *)getMoreData:(NSInteger)count { @@ -196,7 +193,6 @@ static const CGFloat kHorizontalSectionPadding = 10.0f; - (void)collectionNode:(ASCollectionNode *)collectionNode willBeginBatchFetchWithContext:(ASBatchContext *)context { - NSLog(@"fetch additional content"); [self fetchMoreCatsWithCompletion:^(BOOL finished){ [context completeBatchFetching:YES]; }]; diff --git a/examples/CustomCollectionView-Swift/Podfile b/examples/CustomCollectionView-Swift/Podfile index 77782621e2..a0eec32ed0 100644 --- a/examples/CustomCollectionView-Swift/Podfile +++ b/examples/CustomCollectionView-Swift/Podfile @@ -4,5 +4,5 @@ platform :ios, '8.0' use_frameworks! target 'Sample' do - pod 'AsyncDisplayKit', :path => '../..' + pod 'Texture', :path => '../..' end \ No newline at end of file diff --git a/examples/CustomCollectionView-Swift/Sample.xcodeproj/project.pbxproj b/examples/CustomCollectionView-Swift/Sample.xcodeproj/project.pbxproj old mode 100644 new mode 100755 index 621297c9c2..1ecfa3fb97 --- a/examples/CustomCollectionView-Swift/Sample.xcodeproj/project.pbxproj +++ b/examples/CustomCollectionView-Swift/Sample.xcodeproj/project.pbxproj @@ -1,787 +1,383 @@ - - - - - archiveVersion - 1 - classes - - objectVersion - 46 - objects - - 27F2D2683285DCB73EE734BB - - fileRef - F7DA4A9952245B7E9BA8201F - isa - PBXBuildFile - - 3A21D910663270E99063573E - - children - - A9E9A143FF858FD89A482A84 - 73F1C0E45062A0A8CDC033A1 - - isa - PBXGroup - name - Pods - sourceTree - <group> - - 5D823AC81DD3B7760075E14A - - children - - 5D823AD31DD3B7770075E14A - 5D823AD21DD3B7770075E14A - 3A21D910663270E99063573E - 728C7877715727493EFEE42D - - isa - PBXGroup - sourceTree - <group> - - 5D823AC91DD3B7760075E14A - - attributes - - LastSwiftUpdateCheck - 0810 - LastUpgradeCheck - 0810 - ORGANIZATIONNAME - AsyncDisplayKit - TargetAttributes - - 5D823AD01DD3B7770075E14A - - CreatedOnToolsVersion - 8.1 - DevelopmentTeam - 888KTQ92ZP - ProvisioningStyle - Automatic - - - - buildConfigurationList - 5D823ACC1DD3B7760075E14A - compatibilityVersion - Xcode 3.2 - developmentRegion - English - hasScannedForEncodings - 0 - isa - PBXProject - knownRegions - - en - Base - - mainGroup - 5D823AC81DD3B7760075E14A - productRefGroup - 5D823AD21DD3B7770075E14A - projectDirPath - - projectReferences - - projectRoot - - targets - - 5D823AD01DD3B7770075E14A - - - 5D823ACC1DD3B7760075E14A - - buildConfigurations - - 5D823AE11DD3B7770075E14A - 5D823AE21DD3B7770075E14A - - defaultConfigurationIsVisible - 0 - defaultConfigurationName - Release - isa - XCConfigurationList - - 5D823ACD1DD3B7770075E14A - - buildActionMask - 2147483647 - files - - 5D823AE71DD3B7D30075E14A - 5D823AD71DD3B7770075E14A - 5D823AD51DD3B7770075E14A - 5D823AE91DD3B7D70075E14A - - isa - PBXSourcesBuildPhase - runOnlyForDeploymentPostprocessing - 0 - - 5D823ACE1DD3B7770075E14A - - buildActionMask - 2147483647 - files - - 27F2D2683285DCB73EE734BB - - isa - PBXFrameworksBuildPhase - runOnlyForDeploymentPostprocessing - 0 - - 5D823ACF1DD3B7770075E14A - - buildActionMask - 2147483647 - files - - 5D823ADF1DD3B7770075E14A - 5D823ADC1DD3B7770075E14A - 5D823ADA1DD3B7770075E14A - - isa - PBXResourcesBuildPhase - runOnlyForDeploymentPostprocessing - 0 - - 5D823AD01DD3B7770075E14A - - buildConfigurationList - 5D823AE31DD3B7770075E14A - buildPhases - - BAA73690D42731AA5D8001CF - 5D823ACD1DD3B7770075E14A - 5D823ACE1DD3B7770075E14A - 5D823ACF1DD3B7770075E14A - 8293091514A70C5E7E487A36 - 641DF857294FFEAA1878D05C - - buildRules - - dependencies - - isa - PBXNativeTarget - name - Sample - productName - Sample - productReference - 5D823AD11DD3B7770075E14A - productType - com.apple.product-type.application - - 5D823AD11DD3B7770075E14A - - explicitFileType - wrapper.application - includeInIndex - 0 - isa - PBXFileReference - path - Sample.app - sourceTree - BUILT_PRODUCTS_DIR - - 5D823AD21DD3B7770075E14A - - children - - 5D823AD11DD3B7770075E14A - - isa - PBXGroup - name - Products - sourceTree - <group> - - 5D823AD31DD3B7770075E14A - - children - - 5D823AD41DD3B7770075E14A - 5D823AD61DD3B7770075E14A - 5D823AE81DD3B7D70075E14A - 5D823AE61DD3B7D30075E14A - 5D823AD81DD3B7770075E14A - 5D823ADB1DD3B7770075E14A - 5D823ADD1DD3B7770075E14A - 5D823AE01DD3B7770075E14A - - isa - PBXGroup - path - Sample - sourceTree - <group> - - 5D823AD41DD3B7770075E14A - - isa - PBXFileReference - lastKnownFileType - sourcecode.swift - path - AppDelegate.swift - sourceTree - <group> - - 5D823AD51DD3B7770075E14A - - fileRef - 5D823AD41DD3B7770075E14A - isa - PBXBuildFile - - 5D823AD61DD3B7770075E14A - - isa - PBXFileReference - lastKnownFileType - sourcecode.swift - path - ViewController.swift - sourceTree - <group> - - 5D823AD71DD3B7770075E14A - - fileRef - 5D823AD61DD3B7770075E14A - isa - PBXBuildFile - - 5D823AD81DD3B7770075E14A - - children - - 5D823AD91DD3B7770075E14A - - isa - PBXVariantGroup - name - Main.storyboard - sourceTree - <group> - - 5D823AD91DD3B7770075E14A - - isa - PBXFileReference - lastKnownFileType - file.storyboard - name - Base - path - Base.lproj/Main.storyboard - sourceTree - <group> - - 5D823ADA1DD3B7770075E14A - - fileRef - 5D823AD81DD3B7770075E14A - isa - PBXBuildFile - - 5D823ADB1DD3B7770075E14A - - isa - PBXFileReference - lastKnownFileType - folder.assetcatalog - path - Assets.xcassets - sourceTree - <group> - - 5D823ADC1DD3B7770075E14A - - fileRef - 5D823ADB1DD3B7770075E14A - isa - PBXBuildFile - - 5D823ADD1DD3B7770075E14A - - children - - 5D823ADE1DD3B7770075E14A - - isa - PBXVariantGroup - name - LaunchScreen.storyboard - sourceTree - <group> - - 5D823ADE1DD3B7770075E14A - - isa - PBXFileReference - lastKnownFileType - file.storyboard - name - Base - path - Base.lproj/LaunchScreen.storyboard - sourceTree - <group> - - 5D823ADF1DD3B7770075E14A - - fileRef - 5D823ADD1DD3B7770075E14A - isa - PBXBuildFile - - 5D823AE01DD3B7770075E14A - - isa - PBXFileReference - lastKnownFileType - text.plist.xml - path - Info.plist - sourceTree - <group> - - 5D823AE11DD3B7770075E14A - - 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_SUSPICIOUS_MOVES - 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 - 9.3 - MTL_ENABLE_DEBUG_INFO - YES - ONLY_ACTIVE_ARCH - YES - SDKROOT - iphoneos - SWIFT_ACTIVE_COMPILATION_CONDITIONS - DEBUG - SWIFT_OPTIMIZATION_LEVEL - -Onone - - isa - XCBuildConfiguration - name - Debug - - 5D823AE21DD3B7770075E14A - - 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_SUSPICIOUS_MOVES - 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 - 9.3 - MTL_ENABLE_DEBUG_INFO - NO - SDKROOT - iphoneos - SWIFT_OPTIMIZATION_LEVEL - -Owholemodule - VALIDATE_PRODUCT - YES - - isa - XCBuildConfiguration - name - Release - - 5D823AE31DD3B7770075E14A - - buildConfigurations - - 5D823AE41DD3B7770075E14A - 5D823AE51DD3B7770075E14A - - defaultConfigurationIsVisible - 0 - defaultConfigurationName - Release - isa - XCConfigurationList - - 5D823AE41DD3B7770075E14A - - baseConfigurationReference - A9E9A143FF858FD89A482A84 - buildSettings - - ASSETCATALOG_COMPILER_APPICON_NAME - AppIcon - DEVELOPMENT_TEAM - 888KTQ92ZP - INFOPLIST_FILE - Sample/Info.plist - LD_RUNPATH_SEARCH_PATHS - $(inherited) @executable_path/Frameworks - PRODUCT_BUNDLE_IDENTIFIER - com.facebook.AsyncDisplayKit.Sample - PRODUCT_NAME - $(TARGET_NAME) - SWIFT_VERSION - 3.0 - - isa - XCBuildConfiguration - name - Debug - - 5D823AE51DD3B7770075E14A - - baseConfigurationReference - 73F1C0E45062A0A8CDC033A1 - buildSettings - - ASSETCATALOG_COMPILER_APPICON_NAME - AppIcon - DEVELOPMENT_TEAM - 888KTQ92ZP - INFOPLIST_FILE - Sample/Info.plist - LD_RUNPATH_SEARCH_PATHS - $(inherited) @executable_path/Frameworks - PRODUCT_BUNDLE_IDENTIFIER - com.facebook.AsyncDisplayKit.Sample - PRODUCT_NAME - $(TARGET_NAME) - SWIFT_VERSION - 3.0 - - isa - XCBuildConfiguration - name - Release - - 5D823AE61DD3B7D30075E14A - - fileEncoding - 4 - isa - PBXFileReference - lastKnownFileType - sourcecode.swift - path - MosaicCollectionViewLayout.swift - sourceTree - <group> - - 5D823AE71DD3B7D30075E14A - - fileRef - 5D823AE61DD3B7D30075E14A - isa - PBXBuildFile - - 5D823AE81DD3B7D70075E14A - - fileEncoding - 4 - isa - PBXFileReference - lastKnownFileType - sourcecode.swift - path - ImageCellNode.swift - sourceTree - <group> - - 5D823AE91DD3B7D70075E14A - - fileRef - 5D823AE81DD3B7D70075E14A - isa - PBXBuildFile - - 641DF857294FFEAA1878D05C - - buildActionMask - 2147483647 - files - - inputPaths - - isa - PBXShellScriptBuildPhase - name - [CP] Copy Pods Resources - outputPaths - - runOnlyForDeploymentPostprocessing - 0 - shellPath - /bin/sh - shellScript - "${SRCROOT}/Pods/Target Support Files/Pods-Sample/Pods-Sample-resources.sh" - - showEnvVarsInLog - 0 - - 728C7877715727493EFEE42D - - children - - F7DA4A9952245B7E9BA8201F - - isa - PBXGroup - name - Frameworks - sourceTree - <group> - - 73F1C0E45062A0A8CDC033A1 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - text.xcconfig - name - Pods-Sample.release.xcconfig - path - Pods/Target Support Files/Pods-Sample/Pods-Sample.release.xcconfig - sourceTree - <group> - - 8293091514A70C5E7E487A36 - - buildActionMask - 2147483647 - files - - inputPaths - - isa - PBXShellScriptBuildPhase - name - [CP] Embed Pods Frameworks - outputPaths - - runOnlyForDeploymentPostprocessing - 0 - shellPath - /bin/sh - shellScript - "${SRCROOT}/Pods/Target Support Files/Pods-Sample/Pods-Sample-frameworks.sh" - - showEnvVarsInLog - 0 - - A9E9A143FF858FD89A482A84 - - includeInIndex - 1 - isa - PBXFileReference - lastKnownFileType - text.xcconfig - name - Pods-Sample.debug.xcconfig - path - Pods/Target Support Files/Pods-Sample/Pods-Sample.debug.xcconfig - sourceTree - <group> - - BAA73690D42731AA5D8001CF - - buildActionMask - 2147483647 - files - - inputPaths - - isa - PBXShellScriptBuildPhase - name - [CP] Check Pods Manifest.lock - outputPaths - - runOnlyForDeploymentPostprocessing - 0 - shellPath - /bin/sh - shellScript - diff "${PODS_ROOT}/../Podfile.lock" "${PODS_ROOT}/Manifest.lock" > /dev/null -if [[ $? != 0 ]] ; then - cat << EOM -error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation. -EOM - exit 1 -fi - - showEnvVarsInLog - 0 - - F7DA4A9952245B7E9BA8201F - - explicitFileType - wrapper.framework - includeInIndex - 0 - isa - PBXFileReference - path - Pods_Sample.framework - sourceTree - BUILT_PRODUCTS_DIR - - - rootObject - 5D823AC91DD3B7760075E14A - - +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 27F2D2683285DCB73EE734BB /* Pods_Sample.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F7DA4A9952245B7E9BA8201F /* Pods_Sample.framework */; }; + 5D823AD51DD3B7770075E14A /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D823AD41DD3B7770075E14A /* AppDelegate.swift */; }; + 5D823AD71DD3B7770075E14A /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D823AD61DD3B7770075E14A /* ViewController.swift */; }; + 5D823ADC1DD3B7770075E14A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5D823ADB1DD3B7770075E14A /* Assets.xcassets */; }; + 5D823ADF1DD3B7770075E14A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5D823ADD1DD3B7770075E14A /* LaunchScreen.storyboard */; }; + 5D823AE71DD3B7D30075E14A /* MosaicCollectionViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D823AE61DD3B7D30075E14A /* MosaicCollectionViewLayout.swift */; }; + 5D823AE91DD3B7D70075E14A /* ImageCellNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D823AE81DD3B7D70075E14A /* ImageCellNode.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 5D823AD11DD3B7770075E14A /* Sample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Sample.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 5D823AD41DD3B7770075E14A /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 5D823AD61DD3B7770075E14A /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + 5D823ADB1DD3B7770075E14A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 5D823ADE1DD3B7770075E14A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 5D823AE01DD3B7770075E14A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 5D823AE61DD3B7D30075E14A /* MosaicCollectionViewLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MosaicCollectionViewLayout.swift; sourceTree = ""; }; + 5D823AE81DD3B7D70075E14A /* ImageCellNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageCellNode.swift; sourceTree = ""; }; + 73F1C0E45062A0A8CDC033A1 /* 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 = ""; }; + A9E9A143FF858FD89A482A84 /* Pods-Sample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Sample.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Sample/Pods-Sample.debug.xcconfig"; sourceTree = ""; }; + F7DA4A9952245B7E9BA8201F /* Pods_Sample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Sample.framework; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 5D823ACE1DD3B7770075E14A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 27F2D2683285DCB73EE734BB /* Pods_Sample.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 3A21D910663270E99063573E /* Pods */ = { + isa = PBXGroup; + children = ( + A9E9A143FF858FD89A482A84 /* Pods-Sample.debug.xcconfig */, + 73F1C0E45062A0A8CDC033A1 /* Pods-Sample.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; + 5D823AC81DD3B7760075E14A = { + isa = PBXGroup; + children = ( + 5D823AD31DD3B7770075E14A /* Sample */, + 5D823AD21DD3B7770075E14A /* Products */, + 3A21D910663270E99063573E /* Pods */, + 728C7877715727493EFEE42D /* Frameworks */, + ); + sourceTree = ""; + }; + 5D823AD21DD3B7770075E14A /* Products */ = { + isa = PBXGroup; + children = ( + 5D823AD11DD3B7770075E14A /* Sample.app */, + ); + name = Products; + sourceTree = ""; + }; + 5D823AD31DD3B7770075E14A /* Sample */ = { + isa = PBXGroup; + children = ( + 5D823AD41DD3B7770075E14A /* AppDelegate.swift */, + 5D823AD61DD3B7770075E14A /* ViewController.swift */, + 5D823AE81DD3B7D70075E14A /* ImageCellNode.swift */, + 5D823AE61DD3B7D30075E14A /* MosaicCollectionViewLayout.swift */, + 5D823ADB1DD3B7770075E14A /* Assets.xcassets */, + 5D823ADD1DD3B7770075E14A /* LaunchScreen.storyboard */, + 5D823AE01DD3B7770075E14A /* Info.plist */, + ); + path = Sample; + sourceTree = ""; + }; + 728C7877715727493EFEE42D /* Frameworks */ = { + isa = PBXGroup; + children = ( + F7DA4A9952245B7E9BA8201F /* Pods_Sample.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 5D823AD01DD3B7770075E14A /* Sample */ = { + isa = PBXNativeTarget; + buildConfigurationList = 5D823AE31DD3B7770075E14A /* Build configuration list for PBXNativeTarget "Sample" */; + buildPhases = ( + BAA73690D42731AA5D8001CF /* [CP] Check Pods Manifest.lock */, + 5D823ACD1DD3B7770075E14A /* Sources */, + 5D823ACE1DD3B7770075E14A /* Frameworks */, + 5D823ACF1DD3B7770075E14A /* Resources */, + 8293091514A70C5E7E487A36 /* [CP] Embed Pods Frameworks */, + 641DF857294FFEAA1878D05C /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Sample; + productName = Sample; + productReference = 5D823AD11DD3B7770075E14A /* Sample.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 5D823AC91DD3B7760075E14A /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0810; + LastUpgradeCheck = 0810; + ORGANIZATIONNAME = AsyncDisplayKit; + TargetAttributes = { + 5D823AD01DD3B7770075E14A = { + CreatedOnToolsVersion = 8.1; + DevelopmentTeam = 888KTQ92ZP; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 5D823ACC1DD3B7760075E14A /* Build configuration list for PBXProject "Sample" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 5D823AC81DD3B7760075E14A; + productRefGroup = 5D823AD21DD3B7770075E14A /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 5D823AD01DD3B7770075E14A /* Sample */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 5D823ACF1DD3B7770075E14A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 5D823ADF1DD3B7770075E14A /* LaunchScreen.storyboard in Resources */, + 5D823ADC1DD3B7770075E14A /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 641DF857294FFEAA1878D05C /* [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-Sample/Pods-Sample-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + 8293091514A70C5E7E487A36 /* [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-Sample/Pods-Sample-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + BAA73690D42731AA5D8001CF /* [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 */ + 5D823ACD1DD3B7770075E14A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 5D823AE71DD3B7D30075E14A /* MosaicCollectionViewLayout.swift in Sources */, + 5D823AD71DD3B7770075E14A /* ViewController.swift in Sources */, + 5D823AD51DD3B7770075E14A /* AppDelegate.swift in Sources */, + 5D823AE91DD3B7D70075E14A /* ImageCellNode.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 5D823ADD1DD3B7770075E14A /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 5D823ADE1DD3B7770075E14A /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 5D823AE11DD3B7770075E14A /* 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_SUSPICIOUS_MOVES = 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 = 9.3; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 5D823AE21DD3B7770075E14A /* 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_SUSPICIOUS_MOVES = 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 = 9.3; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 5D823AE41DD3B7770075E14A /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = A9E9A143FF858FD89A482A84 /* Pods-Sample.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + DEVELOPMENT_TEAM = 888KTQ92ZP; + INFOPLIST_FILE = Sample/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.facebook.AsyncDisplayKit.Sample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; + }; + name = Debug; + }; + 5D823AE51DD3B7770075E14A /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 73F1C0E45062A0A8CDC033A1 /* Pods-Sample.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + DEVELOPMENT_TEAM = 888KTQ92ZP; + INFOPLIST_FILE = Sample/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.facebook.AsyncDisplayKit.Sample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 5D823ACC1DD3B7760075E14A /* Build configuration list for PBXProject "Sample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 5D823AE11DD3B7770075E14A /* Debug */, + 5D823AE21DD3B7770075E14A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 5D823AE31DD3B7770075E14A /* Build configuration list for PBXNativeTarget "Sample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 5D823AE41DD3B7770075E14A /* Debug */, + 5D823AE51DD3B7770075E14A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 5D823AC91DD3B7760075E14A /* Project object */; +} diff --git a/examples/CustomCollectionView-Swift/Sample/AppDelegate.swift b/examples/CustomCollectionView-Swift/Sample/AppDelegate.swift index 13e0670d1b..7606d4f94e 100644 --- a/examples/CustomCollectionView-Swift/Sample/AppDelegate.swift +++ b/examples/CustomCollectionView-Swift/Sample/AppDelegate.swift @@ -2,19 +2,22 @@ // AppDelegate.swift // Sample // -// Created by Rajeev Gupta on 11/9/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. +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at // -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. // import UIKit @@ -27,6 +30,13 @@ class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. + let window = UIWindow(frame: UIScreen.main.bounds) + window.backgroundColor = .white + window.rootViewController = ViewController() + window.makeKeyAndVisible() + + self.window = window + return true } diff --git a/examples/CustomCollectionView-Swift/Sample/Base.lproj/Main.storyboard b/examples/CustomCollectionView-Swift/Sample/Base.lproj/Main.storyboard deleted file mode 100644 index 273375fc70..0000000000 --- a/examples/CustomCollectionView-Swift/Sample/Base.lproj/Main.storyboard +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/CustomCollectionView-Swift/Sample/ImageCellNode.swift b/examples/CustomCollectionView-Swift/Sample/ImageCellNode.swift index 52f8fdb55f..ecd0e358b1 100644 --- a/examples/CustomCollectionView-Swift/Sample/ImageCellNode.swift +++ b/examples/CustomCollectionView-Swift/Sample/ImageCellNode.swift @@ -2,19 +2,22 @@ // ImageCellNode.swift // Sample // -// Created by Rajeev Gupta on 11/9/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. +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at // -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. // import UIKit @@ -29,9 +32,6 @@ class ImageCellNode: ASCellNode { } override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec { - let imageSize = imageNode.image?.size - print("imageNode= \(imageNode.bounds), image=\(imageSize)") - var imageRatio: CGFloat = 0.5 if imageNode.image != nil { imageRatio = (imageNode.image?.size.height)! / (imageNode.image?.size.width)! diff --git a/examples/CustomCollectionView-Swift/Sample/Info.plist b/examples/CustomCollectionView-Swift/Sample/Info.plist old mode 100644 new mode 100755 index 38e98af23d..b8901eef4f --- a/examples/CustomCollectionView-Swift/Sample/Info.plist +++ b/examples/CustomCollectionView-Swift/Sample/Info.plist @@ -22,8 +22,6 @@ UILaunchStoryboardName LaunchScreen - UIMainStoryboardFile - Main UIRequiredDeviceCapabilities armv7 diff --git a/examples/CustomCollectionView-Swift/Sample/MosaicCollectionViewLayout.swift b/examples/CustomCollectionView-Swift/Sample/MosaicCollectionViewLayout.swift old mode 100644 new mode 100755 index a8770d5282..96c9eae851 --- a/examples/CustomCollectionView-Swift/Sample/MosaicCollectionViewLayout.swift +++ b/examples/CustomCollectionView-Swift/Sample/MosaicCollectionViewLayout.swift @@ -2,19 +2,22 @@ // MosaicCollectionViewLayout // Sample // -// Created by Rajeev Gupta on 11/9/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. +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at // -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. // import Foundation diff --git a/examples/CustomCollectionView-Swift/Sample/ViewController.swift b/examples/CustomCollectionView-Swift/Sample/ViewController.swift index c325f8bc27..a4ee85df93 100644 --- a/examples/CustomCollectionView-Swift/Sample/ViewController.swift +++ b/examples/CustomCollectionView-Swift/Sample/ViewController.swift @@ -2,37 +2,35 @@ // ViewController.swift // Sample // -// Created by Rajeev Gupta on 11/9/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. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. // -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // import UIKit import AsyncDisplayKit -class ViewController: UIViewController, MosaicCollectionViewLayoutDelegate, ASCollectionDataSource, ASCollectionDelegate { +class ViewController: ASViewController, MosaicCollectionViewLayoutDelegate, ASCollectionDataSource, ASCollectionDelegate { var _sections = [[UIImage]]() - let _collectionNode: ASCollectionNode! + let _collectionNode: ASCollectionNode let _layoutInspector = MosaicCollectionViewLayoutInspector() let kNumberOfImages: UInt = 14 - - required init?(coder aDecoder: NSCoder) { + + init() { let layout = MosaicCollectionViewLayout() layout.numberOfColumns = 3; layout.headerHeight = 44; _collectionNode = ASCollectionNode(frame: CGRect.zero, collectionViewLayout: layout) - super.init(coder: aDecoder) + super.init(node: _collectionNode) layout.delegate = self _sections.append([]); @@ -46,28 +44,22 @@ class ViewController: UIViewController, MosaicCollectionViewLayoutDelegate, ASCo } } - _collectionNode.dataSource = self; - _collectionNode.delegate = self; - _collectionNode.view.layoutInspector = _layoutInspector _collectionNode.backgroundColor = UIColor.white - _collectionNode.view.isScrollEnabled = true + _collectionNode.dataSource = self + _collectionNode.delegate = self + _collectionNode.layoutInspector = _layoutInspector _collectionNode.registerSupplementaryNode(ofKind: UICollectionElementKindSectionHeader) } - deinit { - _collectionNode.dataSource = nil; - _collectionNode.delegate = nil; + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") } - + override func viewDidLoad() { super.viewDidLoad() - self.view.addSubnode(_collectionNode!) + _collectionNode.view.isScrollEnabled = true } - - override func viewWillLayoutSubviews() { - _collectionNode.frame = self.view.bounds; - } - + func collectionNode(_ collectionNode: ASCollectionNode, nodeForItemAt indexPath: IndexPath) -> ASCellNode { let image = _sections[indexPath.section][indexPath.item] return ImageCellNode(with: image) @@ -82,7 +74,7 @@ class ViewController: UIViewController, MosaicCollectionViewLayoutDelegate, ASCo let textInsets = UIEdgeInsets(top: 11, left: 0, bottom: 11, right: 0) let textCellNode = ASTextCellNode(attributes: textAttributes as! [AnyHashable : Any], insets: textInsets) textCellNode.text = String(format: "Section %zd", indexPath.section + 1) - return textCellNode; + return textCellNode } diff --git a/examples/CustomCollectionView/Podfile b/examples/CustomCollectionView/Podfile index defaf55058..b75e492fab 100644 --- a/examples/CustomCollectionView/Podfile +++ b/examples/CustomCollectionView/Podfile @@ -1,5 +1,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' target 'Sample' do - pod 'AsyncDisplayKit', :path => '../..' + pod 'Texture', :path => '../..' end diff --git a/examples/CustomCollectionView/Sample.xcodeproj/project.pbxproj b/examples/CustomCollectionView/Sample.xcodeproj/project.pbxproj index d443a206ff..91818f8f89 100644 --- a/examples/CustomCollectionView/Sample.xcodeproj/project.pbxproj +++ b/examples/CustomCollectionView/Sample.xcodeproj/project.pbxproj @@ -7,7 +7,7 @@ objects = { /* Begin PBXBuildFile section */ - 25A1FA851C02F7AC00193875 /* MosaicCollectionViewLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 25A1FA841C02F7AC00193875 /* MosaicCollectionViewLayout.m */; }; + 25A1FA851C02F7AC00193875 /* MosaicCollectionLayoutDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 25A1FA841C02F7AC00193875 /* MosaicCollectionLayoutDelegate.m */; }; 25A1FA881C02FCB000193875 /* ImageCellNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 25A1FA871C02FCB000193875 /* ImageCellNode.m */; }; 576F970133B34DFD583D5CE4 /* libPods-Sample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CC0FB9EE0030992E8FBC0A0 /* libPods-Sample.a */; }; 80364CCA1E3D95A90094400C /* ImageCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 80364CC91E3D95A90094400C /* ImageCollectionViewCell.m */; }; @@ -16,11 +16,11 @@ AC3C4A671A11F47200143C57 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = AC3C4A661A11F47200143C57 /* AppDelegate.m */; }; AC3C4A6A1A11F47200143C57 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = AC3C4A691A11F47200143C57 /* ViewController.m */; }; AC3C4A8E1A11F80C00143C57 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = AC3C4A8D1A11F80C00143C57 /* Images.xcassets */; }; + E5B2252C1F1791EA001E1431 /* MosaicCollectionLayoutInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = E5B2252B1F1791EA001E1431 /* MosaicCollectionLayoutInfo.m */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ - 25A1FA831C02F7AC00193875 /* MosaicCollectionViewLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MosaicCollectionViewLayout.h; sourceTree = ""; }; - 25A1FA841C02F7AC00193875 /* MosaicCollectionViewLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MosaicCollectionViewLayout.m; sourceTree = ""; }; + 25A1FA841C02F7AC00193875 /* MosaicCollectionLayoutDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MosaicCollectionLayoutDelegate.m; sourceTree = ""; }; 25A1FA861C02FCB000193875 /* ImageCellNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ImageCellNode.h; sourceTree = ""; }; 25A1FA871C02FCB000193875 /* ImageCellNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ImageCellNode.m; sourceTree = ""; }; 4CC0FB9EE0030992E8FBC0A0 /* libPods-Sample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Sample.a"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -36,6 +36,9 @@ AC3C4A691A11F47200143C57 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; AC3C4A8D1A11F80C00143C57 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; E2F287D91FFDEA2A747630CE /* Pods-Sample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Sample.release.xcconfig"; path = "Pods/Target Support Files/Pods-Sample/Pods-Sample.release.xcconfig"; sourceTree = ""; }; + E5B2252A1F1791DE001E1431 /* MosaicCollectionLayoutInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MosaicCollectionLayoutInfo.h; sourceTree = ""; }; + E5B2252B1F1791EA001E1431 /* MosaicCollectionLayoutInfo.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MosaicCollectionLayoutInfo.m; sourceTree = ""; }; + E5D73A3A1EA6766B006418A8 /* MosaicCollectionLayoutDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MosaicCollectionLayoutDelegate.h; sourceTree = ""; }; F36BCD8EBAF79797AB5C6708 /* Pods-Sample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Sample.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Sample/Pods-Sample.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -81,8 +84,10 @@ AC3C4A601A11F47200143C57 /* Sample */ = { isa = PBXGroup; children = ( - 25A1FA831C02F7AC00193875 /* MosaicCollectionViewLayout.h */, - 25A1FA841C02F7AC00193875 /* MosaicCollectionViewLayout.m */, + E5D73A3A1EA6766B006418A8 /* MosaicCollectionLayoutDelegate.h */, + 25A1FA841C02F7AC00193875 /* MosaicCollectionLayoutDelegate.m */, + E5B2252A1F1791DE001E1431 /* MosaicCollectionLayoutInfo.h */, + E5B2252B1F1791EA001E1431 /* MosaicCollectionLayoutInfo.m */, AC3C4A651A11F47200143C57 /* AppDelegate.h */, AC3C4A661A11F47200143C57 /* AppDelegate.m */, AC3C4A681A11F47200143C57 /* ViewController.h */, @@ -152,6 +157,7 @@ TargetAttributes = { AC3C4A5D1A11F47200143C57 = { CreatedOnToolsVersion = 6.1; + DevelopmentTeam = XSR3D45JSF; }; }; }; @@ -238,12 +244,13 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 25A1FA851C02F7AC00193875 /* MosaicCollectionViewLayout.m in Sources */, + 25A1FA851C02F7AC00193875 /* MosaicCollectionLayoutDelegate.m in Sources */, AC3C4A6A1A11F47200143C57 /* ViewController.m in Sources */, AC3C4A671A11F47200143C57 /* AppDelegate.m in Sources */, AC3C4A641A11F47200143C57 /* main.m in Sources */, 80364CCA1E3D95A90094400C /* ImageCollectionViewCell.m in Sources */, 25A1FA881C02FCB000193875 /* ImageCellNode.m in Sources */, + E5B2252C1F1791EA001E1431 /* MosaicCollectionLayoutInfo.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -333,9 +340,11 @@ baseConfigurationReference = F36BCD8EBAF79797AB5C6708 /* Pods-Sample.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + DEVELOPMENT_TEAM = XSR3D45JSF; INFOPLIST_FILE = Sample/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.1; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.facebook.AsyncDisplayKit.Sample.CustomCollectionView; PRODUCT_NAME = "$(TARGET_NAME)"; TARGETED_DEVICE_FAMILY = 1; }; @@ -346,9 +355,11 @@ baseConfigurationReference = E2F287D91FFDEA2A747630CE /* Pods-Sample.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + DEVELOPMENT_TEAM = XSR3D45JSF; INFOPLIST_FILE = Sample/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.1; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.facebook.AsyncDisplayKit.Sample.CustomCollectionView; PRODUCT_NAME = "$(TARGET_NAME)"; TARGETED_DEVICE_FAMILY = 1; }; diff --git a/examples/CustomCollectionView/Sample.xcworkspace/contents.xcworkspacedata b/examples/CustomCollectionView/Sample.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000000..7b5a2f3050 --- /dev/null +++ b/examples/CustomCollectionView/Sample.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/examples/CustomCollectionView/Sample/ImageCellNode.m b/examples/CustomCollectionView/Sample/ImageCellNode.m index 722d8def0d..4292b0f189 100644 --- a/examples/CustomCollectionView/Sample/ImageCellNode.m +++ b/examples/CustomCollectionView/Sample/ImageCellNode.m @@ -1,20 +1,18 @@ // // ImageCellNode.m -// Sample -// -// Created by McCallum, Levi on 11/22/15. +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. // -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "ImageCellNode.h" @@ -36,7 +34,10 @@ - (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize { - return [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsZero child:_imageNode]; + CGSize imageSize = self.image.size; + return [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsZero + child:[ASRatioLayoutSpec ratioLayoutSpecWithRatio:imageSize.height/imageSize.width + child:_imageNode]]; } - (void)setImage:(UIImage *)image diff --git a/examples/CustomCollectionView/Sample/MosaicCollectionLayoutDelegate.h b/examples/CustomCollectionView/Sample/MosaicCollectionLayoutDelegate.h new file mode 100644 index 0000000000..78a3f7a17e --- /dev/null +++ b/examples/CustomCollectionView/Sample/MosaicCollectionLayoutDelegate.h @@ -0,0 +1,20 @@ +// +// MosaicCollectionLayoutDelegate.h +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +@interface MosaicCollectionLayoutDelegate : NSObject + +- (instancetype)initWithNumberOfColumns:(NSInteger)numberOfColumns headerHeight:(CGFloat)headerHeight; + +@end diff --git a/examples/CustomCollectionView/Sample/MosaicCollectionLayoutDelegate.m b/examples/CustomCollectionView/Sample/MosaicCollectionLayoutDelegate.m new file mode 100644 index 0000000000..a7383f294b --- /dev/null +++ b/examples/CustomCollectionView/Sample/MosaicCollectionLayoutDelegate.m @@ -0,0 +1,169 @@ +// +// MosaicCollectionLayoutDelegate.m +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import "MosaicCollectionLayoutDelegate.h" +#import "MosaicCollectionLayoutInfo.h" +#import "ImageCellNode.h" + +#import + +@implementation MosaicCollectionLayoutDelegate { + // Read-only properties + MosaicCollectionLayoutInfo *_info; +} + +- (instancetype)initWithNumberOfColumns:(NSInteger)numberOfColumns headerHeight:(CGFloat)headerHeight +{ + self = [super init]; + if (self != nil) { + _info = [[MosaicCollectionLayoutInfo alloc] initWithNumberOfColumns:numberOfColumns + headerHeight:headerHeight + columnSpacing:10.0 + sectionInsets:UIEdgeInsetsMake(10.0, 10.0, 10.0, 10.0) + interItemSpacing:UIEdgeInsetsMake(10.0, 0, 10.0, 0)]; + } + return self; +} + +- (ASScrollDirection)scrollableDirections +{ + return ASScrollDirectionVerticalDirections; +} + +- (id)additionalInfoForLayoutWithElements:(ASElementMap *)elements +{ + return _info; +} + ++ (ASCollectionLayoutState *)calculateLayoutWithContext:(ASCollectionLayoutContext *)context +{ + CGFloat layoutWidth = context.viewportSize.width; + ASElementMap *elements = context.elements; + CGFloat top = 0; + MosaicCollectionLayoutInfo *info = (MosaicCollectionLayoutInfo *)context.additionalInfo; + + NSMapTable *attrsMap = [NSMapTable elementToLayoutAttributesTable]; + NSMutableArray *columnHeights = [NSMutableArray array]; + + NSInteger numberOfSections = [elements numberOfSections]; + for (NSUInteger section = 0; section < numberOfSections; section++) { + NSInteger numberOfItems = [elements numberOfItemsInSection:section]; + + top += info.sectionInsets.top; + + if (info.headerHeight > 0) { + NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:section]; + ASCollectionElement *element = [elements supplementaryElementOfKind:UICollectionElementKindSectionHeader + atIndexPath:indexPath]; + UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader + withIndexPath:indexPath]; + + ASSizeRange sizeRange = [self _sizeRangeForHeaderOfSection:section withLayoutWidth:layoutWidth info:info]; + CGSize size = [element.node layoutThatFits:sizeRange].size; + CGRect frame = CGRectMake(info.sectionInsets.left, top, size.width, size.height); + + attrs.frame = frame; + [attrsMap setObject:attrs forKey:element]; + top = CGRectGetMaxY(frame); + } + + [columnHeights addObject:[NSMutableArray array]]; + for (NSUInteger idx = 0; idx < info.numberOfColumns; idx++) { + [columnHeights[section] addObject:@(top)]; + } + + CGFloat columnWidth = [self _columnWidthForSection:section withLayoutWidth:layoutWidth info:info]; + for (NSUInteger idx = 0; idx < numberOfItems; idx++) { + NSUInteger columnIndex = [self _shortestColumnIndexInSection:section withColumnHeights:columnHeights]; + NSIndexPath *indexPath = [NSIndexPath indexPathForItem:idx inSection:section]; + ASCollectionElement *element = [elements elementForItemAtIndexPath:indexPath]; + UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath]; + + ASSizeRange sizeRange = [self _sizeRangeForItem:element.node atIndexPath:indexPath withLayoutWidth:layoutWidth info:info]; + CGSize size = [element.node layoutThatFits:sizeRange].size; + CGPoint position = CGPointMake(info.sectionInsets.left + (columnWidth + info.columnSpacing) * columnIndex, + [columnHeights[section][columnIndex] floatValue]); + CGRect frame = CGRectMake(position.x, position.y, size.width, size.height); + + attrs.frame = frame; + [attrsMap setObject:attrs forKey:element]; + // TODO Profile and avoid boxing if there are significant retain/release overheads + columnHeights[section][columnIndex] = @(CGRectGetMaxY(frame) + info.interItemSpacing.bottom); + } + + NSUInteger columnIndex = [self _tallestColumnIndexInSection:section withColumnHeights:columnHeights]; + top = [columnHeights[section][columnIndex] floatValue] - info.interItemSpacing.bottom + info.sectionInsets.bottom; + + for (NSUInteger idx = 0; idx < [columnHeights[section] count]; idx++) { + columnHeights[section][idx] = @(top); + } + } + + CGFloat contentHeight = [[[columnHeights lastObject] firstObject] floatValue]; + CGSize contentSize = CGSizeMake(layoutWidth, contentHeight); + return [[ASCollectionLayoutState alloc] initWithContext:context + contentSize:contentSize + elementToLayoutAttributesTable:attrsMap]; +} + ++ (CGFloat)_columnWidthForSection:(NSUInteger)section withLayoutWidth:(CGFloat)layoutWidth info:(MosaicCollectionLayoutInfo *)info +{ + return ([self _widthForSection:section withLayoutWidth:layoutWidth info:info] - ((info.numberOfColumns - 1) * info.columnSpacing)) / info.numberOfColumns; +} + ++ (CGFloat)_widthForSection:(NSUInteger)section withLayoutWidth:(CGFloat)layoutWidth info:(MosaicCollectionLayoutInfo *)info +{ + return layoutWidth - info.sectionInsets.left - info.sectionInsets.right; +} + ++ (ASSizeRange)_sizeRangeForItem:(ASCellNode *)item atIndexPath:(NSIndexPath *)indexPath withLayoutWidth:(CGFloat)layoutWidth info:(MosaicCollectionLayoutInfo *)info +{ + CGFloat itemWidth = [self _columnWidthForSection:indexPath.section withLayoutWidth:layoutWidth info:info]; + if ([item isKindOfClass:[ImageCellNode class]]) { + return ASSizeRangeMake(CGSizeMake(itemWidth, 0), CGSizeMake(itemWidth, CGFLOAT_MAX)); + } else { + return ASSizeRangeMake(CGSizeMake(itemWidth, itemWidth)); // In kShowUICollectionViewCells = YES mode, make those cells itemWidth x itemWidth. + } +} + ++ (ASSizeRange)_sizeRangeForHeaderOfSection:(NSInteger)section withLayoutWidth:(CGFloat)layoutWidth info:(MosaicCollectionLayoutInfo *)info +{ + return ASSizeRangeMake(CGSizeMake(0, info.headerHeight), CGSizeMake([self _widthForSection:section withLayoutWidth:layoutWidth info:info], info.headerHeight)); +} + ++ (NSUInteger)_tallestColumnIndexInSection:(NSUInteger)section withColumnHeights:(NSArray *)columnHeights +{ + __block NSUInteger index = 0; + __block CGFloat tallestHeight = 0; + [columnHeights[section] enumerateObjectsUsingBlock:^(NSNumber *height, NSUInteger idx, BOOL *stop) { + if (height.floatValue > tallestHeight) { + index = idx; + tallestHeight = height.floatValue; + } + }]; + return index; +} + ++ (NSUInteger)_shortestColumnIndexInSection:(NSUInteger)section withColumnHeights:(NSArray *)columnHeights +{ + __block NSUInteger index = 0; + __block CGFloat shortestHeight = CGFLOAT_MAX; + [columnHeights[section] enumerateObjectsUsingBlock:^(NSNumber *height, NSUInteger idx, BOOL *stop) { + if (height.floatValue < shortestHeight) { + index = idx; + shortestHeight = height.floatValue; + } + }]; + return index; +} + +@end diff --git a/examples/CustomCollectionView/Sample/MosaicCollectionLayoutInfo.h b/examples/CustomCollectionView/Sample/MosaicCollectionLayoutInfo.h new file mode 100644 index 0000000000..1e7db52043 --- /dev/null +++ b/examples/CustomCollectionView/Sample/MosaicCollectionLayoutInfo.h @@ -0,0 +1,32 @@ +// +// MosaicCollectionLayoutInfo.h +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +@interface MosaicCollectionLayoutInfo : NSObject + +// Read-only properties +@property (nonatomic, assign, readonly) NSInteger numberOfColumns; +@property (nonatomic, assign, readonly) CGFloat headerHeight; +@property (nonatomic, assign, readonly) CGFloat columnSpacing; +@property (nonatomic, assign, readonly) UIEdgeInsets sectionInsets; +@property (nonatomic, assign, readonly) UIEdgeInsets interItemSpacing; + +- (instancetype)initWithNumberOfColumns:(NSInteger)numberOfColumns + headerHeight:(CGFloat)headerHeight + columnSpacing:(CGFloat)columnSpacing + sectionInsets:(UIEdgeInsets)sectionInsets + interItemSpacing:(UIEdgeInsets)interItemSpacing NS_DESIGNATED_INITIALIZER; + +- (instancetype)init __unavailable; + +@end diff --git a/examples/CustomCollectionView/Sample/MosaicCollectionLayoutInfo.m b/examples/CustomCollectionView/Sample/MosaicCollectionLayoutInfo.m new file mode 100644 index 0000000000..f7a4224ab3 --- /dev/null +++ b/examples/CustomCollectionView/Sample/MosaicCollectionLayoutInfo.m @@ -0,0 +1,78 @@ +// +// MosaicCollectionLayoutInfo.m +// Texture +// +// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import "MosaicCollectionLayoutInfo.h" + +#import + +@implementation MosaicCollectionLayoutInfo + +- (instancetype)initWithNumberOfColumns:(NSInteger)numberOfColumns + headerHeight:(CGFloat)headerHeight + columnSpacing:(CGFloat)columnSpacing + sectionInsets:(UIEdgeInsets)sectionInsets + interItemSpacing:(UIEdgeInsets)interItemSpacing +{ + self = [super init]; + if (self) { + _numberOfColumns = numberOfColumns; + _headerHeight = headerHeight; + _columnSpacing = columnSpacing; + _sectionInsets = sectionInsets; + _interItemSpacing = interItemSpacing; + } + return self; +} + +- (BOOL)isEqualToInfo:(MosaicCollectionLayoutInfo *)info +{ + if (info == nil) { + return NO; + } + + return _numberOfColumns == info.numberOfColumns + && _headerHeight == info.headerHeight + && _columnSpacing == info.columnSpacing + && UIEdgeInsetsEqualToEdgeInsets(_sectionInsets, info.sectionInsets) + && UIEdgeInsetsEqualToEdgeInsets(_interItemSpacing, info.interItemSpacing); +} + +- (BOOL)isEqual:(id)other +{ + if (self == other) { + return YES; + } + if (! [other isKindOfClass:[MosaicCollectionLayoutInfo class]]) { + return NO; + } + return [self isEqualToInfo:other]; +} + +- (NSUInteger)hash +{ + struct { + NSInteger numberOfColumns; + CGFloat headerHeight; + CGFloat columnSpacing; + UIEdgeInsets sectionInsets; + UIEdgeInsets interItemSpacing; + } data = { + _numberOfColumns, + _headerHeight, + _columnSpacing, + _sectionInsets, + _interItemSpacing, + }; + return ASHashBytes(&data, sizeof(data)); +} + +@end diff --git a/examples/CustomCollectionView/Sample/MosaicCollectionViewLayout.h b/examples/CustomCollectionView/Sample/MosaicCollectionViewLayout.h deleted file mode 100644 index 82649f7363..0000000000 --- a/examples/CustomCollectionView/Sample/MosaicCollectionViewLayout.h +++ /dev/null @@ -1,40 +0,0 @@ -// -// MosaicCollectionViewLayout.h -// Sample -// -// Created by McCallum, Levi on 11/22/15. -// -// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. -// This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -#import -#import - -@interface MosaicCollectionViewLayout : UICollectionViewLayout - -@property (assign, nonatomic) NSUInteger numberOfColumns; -@property (assign, nonatomic) CGFloat columnSpacing; -@property (assign, nonatomic) UIEdgeInsets sectionInset; -@property (assign, nonatomic) UIEdgeInsets interItemSpacing; -@property (assign, nonatomic) CGFloat headerHeight; - -- (CGSize)itemSizeAtIndexPath:(NSIndexPath *)indexPath; -- (CGSize)headerSizeForSection:(NSInteger)section; - -@end - -@protocol MosaicCollectionViewLayoutDelegate - -- (CGSize)collectionView:(UICollectionView *)collectionView layout:(MosaicCollectionViewLayout *)layout originalItemSizeAtIndexPath:(NSIndexPath *)indexPath; - -@end diff --git a/examples/CustomCollectionView/Sample/MosaicCollectionViewLayout.m b/examples/CustomCollectionView/Sample/MosaicCollectionViewLayout.m deleted file mode 100644 index 633fd0fc67..0000000000 --- a/examples/CustomCollectionView/Sample/MosaicCollectionViewLayout.m +++ /dev/null @@ -1,201 +0,0 @@ -// -// MosaicCollectionViewLayout.m -// Sample -// -// Created by McCallum, Levi on 11/22/15. -// -// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. -// This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -#import "MosaicCollectionViewLayout.h" - -@implementation MosaicCollectionViewLayout { - NSMutableArray *_columnHeights; - NSMutableArray *_itemAttributes; - NSMutableDictionary *_headerAttributes; - NSMutableArray *_allAttributes; -} - -- (instancetype)init -{ - self = [super init]; - if (self != nil) { - self.numberOfColumns = 3; - self.columnSpacing = 10.0; - self.sectionInset = UIEdgeInsetsMake(10.0, 10.0, 10.0, 10.0); - self.interItemSpacing = UIEdgeInsetsMake(10.0, 0, 10.0, 0); - } - return self; -} - -- (void)prepareLayout -{ - _itemAttributes = [NSMutableArray array]; - _columnHeights = [NSMutableArray array]; - _allAttributes = [NSMutableArray array]; - _headerAttributes = [NSMutableDictionary dictionary]; - - CGFloat top = 0; - - NSInteger numberOfSections = [self.collectionView numberOfSections]; - for (NSUInteger section = 0; section < numberOfSections; section++) { - NSInteger numberOfItems = [self.collectionView numberOfItemsInSection:section]; - - top += _sectionInset.top; - - if (_headerHeight > 0) { - CGSize headerSize = [self headerSizeForSection:section]; - UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes - layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader - withIndexPath:[NSIndexPath indexPathForItem:0 inSection:section]]; - attributes.frame = CGRectMake(_sectionInset.left, top, headerSize.width, headerSize.height); - _headerAttributes[@(section)] = attributes; - [_allAttributes addObject:attributes]; - top = CGRectGetMaxY(attributes.frame); - } - - [_columnHeights addObject:[NSMutableArray array]]; - for (NSUInteger idx = 0; idx < self.numberOfColumns; idx++) { - [_columnHeights[section] addObject:@(top)]; - } - - CGFloat columnWidth = [self _columnWidthForSection:section]; - [_itemAttributes addObject:[NSMutableArray array]]; - for (NSUInteger idx = 0; idx < numberOfItems; idx++) { - NSUInteger columnIndex = [self _shortestColumnIndexInSection:section]; - NSIndexPath *indexPath = [NSIndexPath indexPathForItem:idx inSection:section]; - - CGSize itemSize = [self itemSizeAtIndexPath:indexPath]; - CGFloat xOffset = _sectionInset.left + (columnWidth + _columnSpacing) * columnIndex; - CGFloat yOffset = [_columnHeights[section][columnIndex] floatValue]; - - UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes - layoutAttributesForCellWithIndexPath:indexPath]; - attributes.frame = CGRectMake(xOffset, yOffset, itemSize.width, itemSize.height); - - _columnHeights[section][columnIndex] = @(CGRectGetMaxY(attributes.frame) + _interItemSpacing.bottom); - - [_itemAttributes[section] addObject:attributes]; - [_allAttributes addObject:attributes]; - } - - NSUInteger columnIndex = [self _tallestColumnIndexInSection:section]; - top = [_columnHeights[section][columnIndex] floatValue] - _interItemSpacing.bottom + _sectionInset.bottom; - - for (NSUInteger idx = 0; idx < [_columnHeights[section] count]; idx++) { - _columnHeights[section][idx] = @(top); - } - } -} - -- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect -{ - NSMutableArray *includedAttributes = [NSMutableArray array]; - // Slow search for small batches - for (UICollectionViewLayoutAttributes *attributes in _allAttributes) { - if (CGRectIntersectsRect(attributes.frame, rect)) { - [includedAttributes addObject:attributes]; - } - } - return includedAttributes; -} - -- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath -{ - if (indexPath.section >= _itemAttributes.count) { - return nil; - } else if (indexPath.item >= [_itemAttributes[indexPath.section] count]) { - return nil; - } - return _itemAttributes[indexPath.section][indexPath.item]; -} - -- (UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath -{ - if ([elementKind isEqualToString:UICollectionElementKindSectionHeader]) { - return _headerAttributes[@(indexPath.section)]; - } - return nil; -} - -- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds -{ - if (!CGSizeEqualToSize(self.collectionView.bounds.size, newBounds.size)) { - return YES; - } - return NO; -} - -- (CGFloat)_widthForSection:(NSUInteger)section -{ - return self.collectionView.bounds.size.width - _sectionInset.left - _sectionInset.right; -} - -- (CGFloat)_columnWidthForSection:(NSUInteger)section -{ - return ([self _widthForSection:section] - ((_numberOfColumns - 1) * _columnSpacing)) / _numberOfColumns; -} - -- (CGSize)itemSizeAtIndexPath:(NSIndexPath *)indexPath -{ - CGSize size = CGSizeMake([self _columnWidthForSection:indexPath.section], 0); - CGSize originalSize = [[self _delegate] collectionView:self.collectionView layout:self originalItemSizeAtIndexPath:indexPath]; - if (originalSize.height > 0 && originalSize.width > 0) { - size.height = originalSize.height / originalSize.width * size.width; - } - return size; -} - -- (CGSize)headerSizeForSection:(NSInteger)section -{ - return CGSizeMake([self _widthForSection:section], _headerHeight); -} - -- (CGSize)collectionViewContentSize -{ - CGFloat height = [[[_columnHeights lastObject] firstObject] floatValue]; - return CGSizeMake(self.collectionView.bounds.size.width, height); -} - -- (NSUInteger)_tallestColumnIndexInSection:(NSUInteger)section -{ - __block NSUInteger index = 0; - __block CGFloat tallestHeight = 0; - [_columnHeights[section] enumerateObjectsUsingBlock:^(NSNumber *height, NSUInteger idx, BOOL *stop) { - if (height.floatValue > tallestHeight) { - index = idx; - tallestHeight = height.floatValue; - } - }]; - return index; -} - -- (NSUInteger)_shortestColumnIndexInSection:(NSUInteger)section -{ - __block NSUInteger index = 0; - __block CGFloat shortestHeight = CGFLOAT_MAX; - [_columnHeights[section] enumerateObjectsUsingBlock:^(NSNumber *height, NSUInteger idx, BOOL *stop) { - if (height.floatValue < shortestHeight) { - index = idx; - shortestHeight = height.floatValue; - } - }]; - return index; -} - -- (id)_delegate -{ - return (id)self.collectionView.delegate; -} - -@end diff --git a/examples/CustomCollectionView/Sample/ViewController.m b/examples/CustomCollectionView/Sample/ViewController.m index e9e59f6b35..65445a557c 100644 --- a/examples/CustomCollectionView/Sample/ViewController.m +++ b/examples/CustomCollectionView/Sample/ViewController.m @@ -1,24 +1,25 @@ // // ViewController.m -// Sample +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. // -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "ViewController.h" #import -#import "MosaicCollectionViewLayout.h" +#import +#import "MosaicCollectionLayoutDelegate.h" #import "ImageCellNode.h" #import "ImageCollectionViewCell.h" @@ -42,14 +43,11 @@ static NSUInteger kNumberOfImages = 14; - (instancetype)init { - MosaicCollectionViewLayout *layout = [[MosaicCollectionViewLayout alloc] init]; - layout.numberOfColumns = 2; - layout.headerHeight = 44.0; - - _collectionNode = [[ASCollectionNode alloc] initWithCollectionViewLayout:layout]; + MosaicCollectionLayoutDelegate *layoutDelegate = [[MosaicCollectionLayoutDelegate alloc] initWithNumberOfColumns:2 headerHeight:44.0]; + _collectionNode = [[ASCollectionNode alloc] initWithLayoutDelegate:layoutDelegate layoutFacilitator:nil]; _collectionNode.dataSource = self; _collectionNode.delegate = self; - _collectionNode.backgroundColor = [UIColor whiteColor]; + _collectionNode.layoutInspector = self; if (!(self = [super initWithNode:_collectionNode])) return nil; @@ -74,7 +72,6 @@ static NSUInteger kNumberOfImages = 14; { [super viewDidLoad]; - _collectionNode.view.layoutInspector = self; [_collectionNode.view registerClass:[ImageCollectionViewCell class] forCellWithReuseIdentifier:kReuseIdentifier]; } @@ -98,17 +95,11 @@ static NSUInteger kNumberOfImages = 14; }; } - +// The below 2 methods are required by ASCollectionViewLayoutInspecting, but ASCollectionLayout and its layout delegate are the ones that really determine the size ranges and directions +// TODO Remove these methods once a layout inspector is no longer required under ASCollectionLayout mode - (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath { - MosaicCollectionViewLayout *layout = (MosaicCollectionViewLayout *)[collectionView collectionViewLayout]; - return ASSizeRangeMake(CGSizeZero, [layout itemSizeAtIndexPath:indexPath]); -} - -- (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath -{ - MosaicCollectionViewLayout *layout = (MosaicCollectionViewLayout *)[collectionView collectionViewLayout]; - return ASSizeRangeMake(CGSizeZero, [layout headerSizeForSection:indexPath.section]); + return ASSizeRangeZero; } - (ASScrollDirection)scrollableDirections @@ -146,16 +137,6 @@ static NSUInteger kNumberOfImages = 14; return [_sections[section] count]; } -- (CGSize)collectionView:(ASCollectionNode *)collectionNode layout:(UICollectionViewLayout *)collectionViewLayout originalItemSizeAtIndexPath:(NSIndexPath *)indexPath -{ - ASCellNode *cellNode = [collectionNode nodeForItemAtIndexPath:indexPath]; - if ([cellNode isKindOfClass:[ImageCellNode class]]) { - return [[(ImageCellNode *)cellNode image] size]; - } else { - return CGSizeMake(100, 100); // In kShowUICollectionViewCells = YES mode, make those cells 100x100. - } -} - - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { return [_collectionNode.view dequeueReusableCellWithReuseIdentifier:kReuseIdentifier forIndexPath:indexPath]; diff --git a/examples/HorizontalWithinVerticalScrolling/Podfile b/examples/HorizontalWithinVerticalScrolling/Podfile index defaf55058..b75e492fab 100644 --- a/examples/HorizontalWithinVerticalScrolling/Podfile +++ b/examples/HorizontalWithinVerticalScrolling/Podfile @@ -1,5 +1,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' target 'Sample' do - pod 'AsyncDisplayKit', :path => '../..' + pod 'Texture', :path => '../..' end diff --git a/examples/HorizontalWithinVerticalScrolling/Sample/RandomCoreGraphicsNode.h b/examples/HorizontalWithinVerticalScrolling/Sample/RandomCoreGraphicsNode.h index 8785d084ec..19e86e5712 100644 --- a/examples/HorizontalWithinVerticalScrolling/Sample/RandomCoreGraphicsNode.h +++ b/examples/HorizontalWithinVerticalScrolling/Sample/RandomCoreGraphicsNode.h @@ -9,12 +9,12 @@ // 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. +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/examples/HorizontalWithinVerticalScrolling/Sample/RandomCoreGraphicsNode.m b/examples/HorizontalWithinVerticalScrolling/Sample/RandomCoreGraphicsNode.m index 2aa66a97b3..fce6ea56e9 100644 --- a/examples/HorizontalWithinVerticalScrolling/Sample/RandomCoreGraphicsNode.m +++ b/examples/HorizontalWithinVerticalScrolling/Sample/RandomCoreGraphicsNode.m @@ -9,12 +9,12 @@ // 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. +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "RandomCoreGraphicsNode.h" @@ -46,8 +46,7 @@ CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (CFArrayRef)colors, locations); - CGGradientDrawingOptions drawingOptions; - CGContextDrawLinearGradient(ctx, gradient, CGPointZero, CGPointMake(bounds.size.width, bounds.size.height), drawingOptions); + CGContextDrawLinearGradient(ctx, gradient, CGPointZero, CGPointMake(bounds.size.width, bounds.size.height), 0); CGGradientRelease(gradient); CGColorSpaceRelease(colorSpace); diff --git a/examples/Kittens/Podfile b/examples/Kittens/Podfile index defaf55058..b75e492fab 100644 --- a/examples/Kittens/Podfile +++ b/examples/Kittens/Podfile @@ -1,5 +1,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' target 'Sample' do - pod 'AsyncDisplayKit', :path => '../..' + pod 'Texture', :path => '../..' end diff --git a/examples/Kittens/Sample/AppDelegate.m b/examples/Kittens/Sample/AppDelegate.m index 58fe564898..230173d515 100644 --- a/examples/Kittens/Sample/AppDelegate.m +++ b/examples/Kittens/Sample/AppDelegate.m @@ -18,11 +18,13 @@ #import "AppDelegate.h" #import "ViewController.h" +#import @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + [ASTextNode setExperimentOptions:ASTextNodeExperimentRandomInstances]; self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; self.window.backgroundColor = [UIColor whiteColor]; self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:[[ViewController alloc] init]]; diff --git a/examples/Kittens/Sample/KittenNode.mm b/examples/Kittens/Sample/KittenNode.mm index db215cb40b..c27d54bd2c 100644 --- a/examples/Kittens/Sample/KittenNode.mm +++ b/examples/Kittens/Sample/KittenNode.mm @@ -98,7 +98,20 @@ static const CGFloat kInnerPadding = 10.0f; [self addSubnode:_imageNode]; // lorem ipsum text, plus some nice styling + _textNode = [[ASTextNode alloc] init]; + _textNode.shadowColor = [UIColor blackColor].CGColor; + _textNode.shadowRadius = 3; + _textNode.shadowOffset = CGSizeMake(-2, -2); + _textNode.shadowOpacity = 0.3; + if (_textNode.usingExperiment) { + _textNode.backgroundColor = [UIColor colorWithRed:0.9 green:0.9 blue:1 alpha:1]; + } else { + _textNode.backgroundColor = [UIColor colorWithRed:1 green:0.9 blue:0.9 alpha:1]; + } + _textNode.maximumNumberOfLines = 2; + _textNode.truncationAttributedText = [[NSAttributedString alloc] initWithString:@"…"]; + _textNode.additionalTruncationMessage = [[NSAttributedString alloc] initWithString:@"More"]; _textNode.attributedText = [[NSAttributedString alloc] initWithString:[self kittyIpsum] attributes:[self textStyle]]; [self addSubnode:_textNode]; diff --git a/examples/LayoutSpecExamples-Swift/Podfile b/examples/LayoutSpecExamples-Swift/Podfile index 4013adc2f2..8458e64c60 100644 --- a/examples/LayoutSpecExamples-Swift/Podfile +++ b/examples/LayoutSpecExamples-Swift/Podfile @@ -4,5 +4,5 @@ platform :ios, '8.0' use_frameworks! target 'Sample' do - pod 'AsyncDisplayKit', :path => '../..' + pod 'Texture', :path => '../..' end diff --git a/examples/LayoutSpecExamples-Swift/Sample/LayoutExampleNode.swift b/examples/LayoutSpecExamples-Swift/Sample/LayoutExampleNode.swift index 2f057d7507..75531d9026 100644 --- a/examples/LayoutSpecExamples-Swift/Sample/LayoutExampleNode.swift +++ b/examples/LayoutSpecExamples-Swift/Sample/LayoutExampleNode.swift @@ -73,8 +73,8 @@ class PhotoWithInsetTextOverlay : LayoutExampleNode { backgroundColor = .clear - photoNode.url = URL(string: "http://asyncdisplaykit.org/static/images/layout-examples-photo-with-inset-text-overlay-photo.png") - photoNode.willDisplayNodeContentWithRenderingContext = { context in + photoNode.url = URL(string: "http://texturegroup.org/static/images/layout-examples-photo-with-inset-text-overlay-photo.png") + photoNode.willDisplayNodeContentWithRenderingContext = { context, drawParameters in let bounds = context.boundingBoxOfClipPath UIBezierPath(roundedRect: bounds, cornerRadius: 10).addClip() } @@ -101,9 +101,9 @@ class PhotoWithOutsetIconOverlay : LayoutExampleNode { required init() { super.init() - photoNode.url = URL(string: "http://asyncdisplaykit.org/static/images/layout-examples-photo-with-outset-icon-overlay-photo.png") + photoNode.url = URL(string: "http://texturegroup.org/static/images/layout-examples-photo-with-outset-icon-overlay-photo.png") - iconNode.url = URL(string: "http://asyncdisplaykit.org/static/images/layout-examples-photo-with-outset-icon-overlay-icon.png") + iconNode.url = URL(string: "http://texturegroup.org/static/images/layout-examples-photo-with-outset-icon-overlay-icon.png") iconNode.imageModificationBlock = { image in let profileImageSize = CGSize(width: 60, height: 60) diff --git a/examples/LayoutSpecExamples/Podfile b/examples/LayoutSpecExamples/Podfile index 7a8d8c1a00..922ff50ec1 100644 --- a/examples/LayoutSpecExamples/Podfile +++ b/examples/LayoutSpecExamples/Podfile @@ -1,6 +1,6 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' target 'Sample' do - pod 'AsyncDisplayKit', :path => '../..' + pod 'Texture', :path => '../..' end diff --git a/examples/LayoutSpecExamples/Sample/LayoutExampleNodes.h b/examples/LayoutSpecExamples/Sample/LayoutExampleNodes.h index 65da5f5475..7355285d3c 100644 --- a/examples/LayoutSpecExamples/Sample/LayoutExampleNodes.h +++ b/examples/LayoutSpecExamples/Sample/LayoutExampleNodes.h @@ -1,11 +1,18 @@ // // LayoutExampleNodes.h -// Sample +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/examples/LayoutSpecExamples/Sample/LayoutExampleNodes.m b/examples/LayoutSpecExamples/Sample/LayoutExampleNodes.m index d3409d7831..e7f9fbe6f0 100644 --- a/examples/LayoutSpecExamples/Sample/LayoutExampleNodes.m +++ b/examples/LayoutSpecExamples/Sample/LayoutExampleNodes.m @@ -1,16 +1,25 @@ // // LayoutExampleNodes.m -// Sample +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "LayoutExampleNodes.h" + +#import + #import "Utilities.h" -#import "UIImage+ASConvenience.h" @interface HeaderWithRightAndLeftItems () @property (nonatomic, strong) ASTextNode *usernameNode; @@ -122,8 +131,8 @@ self.backgroundColor = [UIColor clearColor]; _photoNode = [[ASNetworkImageNode alloc] init]; - _photoNode.URL = [NSURL URLWithString:@"http://asyncdisplaykit.org/static/images/layout-examples-photo-with-inset-text-overlay-photo.png"]; - _photoNode.willDisplayNodeContentWithRenderingContext = ^(CGContextRef context) { + _photoNode.URL = [NSURL URLWithString:@"http://texturegroup.org/static/images/layout-examples-photo-with-inset-text-overlay-photo.png"]; + _photoNode.willDisplayNodeContentWithRenderingContext = ^(CGContextRef context, id drawParameters) { CGRect bounds = CGContextGetClipBoundingBox(context); [[UIBezierPath bezierPathWithRoundedRect:bounds cornerRadius:10] addClip]; }; @@ -166,10 +175,10 @@ if (self) { _photoNode = [[ASNetworkImageNode alloc] init]; - _photoNode.URL = [NSURL URLWithString:@"http://asyncdisplaykit.org/static/images/layout-examples-photo-with-outset-icon-overlay-photo.png"]; + _photoNode.URL = [NSURL URLWithString:@"http://texturegroup.org/static/images/layout-examples-photo-with-outset-icon-overlay-photo.png"]; _iconNode = [[ASNetworkImageNode alloc] init]; - _iconNode.URL = [NSURL URLWithString:@"http://asyncdisplaykit.org/static/images/layout-examples-photo-with-outset-icon-overlay-icon.png"]; + _iconNode.URL = [NSURL URLWithString:@"http://texturegroup.org/static/images/layout-examples-photo-with-outset-icon-overlay-icon.png"]; [_iconNode setImageModificationBlock:^UIImage *(UIImage *image) { // FIXME: in framework autocomplete for setImageModificationBlock line seems broken CGSize profileImageSize = CGSizeMake(60, 60); diff --git a/examples/PagerNode/Podfile b/examples/PagerNode/Podfile index defaf55058..b75e492fab 100644 --- a/examples/PagerNode/Podfile +++ b/examples/PagerNode/Podfile @@ -1,5 +1,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' target 'Sample' do - pod 'AsyncDisplayKit', :path => '../..' + pod 'Texture', :path => '../..' end diff --git a/examples/SocialAppLayout-Inverted/Podfile b/examples/SocialAppLayout-Inverted/Podfile index defaf55058..b75e492fab 100644 --- a/examples/SocialAppLayout-Inverted/Podfile +++ b/examples/SocialAppLayout-Inverted/Podfile @@ -1,5 +1,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' target 'Sample' do - pod 'AsyncDisplayKit', :path => '../..' + pod 'Texture', :path => '../..' end diff --git a/examples/SocialAppLayout-Inverted/Sample/AppDelegate.h b/examples/SocialAppLayout-Inverted/Sample/AppDelegate.h index 27e560aafe..445e9559ee 100644 --- a/examples/SocialAppLayout-Inverted/Sample/AppDelegate.h +++ b/examples/SocialAppLayout-Inverted/Sample/AppDelegate.h @@ -1,18 +1,18 @@ // // AppDelegate.h -// Sample +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. // -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/examples/SocialAppLayout-Inverted/Sample/AppDelegate.m b/examples/SocialAppLayout-Inverted/Sample/AppDelegate.m index da7d93f4d8..54b277b146 100644 --- a/examples/SocialAppLayout-Inverted/Sample/AppDelegate.m +++ b/examples/SocialAppLayout-Inverted/Sample/AppDelegate.m @@ -1,18 +1,18 @@ // // AppDelegate.m -// Sample +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. // -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "AppDelegate.h" diff --git a/examples/SocialAppLayout-Inverted/Sample/CommentsNode.h b/examples/SocialAppLayout-Inverted/Sample/CommentsNode.h index 24e39cb5b4..cd1271f9b4 100644 --- a/examples/SocialAppLayout-Inverted/Sample/CommentsNode.h +++ b/examples/SocialAppLayout-Inverted/Sample/CommentsNode.h @@ -1,18 +1,18 @@ // // CommentsNode.h -// Sample +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. // -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/examples/SocialAppLayout-Inverted/Sample/CommentsNode.m b/examples/SocialAppLayout-Inverted/Sample/CommentsNode.m index 48d1d457ad..d90ed58c1d 100644 --- a/examples/SocialAppLayout-Inverted/Sample/CommentsNode.m +++ b/examples/SocialAppLayout-Inverted/Sample/CommentsNode.m @@ -1,18 +1,18 @@ // // CommentsNode.m -// Sample +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. // -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "CommentsNode.h" diff --git a/examples/SocialAppLayout-Inverted/Sample/LikesNode.h b/examples/SocialAppLayout-Inverted/Sample/LikesNode.h index b98cbebef6..21908713c1 100644 --- a/examples/SocialAppLayout-Inverted/Sample/LikesNode.h +++ b/examples/SocialAppLayout-Inverted/Sample/LikesNode.h @@ -1,18 +1,18 @@ // // LikesNode.h -// Sample +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. // -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/examples/SocialAppLayout-Inverted/Sample/LikesNode.m b/examples/SocialAppLayout-Inverted/Sample/LikesNode.m index 593d5ae5de..551c575c72 100644 --- a/examples/SocialAppLayout-Inverted/Sample/LikesNode.m +++ b/examples/SocialAppLayout-Inverted/Sample/LikesNode.m @@ -1,18 +1,18 @@ // // LikesNode.m -// Sample +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. // -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "LikesNode.h" diff --git a/examples/SocialAppLayout-Inverted/Sample/Post.h b/examples/SocialAppLayout-Inverted/Sample/Post.h index 44bed0dd73..c2383c82f2 100644 --- a/examples/SocialAppLayout-Inverted/Sample/Post.h +++ b/examples/SocialAppLayout-Inverted/Sample/Post.h @@ -1,18 +1,18 @@ // // Post.h -// Sample +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. // -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/examples/SocialAppLayout-Inverted/Sample/Post.m b/examples/SocialAppLayout-Inverted/Sample/Post.m index 10bf3a7623..ca029e7aa0 100644 --- a/examples/SocialAppLayout-Inverted/Sample/Post.m +++ b/examples/SocialAppLayout-Inverted/Sample/Post.m @@ -1,18 +1,18 @@ // // Post.m -// Sample +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. // -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "Post.h" diff --git a/examples/SocialAppLayout-Inverted/Sample/PostNode.h b/examples/SocialAppLayout-Inverted/Sample/PostNode.h index 6d8c62696d..c3e27e52b7 100644 --- a/examples/SocialAppLayout-Inverted/Sample/PostNode.h +++ b/examples/SocialAppLayout-Inverted/Sample/PostNode.h @@ -1,18 +1,18 @@ // // PostNode.h -// Sample +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. // -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/examples/SocialAppLayout-Inverted/Sample/PostNode.m b/examples/SocialAppLayout-Inverted/Sample/PostNode.m index 6323e2df01..1c4bad381e 100644 --- a/examples/SocialAppLayout-Inverted/Sample/PostNode.m +++ b/examples/SocialAppLayout-Inverted/Sample/PostNode.m @@ -1,18 +1,18 @@ // // PostNode.m -// Sample +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. // -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "PostNode.h" @@ -184,7 +184,10 @@ [self addSubnode:_optionsNode]; for (ASDisplayNode *node in self.subnodes) { - node.layerBacked = YES; + // ASTextNode with embedded links doesn't support layer backing + if (node.supportsLayerBacking) { + node.layerBacked = YES; + } } } return self; diff --git a/examples/SocialAppLayout-Inverted/Sample/TextStyles.h b/examples/SocialAppLayout-Inverted/Sample/TextStyles.h index b8ef6780c1..39cfe9528c 100644 --- a/examples/SocialAppLayout-Inverted/Sample/TextStyles.h +++ b/examples/SocialAppLayout-Inverted/Sample/TextStyles.h @@ -1,18 +1,18 @@ // // TextStyles.h -// Sample +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. // -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/examples/SocialAppLayout-Inverted/Sample/TextStyles.m b/examples/SocialAppLayout-Inverted/Sample/TextStyles.m index 8f642522b7..68f11954ae 100644 --- a/examples/SocialAppLayout-Inverted/Sample/TextStyles.m +++ b/examples/SocialAppLayout-Inverted/Sample/TextStyles.m @@ -1,18 +1,18 @@ // // TextStyles.m -// Sample +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. // -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "TextStyles.h" diff --git a/examples/SocialAppLayout-Inverted/Sample/ViewController.h b/examples/SocialAppLayout-Inverted/Sample/ViewController.h index bfb359c6b5..437714485d 100644 --- a/examples/SocialAppLayout-Inverted/Sample/ViewController.h +++ b/examples/SocialAppLayout-Inverted/Sample/ViewController.h @@ -1,18 +1,18 @@ // // ViewController.h -// Sample +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. // -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/examples/SocialAppLayout-Inverted/Sample/ViewController.m b/examples/SocialAppLayout-Inverted/Sample/ViewController.m index 6bc0d48a2d..f19962d613 100644 --- a/examples/SocialAppLayout-Inverted/Sample/ViewController.m +++ b/examples/SocialAppLayout-Inverted/Sample/ViewController.m @@ -1,18 +1,18 @@ // // ViewController.m -// Sample +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. // -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "ViewController.h" diff --git a/examples/SocialAppLayout-Inverted/Sample/main.m b/examples/SocialAppLayout-Inverted/Sample/main.m index 791ef4b743..7dd0fecc6d 100644 --- a/examples/SocialAppLayout-Inverted/Sample/main.m +++ b/examples/SocialAppLayout-Inverted/Sample/main.m @@ -1,18 +1,18 @@ // // main.m -// Sample +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. // -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/examples/SocialAppLayout/Podfile b/examples/SocialAppLayout/Podfile index defaf55058..b75e492fab 100644 --- a/examples/SocialAppLayout/Podfile +++ b/examples/SocialAppLayout/Podfile @@ -1,5 +1,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' target 'Sample' do - pod 'AsyncDisplayKit', :path => '../..' + pod 'Texture', :path => '../..' end diff --git a/examples/SocialAppLayout/Sample/PostNode.m b/examples/SocialAppLayout/Sample/PostNode.m index d1b4a9cba1..1c4bad381e 100644 --- a/examples/SocialAppLayout/Sample/PostNode.m +++ b/examples/SocialAppLayout/Sample/PostNode.m @@ -1,18 +1,18 @@ // // PostNode.m -// Sample +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. // -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "PostNode.h" @@ -184,7 +184,10 @@ [self addSubnode:_optionsNode]; for (ASDisplayNode *node in self.subnodes) { - node.layerBacked = YES; + // ASTextNode with embedded links doesn't support layer backing + if (node.supportsLayerBacking) { + node.layerBacked = YES; + } } } return self; diff --git a/examples/Swift/Podfile b/examples/Swift/Podfile index 4013adc2f2..8458e64c60 100644 --- a/examples/Swift/Podfile +++ b/examples/Swift/Podfile @@ -4,5 +4,5 @@ platform :ios, '8.0' use_frameworks! target 'Sample' do - pod 'AsyncDisplayKit', :path => '../..' + pod 'Texture', :path => '../..' end diff --git a/examples/Swift/Sample.xcodeproj/project.pbxproj b/examples/Swift/Sample.xcodeproj/project.pbxproj index 7d5dd724ea..081a5a2d6a 100644 --- a/examples/Swift/Sample.xcodeproj/project.pbxproj +++ b/examples/Swift/Sample.xcodeproj/project.pbxproj @@ -138,7 +138,7 @@ TargetAttributes = { 050E7C6D19D22E19004363C2 = { CreatedOnToolsVersion = 6.0.1; - LastSwiftMigration = 0800; + LastSwiftMigration = 0830; }; }; }; @@ -322,7 +322,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.AsyncDisplayKit.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 2.3; + SWIFT_VERSION = 3.0; }; name = Debug; }; @@ -335,7 +335,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.AsyncDisplayKit.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 2.3; + SWIFT_VERSION = 3.0; }; name = Release; }; diff --git a/examples/Swift/Sample/AppDelegate.swift b/examples/Swift/Sample/AppDelegate.swift index 2aaa33f9c6..e560672966 100644 --- a/examples/Swift/Sample/AppDelegate.swift +++ b/examples/Swift/Sample/AppDelegate.swift @@ -4,15 +4,15 @@ // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. // -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // import UIKit @@ -22,9 +22,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? - func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { - let window = UIWindow(frame: UIScreen.mainScreen().bounds) - window.backgroundColor = UIColor.whiteColor() + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { + let window = UIWindow(frame: UIScreen.main.bounds) + window.backgroundColor = UIColor.white window.rootViewController = UINavigationController(rootViewController: ViewController()); window.makeKeyAndVisible() self.window = window diff --git a/examples/Swift/Sample/TailLoadingCellNode.swift b/examples/Swift/Sample/TailLoadingCellNode.swift index b3a0bff6f2..5327dd4456 100644 --- a/examples/Swift/Sample/TailLoadingCellNode.swift +++ b/examples/Swift/Sample/TailLoadingCellNode.swift @@ -6,15 +6,15 @@ // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. // -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // import AsyncDisplayKit @@ -31,20 +31,20 @@ final class TailLoadingCellNode: ASCellNode { text.attributedText = NSAttributedString( string: "Loading…", attributes: [ - NSFontAttributeName: UIFont.systemFontOfSize(12), - NSForegroundColorAttributeName: UIColor.lightGrayColor(), + NSFontAttributeName: UIFont.systemFont(ofSize: 12), + NSForegroundColorAttributeName: UIColor.lightGray, NSKernAttributeName: -0.3 ]) addSubnode(spinner) } - override func layoutSpecThatFits(constrainedSize: ASSizeRange) -> ASLayoutSpec { + override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec { return ASStackLayoutSpec( - direction: .Horizontal, + direction: .horizontal, spacing: 16, - justifyContent: .Center, - alignItems: .Center, + justifyContent: .center, + alignItems: .center, children: [ text, spinner ]) } } @@ -57,11 +57,11 @@ final class SpinnerNode: ASDisplayNode { override init() { super.init() setViewBlock { - UIActivityIndicatorView(activityIndicatorStyle: .Gray) + UIActivityIndicatorView(activityIndicatorStyle: .gray) } // Set spinner node to default size of the activitiy indicator view - self.style.preferredSize = CGSizeMake(20.0, 20.0) + self.style.preferredSize = CGSize(width: 20.0, height: 20.0) } override func didLoad() { diff --git a/examples/Swift/Sample/ViewController.swift b/examples/Swift/Sample/ViewController.swift index eeae970a75..4d1269e6ae 100644 --- a/examples/Swift/Sample/ViewController.swift +++ b/examples/Swift/Sample/ViewController.swift @@ -4,21 +4,21 @@ // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. // -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // import UIKit import AsyncDisplayKit -final class ViewController: ASViewController, ASTableDataSource, ASTableDelegate { +final class ViewController: ASViewController, ASTableDataSource, ASTableDelegate { struct State { var itemCount: Int @@ -27,15 +27,15 @@ final class ViewController: ASViewController, ASTableDataSource, ASTableDelegate } enum Action { - case BeginBatchFetch - case EndBatchFetch(resultCount: Int) + case beginBatchFetch + case endBatchFetch(resultCount: Int) } var tableNode: ASTableNode { return node as! ASTableNode } - private(set) var state: State = .empty + fileprivate(set) var state: State = .empty init() { super.init(node: ASTableNode()) @@ -49,7 +49,7 @@ final class ViewController: ASViewController, ASTableDataSource, ASTableDelegate // MARK: ASTableNode data source and delegate. - func tableNode(tableNode: ASTableNode, nodeForRowAtIndexPath indexPath: NSIndexPath) -> ASCellNode { + func tableNode(_ tableNode: ASTableNode, nodeForRowAt indexPath: IndexPath) -> ASCellNode { // Should read the row count directly from table view but // https://github.com/facebook/AsyncDisplayKit/issues/1159 let rowCount = self.tableNode(tableNode, numberOfRowsInSection: 0) @@ -66,11 +66,11 @@ final class ViewController: ASViewController, ASTableDataSource, ASTableDelegate return node } - func numberOfSectionsInTableNode(tableNode: ASTableNode) -> Int { + func numberOfSections(in tableNode: ASTableNode) -> Int { return 1 } - func tableNode(tableNode: ASTableNode, numberOfRowsInSection section: Int) -> Int { + func tableNode(_ tableNode: ASTableNode, numberOfRowsInSection section: Int) -> Int { var count = state.itemCount if state.fetchingMore { count += 1 @@ -78,17 +78,17 @@ final class ViewController: ASViewController, ASTableDataSource, ASTableDelegate return count } - func tableNode(tableNode: ASTableNode, willBeginBatchFetchWithContext context: ASBatchContext) { + func tableNode(_ tableNode: ASTableNode, willBeginBatchFetchWith context: ASBatchContext) { /// This call will come in on a background thread. Switch to main /// to add our spinner, then fire off our fetch. - dispatch_async(dispatch_get_main_queue()) { + DispatchQueue.main.async { let oldState = self.state - self.state = ViewController.handleAction(.BeginBatchFetch, fromState: oldState) + self.state = ViewController.handleAction(.beginBatchFetch, fromState: oldState) self.renderDiff(oldState) } ViewController.fetchDataWithCompletion { resultCount in - let action = Action.EndBatchFetch(resultCount: resultCount) + let action = Action.endBatchFetch(resultCount: resultCount) let oldState = self.state self.state = ViewController.handleAction(action, fromState: oldState) self.renderDiff(oldState) @@ -96,7 +96,7 @@ final class ViewController: ASViewController, ASTableDataSource, ASTableDelegate } } - private func renderDiff(oldState: State) { + fileprivate func renderDiff(_ oldState: State) { self.tableNode.performBatchUpdates({ @@ -104,9 +104,9 @@ final class ViewController: ASViewController, ASTableDataSource, ASTableDelegate let rowCountChange = state.itemCount - oldState.itemCount if rowCountChange > 0 { let indexPaths = (oldState.itemCount.. Void) { - let time = dispatch_time(DISPATCH_TIME_NOW, Int64(NSTimeInterval(NSEC_PER_SEC) * 1.0)) - dispatch_after(time, dispatch_get_main_queue()) { + fileprivate static func fetchDataWithCompletion(_ completion: @escaping (Int) -> Void) { + let time = DispatchTime.now() + Double(Int64(TimeInterval(NSEC_PER_SEC) * 1.0)) / Double(NSEC_PER_SEC) + DispatchQueue.main.asyncAfter(deadline: time) { let resultCount = Int(arc4random_uniform(20)) completion(resultCount) } } - private static func handleAction(action: Action, fromState state: State) -> State { + fileprivate static func handleAction(_ action: Action, fromState state: State) -> State { var state = state switch action { - case .BeginBatchFetch: + case .beginBatchFetch: state.fetchingMore = true - case let .EndBatchFetch(resultCount): + case let .endBatchFetch(resultCount): state.itemCount += resultCount state.fetchingMore = false } diff --git a/examples/VerticalWithinHorizontalScrolling/Podfile b/examples/VerticalWithinHorizontalScrolling/Podfile index defaf55058..b75e492fab 100644 --- a/examples/VerticalWithinHorizontalScrolling/Podfile +++ b/examples/VerticalWithinHorizontalScrolling/Podfile @@ -1,5 +1,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' target 'Sample' do - pod 'AsyncDisplayKit', :path => '../..' + pod 'Texture', :path => '../..' end diff --git a/examples/VerticalWithinHorizontalScrolling/Sample/RandomCoreGraphicsNode.h b/examples/VerticalWithinHorizontalScrolling/Sample/RandomCoreGraphicsNode.h index 6eaed935c8..e2b21128f2 100644 --- a/examples/VerticalWithinHorizontalScrolling/Sample/RandomCoreGraphicsNode.h +++ b/examples/VerticalWithinHorizontalScrolling/Sample/RandomCoreGraphicsNode.h @@ -9,12 +9,12 @@ // 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. +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import @@ -24,6 +24,6 @@ ASTextNode *_indexPathTextNode; } -@property (nonatomic) NSIndexPath *indexPath; +@property NSIndexPath *indexPath; @end diff --git a/examples/VerticalWithinHorizontalScrolling/Sample/RandomCoreGraphicsNode.m b/examples/VerticalWithinHorizontalScrolling/Sample/RandomCoreGraphicsNode.m index 16d40df7aa..3b9e09d009 100644 --- a/examples/VerticalWithinHorizontalScrolling/Sample/RandomCoreGraphicsNode.m +++ b/examples/VerticalWithinHorizontalScrolling/Sample/RandomCoreGraphicsNode.m @@ -9,12 +9,12 @@ // 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. +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "RandomCoreGraphicsNode.h" @@ -22,7 +22,7 @@ @implementation RandomCoreGraphicsNode -@synthesize indexPath=_indexPath; +@synthesize indexPath = _indexPath; + (UIColor *)randomColor { @@ -48,8 +48,7 @@ CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (CFArrayRef)colors, locations); - CGGradientDrawingOptions drawingOptions; - CGContextDrawLinearGradient(ctx, gradient, CGPointZero, CGPointMake(bounds.size.width, bounds.size.height), drawingOptions); + CGContextDrawLinearGradient(ctx, gradient, CGPointZero, CGPointMake(bounds.size.width, bounds.size.height), 0); CGGradientRelease(gradient); CGColorSpaceRelease(colorSpace); @@ -69,8 +68,19 @@ - (void)setIndexPath:(NSIndexPath *)indexPath { - _indexPath = indexPath; - _indexPathTextNode.attributedText = [[NSAttributedString alloc] initWithString:[indexPath description] attributes:nil]; + @synchronized (self) { + _indexPath = indexPath; + _indexPathTextNode.attributedText = [[NSAttributedString alloc] initWithString:[indexPath description] attributes:nil]; + } +} + +- (NSIndexPath *)indexPath +{ + NSIndexPath *indexPath = nil; + @synchronized (self) { + indexPath = _indexPath; + } + return indexPath; } - (void)layout diff --git a/examples/Videos/Podfile b/examples/Videos/Podfile index defaf55058..b75e492fab 100644 --- a/examples/Videos/Podfile +++ b/examples/Videos/Podfile @@ -1,5 +1,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' target 'Sample' do - pod 'AsyncDisplayKit', :path => '../..' + pod 'Texture', :path => '../..' end diff --git a/examples_extra/ASDKgram-Swift/Podfile b/examples_extra/ASDKgram-Swift/Podfile index 9e67970879..6ab85b36c6 100644 --- a/examples_extra/ASDKgram-Swift/Podfile +++ b/examples_extra/ASDKgram-Swift/Podfile @@ -3,6 +3,6 @@ target 'ASDKgram-Swift' do use_frameworks! - pod 'AsyncDisplayKit', '>= 2.0' + pod 'Texture', '>= 2.0' end diff --git a/examples_extra/ASLayoutSpecPlayground-Swift/Podfile b/examples_extra/ASLayoutSpecPlayground-Swift/Podfile index 44f51a2a4c..f092b7ba8a 100644 --- a/examples_extra/ASLayoutSpecPlayground-Swift/Podfile +++ b/examples_extra/ASLayoutSpecPlayground-Swift/Podfile @@ -2,5 +2,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' use_frameworks! target 'Sample' do - pod 'AsyncDisplayKit', :path => '../..' + pod 'Texture', :path => '../..' end diff --git a/examples_extra/ASTableViewStressTest/Podfile b/examples_extra/ASTableViewStressTest/Podfile index defaf55058..b75e492fab 100644 --- a/examples_extra/ASTableViewStressTest/Podfile +++ b/examples_extra/ASTableViewStressTest/Podfile @@ -1,5 +1,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' target 'Sample' do - pod 'AsyncDisplayKit', :path => '../..' + pod 'Texture', :path => '../..' end diff --git a/examples_extra/ASTraitCollection/Podfile b/examples_extra/ASTraitCollection/Podfile index defaf55058..b75e492fab 100644 --- a/examples_extra/ASTraitCollection/Podfile +++ b/examples_extra/ASTraitCollection/Podfile @@ -1,5 +1,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' target 'Sample' do - pod 'AsyncDisplayKit', :path => '../..' + pod 'Texture', :path => '../..' end diff --git a/examples_extra/BackgroundPropertySetting/Podfile b/examples_extra/BackgroundPropertySetting/Podfile index 44f51a2a4c..f092b7ba8a 100644 --- a/examples_extra/BackgroundPropertySetting/Podfile +++ b/examples_extra/BackgroundPropertySetting/Podfile @@ -2,5 +2,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' use_frameworks! target 'Sample' do - pod 'AsyncDisplayKit', :path => '../..' + pod 'Texture', :path => '../..' end diff --git a/examples_extra/CollectionViewWithViewControllerCells/Podfile b/examples_extra/CollectionViewWithViewControllerCells/Podfile index defaf55058..b75e492fab 100644 --- a/examples_extra/CollectionViewWithViewControllerCells/Podfile +++ b/examples_extra/CollectionViewWithViewControllerCells/Podfile @@ -1,5 +1,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' target 'Sample' do - pod 'AsyncDisplayKit', :path => '../..' + pod 'Texture', :path => '../..' end diff --git a/examples_extra/EditableText/Podfile b/examples_extra/EditableText/Podfile index 7a8d8c1a00..922ff50ec1 100644 --- a/examples_extra/EditableText/Podfile +++ b/examples_extra/EditableText/Podfile @@ -1,6 +1,6 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' target 'Sample' do - pod 'AsyncDisplayKit', :path => '../..' + pod 'Texture', :path => '../..' end diff --git a/examples_extra/Multiplex/Podfile b/examples_extra/Multiplex/Podfile index defaf55058..b75e492fab 100644 --- a/examples_extra/Multiplex/Podfile +++ b/examples_extra/Multiplex/Podfile @@ -1,5 +1,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' target 'Sample' do - pod 'AsyncDisplayKit', :path => '../..' + pod 'Texture', :path => '../..' end diff --git a/examples_extra/Placeholders/Podfile b/examples_extra/Placeholders/Podfile index defaf55058..b75e492fab 100644 --- a/examples_extra/Placeholders/Podfile +++ b/examples_extra/Placeholders/Podfile @@ -1,5 +1,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' target 'Sample' do - pod 'AsyncDisplayKit', :path => '../..' + pod 'Texture', :path => '../..' end diff --git a/examples_extra/RepoSearcher/Podfile b/examples_extra/RepoSearcher/Podfile index 2652a3aca1..ac26415f17 100644 --- a/examples_extra/RepoSearcher/Podfile +++ b/examples_extra/RepoSearcher/Podfile @@ -6,5 +6,5 @@ target 'RepoSearcher' do use_frameworks! # Pods for RepoSearcher - pod 'AsyncDisplayKit/IGListKit', :path => '../..' + pod 'Texture/IGListKit', :path => '../..' end diff --git a/examples_extra/Shop/Podfile b/examples_extra/Shop/Podfile index c33a398d6b..b12d45be71 100644 --- a/examples_extra/Shop/Podfile +++ b/examples_extra/Shop/Podfile @@ -7,7 +7,7 @@ target 'Shop' do # Pods for Shop -pod 'AsyncDisplayKit' +pod 'Texture' target 'ShopTests' do inherit! :search_paths diff --git a/examples_extra/SynchronousConcurrency/Podfile b/examples_extra/SynchronousConcurrency/Podfile index defaf55058..b75e492fab 100644 --- a/examples_extra/SynchronousConcurrency/Podfile +++ b/examples_extra/SynchronousConcurrency/Podfile @@ -1,5 +1,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' target 'Sample' do - pod 'AsyncDisplayKit', :path => '../..' + pod 'Texture', :path => '../..' end diff --git a/examples_extra/SynchronousKittens/Podfile b/examples_extra/SynchronousKittens/Podfile index defaf55058..b75e492fab 100644 --- a/examples_extra/SynchronousKittens/Podfile +++ b/examples_extra/SynchronousKittens/Podfile @@ -1,5 +1,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' target 'Sample' do - pod 'AsyncDisplayKit', :path => '../..' + pod 'Texture', :path => '../..' end diff --git a/examples_extra/TextStressTest/Podfile b/examples_extra/TextStressTest/Podfile index 7a8d8c1a00..922ff50ec1 100644 --- a/examples_extra/TextStressTest/Podfile +++ b/examples_extra/TextStressTest/Podfile @@ -1,6 +1,6 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' target 'Sample' do - pod 'AsyncDisplayKit', :path => '../..' + pod 'Texture', :path => '../..' end diff --git a/examples_extra/VideoTableView/Podfile b/examples_extra/VideoTableView/Podfile index defaf55058..b75e492fab 100644 --- a/examples_extra/VideoTableView/Podfile +++ b/examples_extra/VideoTableView/Podfile @@ -1,5 +1,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' target 'Sample' do - pod 'AsyncDisplayKit', :path => '../..' + pod 'Texture', :path => '../..' end diff --git a/examples_extra/VideoTableView/Sample/NicCageNode.mm b/examples_extra/VideoTableView/Sample/NicCageNode.mm index 7110143e4b..95a62d957f 100644 --- a/examples_extra/VideoTableView/Sample/NicCageNode.mm +++ b/examples_extra/VideoTableView/Sample/NicCageNode.mm @@ -1,18 +1,18 @@ // // NicCageNode.mm -// Sample +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. // -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "NicCageNode.h" @@ -29,6 +29,9 @@ static const CGFloat kImageSize = 80.0f; static const CGFloat kOuterPadding = 16.0f; static const CGFloat kInnerPadding = 10.0f; +#define kVideoURL @"https://www.w3schools.com/html/mov_bbb.mp4" +#define kVideoStreamURL @"http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8" + @interface NicCageNode () { CGSize _kittenSize; @@ -96,20 +99,20 @@ static const CGFloat kInnerPadding = 10.0f; case 0: // Construct an AVAsset from a URL _videoNode = [[ASVideoNode alloc] init]; - _videoNode.asset = [AVAsset assetWithURL:[NSURL URLWithString:@"https://files.parsetfss.com/8a8a3b0c-619e-4e4d-b1d5-1b5ba9bf2b42/tfss-753fe655-86bb-46da-89b7-aa59c60e49c0-niccage.mp4"]]; + _videoNode.asset = [AVAsset assetWithURL:[NSURL URLWithString:kVideoURL]]; break; case 1: // Construct the video node directly from the .mp4 URL _videoNode = [[ASVideoNode alloc] init]; - _videoNode.asset = [AVAsset assetWithURL:[NSURL URLWithString:@"https://files.parsetfss.com/8a8a3b0c-619e-4e4d-b1d5-1b5ba9bf2b42/tfss-753fe655-86bb-46da-89b7-aa59c60e49c0-niccage.mp4"]]; + _videoNode.asset = [AVAsset assetWithURL:[NSURL URLWithString:kVideoURL]]; break; case 2: // Construct the video node from an HTTP Live Streaming URL // URL from https://developer.apple.com/library/ios/documentation/AudioVideo/Conceptual/AVFoundationPG/Articles/02_Playback.html _videoNode = [[ASVideoNode alloc] init]; - _videoNode.asset = [AVAsset assetWithURL:[NSURL URLWithString:@"http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8"]]; + _videoNode.asset = [AVAsset assetWithURL:[NSURL URLWithString:kVideoStreamURL]]; break; } @@ -167,8 +170,10 @@ static const CGFloat kInnerPadding = 10.0f; { CGSize videoNodeSize = _isImageEnlarged ? CGSizeMake(2.0 * kImageSize, 2.0 * kImageSize) : CGSizeMake(kImageSize, kImageSize); - _videoNode.size = ASRelativeSizeRangeMakeWithExactCGSize(videoNodeSize); - _textNode.flexShrink = 1.0; + + [_videoNode.style setPreferredSize:videoNodeSize]; + + _textNode.style.flexShrink = 1.0; ASStackLayoutSpec *stackSpec = [[ASStackLayoutSpec alloc] init]; stackSpec.direction = ASStackLayoutDirectionHorizontal; diff --git a/plans/LayoutDebugger/Overview.md b/plans/LayoutDebugger/Overview.md new file mode 100644 index 0000000000..593cbb1a79 --- /dev/null +++ b/plans/LayoutDebugger/Overview.md @@ -0,0 +1,43 @@ +# Layout Debugger + +## Motivation + +The layout system is arguably one of the hardest parts to deal with in the framework. There are many factors, such as constrained size range, preferred size and flex properties, that make a layout spec to behave in a certain way. As a result, it's often hard to debug a layout, as well as to help debugging because we have to grab the whole context of the layout in order to pinpoint the problem at hand. Currently we have a couple of tools to help address this issue, namely ASCII art string and the layout inspector project from @hannahmbanana. + +While the layout inspector project is definitely a step toward the right direction and was proven to be helpful, I think we can do a lot more. One drawback of that project is that we didn't have a clear integration contract with the core components of the framework. We ended up introducing lots of complexities to ASDisplayNode that were hard to reason and mantain. Another problem is it was difficult to bootstrap the tool into existing code/layout. + +This project aims to provide a functional layout debugging solution that requires minimal change to the core framework, is easy to setup and works out-of-the-box on existing applications. These will be achieved by building an extension framework that is hosted in a separate repository, integrates with Texture core and leverages Chrome DevTools. + +## Execution plan + +At first, we'll build on top of [PonyDebugger](https://github.com/square/PonyDebugger). I played with it for a few hours and [the result](https://www.dropbox.com/s/8bcpdgogoewmox9/view%20debugger.mov?dl=0) looks promising. The bottom line is that we'll implement a new `PDDomainController` that is inspired by `PDDOMDomainController` but is catered toward `ASLayoutElement`. The controller should expose style properties, allow editting those properties and, as a result, support hot reloading. It should also expose the constrained size passed to each element during the previous layout pass. + +Eventually, we'll possibly move away from PonyDebugger and implement our own framework due to a few reasons: + +1. PonyDebugger is not actively maintained. That might be because it's considered a "done" project, although the amount of open issues may suggest otherwise. +2. It's not easy to setup the environment, especially because of the [ponyd gateway server](https://github.com/square/PonyDebugger/tree/master/ponyd). Ponyd is essentially a middleman that sits between the client code and Chrome DevTools. It is implemented in Python and hosts its own version of DevTools. Its bootstrap script is more or less broken. It's not trivial (at least for me) to setup a working environment. Instead, my limitted research showed that we can do better by letting the client app be a mDNS broadcaster and allowing Chrome DevTools to connect to it. The workflow will be very straight-forward for developers, similar to [Stetho](https://facebook.github.io/stetho/)'s. In addition, the whole project will be simpler because we don't need to maintain ponyd. +3. It contains other features besides layout debugging, such as network monitoring and remote logging. While they are absolutely useful, they are not in the scope of this project and add complexities to it. + +## Integration with Texture (core) + +As mentioned above, this framework will be a seperate project that integrates with Texture. Most of the changes in Texture's components, like `ASLayoutElement`, `ASDisplayNode` and `ASLayoutSpec`s, will be implemented as extensions inside the debugger framework. We'll try as much as we can to minimalize changes in Texture (core) that are needed to support this project. + +## Technical issues related to Texture (core) + +There are a few technical difficulties that we need to address: + +- Layout spec flattening: Currently `ASDisplayNode` flattens its layout tree right after it receives an `ASLayout`. As a result, `ASLayoutSpec` objects are discarded and are not available for inspecting/debugging. My current solution is introducing a new `shouldSkipFlattening` flag that tells `ASDisplayNode` to keep its layout tree as is. This flag defaults to `NO`. In addition, we need to update `-layoutSublayouts` to skip any non-node objects in the tree. We should avoid introducing runtime overheads to production code and projects that don't use the debugger. +- Style properties overrding: It's common for client code to set flex properties to subnodes inside `-layoutSpecThatFits:`. Doing this will override any values set to these properties by the debugger right before a layout pass which is needed for these changes to be taken into account. My current idea is adding a special `style` object that is loaded once from the exising object and can be changed by the debugger. This special object will be preferred over the built-in one when it's time to calculate a new layout. +- Manual layout is not supported: Layouts that are done manually (via `-calculateSizeThatFits:` and `-layout`) can't be updated by the debugger. Nodes inside these layouts are still available for inspecting though. + +## Long-term ideas + +Once we have a functional debugger with a solid foundation, we can start exploring below ideas: + +- Remote debugging: Since the client app is a mDNS broadcaster, I *think* it's possible to support remote debugging as well as pair programming: "I have a layout issue" "Let me connect to your runtime and inspect it". Crazy I know! Inspired by this [Chrome extension](https://github.com/auchenberg/devtools-remote). +- Layout spec injecting: We may try to abstract `-layoutSpecThatFits:` in such a way that the entire layout specification of a node is not only defined within the class but can be loaded (or manipulated) elsewhere, be it from the debugger or even a backend server. + +## Naming + +I'm planning to call this project "Texture Debugger". It'll be a suite of debugging tools tailored mainly for Texture framework. + diff --git a/run_tests_update_status.sh b/run_tests_update_status.sh new file mode 100755 index 0000000000..3f457c07ce --- /dev/null +++ b/run_tests_update_status.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +set -eo pipefail + +UPDATE_STATUS_PATH=$1 +BUILDKITE_PULL_REQUEST=$2 +BUILDKITE_BUILD_URL=$3 + +function updateStatus() { + if [ "${BUILDKITE_PULL_REQUEST}" != "false" ] ; then + ${UPDATE_STATUS_PATH} "TextureGroup" "Texture" ${BUILDKITE_PULL_REQUEST} "$1" ${BUILDKITE_BUILD_URL} "$2" "CI/Pinterest" "$3" + fi +} + +if [[ -z "${UPDATE_STATUS_PATH}" || -z "${BUILDKITE_PULL_REQUEST}" || -z "${BUILDKITE_BUILD_URL}" ]] ; then + echo "Update status path (${UPDATE_STATUS_PATH}), pull request (${BUILDKITE_BUILD_URL}) or build url (${BUILDKITE_PULL_REQUEST}) unset." + trap - EXIT + exit 255 +fi + +trapped="false" +function trap_handler() { + if [[ "$trapped" = "false" ]]; then + updateStatus "failure" "Tests failed…" `pwd`/log.txt + echo "Tests failed, updated status to failure" + rm `pwd`/log.txt + fi + trapped="true" +} +trap trap_handler INT TERM EXIT + +updateStatus "pending" "Starting build…" + +echo "--- Running Danger" +bundle exec danger --verbose 2>&1|tee `pwd`/log.txt + +./build.sh all 2>&1|tee `pwd`/log.txt + +rm `pwd`/log.txt + +updateStatus "success" "Tests passed" + +echo "All tests succeeded, updated status to success" +trap - EXIT +exit 0 \ No newline at end of file diff --git a/smoke-tests/Framework/Sample/AppDelegate.swift b/smoke-tests/Framework/Sample/AppDelegate.swift index 9c90c393af..5c30133002 100644 --- a/smoke-tests/Framework/Sample/AppDelegate.swift +++ b/smoke-tests/Framework/Sample/AppDelegate.swift @@ -1,11 +1,18 @@ // // AppDelegate.swift -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // import UIKit diff --git a/smoke-tests/Framework/Sample/ViewController.swift b/smoke-tests/Framework/Sample/ViewController.swift index f46f2cc50e..24fa79de75 100644 --- a/smoke-tests/Framework/Sample/ViewController.swift +++ b/smoke-tests/Framework/Sample/ViewController.swift @@ -1,11 +1,18 @@ // // ViewController.swift -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // import UIKit diff --git a/smoke-tests/Life Without CocoaPods/Life Without CocoaPods/AppDelegate.h b/smoke-tests/Life Without CocoaPods/Life Without CocoaPods/AppDelegate.h index 56ca2c187d..bda0aacab9 100644 --- a/smoke-tests/Life Without CocoaPods/Life Without CocoaPods/AppDelegate.h +++ b/smoke-tests/Life Without CocoaPods/Life Without CocoaPods/AppDelegate.h @@ -1,11 +1,18 @@ // // AppDelegate.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/smoke-tests/Life Without CocoaPods/Life Without CocoaPods/AppDelegate.m b/smoke-tests/Life Without CocoaPods/Life Without CocoaPods/AppDelegate.m index 3090429202..14306e7930 100644 --- a/smoke-tests/Life Without CocoaPods/Life Without CocoaPods/AppDelegate.m +++ b/smoke-tests/Life Without CocoaPods/Life Without CocoaPods/AppDelegate.m @@ -1,11 +1,18 @@ // // AppDelegate.m -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "AppDelegate.h" diff --git a/smoke-tests/Life Without CocoaPods/Life Without CocoaPods/ViewController.h b/smoke-tests/Life Without CocoaPods/Life Without CocoaPods/ViewController.h index 31443d62eb..1d87bcb6e1 100644 --- a/smoke-tests/Life Without CocoaPods/Life Without CocoaPods/ViewController.h +++ b/smoke-tests/Life Without CocoaPods/Life Without CocoaPods/ViewController.h @@ -1,11 +1,18 @@ // // ViewController.h -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/smoke-tests/Life Without CocoaPods/Life Without CocoaPods/ViewController.m b/smoke-tests/Life Without CocoaPods/Life Without CocoaPods/ViewController.m index 05ac244597..181eda42c2 100644 --- a/smoke-tests/Life Without CocoaPods/Life Without CocoaPods/ViewController.m +++ b/smoke-tests/Life Without CocoaPods/Life Without CocoaPods/ViewController.m @@ -1,11 +1,18 @@ // // ViewController.m -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "ViewController.h" diff --git a/smoke-tests/Life Without CocoaPods/Life Without CocoaPods/main.m b/smoke-tests/Life Without CocoaPods/Life Without CocoaPods/main.m index d99695b526..27c659b765 100644 --- a/smoke-tests/Life Without CocoaPods/Life Without CocoaPods/main.m +++ b/smoke-tests/Life Without CocoaPods/Life Without CocoaPods/main.m @@ -1,11 +1,18 @@ // // main.m -// AsyncDisplayKit +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import