diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index 6ad191c842..76eaeb8a34 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -99,6 +99,7 @@ 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 */; }; + 4496D0731FA9EA6B001CC8D5 /* ASTraitCollectionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4496D0721FA9EA6B001CC8D5 /* ASTraitCollectionTests.m */; }; 4E9127691F64157600499623 /* ASRunLoopQueueTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E9127681F64157600499623 /* ASRunLoopQueueTests.m */; }; 509E68601B3AED8E009B9150 /* ASScrollDirection.m in Sources */ = {isa = PBXBuildFile; fileRef = 205F0E111B371BD7007741D0 /* ASScrollDirection.m */; }; 509E68611B3AEDA0009B9150 /* ASAbstractLayoutController.h in Headers */ = {isa = PBXBuildFile; fileRef = 205F0E171B37339C007741D0 /* ASAbstractLayoutController.h */; }; @@ -402,11 +403,16 @@ 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 */; }; + CCDC9B4D200991D10063C1F8 /* ASGraphicsContext.h in Headers */ = {isa = PBXBuildFile; fileRef = CCDC9B4B200991D10063C1F8 /* ASGraphicsContext.h */; settings = {ATTRIBUTES = (Public, ); }; }; + CCDC9B4E200991D10063C1F8 /* ASGraphicsContext.m in Sources */ = {isa = PBXBuildFile; fileRef = CCDC9B4C200991D10063C1F8 /* ASGraphicsContext.m */; }; CCDD148B1EEDCD9D0020834E /* ASCollectionModernDataSourceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CCDD148A1EEDCD9D0020834E /* ASCollectionModernDataSourceTests.m */; }; CCE4F9B31F0D60AC00062E4E /* ASIntegerMapTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CCE4F9B21F0D60AC00062E4E /* ASIntegerMapTests.m */; }; CCE4F9B51F0DA4F300062E4E /* ASLayoutEngineTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = CCE4F9B41F0DA4F300062E4E /* ASLayoutEngineTests.mm */; }; CCE4F9BA1F0DBB5000062E4E /* ASLayoutTestNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = CCE4F9B71F0DBA5000062E4E /* ASLayoutTestNode.mm */; }; CCE4F9BE1F0ECE5200062E4E /* ASTLayoutFixture.mm in Sources */ = {isa = PBXBuildFile; fileRef = CCE4F9BD1F0ECE5200062E4E /* ASTLayoutFixture.mm */; }; + CCED5E3E2020D36800395C40 /* ASNetworkImageLoadInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = CCED5E3C2020D36800395C40 /* ASNetworkImageLoadInfo.h */; settings = {ATTRIBUTES = (Public, ); }; }; + CCED5E3F2020D36800395C40 /* ASNetworkImageLoadInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = CCED5E3D2020D36800395C40 /* ASNetworkImageLoadInfo.m */; }; + CCED5E412020D49D00395C40 /* ASNetworkImageLoadInfo+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = CCED5E402020D41600395C40 /* ASNetworkImageLoadInfo+Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; 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 */; }; @@ -632,6 +638,7 @@ 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; }; + 4496D0721FA9EA6B001CC8D5 /* ASTraitCollectionTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ASTraitCollectionTests.m; sourceTree = ""; }; 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 = ""; }; 4640521B1A3F83C40061C0BA /* ASTableLayoutController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASTableLayoutController.h; sourceTree = ""; }; @@ -895,6 +902,8 @@ 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 = ""; }; + CCDC9B4B200991D10063C1F8 /* ASGraphicsContext.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ASGraphicsContext.h; sourceTree = ""; }; + CCDC9B4C200991D10063C1F8 /* ASGraphicsContext.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ASGraphicsContext.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 = ""; }; @@ -907,6 +916,9 @@ CCE4F9BB1F0EA67F00062E4E /* debugbreak.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = debugbreak.h; sourceTree = ""; }; CCE4F9BC1F0ECE5200062E4E /* ASTLayoutFixture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASTLayoutFixture.h; sourceTree = ""; }; CCE4F9BD1F0ECE5200062E4E /* ASTLayoutFixture.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASTLayoutFixture.mm; sourceTree = ""; }; + CCED5E3C2020D36800395C40 /* ASNetworkImageLoadInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ASNetworkImageLoadInfo.h; sourceTree = ""; }; + CCED5E3D2020D36800395C40 /* ASNetworkImageLoadInfo.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ASNetworkImageLoadInfo.m; sourceTree = ""; }; + CCED5E402020D41600395C40 /* ASNetworkImageLoadInfo+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ASNetworkImageLoadInfo+Private.h"; sourceTree = ""; }; D3779BCFF841AD3EB56537ED /* Pods-AsyncDisplayKitTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AsyncDisplayKitTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-AsyncDisplayKitTests/Pods-AsyncDisplayKitTests.release.xcconfig"; sourceTree = ""; }; D785F6601A74327E00291744 /* ASScrollNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASScrollNode.h; sourceTree = ""; }; D785F6611A74327E00291744 /* ASScrollNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASScrollNode.mm; sourceTree = ""; }; @@ -1068,6 +1080,8 @@ 058D09B1195D04C000B7D73C /* Source */ = { isa = PBXGroup; children = ( + CCED5E3C2020D36800395C40 /* ASNetworkImageLoadInfo.h */, + CCED5E3D2020D36800395C40 /* ASNetworkImageLoadInfo.m */, CCE04B1D1E313E99006AEBBB /* Collection Data Adapter */, CC58AA4A1E398E1D002C8CB4 /* ASBlockTypes.h */, DBDB83921C6E879900D0098C /* ASPagerFlowLayout.h */, @@ -1252,6 +1266,7 @@ 695BE2541DC1245C008E6EA5 /* ASWrapperSpecSnapshotTests.mm */, 699B83501E3C1BA500433FA4 /* ASLayoutSpecTests.m */, 4E9127681F64157600499623 /* ASRunLoopQueueTests.m */, + 4496D0721FA9EA6B001CC8D5 /* ASTraitCollectionTests.m */, ); path = Tests; sourceTree = ""; @@ -1270,6 +1285,8 @@ 058D09E1195D050800B7D73C /* Details */ = { isa = PBXGroup; children = ( + CCDC9B4B200991D10063C1F8 /* ASGraphicsContext.h */, + CCDC9B4C200991D10063C1F8 /* ASGraphicsContext.m */, CC5601391F06E9A700DC4FBE /* ASIntegerMap.h */, CC56013A1F06E9A700DC4FBE /* ASIntegerMap.mm */, CC0F885E1E4280B800576FED /* _ASCollectionViewCell.h */, @@ -1367,6 +1384,7 @@ 058D0A01195D050800B7D73C /* Private */ = { isa = PBXGroup; children = ( + CCED5E402020D41600395C40 /* ASNetworkImageLoadInfo+Private.h */, CCA282CE1E9EBF6C0037E8B7 /* ASTipsWindow.h */, CCA282CF1E9EBF6C0037E8B7 /* ASTipsWindow.m */, CCA282C61E9EB64B0037E8B7 /* ASDisplayNodeTipState.h */, @@ -1836,6 +1854,7 @@ 68EE0DBE1C1B4ED300BA1B99 /* ASMainSerialQueue.h in Headers */, CCCCCCE11EC3EF060087FE10 /* ASTextUtilities.h in Headers */, B350624B1B010EFD0018CF92 /* _ASPendingState.h in Headers */, + CCDC9B4D200991D10063C1F8 /* ASGraphicsContext.h in Headers */, E5C347B11ECB3D9200EC4BE4 /* ASBatchFetchingDelegate.h in Headers */, CC54A81C1D70079800296A24 /* ASDispatch.h in Headers */, B350624D1B010EFD0018CF92 /* _ASScopeTimer.h in Headers */, @@ -1903,6 +1922,7 @@ DE4843DC1C93EAC100A1F33B /* ASLayoutTransition.h in Headers */, CC57EAF81E3939450034C595 /* ASTableView+Undeprecated.h in Headers */, 254C6B781BF94DF4003EC431 /* ASTextKitContext.h in Headers */, + CCED5E412020D49D00395C40 /* ASNetworkImageLoadInfo+Private.h in Headers */, 9CDC18CD1B910E12004965E2 /* ASLayoutElementPrivate.h in Headers */, B35062201B010EFD0018CF92 /* ASLayoutController.h in Headers */, B35062211B010EFD0018CF92 /* ASLayoutRangeType.h in Headers */, @@ -1954,6 +1974,7 @@ B35062391B010EFD0018CF92 /* ASThread.h in Headers */, 2C107F5B1BA9F54500F13DE5 /* AsyncDisplayKit.h in Headers */, 509E68651B3AEDC5009B9150 /* CoreGraphics+ASConvenience.h in Headers */, + CCED5E3E2020D36800395C40 /* ASNetworkImageLoadInfo.h in Headers */, B350623A1B010EFD0018CF92 /* NSMutableAttributedString+TextKitAdditions.h in Headers */, 044284FF1BAA3BD600D16268 /* UICollectionViewLayout+ASConvenience.h in Headers */, B35062431B010EFD0018CF92 /* UIView+ASConvenience.h in Headers */, @@ -2159,6 +2180,7 @@ buildActionMask = 2147483647; files = ( E51B78BF1F028ABF00E32604 /* ASLayoutFlatteningTests.m in Sources */, + 4496D0731FA9EA6B001CC8D5 /* ASTraitCollectionTests.m in Sources */, 29CDC2E21AAE70D000833CA4 /* ASBasicImageDownloaderContextTests.m in Sources */, CC583AD71EF9BDC100134156 /* NSInvocation+ASTestHelpers.m in Sources */, CC051F1F1D7A286A006434CB /* ASCALayerTests.m in Sources */, @@ -2271,6 +2293,7 @@ E5B078001E69F4EB00C24B5B /* ASElementMap.m in Sources */, 9C8898BC1C738BA800D6B02E /* ASTextKitFontSizeAdjuster.mm in Sources */, 690ED59B1E36D118000627C0 /* ASImageNode+tvOS.m in Sources */, + CCDC9B4E200991D10063C1F8 /* ASGraphicsContext.m in Sources */, CCCCCCD81EC3EF060087FE10 /* ASTextInput.m in Sources */, 34EFC7621B701CA400AD841F /* ASBackgroundLayoutSpec.mm in Sources */, DE8BEAC41C2DF3FC00D57C12 /* ASDelegateProxy.m in Sources */, @@ -2283,6 +2306,7 @@ E55D86331CA8A14000A0C26F /* ASLayoutElement.mm in Sources */, 68FC85EC1CE29C7D00EDD713 /* ASVisibilityProtocols.m in Sources */, CC55A7121E52A0F200594372 /* ASResponderChainEnumerator.m in Sources */, + CCED5E3F2020D36800395C40 /* ASNetworkImageLoadInfo.m in Sources */, 68B8A4E41CBDB958007E4543 /* ASWeakProxy.m in Sources */, E5775B041F16759F00CAC9BC /* ASCollectionLayoutCache.mm in Sources */, 9C70F20A1CDBE949007D6C76 /* ASTableNode.mm in Sources */, diff --git a/CHANGELOG.md b/CHANGELOG.md index 35e4f360bc..5716f6857c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,36 @@ ## master * Add your own contributions to the next release on the line below this with your name. +- [ASRunloopQueue] Introduce new runloop queue(ASCATransactionQueue) to coalesce Interface state update calls for view controller transitions. +- [ASRangeController] Fix stability of "minimum" rangeMode if the app has more than one layout before scrolling. +- **Important** ASDisplayNode's cornerRadius is a new thread-safe bridged property that should be preferred over CALayer's. Use the latter at your own risk! [Huy Nguyen](https://github.com/nguyenhuy) [#749](https://github.com/TextureGroup/Texture/pull/749). +- [ASCellNode] Adds mapping for UITableViewCell focusStyle [Alex Hill](https://github.com/alexhillc) [#727](https://github.com/TextureGroup/Texture/pull/727) +- [ASNetworkImageNode] Fix capturing self in the block while loading image in ASNetworkImageNode. [Denis Mororozov](https://github.com/morozkin) [#777](https://github.com/TextureGroup/Texture/pull/777) +- [ASTraitCollection] Add new properties of UITraitCollection to ASTraitCollection. [Yevgen Pogribnyi](https://github.com/ypogribnyi) - [ASRectMap] Replace implementation of ASRectTable with a simpler one based on unordered_map.[Scott Goodson](https://github.com/appleguy) [#719](https://github.com/TextureGroup/Texture/pull/719) - [ASCollectionView] Add missing flags for ASCollectionDelegate [Ilya Zheleznikov](https://github.com/ilyailya) [#718](https://github.com/TextureGroup/Texture/pull/718) - [ASNetworkImageNode] Deprecates .URLs in favor of .URL [Garrett Moon](https://github.com/garrettmoon) [#699](https://github.com/TextureGroup/Texture/pull/699) - [iOS11] Update project settings and fix errors [Eke](https://github.com/Eke) [#676](https://github.com/TextureGroup/Texture/pull/676) +- [ASCornerLayoutSpec] New layout spec class for declarative corner element layout. [#657](https://github.com/TextureGroup/Texture/pull/657) [huangkun](https://github.com/huang-kun) +- [ASDisplayNode layout] Fix an issue that causes a pending layout to be applied multiple times. [Huy Nguyen](https://github.com/nguyenhuy) [#695](https://github.com/TextureGroup/Texture/pull/695) +- [ASDisplayNode layout] Fix an issue that sometimes causes a node's pending layout to not be applied. [Huy Nguyen](https://github.com/nguyenhuy) [#792](https://github.com/TextureGroup/Texture/pull/792) +- [ASScrollNode] Ensure the node respects the given size range while calculating its layout. [#637](https://github.com/TextureGroup/Texture/pull/637) [Huy Nguyen](https://github.com/nguyenhuy) +- [ASScrollNode] Invalidate the node's calculated layout if its scrollable directions changed. Also add unit tests for the class. [#637](https://github.com/TextureGroup/Texture/pull/637) [Huy Nguyen](https://github.com/nguyenhuy) +- Add new unit testing to the layout engine. [Adlai Holler](https://github.com/Adlai-Holler) [#424](https://github.com/TextureGroup/Texture/pull/424) +- [Automatic Subnode Management] Nodes with ASM enabled now insert/delete their subnodes as soon as they enter preload state, so subnodes can start preloading right away. [Huy Nguyen](https://github.com/nguyenhuy) [#706](https://github.com/TextureGroup/Texture/pull/706) +- [ASCollectionNode] Added support for interactive item movement. [Adlai Holler](https://github.com/Adlai-Holler) +- Added an experimental "no-copy" rendering API. See ASGraphicsContext.h for info. [Adlai Holler](https://github.com/Adlai-Holler) +- Dropped support for iOS 8. [Adlai Holler](https://github.com/Adlai-Holler) +- **Breaking** Changes to ASNetworkImageNode: [Adlai Holler](https://github.com/Adlai-Holler) + - Modified `ASImageDownloaderCompletion` to add an optional `id userInfo` field. Your custom downloader can pass `nil`. + - Modified the last argument to `-[ASNetworkImageNodeDelegate imageNode:didLoadImage:info:]` method from a struct to an object of new class `ASNetworkImageLoadInfo` which includes other metadata about the load operation. +- Removed +load static initializer from ASDisplayNode. [Adlai Holler](https://github.com/Adlai-Holler) +- Optimized ASNetworkImageNode loading and resolved edge cases where the image provided to the delegate was not the image that was loaded. [Adlai Holler](https://github.com/Adlai-Holler) [#778](https://github.com/TextureGroup/Texture/pull/778/) +- Make `ASCellNode` tint color apply to table view cell accessories. [Vladyslav Chapaev](https://github.com/ShogunPhyched) [#764](https://github.com/TextureGroup/Texture/pull/764) +- Fix ASTextNode2 is accessing backgroundColor off main while sizing / layout is happening. [Michael Schneider](https://github.com/maicki) [#794](https://github.com/TextureGroup/Texture/pull/778/) +- Pass scrollViewWillEndDragging delegation through in ASIGListAdapterDataSource for IGListKit integration. [#796](https://github.com/TextureGroup/Texture/pull/796) + +## 2.6 +- [Xcode 9] Updated to require Xcode 9 (to fix warnings) [Garrett Moon](https://github.com/garrettmoon) - [ASCollectionView] Improve performance and behavior of rotation / bounds changes. [Scott Goodson](https://github.com/appleguy) [#431](https://github.com/TextureGroup/Texture/pull/431) - [ASCollectionView] Improve index space translation of Flow Layout Delegate methods. [Scott Goodson](https://github.com/appleguy) - [Animated Image] Adds support for animated WebP as well as improves GIF handling. [#605](https://github.com/TextureGroup/Texture/pull/605) [Garrett Moon](https://github.com/garrettmoon) @@ -13,15 +40,6 @@ - Updated to be backwards compatible with Xcode 8. [Adlai Holler](https://github.com/Adlai-Holler) - [API CHANGES] `ASPerformMainThreadDeallocation` and `ASPerformBackgroundDeallocation` functions take `id *` instead of `id` and they're now more reliable. Also, in Swift, `ASDeallocQueue.sharedDeallocationQueue() -> ASDeallocQueue.sharedDeallocationQueue`. [Adlai Holler](https://github.com/Adlai-Holler) [#651](https://github.com/TextureGroup/Texture/pull/651) - [Collection/Table] Added direct support for mapping section indexes between data spaces. [Adlai Holler](https://github.com/Adlai-Holler) [#651](https://github.com/TextureGroup/Texture/pull/660) -- [ASCornerLayoutSpec] New layout spec class for declarative corner element layout. [#657](https://github.com/TextureGroup/Texture/pull/657) [huangkun](https://github.com/huang-kun) -- [Layout] Fix an issue that causes a pending layout to be applied multiple times. [Huy Nguyen](https://github.com/nguyenhuy) [#695](https://github.com/TextureGroup/Texture/pull/695) -- [ASScrollNode] Ensure the node respects the given size range while calculating its layout. [#637](https://github.com/TextureGroup/Texture/pull/637) [Huy Nguyen](https://github.com/nguyenhuy) -- [ASScrollNode] Invalidate the node's calculated layout if its scrollable directions changed. Also add unit tests for the class. [#637](https://github.com/TextureGroup/Texture/pull/637) [Huy Nguyen](https://github.com/nguyenhuy) -- Add new unit testing to the layout engine. [Adlai Holler](https://github.com/Adlai-Holler) [#424](https://github.com/TextureGroup/Texture/pull/424) -- [Automatic Subnode Management] Nodes with ASM enabled now insert/delete their subnodes as soon as they enter preload state, so the subnodes can preload too. [Huy Nguyen](https://github.com/nguyenhuy) [#706](https://github.com/TextureGroup/Texture/pull/706) - -## 2.6 -- [Xcode 9] Updated to require Xcode 9 (to fix warnings) [Garrett Moon](https://github.com/garrettmoon) ## 2.5.1 - [ASVideoNode] Fix unreleased time observer. [Flo Vouin](https://github.com/flovouin) diff --git a/Cartfile b/Cartfile index aebf9308e8..f1a449b012 100644 --- a/Cartfile +++ b/Cartfile @@ -1,2 +1,2 @@ github "pinterest/PINRemoteImage" "3.0.0-beta.13" -github "pinterest/PINCache" +github "pinterest/PINCache" "3.0.1-beta.6" diff --git a/Dangerfile b/Dangerfile index edd455d53d..e9779d6103 100644 --- a/Dangerfile +++ b/Dangerfile @@ -52,7 +52,11 @@ def check_file_header(files_to_check, licenses) correct_license = false licenses.each do |license| license_header = full_license(license, filename) - if data.start_with?(license_header) + # Hack for https://github.com/TextureGroup/Texture/issues/745 + # If it's already a "modified-post-Texture" file, leave it with it original copyright year. + if data.include? "Modifications to this file made after 4/13/2017" + correct_license = true + elsif data.start_with?(license_header) correct_license = true end end @@ -67,7 +71,7 @@ end # Ensure new files have proper header new_source_license_header = <<-HEREDOC -// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. +// Copyright (c) 2018-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 @@ -87,7 +91,7 @@ modified_source_license_header = <<-HEREDOC // LICENSE file in the /ASDK-Licenses directory of this source tree. An additional // grant of patent rights can be found in the PATENTS file in the same directory. // -// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Modifications to this file made after 4/13/2017 are: Copyright (c) through the present, // Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/Podfile b/Podfile index 0ebd8c98ca..21e72482c1 100644 --- a/Podfile +++ b/Podfile @@ -1,6 +1,6 @@ source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '8.0' +platform :ios, '9.0' target :'AsyncDisplayKitTests' do pod 'OCMock', '~> 3.4' diff --git a/Source/ASCellNode.h b/Source/ASCellNode.h index 3c74366a99..9a962efd19 100644 --- a/Source/ASCellNode.h +++ b/Source/ASCellNode.h @@ -192,6 +192,12 @@ typedef NS_ENUM(NSUInteger, ASCellNodeVisibilityEvent) { */ @property (nonatomic) UITableViewCellSelectionStyle selectionStyle; +/* @abstract The focus style when a cell is focused + * @default UITableViewCellFocusStyleDefault + * ASTableView uses these properties when configuring UITableViewCells that host ASCellNodes. + */ +@property (nonatomic) UITableViewCellFocusStyle focusStyle; + /* @abstract The view used as the background of the cell when it is selected. * ASTableView uses these properties when configuring UITableViewCells that host ASCellNodes. * ASCollectionView uses these properties when configuring UICollectionViewCells that host ASCellNodes. diff --git a/Source/ASCellNode.mm b/Source/ASCellNode.mm index 7710120652..f123637ab2 100644 --- a/Source/ASCellNode.mm +++ b/Source/ASCellNode.mm @@ -60,6 +60,7 @@ // Use UITableViewCell defaults _selectionStyle = UITableViewCellSelectionStyleDefault; + _focusStyle = UITableViewCellFocusStyleDefault; self.clipsToBounds = YES; return self; @@ -89,7 +90,7 @@ if ([_viewController isKindOfClass:[ASViewController class]]) { ASViewController *asViewController = (ASViewController *)_viewController; _viewControllerNode = asViewController.node; - [_viewController view]; + [_viewController loadViewIfNeeded]; } else { // Careful to avoid retain cycle UIViewController *viewController = _viewController; diff --git a/Source/ASCollectionNode.h b/Source/ASCollectionNode.h index 63e53b95e9..1f759b21f8 100644 --- a/Source/ASCollectionNode.h +++ b/Source/ASCollectionNode.h @@ -632,6 +632,32 @@ NS_ASSUME_NONNULL_BEGIN */ - (NSArray *)collectionNode:(ASCollectionNode *)collectionNode supplementaryElementKindsInSection:(NSInteger)section; +/** + * Asks the data source if it's possible to move the specified item interactively. + * + * See @p -[UICollectionViewDataSource collectionView:canMoveItemAtIndexPath:] @c. + * + * @param collectionNode The sender. + * @param node The display node for the item that may be moved. + * + * @return Whether the item represented by @p node may be moved. + */ +- (BOOL)collectionNode:(ASCollectionNode *)collectionNode canMoveItemWithNode:(ASCellNode *)node; + +/** + * Called when the user has interactively moved an item. The data source + * should update its internal data store to reflect the move. Note that you + * should not call [collectionNode moveItemAtIndexPath:toIndexPath:] – the + * collection node's internal state will be updated automatically. + * + * * See @p -[UICollectionViewDataSource collectionView:moveItemAtIndexPath:toIndexPath:] @c. + * + * @param collectionNode The sender. + * @param sourceIndexPath The original item index path. + * @param destinationIndexPath The new item index path. + */ +- (void)collectionNode:(ASCollectionNode *)collectionNode moveItemAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath; + /** * Similar to -collectionView:cellForItemAtIndexPath:. * diff --git a/Source/ASCollectionNode.mm b/Source/ASCollectionNode.mm index 8ad45c4829..956a676c37 100644 --- a/Source/ASCollectionNode.mm +++ b/Source/ASCollectionNode.mm @@ -173,6 +173,18 @@ return self; } +#if ASDISPLAYNODE_ASSERTIONS_ENABLED +- (void)dealloc +{ + if (self.nodeLoaded) { + __weak UIView *view = self.view; + ASPerformBlockOnMainThread(^{ + ASDisplayNodeCAssertNil(view.superview, @"Node's view should be removed from hierarchy."); + }); + } +} +#endif + #pragma mark ASDisplayNode - (void)didLoad diff --git a/Source/ASCollectionView.mm b/Source/ASCollectionView.mm index 1fdcd54dac..6b5a08dc1c 100644 --- a/Source/ASCollectionView.mm +++ b/Source/ASCollectionView.mm @@ -101,6 +101,10 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; NSHashTable *_cellsForLayoutUpdates; id _layoutFacilitator; CGFloat _leadingScreensForBatching; + + // When we update our data controller in response to an interactive move, + // we don't want to tell the collection view about the change (it knows!) + BOOL _updatingInResponseToInteractiveMove; BOOL _inverted; NSUInteger _superBatchUpdateCount; @@ -121,15 +125,6 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; ASCollectionViewInvalidationStyle _nextLayoutInvalidationStyle; - /** - * Our layer, retained. Under iOS < 9, when collection views are removed from the hierarchy, - * their layers may be deallocated and become dangling pointers. This puts the collection view - * into a very dangerous state where pretty much any call will crash it. So we manually retain our layer. - * - * You should never access this, and it will be nil under iOS >= 9. - */ - CALayer *_retainedLayer; - /** * If YES, the `UICollectionView` will reload its data on next layout pass so we should not forward any updates to it. @@ -156,7 +151,12 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; * Counter used to keep track of nested batch updates. */ NSInteger _batchUpdateCount; - + + /** + * Keep a strong reference to node till view is ready to release. + */ + ASCollectionNode *_keepalive_node; + struct { unsigned int scrollViewDidScroll:1; unsigned int scrollViewWillBeginDragging:1; @@ -218,6 +218,8 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; unsigned int numberOfSectionsInCollectionNode:1; unsigned int collectionNodeNumberOfItemsInSection:1; unsigned int collectionNodeContextForSection:1; + unsigned int collectionNodeCanMoveItem:1; + unsigned int collectionNodeMoveItem:1; // Whether this data source conforms to ASCollectionDataSourceInterop unsigned int interop:1; @@ -310,10 +312,6 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; [self registerClass:[_ASCollectionViewCell class] forCellWithReuseIdentifier:kReuseIdentifier]; - if (!AS_AT_LEAST_IOS9) { - _retainedLayer = self.layer; - } - [self _configureCollectionViewLayout:layout]; return self; @@ -454,6 +452,8 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; _asyncDataSourceFlags.collectionNodeNodeBlockForSupplementaryElement = [_asyncDataSource respondsToSelector:@selector(collectionNode:nodeBlockForSupplementaryElementOfKind:atIndexPath:)]; _asyncDataSourceFlags.collectionNodeSupplementaryElementKindsInSection = [_asyncDataSource respondsToSelector:@selector(collectionNode:supplementaryElementKindsInSection:)]; _asyncDataSourceFlags.nodeModelForItem = [_asyncDataSource respondsToSelector:@selector(collectionNode:nodeModelForItemAtIndexPath:)]; + _asyncDataSourceFlags.collectionNodeCanMoveItem = [_asyncDataSource respondsToSelector:@selector(collectionNode:canMoveItemWithNode:)]; + _asyncDataSourceFlags.collectionNodeMoveItem = [_asyncDataSource respondsToSelector:@selector(collectionNode:moveItemAtIndexPath:toIndexPath:)]; _asyncDataSourceFlags.interop = [_asyncDataSource conformsToProtocol:@protocol(ASCollectionDataSourceInterop)]; if (_asyncDataSourceFlags.interop) { @@ -1492,15 +1492,72 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; } } -- (void)scrollViewDidScroll:(UIScrollView *)scrollView +- (BOOL)collectionView:(UICollectionView *)collectionView canMoveItemAtIndexPath:(NSIndexPath *)indexPath { - // If a scroll happenes the current range mode needs to go to full - ASInterfaceState interfaceState = [self interfaceStateForRangeController:_rangeController]; - if (ASInterfaceStateIncludesVisible(interfaceState)) { - [_rangeController updateCurrentRangeWithMode:ASLayoutRangeModeFull]; - [self _checkForBatchFetching]; + // Mimic UIKit's gating logic. + // If the data source doesn't support moving, then all bets are off. + if (!_asyncDataSourceFlags.collectionNodeMoveItem) { + return NO; } + // Currently we do not support interactive moves when using async layout. The reason is, we do not have a mechanism + // to propagate the "presentation data" element map (containing the speculative in-progress moves) to the layout delegate, + // and this can cause exceptions to be thrown from UICV. For example, if you drag an item out of a section, + // the element map will still contain N items in that section, even though there's only N-1 shown, and UICV will + // throw an exception that you specified an element that doesn't exist. + // + // In iOS >= 11, this is made much easier by the UIDataSourceTranslating API. In previous versions of iOS our best bet + // would be to capture the invalidation contexts that are sent during interactive moves and make our own data source translator. + if ([self.collectionViewLayout isKindOfClass:[ASCollectionLayout class]]) { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + as_log_debug(ASCollectionLog(), "Collection node item interactive movement is not supported when using a layout delegate. This message will only be logged once. Node: %@", ASObjectDescriptionMakeTiny(self)); + }); + return NO; + } + + // If the data source implements canMoveItem, let them decide. + if (_asyncDataSourceFlags.collectionNodeCanMoveItem) { + if (auto cellNode = [self nodeForItemAtIndexPath:indexPath]) { + GET_COLLECTIONNODE_OR_RETURN(collectionNode, NO); + return [_asyncDataSource collectionNode:collectionNode canMoveItemWithNode:cellNode]; + } + } + + // Otherwise allow the move for all items. + return YES; +} + +- (void)collectionView:(UICollectionView *)collectionView moveItemAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath +{ + ASDisplayNodeAssert(_asyncDataSourceFlags.collectionNodeMoveItem, @"Should not allow interactive collection item movement if data source does not support it."); + + // Inform the data source first, in case they call nodeForItemAtIndexPath:. + // We want to make sure we return them the node for the item they have in mind. + if (auto collectionNode = self.collectionNode) { + [_asyncDataSource collectionNode:collectionNode moveItemAtIndexPath:sourceIndexPath toIndexPath:destinationIndexPath]; + } + + // Now we update our data controller's store. + // Get up to date + [self waitUntilAllUpdatesAreCommitted]; + // Set our flag to suppress informing super about the change. + ASDisplayNodeAssertFalse(_updatingInResponseToInteractiveMove); + _updatingInResponseToInteractiveMove = YES; + // Submit the move + [self moveItemAtIndexPath:sourceIndexPath toIndexPath:destinationIndexPath]; + // Wait for it to finish – should be fast! + [self waitUntilAllUpdatesAreCommitted]; + // Clear the flag + _updatingInResponseToInteractiveMove = NO; +} + +- (void)scrollViewDidScroll:(UIScrollView *)scrollView +{ + ASInterfaceState interfaceState = [self interfaceStateForRangeController:_rangeController]; + if (ASInterfaceStateIncludesVisible(interfaceState)) { + [self _checkForBatchFetching]; + } for (_ASCollectionViewCell *cell in _cellsForVisibilityUpdates) { // _cellsForVisibilityUpdates only includes cells for ASCellNode subclasses with overrides of the visibility method. [cell cellNodeVisibilityEvent:ASCellNodeVisibilityEventVisibleRectChanged inScrollView:scrollView]; @@ -1539,6 +1596,10 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { + // If a scroll happens the current range mode needs to go to full + _rangeController.contentHasBeenScrolled = YES; + [_rangeController updateCurrentRangeWithMode:ASLayoutRangeModeFull]; + for (_ASCollectionViewCell *cell in _cellsForVisibilityUpdates) { [cell cellNodeVisibilityEvent:ASCellNodeVisibilityEventWillBeginDragging inScrollView:scrollView]; } @@ -1971,28 +2032,6 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; return _rangeController; } -/// The UIKit version of this method is only available on iOS >= 9 -- (NSArray *)asdk_indexPathsForVisibleSupplementaryElementsOfKind:(NSString *)kind -{ - if (AS_AVAILABLE_IOS(9)) { - return [self indexPathsForVisibleSupplementaryElementsOfKind:kind]; - } - - // iOS 8 workaround - // We cannot use willDisplaySupplementaryView/didEndDisplayingSupplementaryView - // because those methods send index paths for _deleted items_ (invalid index paths) - [self layoutIfNeeded]; - NSArray *visibleAttributes = [self.collectionViewLayout layoutAttributesForElementsInRect:self.bounds]; - NSMutableArray *result = [NSMutableArray array]; - for (UICollectionViewLayoutAttributes *attributes in visibleAttributes) { - if (attributes.representedElementCategory == UICollectionElementCategorySupplementaryView - && [attributes.representedElementKind isEqualToString:kind]) { - [result addObject:attributes.indexPath]; - } - } - return result; -} - - (NSHashTable *)visibleElementsForRangeController:(ASRangeController *)rangeController { return ASPointerTableByFlatMapping(_visibleElements, id element, element); @@ -2023,7 +2062,7 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; - (void)rangeController:(ASRangeController *)rangeController updateWithChangeSet:(_ASHierarchyChangeSet *)changeSet updates:(dispatch_block_t)updates { ASDisplayNodeAssertMainThread(); - if (!self.asyncDataSource || _superIsPendingDataLoad) { + if (!self.asyncDataSource || _superIsPendingDataLoad || _updatingInResponseToInteractiveMove) { updates(); [changeSet executeCompletionHandlerWithFinished:NO]; return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes @@ -2216,6 +2255,20 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; } } +- (void)willMoveToSuperview:(UIView *)newSuperview +{ + if (self.superview == nil && newSuperview != nil) { + _keepalive_node = self.collectionNode; + } +} + +- (void)didMoveToSuperview +{ + if (self.superview == nil) { + _keepalive_node = nil; + } +} + #pragma mark ASCALayerExtendedDelegate /** @@ -2278,23 +2331,6 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; 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) - -- (BOOL)collectionView:(UICollectionView *)collectionView canMoveItemAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(9_0) -{ - ASDisplayNodeAssert(![self.asyncDataSource respondsToSelector:_cmd], @"%@ is not supported by ASCollectionView - please remove or disable this data source method.", NSStringFromSelector(_cmd)); - return NO; -} - -- (void)collectionView:(UICollectionView *)collectionView moveItemAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath*)destinationIndexPath NS_AVAILABLE_IOS(9_0) -{ - ASDisplayNodeAssert(![self.asyncDataSource respondsToSelector:_cmd], @"%@ is not supported by ASCollectionView - please remove or disable this data source method.", NSStringFromSelector(_cmd)); -} - -#endif - @end #endif diff --git a/Source/ASDisplayNode+Layout.mm b/Source/ASDisplayNode+Layout.mm index bdf3d74a6f..5c0eeccfab 100644 --- a/Source/ASDisplayNode+Layout.mm +++ b/Source/ASDisplayNode+Layout.mm @@ -2,8 +2,13 @@ // ASDisplayNode+Layout.mm // Texture // -// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. -// Licensed under the Apache License, Version 2.0 (the "License"); +// Copyright (c) 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) through the present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // @@ -304,13 +309,24 @@ ASLayoutElementStyleExtensibilityForwarding } CGSize boundsSizeForLayout = ASCeilSizeValues(bounds.size); + NSUInteger calculatedVersion = _calculatedDisplayNodeLayout->version; // Prefer a newer and not yet applied _pendingDisplayNodeLayout over _calculatedDisplayNodeLayout // If there is no such _pending, check if _calculated is valid to reuse (avoiding recalculation below). - BOOL pendingLayoutIsPreferred = (_pendingDisplayNodeLayout != nullptr - && _pendingDisplayNodeLayout->version >= _layoutVersion - && _pendingDisplayNodeLayout->version > _calculatedDisplayNodeLayout->version); // _pending is not yet applied - BOOL calculatedLayoutIsReusable = (_calculatedDisplayNodeLayout->version >= _layoutVersion + BOOL pendingLayoutIsPreferred = NO; + if (_pendingDisplayNodeLayout != nullptr) { + NSUInteger pendingVersion = _pendingDisplayNodeLayout->version; + if (pendingVersion >= _layoutVersion) { + if (pendingVersion > calculatedVersion) { + pendingLayoutIsPreferred = YES; // Newer _pending + } else if (pendingVersion == calculatedVersion + && !ASSizeRangeEqualToSizeRange(_pendingDisplayNodeLayout->constrainedSize, + _calculatedDisplayNodeLayout->constrainedSize)) { + pendingLayoutIsPreferred = YES; // _pending with a different constrained size + } + } + } + BOOL calculatedLayoutIsReusable = (calculatedVersion >= _layoutVersion && (_calculatedDisplayNodeLayout->requestedLayoutFromAbove || CGSizeEqualToSize(_calculatedDisplayNodeLayout->layout.size, boundsSizeForLayout))); if (!pendingLayoutIsPreferred && calculatedLayoutIsReusable) { diff --git a/Source/ASDisplayNode+Yoga.mm b/Source/ASDisplayNode+Yoga.mm index 70cdd953cc..b52f948212 100644 --- a/Source/ASDisplayNode+Yoga.mm +++ b/Source/ASDisplayNode+Yoga.mm @@ -94,12 +94,10 @@ - (void)semanticContentAttributeDidChange:(UISemanticContentAttribute)attribute { - if (AS_AT_LEAST_IOS9) { - UIUserInterfaceLayoutDirection layoutDirection = - [UIView userInterfaceLayoutDirectionForSemanticContentAttribute:attribute]; - self.style.direction = (layoutDirection == UIUserInterfaceLayoutDirectionLeftToRight - ? YGDirectionLTR : YGDirectionRTL); - } + UIUserInterfaceLayoutDirection layoutDirection = + [UIView userInterfaceLayoutDirectionForSemanticContentAttribute:attribute]; + self.style.direction = (layoutDirection == UIUserInterfaceLayoutDirectionLeftToRight + ? YGDirectionLTR : YGDirectionRTL); } - (void)setYogaParent:(ASDisplayNode *)yogaParent diff --git a/Source/ASDisplayNode.h b/Source/ASDisplayNode.h index 7426ec3a8f..e193c5277d 100644 --- a/Source/ASDisplayNode.h +++ b/Source/ASDisplayNode.h @@ -666,6 +666,12 @@ extern NSInteger const ASDefaultDrawingPriority; * @default ASCornerRoundingTypeDefaultSlowCALayer */ @property (nonatomic, assign) ASCornerRoundingType cornerRoundingType; // default=Slow CALayer .cornerRadius (offscreen rendering) + +/** @abstract The radius to use when rounding corners of the ASDisplayNode. + * + * @discussion This property is thread-safe and should always be preferred over CALayer's cornerRadius property, + * even if corner rounding type is ASCornerRoundingTypeDefaultSlowCALayer. + */ @property (nonatomic, assign) CGFloat cornerRadius; // default=0.0 @property (nonatomic, assign) BOOL clipsToBounds; // default==NO diff --git a/Source/ASDisplayNode.mm b/Source/ASDisplayNode.mm index e4b7927b8a..529caf2226 100644 --- a/Source/ASDisplayNode.mm +++ b/Source/ASDisplayNode.mm @@ -36,6 +36,7 @@ #import #import #import +#import #import #import #import @@ -62,7 +63,7 @@ NSInteger const ASDefaultDrawingPriority = ASDefaultTransactionPriority; // 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 @@ -225,12 +226,6 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) class_replaceMethod(self, @selector(_staticInitialize), staticInitialize, "v:@"); } -+ (void)load -{ - // Ensure this value is cached on the main thread before needed in the background. - ASScreenScale(); -} - + (Class)viewClass { return [_ASDisplayView class]; @@ -258,6 +253,11 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) _viewClass = [self.class viewClass]; _layerClass = [self.class layerClass]; + BOOL isSynchronous = ![_viewClass isSubclassOfClass:[_ASDisplayView class]] + || ![_layerClass isSubclassOfClass:[_ASDisplayLayer class]]; + setFlag(Synchronous, isSynchronous); + + _contentsScaleForDisplay = ASScreenScale(); _drawingPriority = ASDefaultDrawingPriority; @@ -1508,7 +1508,7 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock) BOOL isRight = (idx == 1 || idx == 2); CGSize size = CGSizeMake(radius + 1, radius + 1); - UIGraphicsBeginImageContextWithOptions(size, NO, self.contentsScaleForDisplay); + ASGraphicsBeginImageContextWithOptions(size, NO, self.contentsScaleForDisplay); CGContextRef ctx = UIGraphicsGetCurrentContext(); if (isRight == YES) { @@ -1525,11 +1525,9 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock) // No lock needed, as _clipCornerLayers is only modified on the main thread. CALayer *clipCornerLayer = _clipCornerLayers[idx]; - clipCornerLayer.contents = (id)(UIGraphicsGetImageFromCurrentImageContext().CGImage); + clipCornerLayer.contents = (id)(ASGraphicsGetImageAndEndCurrentContext().CGImage); clipCornerLayer.bounds = CGRectMake(0.0, 0.0, size.width, size.height); clipCornerLayer.anchorPoint = CGPointMake(isRight ? 1.0 : 0.0, isTop ? 1.0 : 0.0); - - UIGraphicsEndImageContext(); } [self _layoutClipCornersIfNeeded]; }); @@ -2742,7 +2740,7 @@ ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) { // Entered or exited range managed state. if ((newState & ASHierarchyStateRangeManaged) != (oldState & ASHierarchyStateRangeManaged)) { if (newState & ASHierarchyStateRangeManaged) { - [self enterInterfaceState:self.supernode.interfaceState]; + [self enterInterfaceState:self.supernode.pendingInterfaceState]; } else { // The case of exiting a range-managed state should be fairly rare. Adding or removing the node // to a view hierarchy will cause its interfaceState to be either fully set or unset (all fields), @@ -2785,30 +2783,34 @@ ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) { ASDisplayNodeAssert(_flags.isExitingHierarchy, @"You should never call -didExitHierarchy directly. Appearance is automatically managed by ASDisplayNode"); ASDisplayNodeAssert(!_flags.isEnteringHierarchy, @"ASDisplayNode inconsistency. __enterHierarchy and __exitHierarchy are mutually exclusive"); ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__); - - if (![self supportsRangeManagedInterfaceState]) { - self.interfaceState = ASInterfaceStateNone; - } else { - // This case is important when tearing down hierarchies. We must deliver a visibileStateDidChange:NO callback, as part our API guarantee that this method can be used for - // things like data analytics about user content viewing. We cannot call the method in the dealloc as any incidental retain operations in client code would fail. - // Additionally, it may be that a Standard UIView which is containing us is moving between hierarchies, and we should not send the call if we will be re-added in the - // same runloop. Strategy: strong reference (might be the last!), wait one runloop, and confirm we are still outside the hierarchy (both layer-backed and view-backed). - // TODO: This approach could be optimized by only performing the dispatch for root elements + recursively apply the interface state change. This would require a closer - // integration with _ASDisplayLayer to ensure that the superlayer pointer has been cleared by this stage (to check if we are root or not), or a different delegate call. - - if (ASInterfaceStateIncludesVisible(self.interfaceState)) { - dispatch_async(dispatch_get_main_queue(), ^{ - // This block intentionally retains self. - __instanceLock__.lock(); - unsigned isInHierarchy = _flags.isInHierarchy; - BOOL isVisible = ASInterfaceStateIncludesVisible(_interfaceState); - ASInterfaceState newState = (_interfaceState & ~ASInterfaceStateVisible); - __instanceLock__.unlock(); - - if (!isInHierarchy && isVisible) { - self.interfaceState = newState; + + // This case is important when tearing down hierarchies. We must deliver a visibileStateDidChange:NO callback, as part our API guarantee that this method can be used for + // things like data analytics about user content viewing. We cannot call the method in the dealloc as any incidental retain operations in client code would fail. + // Additionally, it may be that a Standard UIView which is containing us is moving between hierarchies, and we should not send the call if we will be re-added in the + // same runloop. Strategy: strong reference (might be the last!), wait one runloop, and confirm we are still outside the hierarchy (both layer-backed and view-backed). + // TODO: This approach could be optimized by only performing the dispatch for root elements + recursively apply the interface state change. This would require a closer + // integration with _ASDisplayLayer to ensure that the superlayer pointer has been cleared by this stage (to check if we are root or not), or a different delegate call. + if (ASInterfaceStateIncludesVisible(_pendingInterfaceState)) { + void(^exitVisibleInterfaceState)(void) = ^{ + // This block intentionally retains self. + __instanceLock__.lock(); + unsigned isStillInHierarchy = _flags.isInHierarchy; + BOOL isVisible = ASInterfaceStateIncludesVisible(_pendingInterfaceState); + ASInterfaceState newState = (_pendingInterfaceState & ~ASInterfaceStateVisible); + __instanceLock__.unlock(); + + if (!isStillInHierarchy && isVisible) { + if (![self supportsRangeManagedInterfaceState]) { + newState = ASInterfaceStateNone; } - }); + self.interfaceState = newState; + } + }; + + if ([[ASCATransactionQueue sharedQueue] disabled]) { + dispatch_async(dispatch_get_main_queue(), exitVisibleInterfaceState); + } else { + exitVisibleInterfaceState(); } } } @@ -2869,25 +2871,53 @@ ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) { } - (void)setInterfaceState:(ASInterfaceState)newState +{ + if ([[ASCATransactionQueue sharedQueue] disabled]) { + [self applyPendingInterfaceState:newState]; + } else { + ASDN::MutexLocker l(__instanceLock__); + if (_pendingInterfaceState != newState) { + _pendingInterfaceState = newState; + [[ASCATransactionQueue sharedQueue] enqueue:self]; + } + } +} + +- (ASInterfaceState)pendingInterfaceState +{ + ASDN::MutexLocker l(__instanceLock__); + return _pendingInterfaceState; +} + +- (void)applyPendingInterfaceState:(ASInterfaceState)newPendingState { //This method is currently called on the main thread. The assert has been added here because all of the //did(Enter|Exit)(Display|Visible|Preload)State methods currently guarantee calling on main. ASDisplayNodeAssertMainThread(); - // It should never be possible for a node to be visible but not be allowed / expected to display. - ASDisplayNodeAssertFalse(ASInterfaceStateIncludesVisible(newState) && !ASInterfaceStateIncludesDisplay(newState)); + // This method manages __instanceLock__ itself, to ensure the lock is not held while didEnter/Exit(.*)State methods are called, thus avoid potential deadlocks ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__); ASInterfaceState oldState = ASInterfaceStateNone; + ASInterfaceState newState = ASInterfaceStateNone; { ASDN::MutexLocker l(__instanceLock__); - if (_interfaceState == newState) { - return; + // newPendingState will not be used when ASCATransactionQueue is enabled + // and use _pendingInterfaceState instead for interfaceState update. + if ([[ASCATransactionQueue sharedQueue] disabled]) { + _pendingInterfaceState = newPendingState; } oldState = _interfaceState; + newState = _pendingInterfaceState; + if (newState == oldState) { + return; + } _interfaceState = newState; } + // It should never be possible for a node to be visible but not be allowed / expected to display. + ASDisplayNodeAssertFalse(ASInterfaceStateIncludesVisible(newState) && !ASInterfaceStateIncludesDisplay(newState)); + // TODO: Trigger asynchronous measurement if it is not already cached or being calculated. // if ((newState & ASInterfaceStateMeasureLayout) != (oldState & ASInterfaceStateMeasureLayout)) { // } @@ -2984,6 +3014,12 @@ ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) { [self interfaceStateDidChange:newState fromState:oldState]; } +- (void)prepareForCATransactionCommit +{ + // Apply _pendingInterfaceState actual _interfaceState, note that ASInterfaceStateNone is not used. + [self applyPendingInterfaceState:ASInterfaceStateNone]; +} + - (void)interfaceStateDidChange:(ASInterfaceState)newState fromState:(ASInterfaceState)oldState { // Subclass hook @@ -3081,16 +3117,15 @@ ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) { ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__); + // If this node has ASM enabled and is not yet visible, force a layout pass to apply its applicable pending layout, if any, + // so that its subnodes are inserted/deleted and start preloading right away. + // + // - If it has an up-to-date layout (and subnodes), calling -layoutIfNeeded will be fast. + // + // - If it doesn't have a calculated or pending layout that fits its current bounds, a measurement pass will occur + // (see -__layout and -_u_measureNodeWithBoundsIfNecessary:). This scenario is uncommon, + // and running a measurement pass here is a fine trade-off because preloading any time after this point would be late. if (self.automaticallyManagesSubnodes) { - // Tell the node to apply its applicable pending layout, if any, so that its subnodes are inserted/deleted - // and start preloading right away. - // - // If this node has an up-to-date layout (and subnodes), calling layoutIfNeeded will be fast. - // - // If this node doesn't have a calculated or pending layout that fits its current bounds, a measurement pass will occur - // (see __layout and _u_measureNodeWithBoundsIfNecessary:). - // This scenario should be uncommon, and running a measurement pass here is a fine trade-off because preloading - // any time after this point would be late. [self layoutIfNeeded]; } diff --git a/Source/ASEditableTextNode.h b/Source/ASEditableTextNode.h index dbb097d618..7b1d8098a5 100644 --- a/Source/ASEditableTextNode.h +++ b/Source/ASEditableTextNode.h @@ -219,6 +219,7 @@ NS_ASSUME_NONNULL_BEGIN - (BOOL)editableTextNodeShouldPaste:(ASEditableTextNode *)editableTextNode; - (ASEditableTextNodeTargetForAction * _Nullable)editableTextNodeTargetForAction:(SEL)action; +- (BOOL)editableTextNodeShouldReturn:(ASEditableTextNode *)editableTextNode; @end diff --git a/Source/ASEditableTextNode.mm b/Source/ASEditableTextNode.mm index 0b71595ce5..403974e70c 100644 --- a/Source/ASEditableTextNode.mm +++ b/Source/ASEditableTextNode.mm @@ -95,6 +95,7 @@ @property (nonatomic, copy) bool (^shouldPaste)(); @property (nonatomic, copy) ASEditableTextNodeTargetForAction *(^targetForActionImpl)(SEL); +@property (nonatomic, copy) bool (^shouldReturn)(); @end @@ -161,6 +162,19 @@ } } +- (NSArray *)keyCommands { + UIKeyCommand *plainReturn = [UIKeyCommand keyCommandWithInput:@"\r" modifierFlags:kNilOptions action:@selector(handlePlainReturn:)]; + return @[ + plainReturn + ]; +} + +- (void)handlePlainReturn:(id)__unused sender { + if (_shouldReturn) { + _shouldReturn(); + } +} + #endif - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer @@ -305,6 +319,15 @@ } return nil; }; + textView.shouldReturn = ^bool { + __strong ASEditableTextNode *strongSelf = weakSelf; + if (strongSelf != nil) { + if ([strongSelf->_delegate respondsToSelector:@selector(editableTextNodeShouldReturn:)]) { + return [strongSelf->_delegate editableTextNodeShouldReturn:strongSelf]; + } + } + return true; + }; _textKitComponents.textView = textView; _textKitComponents.textView.scrollEnabled = _scrollEnabled; _textKitComponents.textView.delegate = self; diff --git a/Source/ASImageNode.mm b/Source/ASImageNode.mm index a93cca7235..b33e1e504b 100644 --- a/Source/ASImageNode.mm +++ b/Source/ASImageNode.mm @@ -25,6 +25,7 @@ #import #import #import +#import #import #import #import @@ -218,11 +219,10 @@ typedef void (^ASImageNodeDrawParametersBlock)(ASWeakMapEntry *entry); ASDN::MutexLocker l(__instanceLock__); - UIGraphicsBeginImageContext(size); + ASGraphicsBeginImageContextWithOptions(size, NO, 1); [self.placeholderColor setFill]; UIRectFill(CGRectMake(0, 0, size.width, size.height)); - UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); + UIImage *image = ASGraphicsGetImageAndEndCurrentContext(); return image; } @@ -482,7 +482,7 @@ static ASDN::StaticMutex& cacheLock = *new ASDN::StaticMutex; + (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 + // The following `ASGraphicsBeginImageContextWithOptions` call will sometimes take take longer than 5ms on an // A5 processor for a 400x800 backingSize. // Check for cancellation before we call it. if (isCancelled()) { @@ -491,7 +491,7 @@ static ASDN::StaticMutex& cacheLock = *new ASDN::StaticMutex; // Use contentsScale of 1.0 and do the contentsScale handling in boundsSizeInPixels so ASCroppedImageBackingSizeAndDrawRectInBounds // will do its rounding on pixel instead of point boundaries - UIGraphicsBeginImageContextWithOptions(key.backingSize, key.isOpaque, 1.0); + ASGraphicsBeginImageContextWithOptions(key.backingSize, key.isOpaque, 1.0); BOOL contextIsClean = YES; @@ -532,16 +532,13 @@ static ASDN::StaticMutex& cacheLock = *new ASDN::StaticMutex; key.didDisplayNodeContentWithRenderingContext(context, drawParameters); } - // The following `UIGraphicsGetImageFromCurrentImageContext` call will commonly take more than 20ms on an - // A5 processor. Check for cancellation before we call it. + // Check cancellation one last time before forming image. if (isCancelled()) { - UIGraphicsEndImageContext(); + ASGraphicsEndImageContext(); return nil; } - UIImage *result = UIGraphicsGetImageFromCurrentImageContext(); - - UIGraphicsEndImageContext(); + UIImage *result = ASGraphicsGetImageAndEndCurrentContext(); if (key.imageModificationBlock) { result = key.imageModificationBlock(result); @@ -752,7 +749,7 @@ static ASDN::StaticMutex& cacheLock = *new ASDN::StaticMutex; extern asimagenode_modification_block_t ASImageNodeRoundBorderModificationBlock(CGFloat borderWidth, UIColor *borderColor) { return ^(UIImage *originalImage) { - UIGraphicsBeginImageContextWithOptions(originalImage.size, NO, originalImage.scale); + ASGraphicsBeginImageContextWithOptions(originalImage.size, NO, originalImage.scale); UIBezierPath *roundOutline = [UIBezierPath bezierPathWithOvalInRect:(CGRect){CGPointZero, originalImage.size}]; // Make the image round @@ -768,24 +765,21 @@ extern asimagenode_modification_block_t ASImageNodeRoundBorderModificationBlock( [roundOutline stroke]; } - UIImage *modifiedImage = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - return modifiedImage; + return ASGraphicsGetImageAndEndCurrentContext(); }; } extern asimagenode_modification_block_t ASImageNodeTintColorModificationBlock(UIColor *color) { return ^(UIImage *originalImage) { - UIGraphicsBeginImageContextWithOptions(originalImage.size, NO, originalImage.scale); + ASGraphicsBeginImageContextWithOptions(originalImage.size, NO, originalImage.scale); // Set color and render template [color setFill]; UIImage *templateImage = [originalImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; [templateImage drawAtPoint:CGPointZero blendMode:kCGBlendModeCopy alpha:1]; - UIImage *modifiedImage = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); + UIImage *modifiedImage = ASGraphicsGetImageAndEndCurrentContext(); // if the original image was stretchy, keep it stretchy if (!UIEdgeInsetsEqualToEdgeInsets(originalImage.capInsets, UIEdgeInsetsZero)) { diff --git a/Source/ASMapNode.mm b/Source/ASMapNode.mm index 8d1cc2886d..7396890853 100644 --- a/Source/ASMapNode.mm +++ b/Source/ASMapNode.mm @@ -26,6 +26,7 @@ #import #import +#import #import #import #import @@ -224,7 +225,7 @@ CGRect finalImageRect = CGRectMake(0, 0, image.size.width, image.size.height); - UIGraphicsBeginImageContextWithOptions(image.size, YES, image.scale); + ASGraphicsBeginImageContextWithOptions(image.size, YES, image.scale); [image drawAtPoint:CGPointZero]; UIImage *pinImage; @@ -256,8 +257,7 @@ } } - image = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); + image = ASGraphicsGetImageAndEndCurrentContext(); } strongSelf.image = image; diff --git a/Source/ASMultiplexImageNode.mm b/Source/ASMultiplexImageNode.mm index 2043d1d134..7e6c8d9a1f 100644 --- a/Source/ASMultiplexImageNode.mm +++ b/Source/ASMultiplexImageNode.mm @@ -625,7 +625,7 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent finishedLoadingBlock(downloadedImage, nextImageIdentifier, error); }]; } - // Likewise, if it's a iOS 8 Photo asset, we need to fetch it accordingly. + // Likewise, if it's a Photos asset, we need to fetch it accordingly. else if (ASPhotosFrameworkImageRequest *request = [ASPhotosFrameworkImageRequest requestWithURL:nextImageURL]) { [self _loadPHAssetWithRequest:request identifier:nextImageIdentifier completion:^(UIImage *image, NSError *error) { as_log_verbose(ASImageLoadingLog(), "Acquired image from Photos for %@ %@", weakSelf, nextImageIdentifier); @@ -673,7 +673,11 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent ASDisplayNodeAssertNotNil(imageIdentifier, @"imageIdentifier is required"); ASDisplayNodeAssertNotNil(assetURL, @"assetURL is required"); ASDisplayNodeAssertNotNil(completionBlock, @"completionBlock is required"); - + + // ALAssetsLibrary was replaced in iOS 8 and deprecated in iOS 9. + // We'll drop support very soon. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" ALAssetsLibrary *assetLibrary = [[ALAssetsLibrary alloc] init]; [assetLibrary assetForURL:assetURL resultBlock:^(ALAsset *asset) { @@ -685,6 +689,7 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent } failureBlock:^(NSError *error) { completionBlock(nil, error); }]; +#pragma clang diagnostic pop } - (void)_loadPHAssetWithRequest:(ASPhotosFrameworkImageRequest *)request identifier:(id)imageIdentifier completion:(void (^)(UIImage *image, NSError *error))completionBlock @@ -818,7 +823,7 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent [self _setDownloadIdentifier:[_downloader downloadImageWithURL:imageURL callbackQueue:dispatch_get_main_queue() downloadProgress:downloadProgressBlock - completion:^(id imageContainer, NSError *error, id downloadIdentifier) { + completion:^(id imageContainer, NSError *error, id downloadIdentifier, id userInfo) { // We dereference iVars directly, so we can't have weakSelf going nil on us. __typeof__(self) strongSelf = weakSelf; if (!strongSelf) diff --git a/Source/ASNetworkImageLoadInfo.h b/Source/ASNetworkImageLoadInfo.h new file mode 100644 index 0000000000..51e23e3754 --- /dev/null +++ b/Source/ASNetworkImageLoadInfo.h @@ -0,0 +1,39 @@ +// +// ASNetworkImageLoadInfo.h +// AsyncDisplayKit +// +// Created by Adlai on 1/30/18. +// Copyright © 2018 Facebook. All rights reserved. +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +typedef NS_ENUM(NSInteger, ASNetworkImageSourceType) { + ASNetworkImageSourceUnspecified = 0, + ASNetworkImageSourceSynchronousCache, + ASNetworkImageSourceAsynchronousCache, + ASNetworkImageSourceFileURL, + ASNetworkImageSourceDownload, +}; + +AS_SUBCLASSING_RESTRICTED +@interface ASNetworkImageLoadInfo : NSObject + +/// The type of source from which the image was loaded. +@property (readonly) ASNetworkImageSourceType sourceType; + +/// The image URL that was downloaded. +@property (readonly) NSURL *url; + +/// The download identifier, if one was provided. +@property (nullable, readonly) id downloadIdentifier; + +/// The userInfo object provided by the downloader, if one was provided. +@property (nullable, readonly) id userInfo; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Source/ASNetworkImageLoadInfo.m b/Source/ASNetworkImageLoadInfo.m new file mode 100644 index 0000000000..69dfee760b --- /dev/null +++ b/Source/ASNetworkImageLoadInfo.m @@ -0,0 +1,32 @@ +// +// ASNetworkImageLoadInfo.m +// AsyncDisplayKit +// +// Created by Adlai on 1/30/18. +// Copyright © 2018 Facebook. All rights reserved. +// + +#import +#import + +@implementation ASNetworkImageLoadInfo + +- (instancetype)initWithURL:(NSURL *)url sourceType:(ASNetworkImageSourceType)sourceType downloadIdentifier:(id)downloadIdentifier userInfo:(id)userInfo +{ + if (self = [super init]) { + _url = [url copy]; + _sourceType = sourceType; + _downloadIdentifier = downloadIdentifier; + _userInfo = userInfo; + } + return self; +} + +#pragma mark - NSCopying + +- (id)copyWithZone:(NSZone *)zone +{ + return self; +} + +@end diff --git a/Source/ASNetworkImageNode.h b/Source/ASNetworkImageNode.h index 33220d9e5e..6f159df12d 100644 --- a/Source/ASNetworkImageNode.h +++ b/Source/ASNetworkImageNode.h @@ -22,6 +22,7 @@ NS_ASSUME_NONNULL_BEGIN @protocol ASNetworkImageNodeDelegate, ASImageCacheProtocol, ASImageDownloaderProtocol; +@class ASNetworkImageLoadInfo; /** @@ -136,20 +137,6 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - -typedef NS_ENUM(NSInteger, ASNetworkImageSource) { - ASNetworkImageSourceUnspecified = 0, - ASNetworkImageSourceSynchronousCache, - ASNetworkImageSourceAsynchronousCache, - ASNetworkImageSourceFileURL, - ASNetworkImageSourceDownload, -}; - -/// A struct that carries details about ASNetworkImageNode's image loads. -typedef struct { - /// The source from which the image was loaded. - ASNetworkImageSource imageSource; -} ASNetworkImageNodeDidLoadInfo; - /** * The methods declared by the ASNetworkImageNodeDelegate protocol allow the adopting delegate to respond to * notifications such as finished decoding and downloading an image. @@ -163,11 +150,11 @@ typedef struct { * * @param imageNode The sender. * @param image The newly-loaded image. - * @param info Misc information about the image load. + * @param info Additional information about the image load. * * @discussion Called on a background queue. */ -- (void)imageNode:(ASNetworkImageNode *)imageNode didLoadImage:(UIImage *)image info:(ASNetworkImageNodeDidLoadInfo)info; +- (void)imageNode:(ASNetworkImageNode *)imageNode didLoadImage:(UIImage *)image info:(ASNetworkImageLoadInfo *)info; /** * Notification that the image node finished downloading an image. diff --git a/Source/ASNetworkImageNode.mm b/Source/ASNetworkImageNode.mm index 9603561d44..dea81304dd 100755 --- a/Source/ASNetworkImageNode.mm +++ b/Source/ASNetworkImageNode.mm @@ -28,6 +28,7 @@ #import #import #import +#import #if AS_PIN_REMOTE_IMAGE #import @@ -335,8 +336,9 @@ if (asynchronously == NO && _cacheFlags.cacheSupportsSynchronousFetch) { ASDN::MutexLocker l(__instanceLock__); - if (_imageLoaded == NO && _URL && _downloadIdentifier == nil) { - UIImage *result = [[_cache synchronouslyFetchedCachedImageWithURL:_URL] asdk_image]; + NSURL *url = _URL; + if (_imageLoaded == NO && url && _downloadIdentifier == nil) { + UIImage *result = [[_cache synchronouslyFetchedCachedImageWithURL:url] asdk_image]; if (result) { [self _locked_setCurrentImageQuality:1.0]; [self _locked__setImage:result]; @@ -345,8 +347,7 @@ // Call out to the delegate. if (_delegateFlags.delegateDidLoadImageWithInfo) { ASDN::MutexUnlocker l(__instanceLock__); - ASNetworkImageNodeDidLoadInfo info = {}; - info.imageSource = ASNetworkImageSourceSynchronousCache; + auto info = [[ASNetworkImageLoadInfo alloc] initWithURL:url sourceType:ASNetworkImageSourceSynchronousCache downloadIdentifier:nil userInfo:nil]; [_delegate imageNode:self didLoadImage:result info:info]; } else if (_delegateFlags.delegateDidLoadImage) { ASDN::MutexUnlocker l(__instanceLock__); @@ -554,7 +555,7 @@ _cacheUUID = nil; } -- (void)_downloadImageWithCompletion:(void (^)(id imageContainer, NSError*, id downloadIdentifier))finished +- (void)_downloadImageWithCompletion:(void (^)(id imageContainer, NSError*, id downloadIdentifier, id userInfo))finished { ASPerformBlockOnBackgroundThread(^{ NSURL *url; @@ -573,9 +574,9 @@ downloadIdentifier = [_downloader downloadImageWithURL:url callbackQueue:dispatch_get_main_queue() downloadProgress:NULL - completion:^(id _Nullable imageContainer, NSError * _Nullable error, id _Nullable downloadIdentifier) { + completion:^(id _Nullable imageContainer, NSError * _Nullable error, id _Nullable downloadIdentifier, id _Nullable userInfo) { if (finished != NULL) { - finished(imageContainer, error, downloadIdentifier); + finished(imageContainer, error, downloadIdentifier, userInfo); } }]; as_log_verbose(ASImageLoadingLog(), "Downloading image for %@ url: %@", self, url); @@ -630,15 +631,15 @@ } if (_shouldCacheImage) { - [self _locked__setImage:[UIImage imageNamed:_URL.path.lastPathComponent]]; + [self _locked__setImage:[UIImage imageNamed:URL.path.lastPathComponent]]; } else { // First try to load the path directly, for efficiency assuming a developer who // doesn't want caching is trying to be as minimal as possible. - UIImage *nonAnimatedImage = [UIImage imageWithContentsOfFile:_URL.path]; + UIImage *nonAnimatedImage = [UIImage imageWithContentsOfFile:URL.path]; if (nonAnimatedImage == nil) { // If we couldn't find it, execute an -imageNamed:-like search so we can find resources even if the // extension is not provided in the path. This allows the same path to work regardless of shouldCacheImage. - NSString *filename = [[NSBundle mainBundle] pathForResource:_URL.path.lastPathComponent ofType:nil]; + NSString *filename = [[NSBundle mainBundle] pathForResource:URL.path.lastPathComponent ofType:nil]; if (filename != nil) { nonAnimatedImage = [UIImage imageWithContentsOfFile:filename]; } @@ -647,7 +648,7 @@ // If the file may be an animated gif and then created an animated image. id animatedImage = nil; if (_downloaderFlags.downloaderImplementsAnimatedImage) { - NSData *data = [NSData dataWithContentsOfURL:_URL]; + NSData *data = [NSData dataWithContentsOfURL:URL]; if (data != nil) { animatedImage = [_downloader animatedImageWithData:data]; @@ -670,8 +671,7 @@ if (_delegateFlags.delegateDidLoadImageWithInfo) { ASDN::MutexUnlocker u(__instanceLock__); - ASNetworkImageNodeDidLoadInfo info = {}; - info.imageSource = ASNetworkImageSourceFileURL; + auto info = [[ASNetworkImageLoadInfo alloc] initWithURL:URL sourceType:ASNetworkImageSourceFileURL downloadIdentifier:nil userInfo:nil]; [delegate imageNode:self didLoadImage:self.image info:info]; } else if (_delegateFlags.delegateDidLoadImage) { ASDN::MutexUnlocker u(__instanceLock__); @@ -680,7 +680,7 @@ }); } else { __weak __typeof__(self) weakSelf = self; - auto finished = ^(id imageContainer, NSError *error, id downloadIdentifier, ASNetworkImageSource imageSource) { + auto finished = ^(id imageContainer, NSError *error, id downloadIdentifier, ASNetworkImageSourceType imageSource, id userInfo) { ASPerformBlockOnBackgroundThread(^{ __typeof__(self) strongSelf = weakSelf; if (strongSelf == nil) { @@ -698,12 +698,13 @@ } //No longer in preload range, no point in setting the results (they won't be cleared in exit preload range) - if (ASInterfaceStateIncludesPreload(self->_interfaceState) == NO) { - self->_downloadIdentifier = nil; - self->_cacheUUID = nil; + if (ASInterfaceStateIncludesPreload(strongSelf->_interfaceState) == NO) { + strongSelf->_downloadIdentifier = nil; + strongSelf->_cacheUUID = nil; return; } + UIImage *newImage; if (imageContainer != nil) { [strongSelf _locked_setCurrentImageQuality:1.0]; NSData *animatedImageData = [imageContainer asdk_animatedImageData]; @@ -711,7 +712,8 @@ id animatedImage = [strongSelf->_downloader animatedImageWithData:animatedImageData]; [strongSelf _locked_setAnimatedImage:animatedImage]; } else { - [strongSelf _locked__setImage:[imageContainer asdk_image]]; + newImage = [imageContainer asdk_image]; + [strongSelf _locked__setImage:newImage]; } strongSelf->_imageLoaded = YES; } @@ -719,30 +721,32 @@ strongSelf->_downloadIdentifier = nil; strongSelf->_cacheUUID = nil; - ASPerformBlockOnMainThread(^{ - __typeof__(self) strongSelf = weakSelf; - if (strongSelf == nil) { - return; + void (^calloutBlock)(ASNetworkImageNode *inst); + + if (newImage) { + if (_delegateFlags.delegateDidLoadImageWithInfo) { + calloutBlock = ^(ASNetworkImageNode *strongSelf) { + auto info = [[ASNetworkImageLoadInfo alloc] initWithURL:URL sourceType:imageSource downloadIdentifier:downloadIdentifier userInfo:userInfo]; + [delegate imageNode:strongSelf didLoadImage:newImage info:info]; + }; + } else if (_delegateFlags.delegateDidLoadImage) { + calloutBlock = ^(ASNetworkImageNode *strongSelf) { + [delegate imageNode:strongSelf didLoadImage:newImage]; + }; } - - // Grab the lock for the rest of the block - ASDN::MutexLocker l(strongSelf->__instanceLock__); - - if (imageContainer != nil) { - if (strongSelf->_delegateFlags.delegateDidLoadImageWithInfo) { - ASDN::MutexUnlocker u(strongSelf->__instanceLock__); - ASNetworkImageNodeDidLoadInfo info = {}; - info.imageSource = imageSource; - [delegate imageNode:strongSelf didLoadImage:strongSelf.image info:info]; - } else if (strongSelf->_delegateFlags.delegateDidLoadImage) { - ASDN::MutexUnlocker u(strongSelf->__instanceLock__); - [delegate imageNode:strongSelf didLoadImage:strongSelf.image]; - } - } else if (error && strongSelf->_delegateFlags.delegateDidFailWithError) { - ASDN::MutexUnlocker u(strongSelf->__instanceLock__); + } else if (error && _delegateFlags.delegateDidFailWithError) { + calloutBlock = ^(ASNetworkImageNode *strongSelf) { [delegate imageNode:strongSelf didFailWithError:error]; - } - }); + }; + } + + if (calloutBlock) { + ASPerformBlockOnMainThread(^{ + if (auto strongSelf = weakSelf) { + calloutBlock(strongSelf); + } + }); + } }); }; @@ -767,20 +771,20 @@ } if ([imageContainer asdk_image] == nil && _downloader != nil) { - [self _downloadImageWithCompletion:^(id imageContainer, NSError *error, id downloadIdentifier) { - finished(imageContainer, error, downloadIdentifier, ASNetworkImageSourceDownload); + [self _downloadImageWithCompletion:^(id imageContainer, NSError *error, id downloadIdentifier, id userInfo) { + finished(imageContainer, error, downloadIdentifier, ASNetworkImageSourceDownload, userInfo); }]; } else { as_log_verbose(ASImageLoadingLog(), "Decached image for %@ img: %@ url: %@", self, [imageContainer asdk_image], URL); - finished(imageContainer, nil, nil, ASNetworkImageSourceAsynchronousCache); + finished(imageContainer, nil, nil, ASNetworkImageSourceAsynchronousCache, nil); } }; [_cache cachedImageWithURL:URL callbackQueue:dispatch_get_main_queue() completion:completion]; } else { - [self _downloadImageWithCompletion:^(id imageContainer, NSError *error, id downloadIdentifier) { - finished(imageContainer, error, downloadIdentifier, ASNetworkImageSourceDownload); + [self _downloadImageWithCompletion:^(id imageContainer, NSError *error, id downloadIdentifier, id userInfo) { + finished(imageContainer, error, downloadIdentifier, ASNetworkImageSourceDownload, userInfo); }]; } } diff --git a/Source/ASRunLoopQueue.h b/Source/ASRunLoopQueue.h index 50a0eeb249..02711910bd 100644 --- a/Source/ASRunLoopQueue.h +++ b/Source/ASRunLoopQueue.h @@ -20,8 +20,15 @@ NS_ASSUME_NONNULL_BEGIN +@protocol ASCATransactionQueueObserving +- (void)prepareForCATransactionCommit; +@end + +@interface ASAbstractRunLoopQueue : NSObject +@end + AS_SUBCLASSING_RESTRICTED -@interface ASRunLoopQueue : NSObject +@interface ASRunLoopQueue : ASAbstractRunLoopQueue /** * Create a new queue with the given run loop and handler. @@ -41,13 +48,37 @@ AS_SUBCLASSING_RESTRICTED - (void)enqueue:(ObjectType)object; -@property (nonatomic, readonly) BOOL isEmpty; +@property (atomic, readonly) BOOL isEmpty; @property (nonatomic, assign) NSUInteger batchSize; // Default == 1. @property (nonatomic, assign) BOOL ensureExclusiveMembership; // Default == YES. Set-like behavior. @end +AS_SUBCLASSING_RESTRICTED +@interface ASCATransactionQueue : ASAbstractRunLoopQueue + +@property (atomic, readonly) BOOL isEmpty; +@property (atomic, readonly) BOOL disabled; +/** + * The queue to run on main run loop before CATransaction commit. + * + * @discussion this queue will run after ASRunLoopQueue and before CATransaction commit + * to get last chance of updating/coalesce info like interface state. + * Each node will only be called once per transaction commit to reflect interface change. + */ +@property (class, atomic, readonly) ASCATransactionQueue *sharedQueue; + +- (void)enqueue:(id)object; + +/** + * @abstract Apply a node's interfaceState immediately rather than adding to the queue. + */ +- (void)disable; + +@end + + AS_SUBCLASSING_RESTRICTED @interface ASDeallocQueue : NSObject diff --git a/Source/ASRunLoopQueue.mm b/Source/ASRunLoopQueue.mm index ced7982529..12265d1dfa 100644 --- a/Source/ASRunLoopQueue.mm +++ b/Source/ASRunLoopQueue.mm @@ -57,11 +57,6 @@ static void runLoopSourceCallback(void *info) { - (void)releaseObjectInBackground:(id _Nullable __strong *)objectPtr { - // Disable background deallocation on iOS 8 and below to avoid crashes related to UIAXDelegateClearer (#2767). - if (!AS_AT_LEAST_IOS9) { - return; - } - if (objectPtr != NULL && *objectPtr != nil) { ASDN::MutexLocker l(_queueLock); _queue.push_back(*objectPtr); @@ -186,27 +181,6 @@ static void runLoopSourceCallback(void *info) { @end -#pragma mark - ASRunLoopQueue - -@interface ASRunLoopQueue () { - CFRunLoopRef _runLoop; - CFRunLoopSourceRef _runLoopSource; - 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 -} - -@property (nonatomic, copy) void (^queueConsumer)(id dequeuedItem, BOOL isQueueDrained); - -@end - #if AS_KDEBUG_ENABLE /** * This is real, private CA API. Valid as of iOS 10. @@ -223,7 +197,23 @@ typedef enum { @end #endif -@implementation ASRunLoopQueue +#pragma mark - ASAbstractRunLoopQueue + +@interface ASAbstractRunLoopQueue (Private) ++ (void)load; ++ (void)registerCATransactionObservers; +@end + +@implementation ASAbstractRunLoopQueue + +- (instancetype)init +{ + if (self != [super init]) { + return nil; + } + ASDisplayNodeAssert(self.class != [ASAbstractRunLoopQueue class], @"Should never create instances of abstract class ASAbstractRunLoopQueue."); + return self; +} #if AS_KDEBUG_ENABLE + (void)load @@ -270,6 +260,31 @@ typedef enum { #endif // AS_KDEBUG_ENABLE +@end + +#pragma mark - ASRunLoopQueue + +@interface ASRunLoopQueue () { + CFRunLoopRef _runLoop; + CFRunLoopSourceRef _runLoopSource; + 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 +} + +@property (nonatomic, copy) void (^queueConsumer)(id dequeuedItem, BOOL isQueueDrained); + +@end + +@implementation ASRunLoopQueue + - (instancetype)initWithRunLoop:(CFRunLoopRef)runloop retainObjects:(BOOL)retainsObjects handler:(void (^)(id _Nullable, BOOL))handlerBlock { if (self = [super init]) { @@ -469,3 +484,232 @@ typedef enum { } @end + +#pragma mark - ASCATransactionQueue + +@interface ASCATransactionQueue () { + CFRunLoopRef _runLoop; + CFRunLoopSourceRef _runLoopSource; + CFRunLoopObserverRef _preTransactionObserver; + CFRunLoopObserverRef _postTransactionObserver; + NSPointerArray *_internalQueue; + ASDN::RecursiveMutex _internalQueueLock; + BOOL _disableInterfaceStateCoalesce; + BOOL _CATransactionCommitInProgress; + + // In order to not pollute the top-level activities, each queue has 1 root activity. + os_activity_t _rootActivity; + +#if ASRunLoopQueueLoggingEnabled + NSTimer *_runloopQueueLoggingTimer; +#endif +} + +@end + +@implementation ASCATransactionQueue + +// CoreAnimation commit order is 2000000, the goal of this is to process shortly beforehand +// but after most other scheduled work on the runloop has processed. +static int const kASASCATransactionQueueOrder = 1000000; +// This will mark the end of current loop and any node enqueued between kASASCATransactionQueueOrder +// and kASASCATransactionQueuePostOrder will apply interface change immediately. +static int const kASASCATransactionQueuePostOrder = 3000000; + ++ (ASCATransactionQueue *)sharedQueue +{ + static dispatch_once_t onceToken; + static ASCATransactionQueue *sharedQueue; + dispatch_once(&onceToken, ^{ + sharedQueue = [[ASCATransactionQueue alloc] init]; + }); + return sharedQueue; +} + +- (instancetype)init +{ + if (self = [super init]) { + _runLoop = CFRunLoopGetMain(); + NSPointerFunctionsOptions options = NSPointerFunctionsStrongMemory; + _internalQueue = [[NSPointerArray alloc] initWithOptions:options]; + + // 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. + __unsafe_unretained __typeof__(self) weakSelf = self; + void (^handlerBlock) (CFRunLoopObserverRef observer, CFRunLoopActivity activity) = ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) { + [weakSelf processQueue]; + }; + void (^postHandlerBlock) (CFRunLoopObserverRef observer, CFRunLoopActivity activity) = ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) { + ASDN::MutexLocker l(_internalQueueLock); + _CATransactionCommitInProgress = NO; + }; + _preTransactionObserver = CFRunLoopObserverCreateWithHandler(NULL, kCFRunLoopBeforeWaiting, true, kASASCATransactionQueueOrder, handlerBlock); + _postTransactionObserver = CFRunLoopObserverCreateWithHandler(NULL, kCFRunLoopBeforeWaiting, true, kASASCATransactionQueuePostOrder, postHandlerBlock); + + CFRunLoopAddObserver(_runLoop, _preTransactionObserver, kCFRunLoopCommonModes); + CFRunLoopAddObserver(_runLoop, _postTransactionObserver, kCFRunLoopCommonModes); + + // It is not guaranteed that the runloop will turn if it has no scheduled work, and this causes processing of + // the queue to stop. Attaching a custom loop source to the run loop and signal it if new work needs to be done + CFRunLoopSourceContext sourceContext = {}; + sourceContext.perform = runLoopSourceCallback; +#if ASRunLoopQueueLoggingEnabled + sourceContext.info = (__bridge void *)self; +#endif + _runLoopSource = CFRunLoopSourceCreate(NULL, 0, &sourceContext); + CFRunLoopAddSource(_runLoop, _runLoopSource, kCFRunLoopCommonModes); + +#if ASRunLoopQueueLoggingEnabled + _runloopQueueLoggingTimer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(checkRunLoop) userInfo:nil repeats:YES]; + [[NSRunLoop mainRunLoop] addTimer:_runloopQueueLoggingTimer forMode:NSRunLoopCommonModes]; +#endif + } + return self; +} + +- (void)dealloc +{ + CFRunLoopRemoveSource(_runLoop, _runLoopSource, kCFRunLoopCommonModes); + CFRelease(_runLoopSource); + _runLoopSource = nil; + + if (CFRunLoopObserverIsValid(_preTransactionObserver)) { + CFRunLoopObserverInvalidate(_preTransactionObserver); + } + if (CFRunLoopObserverIsValid(_postTransactionObserver)) { + CFRunLoopObserverInvalidate(_postTransactionObserver); + } + CFRelease(_preTransactionObserver); + CFRelease(_postTransactionObserver); + _preTransactionObserver = nil; + _postTransactionObserver = nil; +} + +#if ASRunLoopQueueLoggingEnabled +- (void)checkRunLoop +{ + NSLog(@"<%@> - Jobs: %ld", self, _internalQueue.size()); +} +#endif + +- (void)processQueue +{ + // If we have an execution block, this vector will be populated, otherwise remains empty. + // This is to avoid needlessly retaining/releasing the objects if we don't have a block. + std::vector itemsToProcess; + + { + ASDN::MutexLocker l(_internalQueueLock); + + // Mark the queue will end coalescing shortly until after CATransactionCommit. + // This will give the queue a chance to apply any further interfaceState changes/enqueue + // immediately within current runloop instead of pushing the work to next runloop cycle. + _CATransactionCommitInProgress = YES; + + NSInteger internalQueueCount = _internalQueue.count; + // Early-exit if the queue is empty. + if (internalQueueCount == 0) { + return; + } + + ASSignpostStart(ASSignpostRunLoopQueueBatch); + + /** + * For each item in the next batch, if it's non-nil then NULL it out + * and if we have an execution block then add it in. + * This could be written a bunch of different ways but + * this particular one nicely balances readability, safety, and efficiency. + */ + NSInteger foundItemCount = 0; + for (NSInteger i = 0; i < internalQueueCount && foundItemCount < internalQueueCount; i++) { + /** + * It is safe to use unsafe_unretained here. If the queue is weak, the + * object will be added to the autorelease pool. If the queue is strong, + * it will retain the object until we transfer it (retain it) in itemsToProcess. + */ + __unsafe_unretained id ptr = (__bridge id)[_internalQueue pointerAtIndex:i]; + if (ptr != nil) { + foundItemCount++; + itemsToProcess.push_back(ptr); + [_internalQueue replacePointerAtIndex:i withPointer:NULL]; + } + } + + [_internalQueue compact]; + } + + // itemsToProcess will be empty if _queueConsumer == nil so no need to check again. + 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++) { + __unsafe_unretained id value = *iterator; + [value prepareForCATransactionCommit]; + as_log_verbose(ASDisplayLog(), "processed %@", value); + } + if (count > 1) { + as_log_verbose(ASDisplayLog(), "processed %lu items", (unsigned long)count); + } + } + + ASSignpostEnd(ASSignpostRunLoopQueueBatch); +} + +- (void)enqueue:(id)object +{ + if (!object) { + return; + } + + if (_disableInterfaceStateCoalesce || _CATransactionCommitInProgress) { + [object prepareForCATransactionCommit]; + return; + } + + ASDN::MutexLocker l(_internalQueueLock); + + // Check if the object exists. + BOOL foundObject = NO; + + for (id currentObject in _internalQueue) { + if (currentObject == object) { + foundObject = YES; + break; + } + } + + if (!foundObject) { + [_internalQueue addPointer:(__bridge void *)object]; + + CFRunLoopSourceSignal(_runLoopSource); + CFRunLoopWakeUp(_runLoop); + } +} + +- (BOOL)isEmpty +{ + ASDN::MutexLocker l(_internalQueueLock); + return _internalQueue.count == 0; +} + +- (void)disable +{ + _disableInterfaceStateCoalesce = YES; +} + +- (BOOL)disabled +{ + return _disableInterfaceStateCoalesce; +} + +@end diff --git a/Source/ASTableNode.mm b/Source/ASTableNode.mm index 25bb59ab7a..e189dcbc08 100644 --- a/Source/ASTableNode.mm +++ b/Source/ASTableNode.mm @@ -105,6 +105,18 @@ return [self initWithStyle:UITableViewStylePlain]; } +#if ASDISPLAYNODE_ASSERTIONS_ENABLED +- (void)dealloc +{ + if (self.nodeLoaded) { + __weak UIView *view = self.view; + ASPerformBlockOnMainThread(^{ + ASDisplayNodeCAssertNil(view.superview, @"Node's view should be removed from hierarchy."); + }); + } +} +#endif + #pragma mark ASDisplayNode - (void)didLoad diff --git a/Source/ASTableView.mm b/Source/ASTableView.mm index 09feccab44..8333b3cb99 100644 --- a/Source/ASTableView.mm +++ b/Source/ASTableView.mm @@ -115,11 +115,12 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; if (node) { self.backgroundColor = node.backgroundColor; - self.selectionStyle = node.selectionStyle; self.selectedBackgroundView = node.selectedBackgroundView; self.separatorInset = node.separatorInset; - self.selectionStyle = node.selectionStyle; + self.selectionStyle = node.selectionStyle; + self.focusStyle = node.focusStyle; self.accessoryType = node.accessoryType; + self.tintColor = node.tintColor; // the following ensures that we clip the entire cell to it's bounds if node.clipsToBounds is set (the default) // This is actually a workaround for a bug we are seeing in some rare cases (selected background view @@ -188,15 +189,6 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; BOOL _automaticallyAdjustsContentOffset; CGPoint _deceleratingVelocity; - - /** - * Our layer, retained. Under iOS < 9, when table views are removed from the hierarchy, - * their layers may be deallocated and become dangling pointers. This puts the table view - * into a very dangerous state where pretty much any call will crash it. So we manually retain our layer. - * - * You should never access this, and it will be nil under iOS >= 9. - */ - CALayer *_retainedLayer; CGFloat _nodesConstrainedWidth; BOOL _queuedNodeHeightUpdate; @@ -225,7 +217,12 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; * Counter used to keep track of nested batch updates. */ NSInteger _batchUpdateCount; - + + /** + * Keep a strong reference to node till view is ready to release. + */ + ASTableNode *_keepalive_node; + struct { unsigned int scrollViewDidScroll:1; unsigned int scrollViewWillBeginDragging:1; @@ -351,10 +348,6 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; [self registerClass:_ASTableViewCell.class forCellReuseIdentifier:kCellReuseIdentifier]; - if (!AS_AT_LEAST_IOS9) { - _retainedLayer = self.layer; - } - // iOS 11 automatically uses estimated heights, so disable those (see PR #485) if (AS_AT_LEAST_IOS11) { super.estimatedRowHeight = 0.0; @@ -1227,13 +1220,10 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; [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)) { - [_rangeController updateCurrentRangeWithMode:ASLayoutRangeModeFull]; [self _checkForBatchFetching]; - } - + } for (_ASTableViewCell *tableCell in _cellsForVisibilityUpdates) { [[tableCell node] cellNodeVisibilityEvent:ASCellNodeVisibilityEventVisibleRectChanged inScrollView:scrollView @@ -1285,6 +1275,10 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; [super scrollViewWillBeginDragging:scrollView]; return; } + // If a scroll happens the current range mode needs to go to full + _rangeController.contentHasBeenScrolled = YES; + [_rangeController updateCurrentRangeWithMode:ASLayoutRangeModeFull]; + for (_ASTableViewCell *tableViewCell in _cellsForVisibilityUpdates) { [[tableViewCell node] cellNodeVisibilityEvent:ASCellNodeVisibilityEventWillBeginDragging inScrollView:scrollView @@ -1925,6 +1919,20 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; } } +- (void)willMoveToSuperview:(UIView *)newSuperview +{ + if (self.superview == nil && newSuperview != nil) { + _keepalive_node = self.tableNode; + } +} + +- (void)didMoveToSuperview +{ + if (self.superview == nil) { + _keepalive_node = nil; + } +} + @end #endif diff --git a/Source/ASTextNode.mm b/Source/ASTextNode.mm index e5338f5217..e2a594b05f 100644 --- a/Source/ASTextNode.mm +++ b/Source/ASTextNode.mm @@ -28,6 +28,7 @@ #import #import #import +#import #import #import @@ -229,6 +230,8 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; { CGColorRelease(_shadowColor); + // TODO: This may not be necessary post-iOS-9 when most UIKit assign APIs + // were changed to weak. if (_longPressGestureRecognizer) { _longPressGestureRecognizer.delegate = nil; [_longPressGestureRecognizer removeTarget:nil action:NULL]; @@ -907,7 +910,7 @@ static CGRect ASTextNodeAdjustRenderRectForShadowPadding(CGRect rendererRect, UI ASDN::MutexLocker l(__instanceLock__); - UIGraphicsBeginImageContext(size); + ASGraphicsBeginImageContextWithOptions(size, NO, 1.0); [self.placeholderColor setFill]; ASTextKitRenderer *renderer = [self _locked_renderer]; @@ -926,8 +929,7 @@ static CGRect ASTextNodeAdjustRenderRectForShadowPadding(CGRect rendererRect, UI } } - UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); + UIImage *image = ASGraphicsGetImageAndEndCurrentContext(); return image; } diff --git a/Source/ASTextNode2.mm b/Source/ASTextNode2.mm index ecd9496c51..54475368d8 100644 --- a/Source/ASTextNode2.mm +++ b/Source/ASTextNode2.mm @@ -232,7 +232,7 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; [self _ensureTruncationText]; NSMutableAttributedString *mutableText = [attributedText mutableCopy]; - [self prepareAttributedStringForDrawing:mutableText]; + [self prepareAttributedString:mutableText]; ASTextLayout *layout = [ASTextNode2 compatibleLayoutWithContainer:container text:mutableText]; [self setNeedsDisplay]; @@ -319,7 +319,7 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; return _textContainer.exclusionPaths; } -- (void)prepareAttributedStringForDrawing:(NSMutableAttributedString *)attributedString +- (void)prepareAttributedString:(NSMutableAttributedString *)attributedString { ASDN::MutexLocker lock(__instanceLock__); @@ -334,12 +334,6 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; [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]; @@ -362,11 +356,19 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; ASTextContainer *copiedContainer = [_textContainer copy]; copiedContainer.size = self.bounds.size; NSMutableAttributedString *mutableText = [self.attributedText mutableCopy] ?: [[NSMutableAttributedString alloc] init]; - [self prepareAttributedStringForDrawing:mutableText]; + + [self prepareAttributedString:mutableText]; + + // Apply background color if needed before drawing. To access the backgroundColor we need to be on the main thread + UIColor *backgroundColor = self.backgroundColor; + if (CGColorGetAlpha(backgroundColor.CGColor) > 0) { + [mutableText addAttribute:NSBackgroundColorAttributeName value:backgroundColor range:NSMakeRange(0, mutableText.length)]; + } + return @{ - @"container": copiedContainer, - @"text": mutableText - }; + @"container": copiedContainer, + @"text": mutableText + }; } /** diff --git a/Source/AsyncDisplayKit.h b/Source/AsyncDisplayKit.h index c20f5077a1..241d8f2bb0 100644 --- a/Source/AsyncDisplayKit.h +++ b/Source/AsyncDisplayKit.h @@ -35,6 +35,7 @@ #import #import #import +#import #import #import @@ -119,6 +120,7 @@ #import #import #import +#import #import #import #import diff --git a/Source/Base/ASAvailability.h b/Source/Base/ASAvailability.h index 6a80e274a4..f78ed9aaf6 100644 --- a/Source/Base/ASAvailability.h +++ b/Source/Base/ASAvailability.h @@ -19,10 +19,6 @@ #pragma once -#ifndef kCFCoreFoundationVersionNumber_iOS_9_0 - #define kCFCoreFoundationVersionNumber_iOS_9_0 1240.10 -#endif - #ifndef kCFCoreFoundationVersionNumber_iOS_10_0 #define kCFCoreFoundationVersionNumber_iOS_10_0 1348.00 #endif @@ -35,7 +31,6 @@ #define __IPHONE_11_0 110000 #endif -#define AS_AT_LEAST_IOS9 (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_9_0) #define AS_AT_LEAST_IOS10 (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_10_0) #define AS_AT_LEAST_IOS11 (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_11_0) diff --git a/Source/Debug/AsyncDisplayKit+Debug.m b/Source/Debug/AsyncDisplayKit+Debug.m index 59f0c4f830..44762b1b80 100644 --- a/Source/Debug/AsyncDisplayKit+Debug.m +++ b/Source/Debug/AsyncDisplayKit+Debug.m @@ -17,6 +17,7 @@ #import #import +#import #import #import #import @@ -148,7 +149,7 @@ static BOOL __enableHitTestDebug = NO; UIColor *clipsBorderColor = [UIColor colorWithRed:30/255.0 green:90/255.0 blue:50/255.0 alpha:0.7]; CGRect imgRect = CGRectMake(0, 0, 2.0 * borderWidth + 1.0, 2.0 * borderWidth + 1.0); - UIGraphicsBeginImageContext(imgRect.size); + ASGraphicsBeginImageContextWithOptions(imgRect.size, NO, 1); [fillColor setFill]; UIRectFill(imgRect); @@ -156,8 +157,7 @@ static BOOL __enableHitTestDebug = NO; [self drawEdgeIfClippedWithEdges:clippedEdges color:clipsBorderColor borderWidth:borderWidth imgRect:imgRect]; [self drawEdgeIfClippedWithEdges:clipsToBoundsClippedEdges color:borderColor borderWidth:borderWidth imgRect:imgRect]; - UIImage *debugHighlightImage = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); + UIImage *debugHighlightImage = ASGraphicsGetImageAndEndCurrentContext(); UIEdgeInsets edgeInsets = UIEdgeInsetsMake(borderWidth, borderWidth, borderWidth, borderWidth); debugOverlay.image = [debugHighlightImage resizableImageWithCapInsets:edgeInsets resizingMode:UIImageResizingModeStretch]; diff --git a/Source/Details/ASBasicImageDownloader.h b/Source/Details/ASBasicImageDownloader.h index 867e50832a..4185d6c1de 100644 --- a/Source/Details/ASBasicImageDownloader.h +++ b/Source/Details/ASBasicImageDownloader.h @@ -27,14 +27,15 @@ NS_ASSUME_NONNULL_BEGIN @interface ASBasicImageDownloader : NSObject /** - * A shared image downloader which can be used by @c ASNetworkImageNodes and @c ASMultiplexImageNodes + * A shared image downloader which can be used by @c ASNetworkImageNodes and @c ASMultiplexImageNodes. + * The userInfo provided by this downloader is `nil`. * * This is a very basic image downloader. It does not support caching, progressive downloading and likely * isn't something you should use in production. If you'd like something production ready, see @c ASPINRemoteImageDownloader * * @note It is strongly recommended you include PINRemoteImage and use @c ASPINRemoteImageDownloader instead. */ -+ (instancetype)sharedImageDownloader; +@property (class, readonly) ASBasicImageDownloader *sharedImageDownloader; + (instancetype)new __attribute__((unavailable("+[ASBasicImageDownloader sharedImageDownloader] must be used."))); - (instancetype)init __attribute__((unavailable("+[ASBasicImageDownloader sharedImageDownloader] must be used."))); diff --git a/Source/Details/ASBasicImageDownloader.mm b/Source/Details/ASBasicImageDownloader.mm index 2926181e8e..113d909b89 100644 --- a/Source/Details/ASBasicImageDownloader.mm +++ b/Source/Details/ASBasicImageDownloader.mm @@ -130,7 +130,7 @@ static ASDN::StaticMutex& currentRequestsLock = *new ASDN::StaticMutex; if (completionBlock) { dispatch_async(callbackQueue, ^{ - completionBlock(image, error, nil); + completionBlock(image, error, nil, nil); }); } } @@ -206,7 +206,7 @@ static const char *kContextKey = NSStringFromClass(ASBasicImageDownloaderContext @implementation ASBasicImageDownloader -+ (instancetype)sharedImageDownloader ++ (ASBasicImageDownloader *)sharedImageDownloader { static ASBasicImageDownloader *sharedImageDownloader = nil; static dispatch_once_t once = 0; @@ -235,9 +235,9 @@ static const char *kContextKey = NSStringFromClass(ASBasicImageDownloaderContext #pragma mark ASImageDownloaderProtocol. - (id)downloadImageWithURL:(NSURL *)URL - callbackQueue:(dispatch_queue_t)callbackQueue - downloadProgress:(nullable ASImageDownloaderProgress)downloadProgress - completion:(ASImageDownloaderCompletion)completion + callbackQueue:(dispatch_queue_t)callbackQueue + downloadProgress:(nullable ASImageDownloaderProgress)downloadProgress + completion:(ASImageDownloaderCompletion)completion { ASBasicImageDownloaderContext *context = [ASBasicImageDownloaderContext contextForURL:URL]; diff --git a/Source/Details/ASDataController.mm b/Source/Details/ASDataController.mm index 4464937b5c..5780895ace 100644 --- a/Source/Details/ASDataController.mm +++ b/Source/Details/ASDataController.mm @@ -528,7 +528,7 @@ typedef dispatch_block_t ASDataControllerCompletionBlock; BOOL canDelegate = (self.layoutDelegate != nil); ASElementMap *newMap; - id layoutContext; + ASCollectionLayoutContext *layoutContext; { as_activity_scope(as_activity_create("Latch new data for collection update", changeSet.rootActivity, OS_ACTIVITY_FLAG_DEFAULT)); diff --git a/Source/Details/ASGraphicsContext.h b/Source/Details/ASGraphicsContext.h new file mode 100644 index 0000000000..26183d5951 --- /dev/null +++ b/Source/Details/ASGraphicsContext.h @@ -0,0 +1,65 @@ +// +// ASGraphicsContext.h +// Texture +// +// Copyright (c) 2018-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 + +@class UIImage; + +/** + * Functions for creating one-shot graphics contexts that do not have to copy + * their contents when an image is generated from them. This is efficient + * for our use, since we do not reuse graphics contexts. + * + * The API mirrors the UIGraphics API, with the exception that forming an image + * ends the context as well. + * + * Note: You must not mix-and-match between ASGraphics* and UIGraphics* functions + * within the same drawing operation. + */ + +NS_ASSUME_NONNULL_BEGIN +ASDISPLAYNODE_EXTERN_C_BEGIN + +/** + * Call this to enable the experimental no-copy rendering. + * + * Returns YES if it was enabled, or NO + assert if it's too late because + * rendering has already started. In practice it's fine to call this + * during -didFinishLaunchingWithOptions:. + */ +extern BOOL ASEnableNoCopyRendering(void); + +/** + * Creates a one-shot context. + * + * Behavior is the same as UIGraphicsBeginImageContextWithOptions. + */ +extern void ASGraphicsBeginImageContextWithOptions(CGSize size, BOOL opaque, CGFloat scale); + +/** + * Generates and image and ends the current one-shot context. + * + * Behavior is the same as UIGraphicsGetImageFromCurrentImageContext followed by UIGraphicsEndImageContext. + */ +extern UIImage * _Nullable ASGraphicsGetImageAndEndCurrentContext(void); + +/** + * Call this if you want to end the current context without making an image. + * + * Behavior is the same as UIGraphicsEndImageContext. + */ +extern void ASGraphicsEndImageContext(void); + +ASDISPLAYNODE_EXTERN_C_END +NS_ASSUME_NONNULL_END diff --git a/Source/Details/ASGraphicsContext.m b/Source/Details/ASGraphicsContext.m new file mode 100644 index 0000000000..3aaa14c090 --- /dev/null +++ b/Source/Details/ASGraphicsContext.m @@ -0,0 +1,194 @@ +// +// ASGraphicsContext.m +// Texture +// +// Copyright (c) 2018-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 "ASGraphicsContext.h" +#import +#import +#import +#import +#import +#import + +#pragma mark - Feature Gating + +// Two flags that we atomically manipulate to control the feature. +typedef NS_OPTIONS(uint, ASNoCopyFlags) { + ASNoCopyEnabled = 1 << 0, + ASNoCopyBlocked = 1 << 1 +}; +static atomic_uint __noCopyFlags; + +// Check if it's blocked, and set the enabled flag if not. +extern BOOL ASEnableNoCopyRendering() +{ + ASNoCopyFlags expectedFlags = 0; + BOOL enabled = atomic_compare_exchange_strong(&__noCopyFlags, &expectedFlags, ASNoCopyEnabled); + ASDisplayNodeCAssert(enabled, @"Can't enable no-copy rendering after first render started."); + return enabled; +} + +// Check if it's enabled and set the "blocked" flag either way. +static BOOL ASNoCopyRenderingBlockAndCheckEnabled() { + ASNoCopyFlags oldFlags = atomic_fetch_or(&__noCopyFlags, ASNoCopyBlocked); + return (oldFlags & ASNoCopyEnabled) != 0; +} + +/** + * Our version of the private CGBitmapGetAlignedBytesPerRow function. + * + * In both 32-bit and 64-bit, this function rounds up to nearest multiple of 32 + * in iOS 9, 10, and 11. We'll try to catch if this ever changes by asserting that + * the bytes-per-row for a 1x1 context from the system is 32. + */ +static size_t ASGraphicsGetAlignedBytesPerRow(size_t baseValue) { + // Add 31 then zero out low 5 bits. + return (baseValue + 31) & ~0x1F; +} + +/** + * A key used to associate CGContextRef -> NSMutableData, nonatomic retain. + * + * That way the data will be released when the context dies. If they pull an image, + * we will retain the data object (in a CGDataProvider) before releasing the context. + */ +static UInt8 __contextDataAssociationKey; + +#pragma mark - Graphics Contexts + +extern void ASGraphicsBeginImageContextWithOptions(CGSize size, BOOL opaque, CGFloat scale) +{ + if (!ASNoCopyRenderingBlockAndCheckEnabled()) { + UIGraphicsBeginImageContextWithOptions(size, opaque, scale); + return; + } + + // We use "reference contexts" to get device-specific options that UIKit + // uses. + static dispatch_once_t onceToken; + static CGContextRef refCtxOpaque; + static CGContextRef refCtxTransparent; + dispatch_once(&onceToken, ^{ + UIGraphicsBeginImageContextWithOptions(CGSizeMake(1, 1), YES, 1); + refCtxOpaque = CGContextRetain(UIGraphicsGetCurrentContext()); + ASDisplayNodeCAssert(CGBitmapContextGetBytesPerRow(refCtxOpaque) == 32, @"Expected bytes per row to be aligned to 32. Has CGBitmapGetAlignedBytesPerRow implementation changed?"); + UIGraphicsEndImageContext(); + + // Make transparent ref context. + UIGraphicsBeginImageContextWithOptions(CGSizeMake(1, 1), NO, 1); + refCtxTransparent = CGContextRetain(UIGraphicsGetCurrentContext()); + UIGraphicsEndImageContext(); + }); + + // These options are taken from UIGraphicsBeginImageContext. + CGContextRef refCtx = opaque ? refCtxOpaque : refCtxTransparent; + CGBitmapInfo bitmapInfo = CGBitmapContextGetBitmapInfo(refCtx); + + if (scale == 0) { + scale = ASScreenScale(); + } + size_t intWidth = (size_t)ceil(size.width * scale); + size_t intHeight = (size_t)ceil(size.height * scale); + size_t bitsPerComponent = CGBitmapContextGetBitsPerComponent(refCtx); + size_t bytesPerRow = CGBitmapContextGetBitsPerPixel(refCtx) * intWidth / 8; + bytesPerRow = ASGraphicsGetAlignedBytesPerRow(bytesPerRow); + size_t bufferSize = bytesPerRow * intHeight; + CGColorSpaceRef colorspace = CGBitmapContextGetColorSpace(refCtx); + + // We create our own buffer, and wrap the context around that. This way we can prevent + // the copy that usually gets made when you form a CGImage from the context. + NSMutableData *data = [[NSMutableData alloc] initWithLength:bufferSize]; + CGContextRef context = CGBitmapContextCreate(data.mutableBytes, intWidth, intHeight, bitsPerComponent, bytesPerRow, colorspace, bitmapInfo); + + // Transfer ownership of the data to the context. So that if the context + // is destroyed before we create an image from it, the data will be released. + objc_setAssociatedObject((__bridge id)context, &__contextDataAssociationKey, data, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + + // Set the CTM to account for iOS orientation & specified scale. + // If only we could use CGContextSetBaseCTM. It doesn't + // seem like there are any consequences for our use case + // but we'll be on the look out. The internet hinted that it + // affects shadowing but I tested and shadowing works. + CGContextTranslateCTM(context, 0, intHeight); + CGContextScaleCTM(context, scale, -scale); + + // Save the state so we can restore it and recover our scale in GetImageAndEnd + CGContextSaveGState(context); + + // Transfer context ownership to the UIKit stack. + UIGraphicsPushContext(context); + CGContextRelease(context); +} + +extern UIImage * _Nullable ASGraphicsGetImageAndEndCurrentContext() +{ + if (!ASNoCopyRenderingBlockAndCheckEnabled()) { + UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + return image; + } + + // Pop the context and make sure we have one. + CGContextRef context = UIGraphicsGetCurrentContext(); + if (context == NULL) { + ASDisplayNodeCFailAssert(@"Can't end image context without having begun one."); + return nil; + } + + // Read the device-specific ICC-based color space to use for the image. + // For DeviceRGB contexts (e.g. UIGraphics), CGBitmapContextCreateImage + // generates an image in a device-specific color space (for wide color support). + // We replicate that behavior, even though at this time CA does not + // require the image to be in this space. Plain DeviceRGB images seem + // to be treated exactly the same, but better safe than sorry. + static CGColorSpaceRef imageColorSpace; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + UIGraphicsBeginImageContextWithOptions(CGSizeMake(1, 1), YES, 0); + UIImage *refImage = UIGraphicsGetImageFromCurrentImageContext(); + imageColorSpace = CGColorSpaceRetain(CGImageGetColorSpace(refImage.CGImage)); + ASDisplayNodeCAssertNotNil(imageColorSpace, nil); + UIGraphicsEndImageContext(); + }); + + // Retrieve our data and wrap it in a CGDataProvider. + // Don't worry, the provider doesn't copy the data – it just retains it. + NSMutableData *data = objc_getAssociatedObject((__bridge id)context, &__contextDataAssociationKey); + ASDisplayNodeCAssertNotNil(data, nil); + CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data); + + // Create the CGImage. Options taken from CGBitmapContextCreateImage. + CGImageRef cgImg = CGImageCreate(CGBitmapContextGetWidth(context), CGBitmapContextGetHeight(context), CGBitmapContextGetBitsPerComponent(context), CGBitmapContextGetBitsPerPixel(context), CGBitmapContextGetBytesPerRow(context), imageColorSpace, CGBitmapContextGetBitmapInfo(context), provider, NULL, true, kCGRenderingIntentDefault); + CGDataProviderRelease(provider); + + // We saved our GState right after setting the CTM so that we could restore it + // here and get the original scale back. + CGContextRestoreGState(context); + CGFloat scale = CGContextGetCTM(context).a; + + // Note: popping from the UIKit stack will probably destroy the context. + context = NULL; + UIGraphicsPopContext(); + + UIImage *result = [[UIImage alloc] initWithCGImage:cgImg scale:scale orientation:UIImageOrientationUp]; + CGImageRelease(cgImg); + return result; +} + +extern void ASGraphicsEndImageContext() +{ + if (!ASNoCopyRenderingBlockAndCheckEnabled()) { + UIGraphicsEndImageContext(); + return; + } + + UIGraphicsPopContext(); +} diff --git a/Source/Details/ASImageProtocols.h b/Source/Details/ASImageProtocols.h index 3fdf321e45..c998672192 100644 --- a/Source/Details/ASImageProtocols.h +++ b/Source/Details/ASImageProtocols.h @@ -72,8 +72,9 @@ typedef void(^ASImageCacherCompletion)(id _Nullable i @param image The image that was downloaded, if the image could be successfully downloaded; nil otherwise. @param error An error describing why the download of `URL` failed, if the download failed; nil otherwise. @param downloadIdentifier The identifier for the download task that completed. + @param userInfo Any additional info that your downloader would like to communicate through Texture. */ -typedef void(^ASImageDownloaderCompletion)(id _Nullable image, NSError * _Nullable error, id _Nullable downloadIdentifier); +typedef void(^ASImageDownloaderCompletion)(id _Nullable image, NSError * _Nullable error, id _Nullable downloadIdentifier, id _Nullable userInfo); /** @param progress The progress of the download, in the range of (0.0, 1.0), inclusive. diff --git a/Source/Details/ASPINRemoteImageDownloader.h b/Source/Details/ASPINRemoteImageDownloader.h index 2cdee5ed9c..4066be8ea9 100644 --- a/Source/Details/ASPINRemoteImageDownloader.h +++ b/Source/Details/ASPINRemoteImageDownloader.h @@ -30,12 +30,13 @@ NS_ASSUME_NONNULL_BEGIN @interface ASPINRemoteImageDownloader : NSObject /** - * A shared image downloader which can be used by @c ASNetworkImageNodes and @c ASMultiplexImageNodes + * A shared image downloader which can be used by @c ASNetworkImageNodes and @c ASMultiplexImageNodes. + * The userInfo provided by this downloader is an instance of `PINRemoteImageManagerResult`. * * This is the default downloader used by network backed image nodes if PINRemoteImage and PINCache are * available. It uses PINRemoteImage's features to provide caching and progressive image downloads. */ -+ (ASPINRemoteImageDownloader *)sharedDownloader; +@property (class, readonly) ASPINRemoteImageDownloader *sharedDownloader; /** diff --git a/Source/Details/ASPINRemoteImageDownloader.m b/Source/Details/ASPINRemoteImageDownloader.m index 7c80d36a03..2fb3c6a8c7 100644 --- a/Source/Details/ASPINRemoteImageDownloader.m +++ b/Source/Details/ASPINRemoteImageDownloader.m @@ -116,7 +116,7 @@ static ASPINRemoteImageDownloader *sharedDownloader = nil; @implementation ASPINRemoteImageDownloader -+ (instancetype)sharedDownloader ++ (ASPINRemoteImageDownloader *)sharedDownloader { static dispatch_once_t onceToken = 0; @@ -237,12 +237,12 @@ static ASPINRemoteImageDownloader *sharedDownloader = nil; [ASPINRemoteImageDownloader _performWithCallbackQueue:callbackQueue work:^{ #if PIN_ANIMATED_AVAILABLE if (result.alternativeRepresentation) { - completion(result.alternativeRepresentation, result.error, result.UUID); + completion(result.alternativeRepresentation, result.error, result.UUID, result); } else { - completion(result.image, result.error, result.UUID); + completion(result.image, result.error, result.UUID, result); } #else - completion(result.image, result.error, result.UUID); + completion(result.image, result.error, result.UUID, result); #endif }]; }; diff --git a/Source/Details/ASRangeController.h b/Source/Details/ASRangeController.h index 2cda9f78f3..b393a808f2 100644 --- a/Source/Details/ASRangeController.h +++ b/Source/Details/ASRangeController.h @@ -102,6 +102,11 @@ AS_SUBCLASSING_RESTRICTED */ @property (nonatomic, weak) id delegate; +/** + * Property that indicates whether the scroll view for this range controller has ever changed its contentOffset. + */ +@property (nonatomic, assign) BOOL contentHasBeenScrolled; + @end diff --git a/Source/Details/ASRangeController.mm b/Source/Details/ASRangeController.mm index a755361035..ccc9f96d01 100644 --- a/Source/Details/ASRangeController.mm +++ b/Source/Details/ASRangeController.mm @@ -46,6 +46,7 @@ NSSet *_allPreviousIndexPaths; NSHashTable *_visibleNodes; ASLayoutRangeMode _currentRangeMode; + BOOL _contentHasBeenScrolled; BOOL _preserveCurrentRangeMode; BOOL _didRegisterForNodeDisplayNotifications; CFTimeInterval _pendingDisplayNodesTimestamp; @@ -78,6 +79,7 @@ static UIApplicationState __ApplicationState = UIApplicationStateActive; _rangeIsValid = YES; _currentRangeMode = ASLayoutRangeModeUnspecified; + _contentHasBeenScrolled = NO; _preserveCurrentRangeMode = NO; _previousScrollDirection = ASScrollDirectionDown | ASScrollDirectionRight; @@ -223,10 +225,6 @@ static UIApplicationState __ApplicationState = UIApplicationStateActive; 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 - } ASSignpostStart(ASSignpostRangeControllerUpdate); // Get the scroll direction. Default to using the previous one, if they're not scrolling. @@ -235,12 +233,28 @@ static UIApplicationState __ApplicationState = UIApplicationStateActive; scrollDirection = _previousScrollDirection; } _previousScrollDirection = scrollDirection; - + + if (visibleElements.count == 0) { // if we don't have any visibleNodes currently (scrolled before or after content)... + // Verify the actual state by checking the layout with a "VisibleOnly" range. + // This allows us to avoid thrashing through -didExitVisibleState in the case of -reloadData, since that generates didEndDisplayingCell calls. + // Those didEndDisplayingCell calls result in items being removed from the visibleElements returned by the _dataSource, even though the layout remains correct. + visibleElements = [_layoutController elementsForScrolling:scrollDirection rangeMode:ASLayoutRangeModeVisibleOnly rangeType:ASLayoutRangeTypeDisplay map:map]; + for (ASCollectionElement *element in visibleElements) { + [newVisibleNodes addObject:element.node]; + } + [self _setVisibleNodes:newVisibleNodes]; + return; // don't do anything for this update, but leave _rangeIsValid == NO to make sure we update it later + } + ASInterfaceState selfInterfaceState = [self interfaceState]; ASLayoutRangeMode rangeMode = _currentRangeMode; - // If the range mode is explicitly set via updateCurrentRangeWithMode: it will last in that mode until the - // range controller becomes visible again or explicitly changes the range mode again - if ((!_preserveCurrentRangeMode && ASInterfaceStateIncludesVisible(selfInterfaceState)) || [[self class] isFirstRangeUpdateForRangeMode:rangeMode]) { + BOOL updateRangeMode = (!_preserveCurrentRangeMode && _contentHasBeenScrolled); + + // If we've never scrolled before, we never update the range mode, so it doesn't jump into Full too early. + // This can happen if we have multiple, noisy updates occurring from application code before the user has engaged. + // If the range mode is explicitly set via updateCurrentRangeWithMode:, we'll preserve that for at least one update cycle. + // Once the user has scrolled and the range is visible, we'll always resume managing the range mode automatically. + if ((updateRangeMode && ASInterfaceStateIncludesVisible(selfInterfaceState)) || [[self class] isFirstRangeUpdateForRangeMode:rangeMode]) { rangeMode = [ASRangeController rangeModeForInterfaceState:selfInterfaceState currentRangeMode:_currentRangeMode]; } @@ -413,7 +427,7 @@ static UIApplicationState __ApplicationState = UIApplicationStateActive; // NSLog(@"custom: %@", visibleNodePathsSet); // } [modifiedIndexPaths sortUsingSelector:@selector(compare:)]; - NSLog(@"Range update complete; modifiedIndexPaths: %@", [self descriptionWithIndexPaths:modifiedIndexPaths]); + NSLog(@"Range update complete; modifiedIndexPaths: %@, rangeMode: %d", [self descriptionWithIndexPaths:modifiedIndexPaths], rangeMode); #endif ASSignpostEnd(ASSignpostRangeControllerUpdate); diff --git a/Source/Details/ASTraitCollection.h b/Source/Details/ASTraitCollection.h index 26714aa649..7f8ae3a476 100644 --- a/Source/Details/ASTraitCollection.h +++ b/Source/Details/ASTraitCollection.h @@ -17,6 +17,7 @@ #import + #import @class ASTraitCollection; @@ -27,14 +28,51 @@ NS_ASSUME_NONNULL_BEGIN ASDISPLAYNODE_EXTERN_C_BEGIN +#pragma mark - ASPrimitiveContentSizeCategory + +/** + * ASPrimitiveContentSizeCategory is a UIContentSizeCategory that can be used inside a struct. + * + * We need an unretained pointer because ARC can't manage struct memory. + * + * WARNING: DO NOT cast UIContentSizeCategory values to ASPrimitiveContentSizeCategory directly. + * Use ASPrimitiveContentSizeCategoryMake(UIContentSizeCategory) instead. + * This is because we make some assumptions about the lifetime of the object it points to. + * Also note that cast from ASPrimitiveContentSizeCategory to UIContentSizeCategory is always safe. + */ +typedef __unsafe_unretained UIContentSizeCategory ASPrimitiveContentSizeCategory; + +/** + * Safely casts from UIContentSizeCategory to ASPrimitiveContentSizeCategory. + * + * The UIKit documentation doesn't specify if we can receive a copy of the UIContentSizeCategory constant. While getting + * copies is fine with ARC, usage of unretained pointers requires us to ensure the lifetime of the object it points to. + * Manual retain&release of the UIContentSizeCategory object is not an option because it would require us to do that + * everywhere ASPrimitiveTraitCollection is used. This is error-prone and can lead to crashes and memory leaks. So, we + * explicitly limit possible values of ASPrimitiveContentSizeCategory to the predetermined set of global constants with + * known lifetime. + * + * @return a pointer to one of the UIContentSizeCategory constants. + */ +extern ASPrimitiveContentSizeCategory ASPrimitiveContentSizeCategoryMake(UIContentSizeCategory sizeCategory); + #pragma mark - ASPrimitiveTraitCollection typedef struct ASPrimitiveTraitCollection { - CGFloat displayScale; UIUserInterfaceSizeClass horizontalSizeClass; - UIUserInterfaceIdiom userInterfaceIdiom; UIUserInterfaceSizeClass verticalSizeClass; + + CGFloat displayScale; + UIDisplayGamut displayGamut; + + UIUserInterfaceIdiom userInterfaceIdiom; UIForceTouchCapability forceTouchCapability; + UITraitEnvironmentLayoutDirection layoutDirection; +#if TARGET_OS_TV + UIUserInterfaceStyle userInterfaceStyle; +#endif + + ASPrimitiveContentSizeCategory preferredContentSizeCategory; CGSize containerSize; } ASPrimitiveTraitCollection; @@ -124,11 +162,21 @@ ASDISPLAYNODE_EXTERN_C_END AS_SUBCLASSING_RESTRICTED @interface ASTraitCollection : NSObject -@property (nonatomic, assign, readonly) CGFloat displayScale; @property (nonatomic, assign, readonly) UIUserInterfaceSizeClass horizontalSizeClass; -@property (nonatomic, assign, readonly) UIUserInterfaceIdiom userInterfaceIdiom; @property (nonatomic, assign, readonly) UIUserInterfaceSizeClass verticalSizeClass; + +@property (nonatomic, assign, readonly) CGFloat displayScale; +@property (nonatomic, assign, readonly) UIDisplayGamut displayGamut; + +@property (nonatomic, assign, readonly) UIUserInterfaceIdiom userInterfaceIdiom; @property (nonatomic, assign, readonly) UIForceTouchCapability forceTouchCapability; +@property (nonatomic, assign, readonly) UITraitEnvironmentLayoutDirection layoutDirection; +#if TARGET_OS_TV +@property (nonatomic, assign, readonly) UIUserInterfaceStyle userInterfaceStyle; +#endif + +@property (nonatomic, assign, readonly) UIContentSizeCategory preferredContentSizeCategory; + @property (nonatomic, assign, readonly) CGSize containerSize; + (ASTraitCollection *)traitCollectionWithASPrimitiveTraitCollection:(ASPrimitiveTraitCollection)traits; @@ -136,18 +184,48 @@ AS_SUBCLASSING_RESTRICTED + (ASTraitCollection *)traitCollectionWithUITraitCollection:(UITraitCollection *)traitCollection containerSize:(CGSize)windowSize; ++ (ASTraitCollection *)traitCollectionWithUITraitCollection:(UITraitCollection *)traitCollection + containerSize:(CGSize)windowSize + fallbackContentSizeCategory:(UIContentSizeCategory)fallbackContentSizeCategory; -+ (ASTraitCollection *)traitCollectionWithDisplayScale:(CGFloat)displayScale - userInterfaceIdiom:(UIUserInterfaceIdiom)userInterfaceIdiom - horizontalSizeClass:(UIUserInterfaceSizeClass)horizontalSizeClass - verticalSizeClass:(UIUserInterfaceSizeClass)verticalSizeClass - forceTouchCapability:(UIForceTouchCapability)forceTouchCapability - containerSize:(CGSize)windowSize; - +#if TARGET_OS_TV ++ (ASTraitCollection *)traitCollectionWithHorizontalSizeClass:(UIUserInterfaceSizeClass)horizontalSizeClass + verticalSizeClass:(UIUserInterfaceSizeClass)verticalSizeClass + displayScale:(CGFloat)displayScale + displayGamut:(UIDisplayGamut)displayGamut + userInterfaceIdiom:(UIUserInterfaceIdiom)userInterfaceIdiom + forceTouchCapability:(UIForceTouchCapability)forceTouchCapability + layoutDirection:(UITraitEnvironmentLayoutDirection)layoutDirection + userInterfaceStyle:(UIUserInterfaceStyle)userInterfaceStyle + preferredContentSizeCategory:(UIContentSizeCategory)preferredContentSizeCategory + containerSize:(CGSize)windowSize; +#else ++ (ASTraitCollection *)traitCollectionWithHorizontalSizeClass:(UIUserInterfaceSizeClass)horizontalSizeClass + verticalSizeClass:(UIUserInterfaceSizeClass)verticalSizeClass + displayScale:(CGFloat)displayScale + displayGamut:(UIDisplayGamut)displayGamut + userInterfaceIdiom:(UIUserInterfaceIdiom)userInterfaceIdiom + forceTouchCapability:(UIForceTouchCapability)forceTouchCapability + layoutDirection:(UITraitEnvironmentLayoutDirection)layoutDirection + preferredContentSizeCategory:(UIContentSizeCategory)preferredContentSizeCategory + containerSize:(CGSize)windowSize; +#endif - (ASPrimitiveTraitCollection)primitiveTraitCollection; - (BOOL)isEqualToTraitCollection:(ASTraitCollection *)traitCollection; @end +@interface ASTraitCollection (Deprecated) + ++ (ASTraitCollection *)traitCollectionWithDisplayScale:(CGFloat)displayScale + userInterfaceIdiom:(UIUserInterfaceIdiom)userInterfaceIdiom + horizontalSizeClass:(UIUserInterfaceSizeClass)horizontalSizeClass + verticalSizeClass:(UIUserInterfaceSizeClass)verticalSizeClass + forceTouchCapability:(UIForceTouchCapability)forceTouchCapability + containerSize:(CGSize)windowSize + ASDISPLAYNODE_DEPRECATED_MSG("Use full version of this method instead."); + +@end + NS_ASSUME_NONNULL_END diff --git a/Source/Details/ASTraitCollection.m b/Source/Details/ASTraitCollection.m index 04eaea608e..dba756bafa 100644 --- a/Source/Details/ASTraitCollection.m +++ b/Source/Details/ASTraitCollection.m @@ -20,6 +20,60 @@ #import #import +#pragma mark - ASPrimitiveContentSizeCategory + +// UIContentSizeCategoryUnspecified is available only in iOS 10.0 and later. +// This is used for compatibility with older iOS versions. +ASDISPLAYNODE_INLINE UIContentSizeCategory AS_UIContentSizeCategoryUnspecified() { + if (AS_AVAILABLE_IOS(10)) { + return UIContentSizeCategoryUnspecified; + } else { + return @"_UICTContentSizeCategoryUnspecified"; + } +} + +ASPrimitiveContentSizeCategory ASPrimitiveContentSizeCategoryMake(UIContentSizeCategory sizeCategory) { + if ([sizeCategory isEqualToString:UIContentSizeCategoryExtraSmall]) { + return UIContentSizeCategoryExtraSmall; + } + if ([sizeCategory isEqualToString:UIContentSizeCategorySmall]) { + return UIContentSizeCategorySmall; + } + if ([sizeCategory isEqualToString:UIContentSizeCategoryMedium]) { + return UIContentSizeCategoryMedium; + } + if ([sizeCategory isEqualToString:UIContentSizeCategoryLarge]) { + return UIContentSizeCategoryLarge; + } + if ([sizeCategory isEqualToString:UIContentSizeCategoryExtraLarge]) { + return UIContentSizeCategoryExtraLarge; + } + if ([sizeCategory isEqualToString:UIContentSizeCategoryExtraExtraLarge]) { + return UIContentSizeCategoryExtraExtraLarge; + } + if ([sizeCategory isEqualToString:UIContentSizeCategoryExtraExtraExtraLarge]) { + return UIContentSizeCategoryExtraExtraExtraLarge; + } + + if ([sizeCategory isEqualToString:UIContentSizeCategoryAccessibilityMedium]) { + return UIContentSizeCategoryAccessibilityMedium; + } + if ([sizeCategory isEqualToString:UIContentSizeCategoryAccessibilityLarge]) { + return UIContentSizeCategoryAccessibilityLarge; + } + if ([sizeCategory isEqualToString:UIContentSizeCategoryAccessibilityExtraLarge]) { + return UIContentSizeCategoryAccessibilityExtraLarge; + } + if ([sizeCategory isEqualToString:UIContentSizeCategoryAccessibilityExtraExtraLarge]) { + return UIContentSizeCategoryAccessibilityExtraExtraLarge; + } + if ([sizeCategory isEqualToString:UIContentSizeCategoryAccessibilityExtraExtraExtraLarge]) { + return UIContentSizeCategoryAccessibilityExtraExtraExtraLarge; + } + + return AS_UIContentSizeCategoryUnspecified(); +} + #pragma mark - ASPrimitiveTraitCollection extern void ASTraitCollectionPropagateDown(id element, ASPrimitiveTraitCollection traitCollection) { @@ -32,63 +86,73 @@ extern void ASTraitCollectionPropagateDown(id element, ASPrimit } } -ASPrimitiveTraitCollection ASPrimitiveTraitCollectionMakeDefault() -{ +ASPrimitiveTraitCollection ASPrimitiveTraitCollectionMakeDefault() { return (ASPrimitiveTraitCollection) { // Default values can be defined in here + .displayGamut = UIDisplayGamutUnspecified, .userInterfaceIdiom = UIUserInterfaceIdiomUnspecified, + .layoutDirection = UITraitEnvironmentLayoutDirectionUnspecified, + .preferredContentSizeCategory = ASPrimitiveContentSizeCategoryMake(AS_UIContentSizeCategoryUnspecified()), .containerSize = CGSizeZero, }; } -ASPrimitiveTraitCollection ASPrimitiveTraitCollectionFromUITraitCollection(UITraitCollection *traitCollection) -{ +ASPrimitiveTraitCollection ASPrimitiveTraitCollectionFromUITraitCollection(UITraitCollection *traitCollection) { ASPrimitiveTraitCollection environmentTraitCollection = ASPrimitiveTraitCollectionMakeDefault(); - environmentTraitCollection.displayScale = traitCollection.displayScale; environmentTraitCollection.horizontalSizeClass = traitCollection.horizontalSizeClass; environmentTraitCollection.verticalSizeClass = traitCollection.verticalSizeClass; + environmentTraitCollection.displayScale = traitCollection.displayScale; environmentTraitCollection.userInterfaceIdiom = traitCollection.userInterfaceIdiom; - if (AS_AVAILABLE_IOS(9)) { - environmentTraitCollection.forceTouchCapability = traitCollection.forceTouchCapability; + environmentTraitCollection.forceTouchCapability = traitCollection.forceTouchCapability; + if (AS_AVAILABLE_IOS(10)) { + environmentTraitCollection.displayGamut = traitCollection.displayGamut; + environmentTraitCollection.layoutDirection = traitCollection.layoutDirection; + + // preferredContentSizeCategory is also available on older iOS versions, but only via UIApplication class. + // It should be noted that [UIApplication sharedApplication] is unavailable because Texture is built with only extension-safe API. + environmentTraitCollection.preferredContentSizeCategory = ASPrimitiveContentSizeCategoryMake(traitCollection.preferredContentSizeCategory); + + #if TARGET_OS_TV + environmentTraitCollection.userInterfaceStyle = traitCollection.userInterfaceStyle; + #endif } return environmentTraitCollection; } -BOOL ASPrimitiveTraitCollectionIsEqualToASPrimitiveTraitCollection(ASPrimitiveTraitCollection lhs, ASPrimitiveTraitCollection rhs) -{ +BOOL ASPrimitiveTraitCollectionIsEqualToASPrimitiveTraitCollection(ASPrimitiveTraitCollection lhs, ASPrimitiveTraitCollection rhs) { + UIContentSizeCategory leftSizeCategory = (UIContentSizeCategory)lhs.preferredContentSizeCategory; + UIContentSizeCategory rightSizeCategory = (UIContentSizeCategory)rhs.preferredContentSizeCategory; + return lhs.verticalSizeClass == rhs.verticalSizeClass && lhs.horizontalSizeClass == rhs.horizontalSizeClass && lhs.displayScale == rhs.displayScale && + lhs.displayGamut == rhs.displayGamut && lhs.userInterfaceIdiom == rhs.userInterfaceIdiom && lhs.forceTouchCapability == rhs.forceTouchCapability && + lhs.layoutDirection == rhs.layoutDirection && + #if TARGET_OS_TV + lhs.userInterfaceStyle == rhs.userInterfaceStyle && + #endif + + [leftSizeCategory isEqualToString:rightSizeCategory] && // Simple pointer comparison should be sufficient here + CGSizeEqualToSize(lhs.containerSize, rhs.containerSize); } // Named so as not to conflict with a hidden Apple function, in case compiler decides not to inline ASDISPLAYNODE_INLINE NSString *AS_NSStringFromUIUserInterfaceIdiom(UIUserInterfaceIdiom idiom) { - if (AS_AVAILABLE_IOS(9)) { - switch (idiom) { - case UIUserInterfaceIdiomTV: - return @"TV"; - case UIUserInterfaceIdiomPad: - return @"Pad"; - case UIUserInterfaceIdiomPhone: - return @"Phone"; - case UIUserInterfaceIdiomCarPlay: - return @"CarPlay"; - default: - return @"Unspecified"; - } - } else { - switch (idiom) { - case UIUserInterfaceIdiomPad: - return @"Pad"; - case UIUserInterfaceIdiomPhone: - return @"Phone"; - default: - return @"Unspecified"; - } + switch (idiom) { + case UIUserInterfaceIdiomTV: + return @"TV"; + case UIUserInterfaceIdiomPad: + return @"Pad"; + case UIUserInterfaceIdiomPhone: + return @"Phone"; + case UIUserInterfaceIdiomCarPlay: + return @"CarPlay"; + default: + return @"Unspecified"; } } @@ -116,14 +180,58 @@ ASDISPLAYNODE_INLINE NSString *AS_NSStringFromUIUserInterfaceSizeClass(UIUserInt } } -NSString *NSStringFromASPrimitiveTraitCollection(ASPrimitiveTraitCollection traits) -{ +// Named so as not to conflict with a hidden Apple function, in case compiler decides not to inline +ASDISPLAYNODE_INLINE NSString *AS_NSStringFromUIDisplayGamut(UIDisplayGamut displayGamut) { + switch (displayGamut) { + case UIDisplayGamutSRGB: + return @"sRGB"; + case UIDisplayGamutP3: + return @"P3"; + default: + return @"Unspecified"; + } +} + +// Named so as not to conflict with a hidden Apple function, in case compiler decides not to inline +ASDISPLAYNODE_INLINE NSString *AS_NSStringFromUITraitEnvironmentLayoutDirection(UITraitEnvironmentLayoutDirection layoutDirection) { + switch (layoutDirection) { + case UITraitEnvironmentLayoutDirectionLeftToRight: + return @"LeftToRight"; + case UITraitEnvironmentLayoutDirectionRightToLeft: + return @"RightToLeft"; + default: + return @"Unspecified"; + } +} + +#if TARGET_OS_TV +// Named so as not to conflict with a hidden Apple function, in case compiler decides not to inline +ASDISPLAYNODE_INLINE NSString *AS_NSStringFromUIUserInterfaceStyle(UIUserInterfaceStyle userInterfaceStyle) { + switch (userInterfaceStyle) { + case UIUserInterfaceStyleLight: + return @"Light"; + case UIUserInterfaceStyleDark: + return @"Dark"; + default: + return @"Unspecified"; + } +} +#endif + +NSString *NSStringFromASPrimitiveTraitCollection(ASPrimitiveTraitCollection traits) { NSMutableArray *props = [NSMutableArray array]; - [props addObject:@{ @"userInterfaceIdiom": AS_NSStringFromUIUserInterfaceIdiom(traits.userInterfaceIdiom) }]; - [props addObject:@{ @"containerSize": NSStringFromCGSize(traits.containerSize) }]; - [props addObject:@{ @"horizontalSizeClass": AS_NSStringFromUIUserInterfaceSizeClass(traits.horizontalSizeClass) }]; [props addObject:@{ @"verticalSizeClass": AS_NSStringFromUIUserInterfaceSizeClass(traits.verticalSizeClass) }]; + [props addObject:@{ @"horizontalSizeClass": AS_NSStringFromUIUserInterfaceSizeClass(traits.horizontalSizeClass) }]; + [props addObject:@{ @"displayScale": [NSString stringWithFormat: @"%.0lf", (double)traits.displayScale] }]; + [props addObject:@{ @"displayGamut": AS_NSStringFromUIDisplayGamut(traits.displayGamut) }]; + [props addObject:@{ @"userInterfaceIdiom": AS_NSStringFromUIUserInterfaceIdiom(traits.userInterfaceIdiom) }]; [props addObject:@{ @"forceTouchCapability": AS_NSStringFromUIForceTouchCapability(traits.forceTouchCapability) }]; + [props addObject:@{ @"layoutDirection": AS_NSStringFromUITraitEnvironmentLayoutDirection(traits.layoutDirection) }]; + #if TARGET_OS_TV + [props addObject:@{ @"userInterfaceStyle": AS_NSStringFromUIUserInterfaceStyle(traits.userInterfaceStyle) }]; + #endif + [props addObject:@{ @"preferredContentSizeCategory": (UIContentSizeCategory)traits.preferredContentSizeCategory }]; + [props addObject:@{ @"containerSize": NSStringFromCGSize(traits.containerSize) }]; return ASObjectDescriptionMakeWithoutObject(props); } @@ -131,73 +239,238 @@ NSString *NSStringFromASPrimitiveTraitCollection(ASPrimitiveTraitCollection trai @implementation ASTraitCollection -- (instancetype)initWithDisplayScale:(CGFloat)displayScale - userInterfaceIdiom:(UIUserInterfaceIdiom)userInterfaceIdiom - horizontalSizeClass:(UIUserInterfaceSizeClass)horizontalSizeClass - verticalSizeClass:(UIUserInterfaceSizeClass)verticalSizeClass - forceTouchCapability:(UIForceTouchCapability)forceTouchCapability - containerSize:(CGSize)windowSize +#if TARGET_OS_TV + +- (instancetype)initWithHorizontalSizeClass:(UIUserInterfaceSizeClass)horizontalSizeClass + verticalSizeClass:(UIUserInterfaceSizeClass)verticalSizeClass + displayScale:(CGFloat)displayScale + displayGamut:(UIDisplayGamut)displayGamut + userInterfaceIdiom:(UIUserInterfaceIdiom)userInterfaceIdiom + forceTouchCapability:(UIForceTouchCapability)forceTouchCapability + layoutDirection:(UITraitEnvironmentLayoutDirection)layoutDirection + userInterfaceStyle:(UIUserInterfaceStyle)userInterfaceStyle + preferredContentSizeCategory:(UIContentSizeCategory)preferredContentSizeCategory + containerSize:(CGSize)windowSize { self = [super init]; if (self) { - _displayScale = displayScale; - _userInterfaceIdiom = userInterfaceIdiom; _horizontalSizeClass = horizontalSizeClass; _verticalSizeClass = verticalSizeClass; + _displayScale = displayScale; + _displayGamut = displayGamut; + _userInterfaceIdiom = userInterfaceIdiom; _forceTouchCapability = forceTouchCapability; + _layoutDirection = layoutDirection; + _userInterfaceStyle = userInterfaceStyle; + _preferredContentSizeCategory = preferredContentSizeCategory; _containerSize = windowSize; } return self; } -+ (instancetype)traitCollectionWithDisplayScale:(CGFloat)displayScale - userInterfaceIdiom:(UIUserInterfaceIdiom)userInterfaceIdiom - horizontalSizeClass:(UIUserInterfaceSizeClass)horizontalSizeClass - verticalSizeClass:(UIUserInterfaceSizeClass)verticalSizeClass - forceTouchCapability:(UIForceTouchCapability)forceTouchCapability - containerSize:(CGSize)windowSize ++ (instancetype)traitCollectionWithHorizontalSizeClass:(UIUserInterfaceSizeClass)horizontalSizeClass + verticalSizeClass:(UIUserInterfaceSizeClass)verticalSizeClass + displayScale:(CGFloat)displayScale + displayGamut:(UIDisplayGamut)displayGamut + userInterfaceIdiom:(UIUserInterfaceIdiom)userInterfaceIdiom + forceTouchCapability:(UIForceTouchCapability)forceTouchCapability + layoutDirection:(UITraitEnvironmentLayoutDirection)layoutDirection + userInterfaceStyle:(UIUserInterfaceStyle)userInterfaceStyle + preferredContentSizeCategory:(UIContentSizeCategory)preferredContentSizeCategory + containerSize:(CGSize)windowSize { - return [[self alloc] initWithDisplayScale:displayScale - userInterfaceIdiom:userInterfaceIdiom - horizontalSizeClass:horizontalSizeClass - verticalSizeClass:verticalSizeClass - forceTouchCapability:forceTouchCapability - containerSize:windowSize]; + return [[self alloc] initWithHorizontalSizeClass:horizontalSizeClass + verticalSizeClass:verticalSizeClass + displayScale:displayScale + displayGamut:displayGamut + userInterfaceIdiom:userInterfaceIdiom + forceTouchCapability:forceTouchCapability + layoutDirection:layoutDirection + userInterfaceStyle:userIntefaceStyle + preferredContentSizeCategory:preferredContentSizeCategory + containerSize:windowSize]; +} + +#else + +- (instancetype)initWithHorizontalSizeClass:(UIUserInterfaceSizeClass)horizontalSizeClass + verticalSizeClass:(UIUserInterfaceSizeClass)verticalSizeClass + displayScale:(CGFloat)displayScale + displayGamut:(UIDisplayGamut)displayGamut + userInterfaceIdiom:(UIUserInterfaceIdiom)userInterfaceIdiom + forceTouchCapability:(UIForceTouchCapability)forceTouchCapability + layoutDirection:(UITraitEnvironmentLayoutDirection)layoutDirection + preferredContentSizeCategory:(UIContentSizeCategory)preferredContentSizeCategory + containerSize:(CGSize)windowSize +{ + self = [super init]; + if (self) { + _horizontalSizeClass = horizontalSizeClass; + _verticalSizeClass = verticalSizeClass; + _displayScale = displayScale; + _displayGamut = displayGamut; + _userInterfaceIdiom = userInterfaceIdiom; + _forceTouchCapability = forceTouchCapability; + _layoutDirection = layoutDirection; + _preferredContentSizeCategory = preferredContentSizeCategory; + _containerSize = windowSize; + } + return self; +} + ++ (instancetype)traitCollectionWithHorizontalSizeClass:(UIUserInterfaceSizeClass)horizontalSizeClass + verticalSizeClass:(UIUserInterfaceSizeClass)verticalSizeClass + displayScale:(CGFloat)displayScale + displayGamut:(UIDisplayGamut)displayGamut + userInterfaceIdiom:(UIUserInterfaceIdiom)userInterfaceIdiom + forceTouchCapability:(UIForceTouchCapability)forceTouchCapability + layoutDirection:(UITraitEnvironmentLayoutDirection)layoutDirection + preferredContentSizeCategory:(UIContentSizeCategory)preferredContentSizeCategory + containerSize:(CGSize)windowSize +{ + return [[self alloc] initWithHorizontalSizeClass:horizontalSizeClass + verticalSizeClass:verticalSizeClass + displayScale:displayScale + displayGamut:displayGamut + userInterfaceIdiom:userInterfaceIdiom + forceTouchCapability:forceTouchCapability + layoutDirection:layoutDirection + preferredContentSizeCategory:preferredContentSizeCategory + containerSize:windowSize]; +} + +#endif + ++ (ASTraitCollection *)traitCollectionWithDisplayScale:(CGFloat)displayScale + userInterfaceIdiom:(UIUserInterfaceIdiom)userInterfaceIdiom + horizontalSizeClass:(UIUserInterfaceSizeClass)horizontalSizeClass + verticalSizeClass:(UIUserInterfaceSizeClass)verticalSizeClass + forceTouchCapability:(UIForceTouchCapability)forceTouchCapability + containerSize:(CGSize)windowSize +{ +#if TARGET_OS_TV + return [self traitCollectionWithHorizontalSizeClass:horizontalSizeClass + verticalSizeClass:verticalSizeClass + displayScale:displayScale + displayGamut:UIDisplayGamutUnspecified + userInterfaceIdiom:userInterfaceIdiom + forceTouchCapability:forceTouchCapability + layoutDirection:UITraitEnvironmentLayoutDirectionUnspecified + userInterfaceStyle:UIUserInterfaceStyleUnspecified + preferredContentSizeCategory:AS_UIContentSizeCategoryUnspecified() + containerSize:windowSize]; +#else + return [self traitCollectionWithHorizontalSizeClass:horizontalSizeClass + verticalSizeClass:verticalSizeClass + displayScale:displayScale + displayGamut:UIDisplayGamutUnspecified + userInterfaceIdiom:userInterfaceIdiom + forceTouchCapability:forceTouchCapability + layoutDirection:UITraitEnvironmentLayoutDirectionUnspecified + preferredContentSizeCategory:AS_UIContentSizeCategoryUnspecified() + containerSize:windowSize]; +#endif } + (instancetype)traitCollectionWithASPrimitiveTraitCollection:(ASPrimitiveTraitCollection)traits { - return [self traitCollectionWithDisplayScale:traits.displayScale - userInterfaceIdiom:traits.userInterfaceIdiom - horizontalSizeClass:traits.horizontalSizeClass - verticalSizeClass:traits.verticalSizeClass - forceTouchCapability:traits.forceTouchCapability - containerSize:traits.containerSize]; +#if TARGET_OS_TV + return [self traitCollectionWithHorizontalSizeClass:traits.horizontalSizeClass + verticalSizeClass:traits.verticalSizeClass + displayScale:traits.displayScale + displayGamut:traits.displayGamut + userInterfaceIdiom:traits.userInterfaceIdiom + forceTouchCapability:traits.forceTouchCapability + layoutDirection:traits.layoutDirection + userInterfaceStyle:traits.userInterfaceStyle + preferredContentSizeCategory:(UIContentSizeCategory)traits.preferredContentSizeCategory + containerSize:traits.containerSize]; +#else + return [self traitCollectionWithHorizontalSizeClass:traits.horizontalSizeClass + verticalSizeClass:traits.verticalSizeClass + displayScale:traits.displayScale + displayGamut:traits.displayGamut + userInterfaceIdiom:traits.userInterfaceIdiom + forceTouchCapability:traits.forceTouchCapability + layoutDirection:traits.layoutDirection + preferredContentSizeCategory:(UIContentSizeCategory)traits.preferredContentSizeCategory + containerSize:traits.containerSize]; +#endif } + (instancetype)traitCollectionWithUITraitCollection:(UITraitCollection *)traitCollection - containerSize:(CGSize)windowSize + containerSize:(CGSize)windowSize { - UIForceTouchCapability forceTouch = UIForceTouchCapabilityUnknown; - if(AS_AVAILABLE_IOS(9)) { - forceTouch = traitCollection.forceTouchCapability; + return [self traitCollectionWithUITraitCollection:traitCollection + containerSize:windowSize + fallbackContentSizeCategory:AS_UIContentSizeCategoryUnspecified()]; +} + + ++ (instancetype)traitCollectionWithUITraitCollection:(UITraitCollection *)traitCollection + containerSize:(CGSize)windowSize + fallbackContentSizeCategory:(UIContentSizeCategory)fallbackContentSizeCategory +{ + UIDisplayGamut displayGamut; + UITraitEnvironmentLayoutDirection layoutDirection; + UIContentSizeCategory sizeCategory; + #if TARGET_OS_TV + UIUserInterfaceStyle userInterfaceStyle; + #endif + if (AS_AVAILABLE_IOS(10)) { + displayGamut = traitCollection.displayGamut; + layoutDirection = traitCollection.layoutDirection; + sizeCategory = traitCollection.preferredContentSizeCategory; + #if TARGET_OS_TV + userInterfaceStyle = traitCollection.userInterfaceStyle; + #endif + } else { + displayGamut = UIDisplayGamutUnspecified; + layoutDirection = UITraitEnvironmentLayoutDirectionUnspecified; + sizeCategory = fallbackContentSizeCategory; + #if TARGET_OS_TV + userInterfaceStyle = UIUserInterfaceStyleUnspecified; + #endif } - return [self traitCollectionWithDisplayScale:traitCollection.displayScale - userInterfaceIdiom:traitCollection.userInterfaceIdiom - horizontalSizeClass:traitCollection.horizontalSizeClass - verticalSizeClass:traitCollection.verticalSizeClass - forceTouchCapability:forceTouch - containerSize:windowSize]; + +#if TARGET_OS_TV + return [self traitCollectionWithHorizontalSizeClass:traitCollection.horizontalSizeClass + verticalSizeClass:traitCollection.verticalSizeClass + displayScale:traitCollection.displayScale + displayGamut:displayGamut + userInterfaceIdiom:traitCollection.userInterfaceIdiom + forceTouchCapability:traitCollection.forceTouchCapability + layoutDirection:layoutDirection + userInterfaceStyle:userInterfaceStyle + preferredContentSizeCategory:sizeCategory + containerSize:windowSize]; +#else + return [self traitCollectionWithHorizontalSizeClass:traitCollection.horizontalSizeClass + verticalSizeClass:traitCollection.verticalSizeClass + displayScale:traitCollection.displayScale + displayGamut:displayGamut + userInterfaceIdiom:traitCollection.userInterfaceIdiom + forceTouchCapability:traitCollection.forceTouchCapability + layoutDirection:layoutDirection + preferredContentSizeCategory:sizeCategory + containerSize:windowSize]; +#endif } - (ASPrimitiveTraitCollection)primitiveTraitCollection { return (ASPrimitiveTraitCollection) { - .displayScale = self.displayScale, .horizontalSizeClass = self.horizontalSizeClass, - .userInterfaceIdiom = self.userInterfaceIdiom, .verticalSizeClass = self.verticalSizeClass, + .displayScale = self.displayScale, + .displayGamut = self.displayGamut, + .userInterfaceIdiom = self.userInterfaceIdiom, .forceTouchCapability = self.forceTouchCapability, + .layoutDirection = self.layoutDirection, + #if TARGET_OS_TV + .userInterfaceStyle = self.userInterfaceStyle, + #endif + .preferredContentSizeCategory = ASPrimitiveContentSizeCategoryMake(self.preferredContentSizeCategory), .containerSize = self.containerSize, }; } @@ -208,12 +481,19 @@ NSString *NSStringFromASPrimitiveTraitCollection(ASPrimitiveTraitCollection trai return YES; } - return self.displayScale == traitCollection.displayScale && - self.horizontalSizeClass == traitCollection.horizontalSizeClass && - self.verticalSizeClass == traitCollection.verticalSizeClass && - self.userInterfaceIdiom == traitCollection.userInterfaceIdiom && - CGSizeEqualToSize(self.containerSize, traitCollection.containerSize) && - self.forceTouchCapability == traitCollection.forceTouchCapability; + return + self.horizontalSizeClass == traitCollection.horizontalSizeClass && + self.verticalSizeClass == traitCollection.verticalSizeClass && + self.displayScale == traitCollection.displayScale && + self.displayGamut == traitCollection.displayGamut && + self.userInterfaceIdiom == traitCollection.userInterfaceIdiom && + self.forceTouchCapability == traitCollection.forceTouchCapability && + self.layoutDirection == traitCollection.layoutDirection && + #if TARGET_OS_TV + self.userInterfaceStyle == traitCollection.userInterfaceStyle && + #endif + [self.preferredContentSizeCategory isEqualToString:traitCollection.preferredContentSizeCategory] && + CGSizeEqualToSize(self.containerSize, traitCollection.containerSize); } @end diff --git a/Source/Details/_ASDisplayLayer.h b/Source/Details/_ASDisplayLayer.h index 11ed695781..97c1e9489d 100644 --- a/Source/Details/_ASDisplayLayer.h +++ b/Source/Details/_ASDisplayLayer.h @@ -19,6 +19,8 @@ #import #import +NS_ASSUME_NONNULL_BEGIN + @class ASDisplayNode; @protocol _ASDisplayLayerDelegate; @@ -28,7 +30,7 @@ @discussion This property overrides the CALayer category method which implements this via associated objects. This should result in much better performance for _ASDisplayLayers. */ -@property (nonatomic, weak) ASDisplayNode *asyncdisplaykit_node; +@property (nullable, nonatomic, weak) ASDisplayNode *asyncdisplaykit_node; /** @summary Set to YES to enable asynchronous display for the receiver. @@ -57,7 +59,7 @@ @desc The asyncDelegate will have the opportunity to override the methods related to async display. */ -@property (atomic, weak) id<_ASDisplayLayerDelegate> asyncDelegate; +@property (nullable, atomic, weak) id<_ASDisplayLayerDelegate> asyncDelegate; /** @summary Suspends both asynchronous and synchronous display of the receiver if YES. @@ -109,7 +111,10 @@ @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:(nullable id)parameters + isCancelled:(AS_NOESCAPE asdisplaynode_iscancelled_block_t)isCancelledBlock + isRasterizing:(BOOL)isRasterizing; /** @summary Delegate override to provide new layer contents as a UIImage. @@ -117,7 +122,8 @@ @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. @return 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. */ -+ (UIImage *)displayWithParameters:(id)parameters isCancelled:(AS_NOESCAPE asdisplaynode_iscancelled_block_t)isCancelledBlock; ++ (UIImage *)displayWithParameters:(nullable id)parameters + isCancelled:(AS_NOESCAPE asdisplaynode_iscancelled_block_t)isCancelledBlock; // Called on the main thread only @@ -147,3 +153,5 @@ - (void)cancelDisplayAsyncLayer:(_ASDisplayLayer *)asyncLayer; @end + +NS_ASSUME_NONNULL_END diff --git a/Source/Details/_ASDisplayView.h b/Source/Details/_ASDisplayView.h index c4968d183c..578dd8d582 100644 --- a/Source/Details/_ASDisplayView.h +++ b/Source/Details/_ASDisplayView.h @@ -17,11 +17,21 @@ #import +NS_ASSUME_NONNULL_BEGIN + // This class is only for use by ASDisplayNode and should never be subclassed or used directly. // Note that the "node" property is added to UIView directly via a category in ASDisplayNode. +@class ASDisplayNode; + @interface _ASDisplayView : UIView +/** + @discussion This property overrides the UIView category method which implements this via associated objects. + This should result in much better performance for _ASDisplayView. + */ +@property (nullable, nonatomic, weak) ASDisplayNode *asyncdisplaykit_node; + // These methods expose a way for ASDisplayNode touch events to let the view call super touch events // Some UIKit mechanisms, like UITableView and UICollectionView selection handling, require this to work - (void)__forwardTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event; @@ -30,3 +40,5 @@ - (void)__forwardTouchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event; @end + +NS_ASSUME_NONNULL_END diff --git a/Source/Details/_ASDisplayView.mm b/Source/Details/_ASDisplayView.mm index 2c2d2c383f..aa2c43eca3 100644 --- a/Source/Details/_ASDisplayView.mm +++ b/Source/Details/_ASDisplayView.mm @@ -27,7 +27,6 @@ #import @interface _ASDisplayView () -@property (nullable, atomic, weak, readwrite) ASDisplayNode *asyncdisplaykit_node; // Keep the node alive while its view is active. If you create a view, add its layer to a layer hierarchy, then release // the view, the layer retains the view to prevent a crash. This replicates this behaviour for the node abstraction. diff --git a/Source/Layout/ASLayoutElement.h b/Source/Layout/ASLayoutElement.h index ca56c67222..e84c545695 100644 --- a/Source/Layout/ASLayoutElement.h +++ b/Source/Layout/ASLayoutElement.h @@ -192,7 +192,7 @@ extern NSString * const ASLayoutElementStyleLayoutPositionProperty; #pragma mark - Sizing /** - * @abstract The width property specifies the height of the content area of an ASLayoutElement. + * @abstract The width property specifies the width of the content area of an ASLayoutElement. * The minWidth and maxWidth properties override width. * Defaults to ASDimensionAuto */ diff --git a/Source/Private/ASCollectionLayout.mm b/Source/Private/ASCollectionLayout.mm index c02c6b5cd5..72dde7eb55 100644 --- a/Source/Private/ASCollectionLayout.mm +++ b/Source/Private/ASCollectionLayout.mm @@ -160,6 +160,21 @@ static const ASScrollDirection kASStaticScrollDirection = (ASScrollDirectionRigh } } +/** + * NOTE: It is suggested practice on the Web to override invalidationContextForInteractivelyMovingItems… and call out to the + * data source to move the item (so that if e.g. the item size depends on the data, you get the data you expect). However, as of iOS 11 this + * doesn't work, because UICV machinery will also call out to the data source to move the item after the interaction is done. The result is + * that your data source state will be incorrect due to this last move call. Plus it's just an API violation. + * + * Things tried: + * - Doing the speculative data source moves, and then UNDOING the last one in invalidationContextForEndingInteractiveMovementOfItems… + * but this does not work because the UICV machinery informs its data source before it calls that method on us, so we are too late. + * + * The correct practice is to use the UIDataSourceTranslating API introduced in iOS 11. Currently Texture does not support this API but we can + * build it if there is demand. We could add an id field onto the layout context object, and the layout client can + * use data source index paths when it reads nodes or other data source data. + */ + - (CGSize)collectionViewContentSize { ASDisplayNodeAssertMainThread(); diff --git a/Source/Private/ASDisplayNode+AsyncDisplay.mm b/Source/Private/ASDisplayNode+AsyncDisplay.mm index 963a542f85..96426d479a 100644 --- a/Source/Private/ASDisplayNode+AsyncDisplay.mm +++ b/Source/Private/ASDisplayNode+AsyncDisplay.mm @@ -21,168 +21,12 @@ #import #import #import +#import #import #import #import -@interface ASDrawingContext : NSObject - -@property (nonatomic, readonly) CGFloat scale; -@property (nonatomic, readonly) int32_t bytesPerRow; -@property (nonatomic, readonly) void *bytes; - -- (instancetype)initWithSize:(CGSize)size scale:(CGFloat)scale opaque:(bool)opaque; -- (UIImage *)generateImage; -- (void)withContext:(void (^)(CGContextRef))f; -- (void)withFlippedContext:(void (^)(CGContextRef))f; -- (void)pushCurrent; -- (void)popCurrent; - -@end - -static void DrawingContextDataProviderReleaseDataCallback(void *info, __unused const void *data, __unused size_t size) { - free(info); -} - -@interface ASDrawingContext () { - CGSize _size; - CGSize _scaledSize; - CGBitmapInfo _bitmapInfo; - int32_t _length; - CGDataProviderRef _provider; - - CGContextRef _context; - bool _didPush; -} - -@end - -@implementation ASDrawingContext - -- (instancetype)initWithSize:(CGSize)size scale:(CGFloat)scale opaque:(bool)opaque { - self = [super init]; - if (self != nil) { - ASDisplayNodeAssert(scale > 0.0f, @"scale > 0.0f"); - _size = size; - _scale = scale; - _scaledSize = CGSizeMake(size.width * _scale, size.height * _scale); - - _bytesPerRow = (4 * ((int32_t)(_scaledSize.width)) + 15) & (~15); - _length = _bytesPerRow * ((int32_t)(_scaledSize.height)); - - _bitmapInfo = kCGBitmapByteOrder32Little | (opaque ? kCGImageAlphaNoneSkipFirst : kCGImageAlphaPremultipliedFirst); - - _bytes = malloc(_length); - memset(_bytes, 0, _length); - _provider = CGDataProviderCreateWithData(_bytes, _bytes, _length, &DrawingContextDataProviderReleaseDataCallback); - } - return self; -} - -- (void)dealloc { - if (_context != nil) { - CGContextRelease(_context); - } - if (_provider != nil) { - CGDataProviderRelease(_provider); - } -} - -- (void)withContext:(void (^)(CGContextRef))f { - if (_context == nil) { - static CGColorSpaceRef deviceColorSpace = NULL; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - deviceColorSpace = CGColorSpaceCreateDeviceRGB(); - }); - _context = CGBitmapContextCreate(_bytes, (int32_t)_scaledSize.width, (int32_t)_scaledSize.height, 8, _bytesPerRow, deviceColorSpace, _bitmapInfo); - if (_context != nil) { - CGContextScaleCTM(_context, _scale, _scale); - } - } - if (_context != nil) { - CGContextTranslateCTM(_context, _size.width / 2.0f, _size.height / 2.0f); - CGContextScaleCTM(_context, 1.0f, -1.0f); - CGContextTranslateCTM(_context, -_size.width / 2.0f, -_size.height / 2.0f); - - if (f) { - f(_context); - } - - CGContextTranslateCTM(_context, _size.width / 2.0f, _size.height / 2.0f); - CGContextScaleCTM(_context, 1.0f, -1.0f); - CGContextTranslateCTM(_context, -_size.width / 2.0f, -_size.height / 2.0f); - } -} - -- (void)withFlippedContext:(void (^)(CGContextRef))f { - if (_context == nil) { - static CGColorSpaceRef deviceColorSpace = NULL; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - deviceColorSpace = CGColorSpaceCreateDeviceRGB(); - }); - _context = CGBitmapContextCreate(_bytes, (int32_t)_scaledSize.width, (int32_t)_scaledSize.height, 8, _bytesPerRow, deviceColorSpace, _bitmapInfo); - if (_context != nil) { - CGContextScaleCTM(_context, _scale, _scale); - } - } - if (_context != nil) { - if (f) { - f(_context); - } - } -} - -- (void)pushCurrent { - if (_context == nil) { - static CGColorSpaceRef deviceColorSpace = NULL; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - deviceColorSpace = CGColorSpaceCreateDeviceRGB(); - }); - _context = CGBitmapContextCreate(_bytes, (int32_t)_scaledSize.width, (int32_t)_scaledSize.height, 8, _bytesPerRow, deviceColorSpace, _bitmapInfo); - if (_context != nil) { - CGContextScaleCTM(_context, _scale, _scale); - } - } - - if (_context != nil) { - CGContextTranslateCTM(_context, _size.width / 2.0f, _size.height / 2.0f); - CGContextScaleCTM(_context, 1.0f, -1.0f); - CGContextTranslateCTM(_context, -_size.width / 2.0f, -_size.height / 2.0f); - - UIGraphicsPushContext(_context); - _didPush = true; - } -} - -- (void)popCurrent { - if (_context != nil && _didPush) { - _didPush = false; - UIGraphicsPopContext(); - } -} - -- (UIImage *)generateImage { - static CGColorSpaceRef deviceColorSpace = NULL; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - deviceColorSpace = CGColorSpaceCreateDeviceRGB(); - }); - CGImageRef image = CGImageCreate((int32_t)_scaledSize.width, (int32_t)_scaledSize.height, 8, 32, _bytesPerRow, deviceColorSpace, _bitmapInfo, _provider, nil, false, kCGRenderingIntentDefault); - if (image != nil) { - UIImage *uiImage = [[UIImage alloc] initWithCGImage:image scale:_scale orientation:UIImageOrientationUp]; - CGImageRelease(image); - return uiImage; - } else { - return nil; - } -} - -@end - @interface ASDisplayNode () <_ASDisplayLayerDelegate> @end @@ -375,25 +219,14 @@ static void DrawingContextDataProviderReleaseDataCallback(void *info, __unused c displayBlock = ^id{ CHECK_CANCELLED_AND_RETURN_NIL(); - ASDrawingContext *context = [[ASDrawingContext alloc] initWithSize:bounds.size scale:contentsScaleForDisplay opaque:opaque]; - [context pushCurrent]; - - for (dispatch_block_t block in displayBlocks) { - CHECK_CANCELLED_AND_RETURN_NIL([context popCurrent]); - block(); - } - [context popCurrent]; - UIImage *image = [context generateImage]; - - /*UIGraphicsBeginImageContextWithOptions(bounds.size, opaque, contentsScaleForDisplay); + ASGraphicsBeginImageContextWithOptions(bounds.size, opaque, contentsScaleForDisplay); for (dispatch_block_t block in displayBlocks) { - CHECK_CANCELLED_AND_RETURN_NIL(UIGraphicsEndImageContext()); + CHECK_CANCELLED_AND_RETURN_NIL(ASGraphicsEndImageContext()); block(); } - UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext();*/ + UIImage *image = ASGraphicsGetImageAndEndCurrentContext(); ASDN_DELAY_FOR_DISPLAY(); return image; @@ -402,13 +235,9 @@ static void DrawingContextDataProviderReleaseDataCallback(void *info, __unused c displayBlock = ^id{ CHECK_CANCELLED_AND_RETURN_NIL(); - ASDrawingContext *context = nil; if (shouldCreateGraphicsContext) { - //UIGraphicsBeginImageContextWithOptions(bounds.size, opaque, contentsScaleForDisplay); - //CHECK_CANCELLED_AND_RETURN_NIL( UIGraphicsEndImageContext(); ); - context = [[ASDrawingContext alloc] initWithSize:bounds.size scale:contentsScaleForDisplay opaque:opaque]; - [context pushCurrent]; - CHECK_CANCELLED_AND_RETURN_NIL( [context popCurrent]; ); + ASGraphicsBeginImageContextWithOptions(bounds.size, opaque, contentsScaleForDisplay); + CHECK_CANCELLED_AND_RETURN_NIL( ASGraphicsEndImageContext(); ); } CGContextRef currentContext = UIGraphicsGetCurrentContext(); @@ -427,13 +256,8 @@ static void DrawingContextDataProviderReleaseDataCallback(void *info, __unused c [self __didDisplayNodeContentWithRenderingContext:currentContext image:&image drawParameters:drawParameters backgroundColor:backgroundColor borderWidth:borderWidth borderColor:borderColor]; if (shouldCreateGraphicsContext) { - CHECK_CANCELLED_AND_RETURN_NIL( [context popCurrent]; ); - [context popCurrent]; - image = [context generateImage]; - context = nil; - //CHECK_CANCELLED_AND_RETURN_NIL( UIGraphicsEndImageContext(); ); - //image = UIGraphicsGetImageFromCurrentImageContext(); - //UIGraphicsEndImageContext(); + CHECK_CANCELLED_AND_RETURN_NIL( ASGraphicsEndImageContext(); ); + image = ASGraphicsGetImageAndEndCurrentContext(); } ASDN_DELAY_FOR_DISPLAY(); @@ -507,7 +331,7 @@ static void DrawingContextDataProviderReleaseDataCallback(void *info, __unused c bounds.size.height *= contentsScale; CGFloat white = 0.0f, alpha = 0.0f; [backgroundColor getWhite:&white alpha:&alpha]; - UIGraphicsBeginImageContextWithOptions(bounds.size, (alpha == 1.0f), contentsScale); + ASGraphicsBeginImageContextWithOptions(bounds.size, (alpha == 1.0f), contentsScale); [*image drawInRect:bounds]; } else { bounds = CGContextGetClipBoundingBox(context); @@ -537,8 +361,7 @@ static void DrawingContextDataProviderReleaseDataCallback(void *info, __unused c [roundedPath stroke]; // Won't do anything if borderWidth is 0 and roundedPath is nil. if (*image) { - *image = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); + *image = ASGraphicsGetImageAndEndCurrentContext(); } } } diff --git a/Source/Private/ASDisplayNode+FrameworkPrivate.h b/Source/Private/ASDisplayNode+FrameworkPrivate.h index dbb5207879..b309bdcc22 100644 --- a/Source/Private/ASDisplayNode+FrameworkPrivate.h +++ b/Source/Private/ASDisplayNode+FrameworkPrivate.h @@ -141,6 +141,10 @@ __unused static NSString * _Nonnull NSStringFromASHierarchyStateChange(ASHierarc // delegate to inform of ASInterfaceState changes (used by ASNodeController) @property (nonatomic, weak) id interfaceStateDelegate; +// The -pendingInterfaceState holds the value that will be applied to -interfaceState by the +// ASCATransactionQueue. If already applied, it matches -interfaceState. Thread-safe access. +@property (nonatomic, readonly) ASInterfaceState pendingInterfaceState; + // These methods are recursive, and either union or remove the provided interfaceState to all sub-elements. - (void)enterInterfaceState:(ASInterfaceState)interfaceState; - (void)exitInterfaceState:(ASInterfaceState)interfaceState; diff --git a/Source/Private/ASDisplayNode+UIViewBridge.mm b/Source/Private/ASDisplayNode+UIViewBridge.mm index 491fb38941..a53a2e70ad 100644 --- a/Source/Private/ASDisplayNode+UIViewBridge.mm +++ b/Source/Private/ASDisplayNode+UIViewBridge.mm @@ -186,17 +186,12 @@ if (shouldApply) { _layer.layerProperty = (layerValueExpr); } else { ASDisplayNo - (CGFloat)cornerRadius { ASDN::MutexLocker l(__instanceLock__); - if (_cornerRoundingType == ASCornerRoundingTypeDefaultSlowCALayer) { - return self.layerCornerRadius; - } else { - return _cornerRadius; - } + return _cornerRadius; } - (void)setCornerRadius:(CGFloat)newCornerRadius { - ASDN::MutexLocker l(__instanceLock__); - [self updateCornerRoundingWithType:_cornerRoundingType cornerRadius:newCornerRadius]; + [self updateCornerRoundingWithType:self.cornerRoundingType cornerRadius:newCornerRadius]; } - (ASCornerRoundingType)cornerRoundingType @@ -207,8 +202,7 @@ if (shouldApply) { _layer.layerProperty = (layerValueExpr); } else { ASDisplayNo - (void)setCornerRoundingType:(ASCornerRoundingType)newRoundingType { - ASDN::MutexLocker l(__instanceLock__); - [self updateCornerRoundingWithType:newRoundingType cornerRadius:_cornerRadius]; + [self updateCornerRoundingWithType:newRoundingType cornerRadius:self.cornerRadius]; } - (NSString *)contentsGravity @@ -857,21 +851,16 @@ if (shouldApply) { _layer.layerProperty = (layerValueExpr); } else { ASDisplayNo - (UISemanticContentAttribute)semanticContentAttribute { _bridge_prologue_read; - if (AS_AT_LEAST_IOS9) { - return _getFromViewOnly(semanticContentAttribute); - } - return UISemanticContentAttributeUnspecified; + return _getFromViewOnly(semanticContentAttribute); } - (void)setSemanticContentAttribute:(UISemanticContentAttribute)semanticContentAttribute { _bridge_prologue_write; - if (AS_AT_LEAST_IOS9) { - _setToViewOnly(semanticContentAttribute, semanticContentAttribute); + _setToViewOnly(semanticContentAttribute, semanticContentAttribute); #if YOGA - [self semanticContentAttributeDidChange:semanticContentAttribute]; + [self semanticContentAttributeDidChange:semanticContentAttribute]; #endif - } } @end diff --git a/Source/Private/ASDisplayNodeInternal.h b/Source/Private/ASDisplayNodeInternal.h index 1be9e8c94c..545f1c5a20 100644 --- a/Source/Private/ASDisplayNodeInternal.h +++ b/Source/Private/ASDisplayNodeInternal.h @@ -77,7 +77,7 @@ FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBefo { @package _ASPendingState *_pendingViewState; - + ASInterfaceState _pendingInterfaceState; UIView *_view; CALayer *_layer; diff --git a/Source/Private/ASIGListAdapterBasedDataSource.m b/Source/Private/ASIGListAdapterBasedDataSource.m index 31a6c4582b..ab18636228 100644 --- a/Source/Private/ASIGListAdapterBasedDataSource.m +++ b/Source/Private/ASIGListAdapterBasedDataSource.m @@ -102,6 +102,14 @@ typedef struct { [self.delegate scrollViewWillBeginDragging:scrollView]; } +- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset +{ + // IGListAdapter doesn't implement scrollViewWillEndDragging yet (pending pull request), so we need this check for now. Doesn't hurt to have it anyways :) + if ([self.delegate respondsToSelector:@selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:)]) { + [self.delegate scrollViewWillEndDragging:scrollView withVelocity:velocity targetContentOffset:targetContentOffset]; + } +} + - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate { [self.delegate scrollViewDidEndDragging:scrollView willDecelerate:decelerate]; diff --git a/Source/Private/ASInternalHelpers.m b/Source/Private/ASInternalHelpers.m index ad55f295aa..dab3d2ae5f 100644 --- a/Source/Private/ASInternalHelpers.m +++ b/Source/Private/ASInternalHelpers.m @@ -143,8 +143,9 @@ CGFloat ASScreenScale() static CGFloat __scale = 0.0; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - ASDisplayNodeCAssertMainThread(); - __scale = [[UIScreen mainScreen] scale]; + UIGraphicsBeginImageContextWithOptions(CGSizeMake(1, 1), YES, 0); + __scale = CGContextGetCTM(UIGraphicsGetCurrentContext()).a; + UIGraphicsEndImageContext(); }); return __scale; } diff --git a/Source/Private/ASNetworkImageLoadInfo+Private.h b/Source/Private/ASNetworkImageLoadInfo+Private.h new file mode 100644 index 0000000000..db04352311 --- /dev/null +++ b/Source/Private/ASNetworkImageLoadInfo+Private.h @@ -0,0 +1,22 @@ +// +// ASNetworkImageLoadInfo+Private.h +// AsyncDisplayKit +// +// Created by Adlai on 1/30/18. +// Copyright © 2018 Facebook. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface ASNetworkImageLoadInfo () + +- (instancetype)initWithURL:(NSURL *)url + sourceType:(ASNetworkImageSourceType)sourceType + downloadIdentifier:(nullable id)downloadIdentifier + userInfo:(nullable id)userInfo; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Source/Private/TextExperiment/Component/ASTextLayout.m b/Source/Private/TextExperiment/Component/ASTextLayout.m index df8d356f6b..fa61946c53 100755 --- a/Source/Private/TextExperiment/Component/ASTextLayout.m +++ b/Source/Private/TextExperiment/Component/ASTextLayout.m @@ -405,25 +405,9 @@ dispatch_semaphore_signal(_lock); 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]; - } + BOOL needFixLayoutSizeBug = AS_AT_LEAST_IOS10; layout = [[ASTextLayout alloc] _init]; layout.text = text; diff --git a/Source/Private/TextExperiment/String/ASTextAttribute.m b/Source/Private/TextExperiment/String/ASTextAttribute.m index 084bdcf820..0cc8e22a72 100755 --- a/Source/Private/TextExperiment/String/ASTextAttribute.m +++ b/Source/Private/TextExperiment/String/ASTextAttribute.m @@ -63,7 +63,10 @@ ASTextAttributeType ASTextAttributeGetType(NSString *name){ dic[(id)kCTSuperscriptAttributeName] = UIKit; //it's a CoreText attrubite, but only supported by UIKit... dic[NSVerticalGlyphFormAttributeName] = All; dic[(id)kCTGlyphInfoAttributeName] = CoreText_ASText; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" dic[(id)kCTCharacterShapeAttributeName] = CoreText_ASText; +#pragma clang diagnostic pop dic[(id)kCTRunDelegateAttributeName] = CoreText_ASText; dic[(id)kCTBaselineClassAttributeName] = CoreText_ASText; dic[(id)kCTBaselineInfoAttributeName] = CoreText_ASText; diff --git a/Source/Private/TextExperiment/Utility/NSAttributedString+ASText.h b/Source/Private/TextExperiment/Utility/NSAttributedString+ASText.h index 9fc8ec2228..31ef75d121 100755 --- a/Source/Private/TextExperiment/Utility/NSAttributedString+ASText.h +++ b/Source/Private/TextExperiment/Utility/NSAttributedString+ASText.h @@ -1357,21 +1357,6 @@ NS_ASSUME_NONNULL_BEGIN */ - (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`. diff --git a/Source/Private/TextExperiment/Utility/NSAttributedString+ASText.m b/Source/Private/TextExperiment/Utility/NSAttributedString+ASText.m index d67dd15d2c..327bc41e2c 100755 --- a/Source/Private/TextExperiment/Utility/NSAttributedString+ASText.m +++ b/Source/Private/TextExperiment/Utility/NSAttributedString+ASText.m @@ -600,7 +600,10 @@ return style. _attr_; dispatch_once(&onceToken, ^{ failSet = [NSMutableSet new]; [failSet addObject:(id)kCTGlyphInfoAttributeName]; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" [failSet addObject:(id)kCTCharacterShapeAttributeName]; +#pragma clang diagnostic pop [failSet addObject:(id)kCTLanguageAttributeName]; [failSet addObject:(id)kCTRunDelegateAttributeName]; [failSet addObject:(id)kCTBaselineClassAttributeName]; @@ -1049,7 +1052,10 @@ style. _attr_ = _attr_; \ } - (void)as_setCharacterShape:(NSNumber *)characterShape range:(NSRange)range { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" [self as_setAttribute:(id)kCTCharacterShapeAttributeName value:characterShape range:range]; +#pragma clang diagnostic pop } - (void)as_setRunDelegate:(CTRunDelegateRef)runDelegate range:(NSRange)range { @@ -1178,51 +1184,6 @@ style. _attr_ = _attr_; \ [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) { diff --git a/Source/Private/_ASPendingState.mm b/Source/Private/_ASPendingState.mm index 4374401d87..1a87e496b3 100644 --- a/Source/Private/_ASPendingState.mm +++ b/Source/Private/_ASPendingState.mm @@ -295,9 +295,7 @@ static BOOL defaultAllowsEdgeAntialiasing = NO; accessibilityActivationPoint = CGPointZero; accessibilityPath = nil; edgeAntialiasingMask = (kCALayerLeftEdge | kCALayerRightEdge | kCALayerTopEdge | kCALayerBottomEdge); - if (AS_AVAILABLE_IOS(9)) { - semanticContentAttribute = UISemanticContentAttributeUnspecified; - } + semanticContentAttribute = UISemanticContentAttributeUnspecified; return self; } @@ -1051,10 +1049,8 @@ static BOOL defaultAllowsEdgeAntialiasing = NO; if (flags.setOpaque) ASDisplayNodeAssert(layer.opaque == opaque, @"Didn't set opaque as desired"); - if (AS_AVAILABLE_IOS(9)) { - if (flags.setSemanticContentAttribute) { - view.semanticContentAttribute = semanticContentAttribute; - } + if (flags.setSemanticContentAttribute) { + view.semanticContentAttribute = semanticContentAttribute; } if (flags.setIsAccessibilityElement) @@ -1215,9 +1211,7 @@ static BOOL defaultAllowsEdgeAntialiasing = NO; pendingState.allowsGroupOpacity = layer.allowsGroupOpacity; pendingState.allowsEdgeAntialiasing = layer.allowsEdgeAntialiasing; pendingState.edgeAntialiasingMask = layer.edgeAntialiasingMask; - if (AS_AVAILABLE_IOS(9)) { - pendingState.semanticContentAttribute = view.semanticContentAttribute; - } + pendingState.semanticContentAttribute = view.semanticContentAttribute; pendingState.isAccessibilityElement = view.isAccessibilityElement; pendingState.accessibilityLabel = view.accessibilityLabel; pendingState.accessibilityHint = view.accessibilityHint; diff --git a/Source/UIImage+ASConvenience.m b/Source/UIImage+ASConvenience.m index efc1e35360..35e6f0d563 100644 --- a/Source/UIImage+ASConvenience.m +++ b/Source/UIImage+ASConvenience.m @@ -16,6 +16,7 @@ // #import +#import #import #import @@ -138,7 +139,7 @@ UIImage *cachedImageNamed(NSString *imageName, UITraitCollection *traitCollectio // We should probably check if the background color has any alpha component but that // might be expensive due to needing to check mulitple color spaces. - UIGraphicsBeginImageContextWithOptions(bounds.size, cornerColor != nil, scale); + ASGraphicsBeginImageContextWithOptions(bounds.size, cornerColor != nil, scale); BOOL contextIsClean = YES; if (cornerColor) { @@ -168,8 +169,7 @@ UIImage *cachedImageNamed(NSString *imageName, UITraitCollection *traitCollectio [strokePath strokeWithBlendMode:(canUseCopy ? kCGBlendModeCopy : kCGBlendModeNormal) alpha:1]; } - UIImage *result = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); + UIImage *result = ASGraphicsGetImageAndEndCurrentContext(); UIEdgeInsets capInsets = UIEdgeInsetsMake(cornerRadius, cornerRadius, cornerRadius, cornerRadius); result = [result resizableImageWithCapInsets:capInsets resizingMode:UIImageResizingModeStretch]; diff --git a/SubspecWorkspaces/ASDKListKit/ASDKListKit.xcodeproj/project.pbxproj b/SubspecWorkspaces/ASDKListKit/ASDKListKit.xcodeproj/project.pbxproj index 814ad945a4..e275becc19 100644 --- a/SubspecWorkspaces/ASDKListKit/ASDKListKit.xcodeproj/project.pbxproj +++ b/SubspecWorkspaces/ASDKListKit/ASDKListKit.xcodeproj/project.pbxproj @@ -283,14 +283,14 @@ CC55321D1E16EB7A0011C01F /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; }; name = Debug; }; CC55321E1E16EB7A0011C01F /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; }; name = Release; }; @@ -336,7 +336,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; INFOPLIST_FILE = ASDKListKitTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; @@ -382,7 +382,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; INFOPLIST_FILE = ASDKListKitTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.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/SubspecWorkspaces/ASDKListKit/Podfile b/SubspecWorkspaces/ASDKListKit/Podfile index d5b84556fb..36498c2473 100644 --- a/SubspecWorkspaces/ASDKListKit/Podfile +++ b/SubspecWorkspaces/ASDKListKit/Podfile @@ -1,6 +1,6 @@ source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '8.0' +platform :ios, '9.0' target 'ASDKListKitTests' do pod 'Texture/IGListKit', :path => '../..' pod 'OCMock', '~> 3.4' diff --git a/Tests/ASBasicImageDownloaderTests.m b/Tests/ASBasicImageDownloaderTests.m index 2dd7d3375e..6144b5c253 100644 --- a/Tests/ASBasicImageDownloaderTests.m +++ b/Tests/ASBasicImageDownloaderTests.m @@ -38,14 +38,14 @@ [downloader downloadImageWithURL:URL callbackQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) downloadProgress:nil - completion:^(id _Nullable image, NSError * _Nullable error, id _Nullable downloadIdentifier) { + completion:^(id _Nullable image, NSError * _Nullable error, id _Nullable downloadIdentifier, id _Nullable userInfo) { [firstExpectation fulfill]; }]; [downloader downloadImageWithURL:URL callbackQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) downloadProgress:nil - completion:^(id _Nullable image, NSError * _Nullable error, id _Nullable downloadIdentifier) { + completion:^(id _Nullable image, NSError * _Nullable error, id _Nullable downloadIdentifier, id _Nullable userInfo) { [secondExpectation fulfill]; }]; diff --git a/Tests/ASCollectionViewTests.mm b/Tests/ASCollectionViewTests.mm index 06c6fcd890..c3dc8a2c47 100644 --- a/Tests/ASCollectionViewTests.mm +++ b/Tests/ASCollectionViewTests.mm @@ -23,6 +23,8 @@ #import #import #import +#import +#import "ASDisplayNodeTestsHelper.h" @interface ASTextCellNodeWithSetSelectedCounter : ASTextCellNode @@ -860,6 +862,7 @@ [cn waitUntilAllUpdatesAreProcessed]; [cn.view layoutIfNeeded]; ASCellNode *node = [cn nodeForItemAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]; + ASCATransactionQueueWait(); XCTAssertTrue(node.visible); testController.asyncDelegate->_itemCounts = {0}; [cn deleteItemsAtIndexPaths: @[[NSIndexPath indexPathForItem:0 inSection:0]]]; @@ -1047,7 +1050,7 @@ window.rootViewController = testController; [window makeKeyAndVisible]; - // Trigger the initial reload to start + // Trigger the initial reload to start [window layoutIfNeeded]; // Test the APIs that monitor ASCollectionNode update handling @@ -1071,6 +1074,7 @@ for (NSInteger i = 0; i < c; i++) { NSIndexPath *ip = [NSIndexPath indexPathForItem:i inSection:s]; ASCellNode *node = [cn nodeForItemAtIndexPath:ip]; + ASCATransactionQueueWait(); if (node.inPreloadState) { CGRect frame = [cn.view layoutAttributesForItemAtIndexPath:ip].frame; r = CGRectUnion(r, frame); @@ -1097,7 +1101,7 @@ [window layoutIfNeeded]; // The initial reload is async, changing the trait collection here should be "mid-update" - ASPrimitiveTraitCollection traitCollection; + ASPrimitiveTraitCollection traitCollection = ASPrimitiveTraitCollectionMakeDefault(); traitCollection.displayScale = cn.primitiveTraitCollection.displayScale + 1; // Just a dummy change traitCollection.containerSize = screenBounds.size; cn.primitiveTraitCollection = traitCollection; diff --git a/Tests/ASDisplayNodeImplicitHierarchyTests.m b/Tests/ASDisplayNodeImplicitHierarchyTests.m index 11e2ce4afa..d033ac4f6e 100644 --- a/Tests/ASDisplayNodeImplicitHierarchyTests.m +++ b/Tests/ASDisplayNodeImplicitHierarchyTests.m @@ -114,6 +114,7 @@ } ASSpecTestDisplayNode *node = [[ASSpecTestDisplayNode alloc] init]; + [node setHierarchyState:ASHierarchyStateRangeManaged]; node.automaticallyManagesSubnodes = YES; node.layoutSpecBlock = ^(ASDisplayNode *weakNode, ASSizeRange constrainedSize) { ASAbsoluteLayoutSpec *absoluteLayout = [ASAbsoluteLayoutSpec absoluteLayoutSpecWithChildren:@[subnodes[3]]]; @@ -130,6 +131,7 @@ ASDisplayNodeSizeToFitSizeRange(node, ASSizeRangeMake(CGSizeZero, CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX))); [node recursivelySetInterfaceState:ASInterfaceStatePreload]; + ASCATransactionQueueWait(); // No premature view allocation XCTAssertFalse(node.isNodeLoaded); // Subnodes should be inserted, laid out and entered preload state diff --git a/Tests/ASDisplayNodeTests.mm b/Tests/ASDisplayNodeTests.mm index f2f6d68ef1..495ed393b7 100644 --- a/Tests/ASDisplayNodeTests.mm +++ b/Tests/ASDisplayNodeTests.mm @@ -91,7 +91,7 @@ for (ASDisplayNode *n in @[ nodes ]) {\ @interface ASDisplayNode (HackForTests) - (id)initWithViewClass:(Class)viewClass; - (id)initWithLayerClass:(Class)layerClass; - +- (void)setInterfaceState:(ASInterfaceState)state; // FIXME: Importing ASDisplayNodeInternal.h causes a heap of problems. - (void)enterInterfaceState:(ASInterfaceState)interfaceState; @end @@ -127,6 +127,12 @@ for (ASDisplayNode *n in @[ nodes ]) {\ @implementation ASTestDisplayNode +- (void)setInterfaceState:(ASInterfaceState)state +{ + [super setInterfaceState:state]; + ASCATransactionQueueWait(); +} + - (CGSize)calculateSizeThatFits:(CGSize)constrainedSize { return _calculateSizeBlock ? _calculateSizeBlock(self, constrainedSize) : CGSizeZero; @@ -178,6 +184,28 @@ for (ASDisplayNode *n in @[ nodes ]) {\ @end +@interface ASSynchronousTestDisplayNodeViaViewClass : ASDisplayNode +@end + +@implementation ASSynchronousTestDisplayNodeViaViewClass + ++ (Class)viewClass { + return [UIView class]; +} + +@end + +@interface ASSynchronousTestDisplayNodeViaLayerClass : ASDisplayNode +@end + +@implementation ASSynchronousTestDisplayNodeViaLayerClass + ++ (Class)layerClass { + return [CALayer class]; +} + +@end + @interface UIDisplayNodeTestView : UIView @end @@ -2041,9 +2069,9 @@ static bool stringContainsPointer(NSString *description, id p) { // Underlying issue for: https://github.com/facebook/AsyncDisplayKit/issues/2205 - (void)testThatRasterizedNodesGetInterfaceStateUpdatesWhenContainerEntersHierarchy { - ASDisplayNode *supernode = [[ASDisplayNode alloc] init]; + ASDisplayNode *supernode = [[ASTestDisplayNode alloc] init]; [supernode enableSubtreeRasterization]; - ASDisplayNode *subnode = [[ASDisplayNode alloc] init]; + ASDisplayNode *subnode = [[ASTestDisplayNode alloc] init]; ASSetDebugNames(supernode, subnode); UIWindow *window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; [supernode addSubnode:subnode]; @@ -2059,9 +2087,9 @@ static bool stringContainsPointer(NSString *description, id p) { // Underlying issue for: https://github.com/facebook/AsyncDisplayKit/issues/2205 - (void)testThatRasterizedNodesGetInterfaceStateUpdatesWhenAddedToContainerThatIsInHierarchy { - ASDisplayNode *supernode = [[ASDisplayNode alloc] init]; + ASDisplayNode *supernode = [[ASTestDisplayNode alloc] init]; [supernode enableSubtreeRasterization]; - ASDisplayNode *subnode = [[ASDisplayNode alloc] init]; + ASDisplayNode *subnode = [[ASTestDisplayNode alloc] init]; ASSetDebugNames(supernode, subnode); UIWindow *window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; @@ -2175,8 +2203,7 @@ static bool stringContainsPointer(NSString *description, id p) { [node view]; // Node needs to be loaded [node enterInterfaceState:ASInterfaceStatePreload]; - - + XCTAssertTrue((node.interfaceState & ASInterfaceStatePreload) == ASInterfaceStatePreload); XCTAssertTrue((subnode.interfaceState & ASInterfaceStatePreload) == ASInterfaceStatePreload); XCTAssertTrue(node.hasPreloaded); @@ -2354,4 +2381,21 @@ static bool stringContainsPointer(NSString *description, id p) { XCTAssert(hasVC); } +- (void)testScreenScale +{ + XCTAssertEqual(ASScreenScale(), UIScreen.mainScreen.scale); +} + +- (void)testThatIfViewClassIsOverwrittenItsSynchronous +{ + ASSynchronousTestDisplayNodeViaViewClass *node = [[ASSynchronousTestDisplayNodeViaViewClass alloc] init]; + XCTAssertTrue([node isSynchronous], @"Node should be synchronous if viewClass is ovewritten and not a subclass of _ASDisplayView"); +} + +- (void)testThatIfLayerClassIsOverwrittenItsSynchronous +{ + ASSynchronousTestDisplayNodeViaLayerClass *node = [[ASSynchronousTestDisplayNodeViaLayerClass alloc] init]; + XCTAssertTrue([node isSynchronous], @"Node should be synchronous if viewClass is ovewritten and not a subclass of _ASDisplayView"); +} + @end diff --git a/Tests/ASDisplayNodeTestsHelper.h b/Tests/ASDisplayNodeTestsHelper.h index 4473500048..98b4719c67 100644 --- a/Tests/ASDisplayNodeTestsHelper.h +++ b/Tests/ASDisplayNodeTestsHelper.h @@ -28,5 +28,6 @@ BOOL ASDisplayNodeRunRunLoopUntilBlockIsTrue(as_condition_block_t block); void ASDisplayNodeSizeToFitSize(ASDisplayNode *node, CGSize size); void ASDisplayNodeSizeToFitSizeRange(ASDisplayNode *node, ASSizeRange sizeRange); +void ASCATransactionQueueWait(void); ASDISPLAYNODE_EXTERN_C_END diff --git a/Tests/ASDisplayNodeTestsHelper.m b/Tests/ASDisplayNodeTestsHelper.m index 05397b83b1..0a37da1e40 100644 --- a/Tests/ASDisplayNodeTestsHelper.m +++ b/Tests/ASDisplayNodeTestsHelper.m @@ -18,6 +18,7 @@ #import "ASDisplayNodeTestsHelper.h" #import #import +#import #import @@ -62,3 +63,14 @@ void ASDisplayNodeSizeToFitSizeRange(ASDisplayNode *node, ASSizeRange sizeRange) CGSize sizeThatFits = [node layoutThatFits:sizeRange].size; node.bounds = (CGRect){.origin = CGPointZero, .size = sizeThatFits}; } + +void ASCATransactionQueueWait(void) +{ + NSDate *date = [NSDate dateWithTimeIntervalSinceNow:1]; + BOOL whileResult = YES; + while ([date timeIntervalSinceNow] > 0 && + (whileResult = ![[ASCATransactionQueue sharedQueue] isEmpty])) { + [[NSRunLoop currentRunLoop] runUntilDate: + [NSDate dateWithTimeIntervalSinceNow:0.01]]; + } +} diff --git a/Tests/ASMultiplexImageNodeTests.m b/Tests/ASMultiplexImageNodeTests.m index 9eb41874b8..71b1fe05e4 100644 --- a/Tests/ASMultiplexImageNodeTests.m +++ b/Tests/ASMultiplexImageNodeTests.m @@ -238,7 +238,7 @@ // Simulate completion. ASImageDownloaderCompletion completionBlock = [inv as_argumentAtIndexAsObject:5]; - completionBlock([self _testImage], nil, nil); + completionBlock([self _testImage], nil, nil, nil); }); NSNumber *imageIdentifier = @1; diff --git a/Tests/ASRunLoopQueueTests.m b/Tests/ASRunLoopQueueTests.m index ccf15ae7b7..2fc9c04b56 100644 --- a/Tests/ASRunLoopQueueTests.m +++ b/Tests/ASRunLoopQueueTests.m @@ -2,8 +2,8 @@ // ASRunLoopQueueTests.m // Texture // -// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. -// Licensed under the Apache License, Version 2.0 (the "License"); +// Copyright (c) 2017-present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // @@ -12,9 +12,21 @@ #import #import +#import "ASDisplayNodeTestsHelper.h" static NSTimeInterval const kRunLoopRunTime = 0.001; // Allow the RunLoop to run for one millisecond each time. +@interface QueueObject : NSObject +@property (nonatomic, assign) BOOL queueObjectProcessed; +@end + +@implementation QueueObject +- (void)prepareForCATransactionCommit +{ + self.queueObjectProcessed = YES; +} +@end + @interface ASRunLoopQueueTests : XCTestCase @end @@ -157,4 +169,24 @@ static NSTimeInterval const kRunLoopRunTime = 0.001; // Allow the RunLoop to run XCTAssertTrue(queue.isEmpty); } +- (void)testASCATransactionQueueDisable +{ + ASCATransactionQueue *queue = [[ASCATransactionQueue alloc] init]; + [queue disable]; + QueueObject *object = [[QueueObject alloc] init]; + [[ASCATransactionQueue sharedQueue] enqueue:object]; + XCTAssertTrue([queue isEmpty]); + XCTAssertTrue([queue disabled]); +} + +- (void)testASCATransactionQueueProcess +{ + ASCATransactionQueue *queue = [[ASCATransactionQueue alloc] init]; + QueueObject *object = [[QueueObject alloc] init]; + [queue enqueue:object]; + XCTAssertFalse(object.queueObjectProcessed); + ASCATransactionQueueWait(); + XCTAssertTrue(object.queueObjectProcessed); +} + @end diff --git a/Tests/ASTraitCollectionTests.m b/Tests/ASTraitCollectionTests.m new file mode 100644 index 0000000000..aa106d75ec --- /dev/null +++ b/Tests/ASTraitCollectionTests.m @@ -0,0 +1,34 @@ +// +// ASTraitCollectionTests.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 + +@interface ASTraitCollectionTests : XCTestCase + +@end + +@implementation ASTraitCollectionTests + +- (void)testPrimitiveContentSizeCategoryLifetime +{ + ASPrimitiveContentSizeCategory primitiveContentSize; + @autoreleasepool { + // Make sure the compiler won't optimize string alloc/dealloc + NSString *contentSizeCategory = [NSString stringWithCString:"UICTContentSizeCategoryL" encoding:NSUTF8StringEncoding]; + primitiveContentSize = ASPrimitiveContentSizeCategoryMake(contentSizeCategory); + } + + XCTAssertEqual(primitiveContentSize, UIContentSizeCategoryLarge); +} + +@end diff --git a/Tests/ASVideoNodeTests.m b/Tests/ASVideoNodeTests.m index 278509efe0..96892b176e 100644 --- a/Tests/ASVideoNodeTests.m +++ b/Tests/ASVideoNodeTests.m @@ -21,6 +21,7 @@ #import #import #import +#import "ASDisplayNodeTestsHelper.h" @interface ASVideoNodeTests : XCTestCase { @@ -351,9 +352,9 @@ [_videoNode setInterfaceState:ASInterfaceStateVisible | ASInterfaceStateDisplay | ASInterfaceStatePreload]; [_videoNode prepareToPlayAsset:assetMock withKeys:_requestedKeys]; + ASCATransactionQueueWait(); [_videoNode pause]; _videoNode.shouldBePlaying = YES; - XCTAssertFalse(_videoNode.isPlaying); [_videoNode observeValueForKeyPath:@"playbackLikelyToKeepUp" ofObject:[_videoNode currentItem] change:@{NSKeyValueChangeNewKey : @YES} context:NULL]; diff --git a/Texture.podspec b/Texture.podspec index 73e9047728..b780df2830 100644 --- a/Texture.podspec +++ b/Texture.podspec @@ -14,7 +14,7 @@ Pod::Spec.new do |spec| spec.weak_frameworks = 'Photos','MapKit','AssetsLibrary' spec.requires_arc = true - spec.ios.deployment_target = '8.0' + spec.ios.deployment_target = '9.0' # Uncomment when fixed: issues with tvOS build for release 2.0 # spec.tvos.deployment_target = '9.0' @@ -51,7 +51,7 @@ Pod::Spec.new do |spec| end spec.subspec 'IGListKit' do |igl| - igl.dependency 'IGListKit', '3.0.0' + igl.dependency 'IGListKit', '~> 3.0' igl.dependency 'Texture/Core' end diff --git a/docs/_docs/editable-text-node.md b/docs/_docs/editable-text-node.md index a1fccd44a4..9eccfd0321 100755 --- a/docs/_docs/editable-text-node.md +++ b/docs/_docs/editable-text-node.md @@ -137,7 +137,7 @@ optional public func editableTextNodeDidUpdateText(_ editableTextNode: ASEditabl ---  Indicates to the delegate that teh text node has finished editing. +--  Indicates to the delegate that the text node has finished editing.
SwiftObjective-C diff --git a/docs/_docs/layout2-layoutspec-types.md b/docs/_docs/layout2-layoutspec-types.md index ad14cf0754..f1bb432e34 100755 --- a/docs/_docs/layout2-layoutspec-types.md +++ b/docs/_docs/layout2-layoutspec-types.md @@ -394,17 +394,17 @@ Within `ASAbsoluteLayoutSpec` you can specify exact locations (x/y coordinates) 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)); + guitarVideoNode.style.layoutPosition = CGPointMake(0, 0); + guitarVideoNode.style.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)); + nicCageVideoNode.style.layoutPosition = CGPointMake(maxConstrainedSize.width / 2.0, maxConstrainedSize.height / 3.0); + nicCageVideoNode.style.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)); + simonVideoNode.style.layoutPosition = CGPointMake(0.0, maxConstrainedSize.height - (maxConstrainedSize.height / 3.0)); + simonVideoNode.style.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)); + hlsVideoNode.style.layoutPosition = CGPointMake(0.0, maxConstrainedSize.height / 3.0); + hlsVideoNode.style.size = ASSizeMakeFromCGSize(CGSizeMake(maxConstrainedSize.width / 2.0, maxConstrainedSize.height / 3.0)); return [ASAbsoluteLayoutSpec absoluteLayoutSpecWithChildren:@[guitarVideoNode, nicCageVideoNode, simonVideoNode, hlsVideoNode]]; } diff --git a/docs/_docs/subclassing.md b/docs/_docs/subclassing.md index 44c3260eed..ea4cd24b9a 100755 --- a/docs/_docs/subclassing.md +++ b/docs/_docs/subclassing.md @@ -51,7 +51,7 @@ An `ASViewController` is a regular `UIViewController` subclass that has special ### `-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. +This method is called once, at the very beginning 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. diff --git a/docs/showcase.md b/docs/showcase.md index 4b3e68233e..95b72f3317 100755 --- a/docs/showcase.md +++ b/docs/showcase.md @@ -229,6 +229,16 @@ permalink: /showcase.html + + + + +
+ MensXP + + + +
diff --git a/examples/ASCollectionView/Podfile b/examples/ASCollectionView/Podfile index 922ff50ec1..08d1b7add6 100644 --- a/examples/ASCollectionView/Podfile +++ b/examples/ASCollectionView/Podfile @@ -1,5 +1,5 @@ source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '8.0' +platform :ios, '9.0' target 'Sample' do pod 'Texture', :path => '../..' end diff --git a/examples/ASCollectionView/Sample.xcodeproj/project.pbxproj b/examples/ASCollectionView/Sample.xcodeproj/project.pbxproj index 634184297c..967d1f7cd6 100644 --- a/examples/ASCollectionView/Sample.xcodeproj/project.pbxproj +++ b/examples/ASCollectionView/Sample.xcodeproj/project.pbxproj @@ -240,13 +240,16 @@ files = ( ); inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", ); name = "[CP] Check Pods Manifest.lock"; outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Sample-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/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# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -302,7 +305,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.1; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -338,7 +341,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.1; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; @@ -353,7 +356,6 @@ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; INFOPLIST_FILE = Sample/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 8.1; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_NAME = "$(TARGET_NAME)"; TARGETED_DEVICE_FAMILY = 1; @@ -367,7 +369,6 @@ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; INFOPLIST_FILE = Sample/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 8.1; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_NAME = "$(TARGET_NAME)"; TARGETED_DEVICE_FAMILY = 1; diff --git a/examples/ASCollectionView/Sample/ViewController.m b/examples/ASCollectionView/Sample/ViewController.m index 3755b01d69..10c974776f 100644 --- a/examples/ASCollectionView/Sample/ViewController.m +++ b/examples/ASCollectionView/Sample/ViewController.m @@ -23,10 +23,13 @@ #define ASYNC_COLLECTION_LAYOUT 0 +static CGSize const kItemSize = (CGSize){180, 90}; + @interface ViewController () @property (nonatomic, strong) ASCollectionNode *collectionNode; -@property (nonatomic, strong) NSArray *data; +@property (nonatomic, strong) NSMutableArray *> *data; +@property (nonatomic, strong) UILongPressGestureRecognizer *moveRecognizer; @end @@ -34,18 +37,13 @@ #pragma mark - Lifecycle -- (void)dealloc -{ - self.collectionNode.dataSource = nil; - self.collectionNode.delegate = nil; - - NSLog(@"ViewController is deallocing"); -} - - (void)viewDidLoad { [super viewDidLoad]; + self.moveRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress)]; + [self.view addGestureRecognizer:self.moveRecognizer]; + #if ASYNC_COLLECTION_LAYOUT ASCollectionGalleryLayoutDelegate *layoutDelegate = [[ASCollectionGalleryLayoutDelegate alloc] initWithScrollableDirections:ASScrollDirectionVerticalDirections]; layoutDelegate.propertiesProvider = self; @@ -54,6 +52,7 @@ UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; layout.headerReferenceSize = CGSizeMake(50.0, 50.0); layout.footerReferenceSize = CGSizeMake(50.0, 50.0); + layout.itemSize = kItemSize; self.collectionNode = [[ASCollectionNode alloc] initWithFrame:self.view.bounds collectionViewLayout:layout]; [self.collectionNode registerSupplementaryNodeOfKind:UICollectionElementKindSectionHeader]; [self.collectionNode registerSupplementaryNodeOfKind:UICollectionElementKindSectionFooter]; @@ -73,34 +72,37 @@ self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh target:self action:@selector(reloadTapped)]; -#endif - -#if SIMULATE_WEB_RESPONSE + [self loadData]; +#else __weak typeof(self) weakSelf = self; - void(^mockWebService)() = ^{ - NSLog(@"ViewController \"got data from a web service\""); - ViewController *strongSelf = weakSelf; - if (strongSelf != nil) - { - NSLog(@"ViewController is not nil"); - strongSelf->_data = [[NSArray alloc] init]; - [strongSelf->_collectionNode performBatchUpdates:^{ - [strongSelf->_collectionNode insertSections:[[NSIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, 100)]]; - } completion:nil]; - NSLog(@"ViewController finished updating collectionNode"); - } - else { - NSLog(@"ViewController is nil - won't update collectionNode"); - } - }; - - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), mockWebService); - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - [self.navigationController popViewControllerAnimated:YES]; + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + [weakSelf handleSimulatedWebResponse]; }); #endif } +- (void)handleSimulatedWebResponse +{ + [self.collectionNode performBatchUpdates:^{ + [self loadData]; + [self.collectionNode insertSections:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, self.data.count)]]; + } completion:nil]; +} + +- (void)loadData +{ + // Form our data array + typeof(self.data) data = [NSMutableArray array]; + for (NSInteger s = 0; s < 100; s++) { + NSMutableArray *items = [NSMutableArray array]; + for (NSInteger i = 0; i < 10; i++) { + items[i] = [NSString stringWithFormat:@"[%zd.%zd] says hi", s, i]; + } + data[s] = items; + } + self.data = data; +} + #pragma mark - Button Actions - (void)reloadTapped @@ -115,14 +117,42 @@ - (CGSize)galleryLayoutDelegate:(ASCollectionGalleryLayoutDelegate *)delegate sizeForElements:(ASElementMap *)elements { ASDisplayNodeAssertMainThread(); - return CGSizeMake(180, 90); + return kItemSize; } -#pragma mark - ASCollectionView Data Source +- (void)handleLongPress +{ + UICollectionView *collectionView = self.collectionNode.view; + CGPoint location = [self.moveRecognizer locationInView:collectionView]; + switch (self.moveRecognizer.state) { + case UIGestureRecognizerStateBegan: { + NSIndexPath *indexPath = [collectionView indexPathForItemAtPoint:location]; + if (indexPath) { + [collectionView beginInteractiveMovementForItemAtIndexPath:indexPath]; + } + break; + } + case UIGestureRecognizerStateChanged: + [collectionView updateInteractiveMovementTargetPosition:location]; + break; + case UIGestureRecognizerStateEnded: + [collectionView endInteractiveMovement]; + break; + case UIGestureRecognizerStateFailed: + case UIGestureRecognizerStateCancelled: + [collectionView cancelInteractiveMovement]; + break; + case UIGestureRecognizerStatePossible: + // nop + break; + } +} + +#pragma mark - ASCollectionDataSource - (ASCellNodeBlock)collectionNode:(ASCollectionNode *)collectionNode nodeBlockForItemAtIndexPath:(NSIndexPath *)indexPath; { - NSString *text = [NSString stringWithFormat:@"[%zd.%zd] says hi", indexPath.section, indexPath.item]; + NSString *text = self.data[indexPath.section][indexPath.item]; return ^{ return [[ItemNode alloc] initWithString:text]; }; @@ -139,18 +169,29 @@ - (NSInteger)collectionNode:(ASCollectionNode *)collectionNode numberOfItemsInSection:(NSInteger)section { - return 10; + return self.data[section].count; } - (NSInteger)numberOfSectionsInCollectionNode:(ASCollectionNode *)collectionNode { -#if SIMULATE_WEB_RESPONSE - return _data == nil ? 0 : 100; -#else - return 100; -#endif + return self.data.count; } +- (BOOL)collectionNode:(ASCollectionNode *)collectionNode canMoveItemWithNode:(ASCellNode *)node +{ + return YES; +} + +- (void)collectionNode:(ASCollectionNode *)collectionNode moveItemAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath +{ + __auto_type sectionArray = self.data[sourceIndexPath.section]; + __auto_type object = sectionArray[sourceIndexPath.item]; + [sectionArray removeObjectAtIndex:sourceIndexPath.item]; + [self.data[destinationIndexPath.section] insertObject:object atIndex:destinationIndexPath.item]; +} + +#pragma mark - ASCollectionDelegate + - (void)collectionNode:(ASCollectionNode *)collectionNode willBeginBatchFetchWithContext:(ASBatchContext *)context { NSLog(@"fetch additional content"); diff --git a/examples/ASDKLayoutTransition/Podfile b/examples/ASDKLayoutTransition/Podfile index 90c6ad7ea8..0fbf4c9524 100644 --- a/examples/ASDKLayoutTransition/Podfile +++ b/examples/ASDKLayoutTransition/Podfile @@ -1,5 +1,5 @@ source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '8.0' +platform :ios, '9.0' target 'Sample' do pod 'Texture', :path => '../..' pod 'Texture/Yoga', :path => '../..' diff --git a/examples/ASDKLayoutTransition/Sample.xcodeproj/project.pbxproj b/examples/ASDKLayoutTransition/Sample.xcodeproj/project.pbxproj index 3b4f1e6f1d..17537d093d 100644 --- a/examples/ASDKLayoutTransition/Sample.xcodeproj/project.pbxproj +++ b/examples/ASDKLayoutTransition/Sample.xcodeproj/project.pbxproj @@ -196,13 +196,16 @@ files = ( ); inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", ); name = "[CP] Check Pods Manifest.lock"; outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Sample-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/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# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; F012A6F39E0149F18F564F50 /* [CP] Copy Pods Resources */ = { @@ -271,7 +274,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -306,7 +309,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; diff --git a/examples/ASDKTube/Podfile b/examples/ASDKTube/Podfile index b75e492fab..71a7f2c4b2 100644 --- a/examples/ASDKTube/Podfile +++ b/examples/ASDKTube/Podfile @@ -1,5 +1,5 @@ source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '8.0' +platform :ios, '9.0' target 'Sample' do pod 'Texture', :path => '../..' end diff --git a/examples/ASDKTube/Sample.xcodeproj/project.pbxproj b/examples/ASDKTube/Sample.xcodeproj/project.pbxproj index 0e6c890fa4..1b8c982bed 100644 --- a/examples/ASDKTube/Sample.xcodeproj/project.pbxproj +++ b/examples/ASDKTube/Sample.xcodeproj/project.pbxproj @@ -277,13 +277,16 @@ files = ( ); inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", ); name = "[CP] Check Pods Manifest.lock"; outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Sample-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/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# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; F012A6F39E0149F18F564F50 /* [CP] Copy Pods Resources */ = { @@ -356,7 +359,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -391,7 +394,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; diff --git a/examples/ASDKgram/Podfile b/examples/ASDKgram/Podfile index eb879f3f12..4aebd04935 100644 --- a/examples/ASDKgram/Podfile +++ b/examples/ASDKgram/Podfile @@ -1,5 +1,5 @@ source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '8.0' +platform :ios, '9.0' target 'Sample' do pod 'Texture/IGListKit', :path => '../..' pod 'Texture/PINRemoteImage', :path => '../..' diff --git a/examples/ASDKgram/Sample.xcodeproj/project.pbxproj b/examples/ASDKgram/Sample.xcodeproj/project.pbxproj index 9cfb0e254d..743b9456e8 100644 --- a/examples/ASDKgram/Sample.xcodeproj/project.pbxproj +++ b/examples/ASDKgram/Sample.xcodeproj/project.pbxproj @@ -381,13 +381,16 @@ files = ( ); inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", ); name = "[CP] Check Pods Manifest.lock"; outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Sample-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/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# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; F012A6F39E0149F18F564F50 /* [CP] Copy Pods Resources */ = { @@ -477,7 +480,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -512,7 +515,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; diff --git a/examples/ASDKgram/Sample/AppDelegate.m b/examples/ASDKgram/Sample/AppDelegate.m index 16484467f3..21c47a2737 100644 --- a/examples/ASDKgram/Sample/AppDelegate.m +++ b/examples/ASDKgram/Sample/AppDelegate.m @@ -22,6 +22,8 @@ #import "WindowWithStatusBarUnderlay.h" #import "Utilities.h" +#import + #define WEAVER 0 #if WEAVER @@ -38,6 +40,8 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + ASEnableNoCopyRendering(); + // this UIWindow subclass is neccessary to make the status bar opaque _window = [[WindowWithStatusBarUnderlay alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; _window.backgroundColor = [UIColor whiteColor]; diff --git a/examples/ASDKgram/Sample/PhotoCellNode.m b/examples/ASDKgram/Sample/PhotoCellNode.m index d424587c60..a094ad5607 100644 --- a/examples/ASDKgram/Sample/PhotoCellNode.m +++ b/examples/ASDKgram/Sample/PhotoCellNode.m @@ -44,6 +44,9 @@ #define InsetForHeader UIEdgeInsetsMake(0, HORIZONTAL_BUFFER, 0, HORIZONTAL_BUFFER) #define InsetForFooter UIEdgeInsetsMake(VERTICAL_BUFFER, HORIZONTAL_BUFFER, VERTICAL_BUFFER, HORIZONTAL_BUFFER) +@interface PhotoCellNode () +@end + @implementation PhotoCellNode { PhotoModel *_photoModel; @@ -77,6 +80,7 @@ }]; _photoImageNode = [[ASNetworkImageNode alloc] init]; + _photoImageNode.delegate = self; _photoImageNode.URL = photo.URL; _photoImageNode.layerBacked = YES; @@ -284,6 +288,19 @@ }]; } +#pragma mark - Network Image Delegate + +- (void)imageNode:(ASNetworkImageNode *)imageNode didLoadImage:(UIImage *)image info:(ASNetworkImageLoadInfo *)info +{ + // Docs say method is called from bg but right now it's called from main. + // Save main thread time by shunting this. + if (info.sourceType == ASNetworkImageSourceDownload) { + ASPerformBlockOnBackgroundThread(^{ + NSLog(@"Received image %@ from %@ with userInfo %@", image, info.url.path, ASObjectDescriptionMakeTiny(info.userInfo)); + }); + } +} + #pragma mark - Helper Methods - (ASTextNode *)createLayerBackedTextNodeWithString:(NSAttributedString *)attributedString diff --git a/examples/ASDKgram/Sample/PhotoFeedModel.m b/examples/ASDKgram/Sample/PhotoFeedModel.m index 27cf185e9d..afac8ca9fe 100644 --- a/examples/ASDKgram/Sample/PhotoFeedModel.m +++ b/examples/ASDKgram/Sample/PhotoFeedModel.m @@ -1,20 +1,18 @@ // // PhotoFeedModel.m -// Sample -// -// Created by Hannah Troisi 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. // -// 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 "PhotoFeedModel.h" @@ -184,9 +182,11 @@ // early return if reached end of pages if (_totalPages) { if (_currentPage == _totalPages) { - if (block){ - block(@[]); - } + dispatch_async(dispatch_get_main_queue(), ^{ + if (block) { + block(@[]); + } + }); return; } } diff --git a/examples/ASMapNode/Podfile b/examples/ASMapNode/Podfile index 922ff50ec1..08d1b7add6 100644 --- a/examples/ASMapNode/Podfile +++ b/examples/ASMapNode/Podfile @@ -1,5 +1,5 @@ source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '8.0' +platform :ios, '9.0' target 'Sample' do pod 'Texture', :path => '../..' end diff --git a/examples/ASMapNode/Sample.xcodeproj/project.pbxproj b/examples/ASMapNode/Sample.xcodeproj/project.pbxproj index 529b587837..48093ffda1 100644 --- a/examples/ASMapNode/Sample.xcodeproj/project.pbxproj +++ b/examples/ASMapNode/Sample.xcodeproj/project.pbxproj @@ -214,13 +214,16 @@ files = ( ); inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", ); name = "[CP] Check Pods Manifest.lock"; outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Sample-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/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# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -288,7 +291,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -325,7 +328,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; diff --git a/examples/ASViewController/Podfile b/examples/ASViewController/Podfile index 922ff50ec1..08d1b7add6 100644 --- a/examples/ASViewController/Podfile +++ b/examples/ASViewController/Podfile @@ -1,5 +1,5 @@ source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '8.0' +platform :ios, '9.0' target 'Sample' do pod 'Texture', :path => '../..' end diff --git a/examples/ASViewController/Sample.xcodeproj/project.pbxproj b/examples/ASViewController/Sample.xcodeproj/project.pbxproj index 200b3c2255..e304fdb8e3 100644 --- a/examples/ASViewController/Sample.xcodeproj/project.pbxproj +++ b/examples/ASViewController/Sample.xcodeproj/project.pbxproj @@ -219,13 +219,16 @@ files = ( ); inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", ); name = "[CP] Check Pods Manifest.lock"; outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Sample-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/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# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -294,7 +297,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -331,7 +334,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; diff --git a/examples/AnimatedGIF/Podfile b/examples/AnimatedGIF/Podfile index e784c52d14..c998fa0a8d 100644 --- a/examples/AnimatedGIF/Podfile +++ b/examples/AnimatedGIF/Podfile @@ -1,5 +1,5 @@ source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '8.0' +platform :ios, '9.0' target 'Sample' do pod 'Texture', :path => '../..' pod 'PINRemoteImage/WebP' diff --git a/examples/AnimatedGIF/Sample.xcodeproj/project.pbxproj b/examples/AnimatedGIF/Sample.xcodeproj/project.pbxproj index 7498a38dde..453c359c0e 100644 --- a/examples/AnimatedGIF/Sample.xcodeproj/project.pbxproj +++ b/examples/AnimatedGIF/Sample.xcodeproj/project.pbxproj @@ -214,13 +214,16 @@ files = ( ); inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", ); name = "[CP] Check Pods Manifest.lock"; outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Sample-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/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# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ diff --git a/examples/AsyncDisplayKitOverview/Podfile b/examples/AsyncDisplayKitOverview/Podfile index 84d7dee82e..ff6cb63a31 100644 --- a/examples/AsyncDisplayKitOverview/Podfile +++ b/examples/AsyncDisplayKitOverview/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -platform :ios, '8.0' +platform :ios, '9.0' # Uncomment this line if you're using Swift # use_frameworks! diff --git a/examples/AsyncDisplayKitOverview/Sample.xcodeproj/project.pbxproj b/examples/AsyncDisplayKitOverview/Sample.xcodeproj/project.pbxproj index 8d5abb4bbc..3683dbd638 100644 --- a/examples/AsyncDisplayKitOverview/Sample.xcodeproj/project.pbxproj +++ b/examples/AsyncDisplayKitOverview/Sample.xcodeproj/project.pbxproj @@ -217,13 +217,16 @@ files = ( ); inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", ); name = "[CP] Check Pods Manifest.lock"; outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Sample-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/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# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; 84F93825AFB1CA7FBB116BA4 /* [CP] Copy Pods Resources */ = { @@ -309,7 +312,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -348,7 +351,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/examples/CatDealsCollectionView/Podfile b/examples/CatDealsCollectionView/Podfile index b75e492fab..71a7f2c4b2 100644 --- a/examples/CatDealsCollectionView/Podfile +++ b/examples/CatDealsCollectionView/Podfile @@ -1,5 +1,5 @@ source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '8.0' +platform :ios, '9.0' target 'Sample' do pod 'Texture', :path => '../..' end diff --git a/examples/CatDealsCollectionView/Sample.xcodeproj/project.pbxproj b/examples/CatDealsCollectionView/Sample.xcodeproj/project.pbxproj index 5a9485def8..29fb32d342 100644 --- a/examples/CatDealsCollectionView/Sample.xcodeproj/project.pbxproj +++ b/examples/CatDealsCollectionView/Sample.xcodeproj/project.pbxproj @@ -242,13 +242,16 @@ files = ( ); inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", ); name = "[CP] Check Pods Manifest.lock"; outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Sample-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/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# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -308,7 +311,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.1; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -344,7 +347,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.1; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/examples/CatDealsCollectionView/Sample/BlurbNode.h b/examples/CatDealsCollectionView/Sample/BlurbNode.h index e6574bcd05..e40f99d8a6 100644 --- a/examples/CatDealsCollectionView/Sample/BlurbNode.h +++ b/examples/CatDealsCollectionView/Sample/BlurbNode.h @@ -1,18 +1,18 @@ // // BlurbNode.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) through the present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file 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/BlurbNode.m b/examples/CatDealsCollectionView/Sample/BlurbNode.m index 7dbe86a8b8..0b358273c0 100644 --- a/examples/CatDealsCollectionView/Sample/BlurbNode.m +++ b/examples/CatDealsCollectionView/Sample/BlurbNode.m @@ -1,18 +1,18 @@ // // BlurbNode.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) through the present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "BlurbNode.h" @@ -57,16 +57,15 @@ static CGFloat kTextPadding = 10.0f; NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString:blurb]; [string addAttribute:NSFontAttributeName value:[UIFont fontWithName:@"HelveticaNeue-Light" size:16.0f] range:NSMakeRange(0, blurb.length)]; [string addAttributes:@{ - NSLinkAttributeName: [NSURL URLWithString:@"http://lorempixel.com/"], - NSForegroundColorAttributeName: [UIColor blueColor], - NSUnderlineStyleAttributeName: @(NSUnderlineStyleSingle | NSUnderlinePatternDot), - } - range:[blurb rangeOfString:@"lorempixel.com"]]; + NSLinkAttributeName: [NSURL URLWithString:@"http://lorempixel.com/"], + NSForegroundColorAttributeName: [UIColor blueColor], + NSUnderlineStyleAttributeName: @(NSUnderlineStyleSingle | NSUnderlinePatternDot), + } range:[blurb rangeOfString:@"lorempixel.com"]]; [string addAttributes:@{ - NSLinkAttributeName: [NSURL URLWithString:@"http://www.catipsum.com/"], - NSForegroundColorAttributeName: [UIColor blueColor], - NSUnderlineStyleAttributeName: @(NSUnderlineStyleSingle | NSUnderlinePatternDot), - } range:[blurb rangeOfString:@"catipsum.com"]]; + NSLinkAttributeName: [NSURL URLWithString:@"http://www.catipsum.com/"], + NSForegroundColorAttributeName: [UIColor blueColor], + NSUnderlineStyleAttributeName: @(NSUnderlineStyleSingle | NSUnderlinePatternDot), + } range:[blurb rangeOfString:@"catipsum.com"]]; _textNode.attributedText = string; // add it as a subnode, and we're done @@ -90,7 +89,7 @@ static CGFloat kTextPadding = 10.0f; centerSpec.sizingOptions = ASCenterLayoutSpecSizingOptionMinimumY; centerSpec.child = _textNode; - UIEdgeInsets padding =UIEdgeInsetsMake(kTextPadding, kTextPadding, kTextPadding, kTextPadding); + UIEdgeInsets padding = UIEdgeInsetsMake(kTextPadding, kTextPadding, kTextPadding, kTextPadding); return [ASInsetLayoutSpec insetLayoutSpecWithInsets:padding child:centerSpec]; } diff --git a/examples/CatDealsCollectionView/Sample/ItemNode.h b/examples/CatDealsCollectionView/Sample/ItemNode.h index 678327e111..bb0f06857d 100644 --- a/examples/CatDealsCollectionView/Sample/ItemNode.h +++ b/examples/CatDealsCollectionView/Sample/ItemNode.h @@ -20,7 +20,6 @@ @interface ItemNode : ASCellNode -- initWithViewModel:(ItemViewModel *)viewModel; + (CGSize)sizeForWidth:(CGFloat)width; + (CGSize)preferredViewSize; diff --git a/examples/CatDealsCollectionView/Sample/ItemNode.m b/examples/CatDealsCollectionView/Sample/ItemNode.m index 400f5b79e8..4fd76e8a01 100644 --- a/examples/CatDealsCollectionView/Sample/ItemNode.m +++ b/examples/CatDealsCollectionView/Sample/ItemNode.m @@ -20,6 +20,16 @@ #import "PlaceholderNetworkImageNode.h" #import +static CGFloat ASIsRTL() +{ + static BOOL __isRTL = NO; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + __isRTL = [UIApplication sharedApplication].userInterfaceLayoutDirection == UIUserInterfaceLayoutDirectionRightToLeft; + }); + return __isRTL; +} + const CGFloat kFixedLabelsAreaHeight = 96.0; const CGFloat kDesignWidth = 320.0; const CGFloat kDesignHeight = 299.0; @@ -28,7 +38,7 @@ const CGFloat kSoldOutGBHeight = 50.0; @interface ItemNode() -@property (nonatomic, strong) ItemViewModel *viewModel; +@property (nonatomic, strong) ItemViewModel *nodeModel; @property (nonatomic, strong) PlaceholderNetworkImageNode *dealImageView; @@ -46,28 +56,35 @@ const CGFloat kSoldOutGBHeight = 50.0; @end @implementation ItemNode -@dynamic viewModel; +// Defined on ASCellNode +@dynamic nodeModel; -- (instancetype)initWithViewModel:(ItemViewModel *)viewModel ++ (void)load +{ + // Need to happen on main thread. + ASIsRTL(); +} + +- (instancetype)init { self = [super init]; if (self != nil) { - self.viewModel = viewModel; - [self setup]; - [self updateLabels]; + [self setupNodes]; [self updateBackgroundColor]; - - ASSetDebugName(self, @"Item #%zd", viewModel.identifier); - self.accessibilityIdentifier = viewModel.titleText; } return self; } -+ (BOOL)isRTL { - return [UIApplication sharedApplication].userInterfaceLayoutDirection == UIUserInterfaceLayoutDirectionRightToLeft; +- (void)setNodeModel:(ItemViewModel *)nodeModel +{ + [super setNodeModel:nodeModel]; + + [self updateLabels]; + [self updateAccessibilityIdentifier]; } -- (void)setup { +- (void)setupNodes +{ self.dealImageView = [[PlaceholderNetworkImageNode alloc] init]; self.dealImageView.delegate = self; self.dealImageView.placeholderEnabled = YES; @@ -138,7 +155,7 @@ const CGFloat kSoldOutGBHeight = 50.0; self.soldOutLabelBackground.hidden = YES; self.soldOutLabelFlat.hidden = YES; - if ([ItemNode isRTL]) { + if (ASIsRTL()) { self.titleLabel.style.alignSelf = ASStackLayoutAlignSelfEnd; self.firstInfoLabel.style.alignSelf = ASStackLayoutAlignSelfEnd; self.distanceLabel.style.alignSelf = ASStackLayoutAlignSelfEnd; @@ -154,49 +171,56 @@ const CGFloat kSoldOutGBHeight = 50.0; } } -- (void)updateLabels { +- (void)updateLabels +{ // Set Title text - if (self.viewModel.titleText) { - self.titleLabel.attributedText = [[NSAttributedString alloc] initWithString:self.viewModel.titleText attributes:[ItemStyles titleStyle]]; + if (self.nodeModel.titleText) { + self.titleLabel.attributedText = [[NSAttributedString alloc] initWithString:self.nodeModel.titleText attributes:[ItemStyles titleStyle]]; } - if (self.viewModel.firstInfoText) { - self.firstInfoLabel.attributedText = [[NSAttributedString alloc] initWithString:self.viewModel.firstInfoText attributes:[ItemStyles subtitleStyle]]; + if (self.nodeModel.firstInfoText) { + self.firstInfoLabel.attributedText = [[NSAttributedString alloc] initWithString:self.nodeModel.firstInfoText attributes:[ItemStyles subtitleStyle]]; } - if (self.viewModel.secondInfoText) { - self.secondInfoLabel.attributedText = [[NSAttributedString alloc] initWithString:self.viewModel.secondInfoText attributes:[ItemStyles secondInfoStyle]]; + if (self.nodeModel.secondInfoText) { + self.secondInfoLabel.attributedText = [[NSAttributedString alloc] initWithString:self.nodeModel.secondInfoText attributes:[ItemStyles secondInfoStyle]]; } - if (self.viewModel.originalPriceText) { - self.originalPriceLabel.attributedText = [[NSAttributedString alloc] initWithString:self.viewModel.originalPriceText attributes:[ItemStyles originalPriceStyle]]; + if (self.nodeModel.originalPriceText) { + self.originalPriceLabel.attributedText = [[NSAttributedString alloc] initWithString:self.nodeModel.originalPriceText attributes:[ItemStyles originalPriceStyle]]; } - if (self.viewModel.finalPriceText) { - self.finalPriceLabel.attributedText = [[NSAttributedString alloc] initWithString:self.viewModel.finalPriceText attributes:[ItemStyles finalPriceStyle]]; + if (self.nodeModel.finalPriceText) { + self.finalPriceLabel.attributedText = [[NSAttributedString alloc] initWithString:self.nodeModel.finalPriceText attributes:[ItemStyles finalPriceStyle]]; } - if (self.viewModel.distanceLabelText) { - NSString *format = [ItemNode isRTL] ? @"%@ •" : @"• %@"; - NSString *distanceText = [NSString stringWithFormat:format, self.viewModel.distanceLabelText]; + if (self.nodeModel.distanceLabelText) { + NSString *format = ASIsRTL() ? @"%@ •" : @"• %@"; + NSString *distanceText = [NSString stringWithFormat:format, self.nodeModel.distanceLabelText]; self.distanceLabel.attributedText = [[NSAttributedString alloc] initWithString:distanceText attributes:[ItemStyles distanceStyle]]; } - BOOL isSoldOut = self.viewModel.soldOutText != nil; + BOOL isSoldOut = self.nodeModel.soldOutText != nil; if (isSoldOut) { - NSString *soldOutText = self.viewModel.soldOutText; + NSString *soldOutText = self.nodeModel.soldOutText; self.soldOutLabelFlat.attributedText = [[NSAttributedString alloc] initWithString:soldOutText attributes:[ItemStyles soldOutStyle]]; } self.soldOutOverlay.hidden = !isSoldOut; self.soldOutLabelFlat.hidden = !isSoldOut; self.soldOutLabelBackground.hidden = !isSoldOut; - BOOL hasBadge = self.viewModel.badgeText != nil; + BOOL hasBadge = self.nodeModel.badgeText != nil; if (hasBadge) { - self.badge.attributedText = [[NSAttributedString alloc] initWithString:self.viewModel.badgeText attributes:[ItemStyles badgeStyle]]; + self.badge.attributedText = [[NSAttributedString alloc] initWithString:self.nodeModel.badgeText attributes:[ItemStyles badgeStyle]]; self.badge.backgroundColor = [ItemStyles badgeColor]; } self.badge.hidden = !hasBadge; } +- (void)updateAccessibilityIdentifier +{ + ASSetDebugName(self, @"Item #%zd", self.nodeModel.identifier); + self.accessibilityIdentifier = self.nodeModel.titleText; +} + - (void)updateBackgroundColor { if (self.highlighted) { @@ -208,9 +232,6 @@ const CGFloat kSoldOutGBHeight = 50.0; } } -- (void)imageNode:(ASNetworkImageNode *)imageNode didLoadImage:(UIImage *)image { -} - - (void)setSelected:(BOOL)selected { [super setSelected:selected]; @@ -223,21 +244,22 @@ const CGFloat kSoldOutGBHeight = 50.0; [self updateBackgroundColor]; } -#pragma mark - superclass +#pragma mark - ASDisplayNode -- (void)displayWillStart { +- (void)displayWillStart +{ [super displayWillStart]; [self didEnterPreloadState]; } -- (void)didEnterPreloadState { +- (void)didEnterPreloadState +{ [super didEnterPreloadState]; - if (self.viewModel) { + if (self.nodeModel) { [self loadImage]; } } - - (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize { ASLayoutSpec *textSpec = [self textSpec]; @@ -253,7 +275,8 @@ const CGFloat kSoldOutGBHeight = 50.0; return soldOutOverlay; } -- (ASLayoutSpec *)textSpec { +- (ASLayoutSpec *)textSpec +{ CGFloat kInsetHorizontal = 16.0; CGFloat kInsetTop = 6.0; CGFloat kInsetBottom = 0.0; @@ -271,7 +294,7 @@ const CGFloat kSoldOutGBHeight = 50.0; NSArray *info1Children = @[self.firstInfoLabel, self.distanceLabel, horizontalSpacer1, self.originalPriceLabel]; NSArray *info2Children = @[self.secondInfoLabel, horizontalSpacer2, self.finalPriceLabel]; - if ([ItemNode isRTL]) { + if (ASIsRTL()) { info1Children = [[info1Children reverseObjectEnumerator] allObjects]; info2Children = [[info2Children reverseObjectEnumerator] allObjects]; } @@ -288,7 +311,8 @@ const CGFloat kSoldOutGBHeight = 50.0; return textWrapper; } -- (ASLayoutSpec *)imageSpecWithSize:(ASSizeRange)constrainedSize { +- (ASLayoutSpec *)imageSpecWithSize:(ASSizeRange)constrainedSize +{ CGFloat imageRatio = [self imageRatioFromSize:constrainedSize.max]; ASRatioLayoutSpec *imagePlace = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:imageRatio child:self.dealImageView]; @@ -310,34 +334,38 @@ const CGFloat kSoldOutGBHeight = 50.0; return soldOutLabelOverBackground; } - -+ (CGSize)sizeForWidth:(CGFloat)width { ++ (CGSize)sizeForWidth:(CGFloat)width +{ CGFloat height = [self scaledHeightForPreferredSize:[self preferredViewSize] scaledWidth:width]; return CGSizeMake(width, height); } -+ (CGSize)preferredViewSize { ++ (CGSize)preferredViewSize +{ return CGSizeMake(kDesignWidth, kDesignHeight); } -+ (CGFloat)scaledHeightForPreferredSize:(CGSize)preferredSize scaledWidth:(CGFloat)scaledWidth { ++ (CGFloat)scaledHeightForPreferredSize:(CGSize)preferredSize scaledWidth:(CGFloat)scaledWidth +{ CGFloat scale = scaledWidth / kDesignWidth; CGFloat scaledHeight = ceilf(scale * (kDesignHeight - kFixedLabelsAreaHeight)) + kFixedLabelsAreaHeight; return scaledHeight; } -#pragma mark - view operations +#pragma mark - Image -- (CGFloat)imageRatioFromSize:(CGSize)size { +- (CGFloat)imageRatioFromSize:(CGSize)size +{ CGFloat imageHeight = size.height - kFixedLabelsAreaHeight; CGFloat imageRatio = imageHeight / size.width; return imageRatio; } -- (CGSize)imageSize { +- (CGSize)imageSize +{ if (!CGSizeEqualToSize(self.dealImageView.frame.size, CGSizeZero)) { return self.dealImageView.frame.size; } else if (!CGSizeEqualToSize(self.calculatedSize, CGSizeZero)) { @@ -349,13 +377,14 @@ const CGFloat kSoldOutGBHeight = 50.0; } } -- (void)loadImage { +- (void)loadImage +{ CGSize imageSize = [self imageSize]; if (CGSizeEqualToSize(CGSizeZero, imageSize)) { return; } - NSURL *url = [self.viewModel imageURLWithSize:imageSize]; + NSURL *url = [self.nodeModel imageURLWithSize:imageSize]; // if we're trying to set the deal image to what it already was, skip the work if ([[url absoluteString] isEqualToString:[self.dealImageView.URL absoluteString]]) { diff --git a/examples/CatDealsCollectionView/Sample/ItemStyles.h b/examples/CatDealsCollectionView/Sample/ItemStyles.h index a5af90a017..09d3b81c88 100644 --- a/examples/CatDealsCollectionView/Sample/ItemStyles.h +++ b/examples/CatDealsCollectionView/Sample/ItemStyles.h @@ -1,20 +1,18 @@ // // ItemStyles.h -// Sample -// -// Created by Samuel Stow on 12/30/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) through the present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file 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/ItemStyles.m b/examples/CatDealsCollectionView/Sample/ItemStyles.m index 12871e3be1..690980d49c 100644 --- a/examples/CatDealsCollectionView/Sample/ItemStyles.m +++ b/examples/CatDealsCollectionView/Sample/ItemStyles.m @@ -1,20 +1,18 @@ // // ItemStyles.m -// Sample -// -// Created by Samuel Stow on 12/30/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) through the present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "ItemStyles.h" @@ -95,9 +93,10 @@ UIFont *kInfoFont; + (UIImage *)placeholderImage { static UIImage *__catFace = nil; - if (!__catFace) { + static dispatch_once_t onceToken; + dispatch_once (&onceToken, ^{ __catFace = [UIImage imageNamed:@"cat_face"]; - } + }); return __catFace; } diff --git a/examples/CatDealsCollectionView/Sample/ItemViewModel.m b/examples/CatDealsCollectionView/Sample/ItemViewModel.m index 4cf925e5d6..6206e399c2 100644 --- a/examples/CatDealsCollectionView/Sample/ItemViewModel.m +++ b/examples/CatDealsCollectionView/Sample/ItemViewModel.m @@ -35,7 +35,8 @@ NSArray *badges; return [[ItemViewModel alloc] init]; } -- (instancetype)init { +- (instancetype)init +{ self = [super init]; if (self) { static _Atomic(NSInteger) nextID = ATOMIC_VAR_INIT(1); @@ -45,11 +46,9 @@ NSArray *badges; _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; + _soldOutText = (arc4random() % 5 == 0) ? @"SOLD OUT" : nil; _distanceLabelText = [NSString stringWithFormat:@"%zd mi", [self randomNumberInRange:1 to:20]]; - BOOL isBadged = arc4random() % 2 == 0; - if (isBadged) { + if (arc4random() % 2 == 0) { _badgeText = [self randomObjectFromArray:badges]; } _catNumber = [self randomNumberInRange:1 to:10]; @@ -58,18 +57,20 @@ NSArray *badges; return self; } -- (NSURL *)imageURLWithSize:(CGSize)size { +- (NSURL *)imageURLWithSize:(CGSize)size +{ NSString *imageText = [NSString stringWithFormat:@"Fun cat pic %zd", self.labelNumber]; NSString *urlString = [NSString stringWithFormat:@"http://lorempixel.com/%zd/%zd/cats/%zd/%@", (NSInteger)roundl(size.width), (NSInteger)roundl(size.height), self.catNumber, imageText]; - urlString = [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; - + + urlString = [urlString stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLFragmentAllowedCharacterSet]]; return [NSURL URLWithString:urlString]; } // titles courtesy of http://www.catipsum.com/ -+ (void)initialize { ++ (void)initialize +{ titles = @[@"Leave fur on owners clothes intrigued by the shower", @"Meowwww", @"Immediately regret falling into bathtub stare out the window", diff --git a/examples/CatDealsCollectionView/Sample/LoadingNode.h b/examples/CatDealsCollectionView/Sample/LoadingNode.h index d144de01a9..6c07157265 100644 --- a/examples/CatDealsCollectionView/Sample/LoadingNode.h +++ b/examples/CatDealsCollectionView/Sample/LoadingNode.h @@ -1,20 +1,18 @@ // // LoadingNode.h -// Sample -// -// Created by Samuel Stow on 1/9/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) through the present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file 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/LoadingNode.m b/examples/CatDealsCollectionView/Sample/LoadingNode.m index 4fa29d6e3d..03a6825c65 100644 --- a/examples/CatDealsCollectionView/Sample/LoadingNode.m +++ b/examples/CatDealsCollectionView/Sample/LoadingNode.m @@ -1,41 +1,29 @@ // // LoadingNode.m -// Sample -// -// Created by Samuel Stow on 1/9/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) through the present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "LoadingNode.h" -#import -#import -#import #import -@interface LoadingNode () -{ +@implementation LoadingNode { ASDisplayNode *_loadingSpinner; } -@end - -@implementation LoadingNode - - -#pragma mark - -#pragma mark ASCellNode. +#pragma mark - ASCellNode - (instancetype)init { @@ -61,7 +49,6 @@ centerSpec.centeringOptions = ASCenterLayoutSpecCenteringXY; centerSpec.sizingOptions = ASCenterLayoutSpecSizingOptionDefault; centerSpec.child = _loadingSpinner; - return centerSpec; } diff --git a/examples/CatDealsCollectionView/Sample/PlaceholderNetworkImageNode.h b/examples/CatDealsCollectionView/Sample/PlaceholderNetworkImageNode.h index 53ba71544c..31470610bd 100644 --- a/examples/CatDealsCollectionView/Sample/PlaceholderNetworkImageNode.h +++ b/examples/CatDealsCollectionView/Sample/PlaceholderNetworkImageNode.h @@ -1,20 +1,18 @@ // // PlaceholderNetworkImageNode.h -// Sample -// -// Created by Samuel Stow on 1/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. // -// 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) through the present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file 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/PlaceholderNetworkImageNode.m b/examples/CatDealsCollectionView/Sample/PlaceholderNetworkImageNode.m index cb1b7e81bc..e7f0de47f9 100644 --- a/examples/CatDealsCollectionView/Sample/PlaceholderNetworkImageNode.m +++ b/examples/CatDealsCollectionView/Sample/PlaceholderNetworkImageNode.m @@ -1,29 +1,27 @@ // // PlaceholderNetworkImageNode.m -// Sample -// -// Created by Samuel Stow on 1/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. // -// 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) through the present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "PlaceholderNetworkImageNode.h" @implementation PlaceholderNetworkImageNode -- (UIImage *)placeholderImage { +- (UIImage *)placeholderImage +{ return self.placeholderImageOverride; } - @end diff --git a/examples/CatDealsCollectionView/Sample/ViewController.m b/examples/CatDealsCollectionView/Sample/ViewController.m index 6e63dabe09..78b58d67a6 100644 --- a/examples/CatDealsCollectionView/Sample/ViewController.m +++ b/examples/CatDealsCollectionView/Sample/ViewController.m @@ -50,7 +50,6 @@ static const CGFloat kHorizontalSectionPadding = 10.0f; self = [super initWithNode:_collectionNode]; if (self) { - self.title = @"Cat Deals"; _collectionNode.dataSource = self; @@ -89,7 +88,8 @@ static const CGFloat kHorizontalSectionPadding = 10.0f; [self fetchMoreCatsWithCompletion:nil]; } -- (void)fetchMoreCatsWithCompletion:(void (^)(BOOL))completion { +- (void)fetchMoreCatsWithCompletion:(void (^)(BOOL))completion +{ if (kSimulateWebResponse) { __weak typeof(self) weakSelf = self; void(^mockWebService)() = ^{ @@ -110,7 +110,8 @@ static const CGFloat kHorizontalSectionPadding = 10.0f; } } -- (void)appendMoreItems:(NSInteger)numberOfNewItems completion:(void (^)(BOOL))completion { +- (void)appendMoreItems:(NSInteger)numberOfNewItems completion:(void (^)(BOOL))completion +{ NSArray *newData = [self getMoreData:numberOfNewItems]; [_collectionNode performBatchAnimated:YES updates:^{ [_data addObjectsFromArray:newData]; @@ -119,7 +120,8 @@ static const CGFloat kHorizontalSectionPadding = 10.0f; } completion:completion]; } -- (NSArray *)getMoreData:(NSInteger)count { +- (NSArray *)getMoreData:(NSInteger)count +{ NSMutableArray *data = [NSMutableArray array]; for (int i = 0; i < count; i++) { [data addObject:[ItemViewModel randomItem]]; @@ -127,7 +129,8 @@ static const CGFloat kHorizontalSectionPadding = 10.0f; return data; } -- (NSArray *)indexPathsForObjects:(NSArray *)data { +- (NSArray *)indexPathsForObjects:(NSArray *)data +{ NSMutableArray *indexPaths = [NSMutableArray array]; NSInteger section = 0; for (ItemViewModel *viewModel in data) { @@ -138,7 +141,8 @@ static const CGFloat kHorizontalSectionPadding = 10.0f; return indexPaths; } -- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id)coordinator { +- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id)coordinator +{ [_collectionNode.view.collectionViewLayout invalidateLayout]; } @@ -151,12 +155,16 @@ static const CGFloat kHorizontalSectionPadding = 10.0f; - (ASCellNodeBlock)collectionNode:(ASCollectionNode *)collectionNode nodeBlockForItemAtIndexPath:(NSIndexPath *)indexPath { - ItemViewModel *viewModel = _data[indexPath.item]; return ^{ - return [[ItemNode alloc] initWithViewModel:viewModel]; + return [[ItemNode alloc] init]; }; } +- (id)collectionNode:(ASCollectionNode *)collectionNode nodeModelForItemAtIndexPath:(NSIndexPath *)indexPath +{ + return _data[indexPath.item]; +} + - (ASCellNode *)collectionNode:(ASCollectionNode *)collectionNode nodeForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { if ([kind isEqualToString:UICollectionElementKindSectionHeader] && indexPath.section == 0) { diff --git a/examples/CustomCollectionView-Swift/Podfile b/examples/CustomCollectionView-Swift/Podfile index a0eec32ed0..92a9acc9c8 100644 --- a/examples/CustomCollectionView-Swift/Podfile +++ b/examples/CustomCollectionView-Swift/Podfile @@ -1,5 +1,5 @@ source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '8.0' +platform :ios, '9.0' use_frameworks! diff --git a/examples/CustomCollectionView-Swift/Sample.xcodeproj/project.pbxproj b/examples/CustomCollectionView-Swift/Sample.xcodeproj/project.pbxproj index 6969496c87..e1912f909f 100755 --- a/examples/CustomCollectionView-Swift/Sample.xcodeproj/project.pbxproj +++ b/examples/CustomCollectionView-Swift/Sample.xcodeproj/project.pbxproj @@ -294,7 +294,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -344,7 +344,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; diff --git a/examples/CustomCollectionView/Podfile b/examples/CustomCollectionView/Podfile index b75e492fab..71a7f2c4b2 100644 --- a/examples/CustomCollectionView/Podfile +++ b/examples/CustomCollectionView/Podfile @@ -1,5 +1,5 @@ source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '8.0' +platform :ios, '9.0' target 'Sample' do pod 'Texture', :path => '../..' end diff --git a/examples/CustomCollectionView/Sample.xcodeproj/project.pbxproj b/examples/CustomCollectionView/Sample.xcodeproj/project.pbxproj index 91818f8f89..edaa0d1278 100644 --- a/examples/CustomCollectionView/Sample.xcodeproj/project.pbxproj +++ b/examples/CustomCollectionView/Sample.xcodeproj/project.pbxproj @@ -228,13 +228,16 @@ files = ( ); inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", ); name = "[CP] Check Pods Manifest.lock"; outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Sample-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/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# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -291,7 +294,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.1; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -327,7 +330,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.1; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; @@ -342,7 +345,6 @@ 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)"; @@ -357,7 +359,6 @@ 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)"; diff --git a/examples/CustomCollectionView/Sample/AppDelegate.m b/examples/CustomCollectionView/Sample/AppDelegate.m index c0769e5d5a..dec3c29fe7 100644 --- a/examples/CustomCollectionView/Sample/AppDelegate.m +++ b/examples/CustomCollectionView/Sample/AppDelegate.m @@ -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) through the present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file 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" @@ -23,7 +23,7 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; + self.window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds]; self.window.backgroundColor = [UIColor whiteColor]; self.window.rootViewController = [[ViewController alloc] init]; diff --git a/examples/HorizontalWithinVerticalScrolling/Podfile b/examples/HorizontalWithinVerticalScrolling/Podfile index b75e492fab..71a7f2c4b2 100644 --- a/examples/HorizontalWithinVerticalScrolling/Podfile +++ b/examples/HorizontalWithinVerticalScrolling/Podfile @@ -1,5 +1,5 @@ source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '8.0' +platform :ios, '9.0' target 'Sample' do pod 'Texture', :path => '../..' end diff --git a/examples/HorizontalWithinVerticalScrolling/Sample.xcodeproj/project.pbxproj b/examples/HorizontalWithinVerticalScrolling/Sample.xcodeproj/project.pbxproj index c999679905..6472106a7b 100644 --- a/examples/HorizontalWithinVerticalScrolling/Sample.xcodeproj/project.pbxproj +++ b/examples/HorizontalWithinVerticalScrolling/Sample.xcodeproj/project.pbxproj @@ -123,12 +123,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 */, - A79A9172A45D7C9595AA01CC /* Embed Pods Frameworks */, + F012A6F39E0149F18F564F50 /* [CP] Copy Pods Resources */, + A79A9172A45D7C9595AA01CC /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -185,14 +185,14 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - A79A9172A45D7C9595AA01CC /* Embed Pods Frameworks */ = { + A79A9172A45D7C9595AA01CC /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Embed Pods Frameworks"; + name = "[CP] Embed Pods Frameworks"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; @@ -200,29 +200,32 @@ 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 = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", ); - name = "Check Pods Manifest.lock"; + name = "[CP] Check Pods Manifest.lock"; outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Sample-checkManifestLockResult.txt", ); 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_PODFILE_DIR_PATH}/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# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\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; @@ -282,7 +285,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -317,7 +320,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; diff --git a/examples/Kittens/Podfile b/examples/Kittens/Podfile index b75e492fab..71a7f2c4b2 100644 --- a/examples/Kittens/Podfile +++ b/examples/Kittens/Podfile @@ -1,5 +1,5 @@ source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '8.0' +platform :ios, '9.0' target 'Sample' do pod 'Texture', :path => '../..' end diff --git a/examples/Kittens/Sample.xcodeproj/project.pbxproj b/examples/Kittens/Sample.xcodeproj/project.pbxproj index 7e5f1a9ef9..3c2cb1a592 100644 --- a/examples/Kittens/Sample.xcodeproj/project.pbxproj +++ b/examples/Kittens/Sample.xcodeproj/project.pbxproj @@ -125,12 +125,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 */, - 9C2078E0C7EEEFF207C7F6A9 /* Embed Pods Frameworks */, + F012A6F39E0149F18F564F50 /* [CP] Copy Pods Resources */, + 9C2078E0C7EEEFF207C7F6A9 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -187,14 +187,14 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 9C2078E0C7EEEFF207C7F6A9 /* Embed Pods Frameworks */ = { + 9C2078E0C7EEEFF207C7F6A9 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Embed Pods Frameworks"; + name = "[CP] Embed Pods Frameworks"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; @@ -202,29 +202,32 @@ 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 = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", ); - name = "Check Pods Manifest.lock"; + name = "[CP] Check Pods Manifest.lock"; outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Sample-checkManifestLockResult.txt", ); 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_PODFILE_DIR_PATH}/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# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\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; @@ -285,7 +288,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -320,7 +323,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; diff --git a/examples/LayoutSpecExamples-Swift/Podfile b/examples/LayoutSpecExamples-Swift/Podfile index 8458e64c60..83d2cae8bf 100644 --- a/examples/LayoutSpecExamples-Swift/Podfile +++ b/examples/LayoutSpecExamples-Swift/Podfile @@ -1,5 +1,5 @@ source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '8.0' +platform :ios, '9.0' use_frameworks! diff --git a/examples/LayoutSpecExamples-Swift/Sample.xcodeproj/project.pbxproj b/examples/LayoutSpecExamples-Swift/Sample.xcodeproj/project.pbxproj index d69d6e6d85..5ac0a8718a 100644 --- a/examples/LayoutSpecExamples-Swift/Sample.xcodeproj/project.pbxproj +++ b/examples/LayoutSpecExamples-Swift/Sample.xcodeproj/project.pbxproj @@ -310,7 +310,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -355,7 +355,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; diff --git a/examples/LayoutSpecExamples/Podfile b/examples/LayoutSpecExamples/Podfile index 922ff50ec1..08d1b7add6 100644 --- a/examples/LayoutSpecExamples/Podfile +++ b/examples/LayoutSpecExamples/Podfile @@ -1,5 +1,5 @@ source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '8.0' +platform :ios, '9.0' target 'Sample' do pod 'Texture', :path => '../..' end diff --git a/examples/LayoutSpecExamples/Sample.xcodeproj/project.pbxproj b/examples/LayoutSpecExamples/Sample.xcodeproj/project.pbxproj index 3c9041a2b3..c065fcab3f 100644 --- a/examples/LayoutSpecExamples/Sample.xcodeproj/project.pbxproj +++ b/examples/LayoutSpecExamples/Sample.xcodeproj/project.pbxproj @@ -299,7 +299,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -336,7 +336,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; diff --git a/examples/PagerNode/Podfile b/examples/PagerNode/Podfile index b75e492fab..71a7f2c4b2 100644 --- a/examples/PagerNode/Podfile +++ b/examples/PagerNode/Podfile @@ -1,5 +1,5 @@ source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '8.0' +platform :ios, '9.0' target 'Sample' do pod 'Texture', :path => '../..' end diff --git a/examples/PagerNode/Sample.xcodeproj/project.pbxproj b/examples/PagerNode/Sample.xcodeproj/project.pbxproj index 16d7ec8406..febfa871c5 100644 --- a/examples/PagerNode/Sample.xcodeproj/project.pbxproj +++ b/examples/PagerNode/Sample.xcodeproj/project.pbxproj @@ -118,12 +118,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 */, - 6E05308BEF86AD80AEB4EEE7 /* Embed Pods Frameworks */, + F012A6F39E0149F18F564F50 /* [CP] Copy Pods Resources */, + 6E05308BEF86AD80AEB4EEE7 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -180,14 +180,14 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 6E05308BEF86AD80AEB4EEE7 /* Embed Pods Frameworks */ = { + 6E05308BEF86AD80AEB4EEE7 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Embed Pods Frameworks"; + name = "[CP] Embed Pods Frameworks"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; @@ -195,29 +195,32 @@ 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 = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", ); - name = "Check Pods Manifest.lock"; + name = "[CP] Check Pods Manifest.lock"; outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Sample-checkManifestLockResult.txt", ); 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_PODFILE_DIR_PATH}/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# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\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; @@ -277,7 +280,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -312,7 +315,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; diff --git a/examples/SocialAppLayout-Inverted/Podfile b/examples/SocialAppLayout-Inverted/Podfile index b75e492fab..71a7f2c4b2 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' +platform :ios, '9.0' target 'Sample' do pod 'Texture', :path => '../..' end diff --git a/examples/SocialAppLayout-Inverted/Sample.xcodeproj/project.pbxproj b/examples/SocialAppLayout-Inverted/Sample.xcodeproj/project.pbxproj index b43fcaac6e..3ad0bf8772 100644 --- a/examples/SocialAppLayout-Inverted/Sample.xcodeproj/project.pbxproj +++ b/examples/SocialAppLayout-Inverted/Sample.xcodeproj/project.pbxproj @@ -319,13 +319,16 @@ files = ( ); inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", ); name = "[CP] Check Pods Manifest.lock"; outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Sample-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/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# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -385,7 +388,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -422,7 +425,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; diff --git a/examples/SocialAppLayout/Podfile b/examples/SocialAppLayout/Podfile index b75e492fab..71a7f2c4b2 100644 --- a/examples/SocialAppLayout/Podfile +++ b/examples/SocialAppLayout/Podfile @@ -1,5 +1,5 @@ source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '8.0' +platform :ios, '9.0' target 'Sample' do pod 'Texture', :path => '../..' end diff --git a/examples/SocialAppLayout/Sample.xcodeproj/project.pbxproj b/examples/SocialAppLayout/Sample.xcodeproj/project.pbxproj index abd0cb6653..b187eec74a 100644 --- a/examples/SocialAppLayout/Sample.xcodeproj/project.pbxproj +++ b/examples/SocialAppLayout/Sample.xcodeproj/project.pbxproj @@ -200,12 +200,12 @@ isa = PBXNativeTarget; buildConfigurationList = 3EEA4EFB1BECC4A1008A7F35 /* Build configuration list for PBXNativeTarget "Sample" */; buildPhases = ( - B5BD9E5609B2CB179EEE0CF4 /* Check Pods Manifest.lock */, + B5BD9E5609B2CB179EEE0CF4 /* [CP] Check Pods Manifest.lock */, 3EEA4EE01BECC4A1008A7F35 /* Sources */, 3EEA4EE11BECC4A1008A7F35 /* Frameworks */, 3EEA4EE21BECC4A1008A7F35 /* Resources */, - 21F2C1D9B53F9468EAF1653F /* Copy Pods Resources */, - 852437589F1D53B9483A75DF /* Embed Pods Frameworks */, + 21F2C1D9B53F9468EAF1653F /* [CP] Copy Pods Resources */, + 852437589F1D53B9483A75DF /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -281,14 +281,14 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 21F2C1D9B53F9468EAF1653F /* Copy Pods Resources */ = { + 21F2C1D9B53F9468EAF1653F /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Copy Pods Resources"; + name = "[CP] Copy Pods Resources"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; @@ -296,14 +296,14 @@ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Sample/Pods-Sample-resources.sh\"\n"; showEnvVarsInLog = 0; }; - 852437589F1D53B9483A75DF /* Embed Pods Frameworks */ = { + 852437589F1D53B9483A75DF /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Embed Pods Frameworks"; + name = "[CP] Embed Pods Frameworks"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; @@ -311,19 +311,22 @@ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Sample/Pods-Sample-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - B5BD9E5609B2CB179EEE0CF4 /* Check Pods Manifest.lock */ = { + B5BD9E5609B2CB179EEE0CF4 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", ); - name = "Check Pods Manifest.lock"; + name = "[CP] Check Pods Manifest.lock"; outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Sample-checkManifestLockResult.txt", ); 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_PODFILE_DIR_PATH}/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# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -383,7 +386,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -420,7 +423,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; diff --git a/examples/Swift/Podfile b/examples/Swift/Podfile index 8458e64c60..83d2cae8bf 100644 --- a/examples/Swift/Podfile +++ b/examples/Swift/Podfile @@ -1,5 +1,5 @@ source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '8.0' +platform :ios, '9.0' use_frameworks! diff --git a/examples/Swift/Sample.xcodeproj/project.pbxproj b/examples/Swift/Sample.xcodeproj/project.pbxproj index a377337a3a..533a738ea5 100644 --- a/examples/Swift/Sample.xcodeproj/project.pbxproj +++ b/examples/Swift/Sample.xcodeproj/project.pbxproj @@ -291,7 +291,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -336,7 +336,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; diff --git a/examples/VerticalWithinHorizontalScrolling/Podfile b/examples/VerticalWithinHorizontalScrolling/Podfile index b75e492fab..71a7f2c4b2 100644 --- a/examples/VerticalWithinHorizontalScrolling/Podfile +++ b/examples/VerticalWithinHorizontalScrolling/Podfile @@ -1,5 +1,5 @@ source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '8.0' +platform :ios, '9.0' target 'Sample' do pod 'Texture', :path => '../..' end diff --git a/examples/VerticalWithinHorizontalScrolling/Sample.xcodeproj/project.pbxproj b/examples/VerticalWithinHorizontalScrolling/Sample.xcodeproj/project.pbxproj index 7f7950060a..80493caefa 100644 --- a/examples/VerticalWithinHorizontalScrolling/Sample.xcodeproj/project.pbxproj +++ b/examples/VerticalWithinHorizontalScrolling/Sample.xcodeproj/project.pbxproj @@ -123,12 +123,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 */, - 6E94068CFAA7736333E7D960 /* Embed Pods Frameworks */, + F012A6F39E0149F18F564F50 /* [CP] Copy Pods Resources */, + 6E94068CFAA7736333E7D960 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -185,14 +185,14 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 6E94068CFAA7736333E7D960 /* Embed Pods Frameworks */ = { + 6E94068CFAA7736333E7D960 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Embed Pods Frameworks"; + name = "[CP] Embed Pods Frameworks"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; @@ -200,29 +200,32 @@ 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 = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", ); - name = "Check Pods Manifest.lock"; + name = "[CP] Check Pods Manifest.lock"; outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Sample-checkManifestLockResult.txt", ); 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_PODFILE_DIR_PATH}/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# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\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; @@ -282,7 +285,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -317,7 +320,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; diff --git a/examples/Videos/Podfile b/examples/Videos/Podfile index b75e492fab..71a7f2c4b2 100644 --- a/examples/Videos/Podfile +++ b/examples/Videos/Podfile @@ -1,5 +1,5 @@ source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '8.0' +platform :ios, '9.0' target 'Sample' do pod 'Texture', :path => '../..' end diff --git a/examples/Videos/Sample.xcodeproj/project.pbxproj b/examples/Videos/Sample.xcodeproj/project.pbxproj index b43c7e7fc8..8a04601003 100644 --- a/examples/Videos/Sample.xcodeproj/project.pbxproj +++ b/examples/Videos/Sample.xcodeproj/project.pbxproj @@ -125,12 +125,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 = ( ); @@ -191,14 +191,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; @@ -206,29 +206,32 @@ 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 = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", ); - name = "📦 Check Pods Manifest.lock"; + name = "[CP] Check Pods Manifest.lock"; outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Sample-checkManifestLockResult.txt", ); 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_PODFILE_DIR_PATH}/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# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\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; @@ -286,7 +289,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -321,7 +324,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; diff --git a/examples_extra/ASDKgram-Swift/ASDKgram-Swift/PhotoFeedModel.swift b/examples_extra/ASDKgram-Swift/ASDKgram-Swift/PhotoFeedModel.swift index 9a7cd52020..f344df34a7 100644 --- a/examples_extra/ASDKgram-Swift/ASDKgram-Swift/PhotoFeedModel.swift +++ b/examples_extra/ASDKgram-Swift/ASDKgram-Swift/PhotoFeedModel.swift @@ -67,7 +67,10 @@ final class PhotoFeedModel { private func fetchNextPageOfPopularPhotos(replaceData: Bool, numberOfAdditionsCompletion: @escaping (Int, NetworkingErrors?) -> ()) { if currentPage == totalPages, currentPage != 0 { - return numberOfAdditionsCompletion(0, .customError("No pages left to parse")) + DispatchQueue.main.async { + numberOfAdditionsCompletion(0, .customError("No pages left to parse")) + } + return } var newPhotos: [PhotoModel] = [] @@ -106,7 +109,9 @@ final class PhotoFeedModel { case .failure(let fail): print(fail) - numberOfAdditionsCompletion(0, fail) + DispatchQueue.main.async { + numberOfAdditionsCompletion(0, fail) + } } } } diff --git a/examples_extra/ASLayoutSpecPlayground-Swift/Podfile b/examples_extra/ASLayoutSpecPlayground-Swift/Podfile index f092b7ba8a..3b379097a0 100644 --- a/examples_extra/ASLayoutSpecPlayground-Swift/Podfile +++ b/examples_extra/ASLayoutSpecPlayground-Swift/Podfile @@ -1,5 +1,5 @@ source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '8.0' +platform :ios, '9.0' use_frameworks! target 'Sample' do pod 'Texture', :path => '../..' diff --git a/examples_extra/ASLayoutSpecPlayground-Swift/Sample.xcodeproj/project.pbxproj b/examples_extra/ASLayoutSpecPlayground-Swift/Sample.xcodeproj/project.pbxproj index ced4aae5c4..b77a5e410a 100644 --- a/examples_extra/ASLayoutSpecPlayground-Swift/Sample.xcodeproj/project.pbxproj +++ b/examples_extra/ASLayoutSpecPlayground-Swift/Sample.xcodeproj/project.pbxproj @@ -188,13 +188,16 @@ files = ( ); inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", ); name = "[CP] Check Pods Manifest.lock"; outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Sample-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/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# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -245,7 +248,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 7.1; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -280,7 +283,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 7.1; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; @@ -305,7 +308,6 @@ GCC_NO_COMMON_BLOCKS = YES; INFOPLIST_FILE = Sample/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; LIBRARY_SEARCH_PATHS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = com.facebook.AsyncDisplayKit.Sample; @@ -339,7 +341,6 @@ GCC_NO_COMMON_BLOCKS = YES; INFOPLIST_FILE = Sample/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; LIBRARY_SEARCH_PATHS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = com.facebook.AsyncDisplayKit.Sample; diff --git a/examples_extra/ASLayoutSpecPlayground-Swift/Sample.xcworkspace/contents.xcworkspacedata b/examples_extra/ASLayoutSpecPlayground-Swift/Sample.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000000..7b5a2f3050 --- /dev/null +++ b/examples_extra/ASLayoutSpecPlayground-Swift/Sample.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/examples_extra/ASTableViewStressTest/Podfile b/examples_extra/ASTableViewStressTest/Podfile index b75e492fab..71a7f2c4b2 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' +platform :ios, '9.0' target 'Sample' do pod 'Texture', :path => '../..' end diff --git a/examples_extra/ASTableViewStressTest/Sample.xcodeproj/project.pbxproj b/examples_extra/ASTableViewStressTest/Sample.xcodeproj/project.pbxproj index 3e68545a8d..c0da5a2015 100644 --- a/examples_extra/ASTableViewStressTest/Sample.xcodeproj/project.pbxproj +++ b/examples_extra/ASTableViewStressTest/Sample.xcodeproj/project.pbxproj @@ -113,12 +113,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 */, - 75CADB9ECE58AB74892E1D67 /* Embed Pods Frameworks */, + F012A6F39E0149F18F564F50 /* [CP] Copy Pods Resources */, + 75CADB9ECE58AB74892E1D67 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -175,14 +175,14 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 75CADB9ECE58AB74892E1D67 /* Embed Pods Frameworks */ = { + 75CADB9ECE58AB74892E1D67 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Embed Pods Frameworks"; + name = "[CP] Embed Pods Frameworks"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; @@ -190,29 +190,32 @@ 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 = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", ); - name = "Check Pods Manifest.lock"; + name = "[CP] Check Pods Manifest.lock"; outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Sample-checkManifestLockResult.txt", ); 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_PODFILE_DIR_PATH}/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# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\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; @@ -270,7 +273,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -305,7 +308,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; @@ -318,7 +321,6 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; INFOPLIST_FILE = Sample/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 7.1; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_NAME = "$(TARGET_NAME)"; }; @@ -330,7 +332,6 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; INFOPLIST_FILE = Sample/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 7.1; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_NAME = "$(TARGET_NAME)"; }; diff --git a/examples_extra/ASTableViewStressTest/Sample.xcworkspace/contents.xcworkspacedata b/examples_extra/ASTableViewStressTest/Sample.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000000..7b5a2f3050 --- /dev/null +++ b/examples_extra/ASTableViewStressTest/Sample.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/examples_extra/ASTraitCollection/Podfile b/examples_extra/ASTraitCollection/Podfile index b75e492fab..71a7f2c4b2 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' +platform :ios, '9.0' target 'Sample' do pod 'Texture', :path => '../..' end diff --git a/examples_extra/ASTraitCollection/Sample.xcodeproj/project.pbxproj b/examples_extra/ASTraitCollection/Sample.xcodeproj/project.pbxproj index 20186b37ad..ca9ec160eb 100644 --- a/examples_extra/ASTraitCollection/Sample.xcodeproj/project.pbxproj +++ b/examples_extra/ASTraitCollection/Sample.xcodeproj/project.pbxproj @@ -292,7 +292,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -327,7 +327,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; diff --git a/examples_extra/BackgroundPropertySetting/Podfile b/examples_extra/BackgroundPropertySetting/Podfile index f092b7ba8a..3b379097a0 100644 --- a/examples_extra/BackgroundPropertySetting/Podfile +++ b/examples_extra/BackgroundPropertySetting/Podfile @@ -1,5 +1,5 @@ source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '8.0' +platform :ios, '9.0' use_frameworks! target 'Sample' do pod 'Texture', :path => '../..' diff --git a/examples_extra/CollectionViewWithViewControllerCells/Podfile b/examples_extra/CollectionViewWithViewControllerCells/Podfile index b75e492fab..71a7f2c4b2 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' +platform :ios, '9.0' target 'Sample' do pod 'Texture', :path => '../..' end diff --git a/examples_extra/CollectionViewWithViewControllerCells/Sample.xcodeproj/project.pbxproj b/examples_extra/CollectionViewWithViewControllerCells/Sample.xcodeproj/project.pbxproj index 0c20d49148..718ba91ba8 100644 --- a/examples_extra/CollectionViewWithViewControllerCells/Sample.xcodeproj/project.pbxproj +++ b/examples_extra/CollectionViewWithViewControllerCells/Sample.xcodeproj/project.pbxproj @@ -284,7 +284,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.1; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -320,7 +320,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.1; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/examples_extra/EditableText/Podfile b/examples_extra/EditableText/Podfile index 922ff50ec1..08d1b7add6 100644 --- a/examples_extra/EditableText/Podfile +++ b/examples_extra/EditableText/Podfile @@ -1,5 +1,5 @@ source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '8.0' +platform :ios, '9.0' target 'Sample' do pod 'Texture', :path => '../..' end diff --git a/examples_extra/EditableText/Sample.xcodeproj/project.pbxproj b/examples_extra/EditableText/Sample.xcodeproj/project.pbxproj index 46aa0e9425..2afe6c7b24 100644 --- a/examples_extra/EditableText/Sample.xcodeproj/project.pbxproj +++ b/examples_extra/EditableText/Sample.xcodeproj/project.pbxproj @@ -273,7 +273,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -308,7 +308,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; diff --git a/examples_extra/Multiplex/Podfile b/examples_extra/Multiplex/Podfile index b75e492fab..71a7f2c4b2 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' +platform :ios, '9.0' target 'Sample' do pod 'Texture', :path => '../..' end diff --git a/examples_extra/Placeholders/Podfile b/examples_extra/Placeholders/Podfile index b75e492fab..71a7f2c4b2 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' +platform :ios, '9.0' target 'Sample' do pod 'Texture', :path => '../..' end diff --git a/examples_extra/Placeholders/Sample.xcodeproj/project.pbxproj b/examples_extra/Placeholders/Sample.xcodeproj/project.pbxproj index e360fd6cd7..6054b17bd9 100644 --- a/examples_extra/Placeholders/Sample.xcodeproj/project.pbxproj +++ b/examples_extra/Placeholders/Sample.xcodeproj/project.pbxproj @@ -298,7 +298,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -333,7 +333,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; diff --git a/examples_extra/RepoSearcher/Podfile b/examples_extra/RepoSearcher/Podfile index ac26415f17..21dc49860f 100644 --- a/examples_extra/RepoSearcher/Podfile +++ b/examples_extra/RepoSearcher/Podfile @@ -1,5 +1,4 @@ -# Uncomment the next line to define a global platform for your project -# platform :ios, '9.0' +platform :ios, '9.0' target 'RepoSearcher' do # Comment the next line if you're not using Swift and don't want to use dynamic frameworks diff --git a/examples_extra/RepoSearcher/RepoSearcher.xcodeproj/project.pbxproj b/examples_extra/RepoSearcher/RepoSearcher.xcodeproj/project.pbxproj index 9439f16227..aa470f0eb7 100644 --- a/examples_extra/RepoSearcher/RepoSearcher.xcodeproj/project.pbxproj +++ b/examples_extra/RepoSearcher/RepoSearcher.xcodeproj/project.pbxproj @@ -330,7 +330,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -380,7 +380,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; diff --git a/examples_extra/Shop/Shop.xcodeproj/project.pbxproj b/examples_extra/Shop/Shop.xcodeproj/project.pbxproj index 836c0e7716..5c9cc401b9 100644 --- a/examples_extra/Shop/Shop.xcodeproj/project.pbxproj +++ b/examples_extra/Shop/Shop.xcodeproj/project.pbxproj @@ -523,7 +523,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -573,7 +573,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; diff --git a/examples_extra/SynchronousConcurrency/Podfile b/examples_extra/SynchronousConcurrency/Podfile index b75e492fab..71a7f2c4b2 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' +platform :ios, '9.0' target 'Sample' do pod 'Texture', :path => '../..' end diff --git a/examples_extra/SynchronousConcurrency/Sample.xcodeproj/project.pbxproj b/examples_extra/SynchronousConcurrency/Sample.xcodeproj/project.pbxproj index 80d3e6e37e..f3964a9562 100644 --- a/examples_extra/SynchronousConcurrency/Sample.xcodeproj/project.pbxproj +++ b/examples_extra/SynchronousConcurrency/Sample.xcodeproj/project.pbxproj @@ -123,12 +123,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 */, - 0342F7A1563F38A62746D4B8 /* Embed Pods Frameworks */, + F012A6F39E0149F18F564F50 /* [CP] Copy Pods Resources */, + 0342F7A1563F38A62746D4B8 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -185,14 +185,14 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 0342F7A1563F38A62746D4B8 /* Embed Pods Frameworks */ = { + 0342F7A1563F38A62746D4B8 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Embed Pods Frameworks"; + name = "[CP] Embed Pods Frameworks"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; @@ -200,29 +200,32 @@ 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 = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", ); - name = "Check Pods Manifest.lock"; + name = "[CP] Check Pods Manifest.lock"; outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Sample-checkManifestLockResult.txt", ); 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_PODFILE_DIR_PATH}/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# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\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; @@ -282,7 +285,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -317,7 +320,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; diff --git a/examples_extra/SynchronousConcurrency/Sample.xcworkspace/contents.xcworkspacedata b/examples_extra/SynchronousConcurrency/Sample.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000000..7b5a2f3050 --- /dev/null +++ b/examples_extra/SynchronousConcurrency/Sample.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/examples_extra/SynchronousConcurrency/Sample/AsyncTableViewController.m b/examples_extra/SynchronousConcurrency/Sample/AsyncTableViewController.m index 3ab470844a..d87c22ea54 100644 --- a/examples_extra/SynchronousConcurrency/Sample/AsyncTableViewController.m +++ b/examples_extra/SynchronousConcurrency/Sample/AsyncTableViewController.m @@ -64,7 +64,7 @@ tuningParameters.leadingBufferScreenfuls = 0.5; tuningParameters.trailingBufferScreenfuls = 1.0; [_tableView setTuningParameters:tuningParameters forRangeType:ASLayoutRangeTypePreload]; - [_tableView setTuningParameters:tuningParameters forRangeType:ASLayoutRangeTypeRender]; + [_tableView setTuningParameters:tuningParameters forRangeType:ASLayoutRangeTypeDisplay]; [self.view addSubview:_tableView]; } @@ -75,7 +75,7 @@ { return ^{ RandomCoreGraphicsNode *elementNode = [[RandomCoreGraphicsNode alloc] init]; - elementNode.size = ASRelativeSizeRangeMakeWithExactCGSize(CGSizeMake(320, 100)); + elementNode.style.preferredSize = CGSizeMake(320, 100); return elementNode; }; } diff --git a/examples_extra/SynchronousConcurrency/Sample/AsyncViewController.h b/examples_extra/SynchronousConcurrency/Sample/AsyncViewController.h index fd357593ca..f307e65d6b 100644 --- a/examples_extra/SynchronousConcurrency/Sample/AsyncViewController.h +++ b/examples_extra/SynchronousConcurrency/Sample/AsyncViewController.h @@ -17,7 +17,7 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // -#import "ASViewController.h" +#import @interface AsyncViewController : ASViewController diff --git a/examples_extra/SynchronousConcurrency/Sample/RandomCoreGraphicsNode.m b/examples_extra/SynchronousConcurrency/Sample/RandomCoreGraphicsNode.m index d2afbd6e17..66b490df06 100644 --- a/examples_extra/SynchronousConcurrency/Sample/RandomCoreGraphicsNode.m +++ b/examples_extra/SynchronousConcurrency/Sample/RandomCoreGraphicsNode.m @@ -86,7 +86,7 @@ - (CGSize)calculateSizeThatFits:(CGSize)constrainedSize { - [_textNode measure:constrainedSize]; + [_textNode layoutThatFits:ASSizeRangeMake(CGSizeZero, constrainedSize)]; return CGSizeMake(constrainedSize.width, 100); } diff --git a/examples_extra/SynchronousKittens/Podfile b/examples_extra/SynchronousKittens/Podfile index b75e492fab..71a7f2c4b2 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' +platform :ios, '9.0' target 'Sample' do pod 'Texture', :path => '../..' end diff --git a/examples_extra/SynchronousKittens/Sample.xcodeproj/project.pbxproj b/examples_extra/SynchronousKittens/Sample.xcodeproj/project.pbxproj index e7ecd2fc26..05a646e3c3 100644 --- a/examples_extra/SynchronousKittens/Sample.xcodeproj/project.pbxproj +++ b/examples_extra/SynchronousKittens/Sample.xcodeproj/project.pbxproj @@ -282,7 +282,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -317,7 +317,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; diff --git a/examples_extra/TextStressTest/Podfile b/examples_extra/TextStressTest/Podfile index 6670022698..73e26195cd 100644 --- a/examples_extra/TextStressTest/Podfile +++ b/examples_extra/TextStressTest/Podfile @@ -1,5 +1,5 @@ source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '8.0' +platform :ios, '9.0' target 'Sample' do pod 'Texture/Yoga', :path => '../..' end diff --git a/examples_extra/TextStressTest/Sample.xcodeproj/project.pbxproj b/examples_extra/TextStressTest/Sample.xcodeproj/project.pbxproj index 86a3f35f70..806d9a2e8e 100644 --- a/examples_extra/TextStressTest/Sample.xcodeproj/project.pbxproj +++ b/examples_extra/TextStressTest/Sample.xcodeproj/project.pbxproj @@ -292,7 +292,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -327,7 +327,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; diff --git a/examples_extra/TextStressTest/Sample.xcworkspace/contents.xcworkspacedata b/examples_extra/TextStressTest/Sample.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000000..7b5a2f3050 --- /dev/null +++ b/examples_extra/TextStressTest/Sample.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/examples_extra/VideoTableView/Podfile b/examples_extra/VideoTableView/Podfile index b75e492fab..71a7f2c4b2 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' +platform :ios, '9.0' target 'Sample' do pod 'Texture', :path => '../..' end diff --git a/examples_extra/VideoTableView/Sample.xcodeproj/project.pbxproj b/examples_extra/VideoTableView/Sample.xcodeproj/project.pbxproj index 8bf0789b10..c7a68ff0bf 100644 --- a/examples_extra/VideoTableView/Sample.xcodeproj/project.pbxproj +++ b/examples_extra/VideoTableView/Sample.xcodeproj/project.pbxproj @@ -282,7 +282,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -317,7 +317,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; diff --git a/smoke-tests/Framework/Sample.xcodeproj/project.pbxproj b/smoke-tests/Framework/Sample.xcodeproj/project.pbxproj index b4eeb8cc2c..62bb5e98b1 100644 --- a/smoke-tests/Framework/Sample.xcodeproj/project.pbxproj +++ b/smoke-tests/Framework/Sample.xcodeproj/project.pbxproj @@ -298,7 +298,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -334,7 +334,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; diff --git a/smoke-tests/Life Without CocoaPods/Life Without CocoaPods.xcodeproj/project.pbxproj b/smoke-tests/Life Without CocoaPods/Life Without CocoaPods.xcodeproj/project.pbxproj index 75e17d52f7..dd795b7545 100644 --- a/smoke-tests/Life Without CocoaPods/Life Without CocoaPods.xcodeproj/project.pbxproj +++ b/smoke-tests/Life Without CocoaPods/Life Without CocoaPods.xcodeproj/project.pbxproj @@ -410,7 +410,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -447,7 +447,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES;