mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-17 03:40:18 +00:00
Merge commit '2618c50073092e99fe1df5213ba6a0619e908988'
# Conflicts: # AsyncDisplayKit.xcodeproj/project.pbxproj # Source/Private/ASDisplayNode+AsyncDisplay.mm
This commit is contained in:
commit
3a2c2ea690
@ -99,6 +99,7 @@
|
|||||||
3917EBD41E9C2FC400D04A01 /* _ASCollectionReusableView.h in Headers */ = {isa = PBXBuildFile; fileRef = 3917EBD21E9C2FC400D04A01 /* _ASCollectionReusableView.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
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 */; };
|
3917EBD51E9C2FC400D04A01 /* _ASCollectionReusableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3917EBD31E9C2FC400D04A01 /* _ASCollectionReusableView.m */; };
|
||||||
3C9C128519E616EF00E942A0 /* ASTableViewTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3C9C128419E616EF00E942A0 /* ASTableViewTests.mm */; };
|
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 */; };
|
4E9127691F64157600499623 /* ASRunLoopQueueTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E9127681F64157600499623 /* ASRunLoopQueueTests.m */; };
|
||||||
509E68601B3AED8E009B9150 /* ASScrollDirection.m in Sources */ = {isa = PBXBuildFile; fileRef = 205F0E111B371BD7007741D0 /* ASScrollDirection.m */; };
|
509E68601B3AED8E009B9150 /* ASScrollDirection.m in Sources */ = {isa = PBXBuildFile; fileRef = 205F0E111B371BD7007741D0 /* ASScrollDirection.m */; };
|
||||||
509E68611B3AEDA0009B9150 /* ASAbstractLayoutController.h in Headers */ = {isa = PBXBuildFile; fileRef = 205F0E171B37339C007741D0 /* ASAbstractLayoutController.h */; };
|
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 */; };
|
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 */; };
|
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 */; };
|
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 */; };
|
CCDD148B1EEDCD9D0020834E /* ASCollectionModernDataSourceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CCDD148A1EEDCD9D0020834E /* ASCollectionModernDataSourceTests.m */; };
|
||||||
CCE4F9B31F0D60AC00062E4E /* ASIntegerMapTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CCE4F9B21F0D60AC00062E4E /* ASIntegerMapTests.m */; };
|
CCE4F9B31F0D60AC00062E4E /* ASIntegerMapTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CCE4F9B21F0D60AC00062E4E /* ASIntegerMapTests.m */; };
|
||||||
CCE4F9B51F0DA4F300062E4E /* ASLayoutEngineTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = CCE4F9B41F0DA4F300062E4E /* ASLayoutEngineTests.mm */; };
|
CCE4F9B51F0DA4F300062E4E /* ASLayoutEngineTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = CCE4F9B41F0DA4F300062E4E /* ASLayoutEngineTests.mm */; };
|
||||||
CCE4F9BA1F0DBB5000062E4E /* ASLayoutTestNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = CCE4F9B71F0DBA5000062E4E /* ASLayoutTestNode.mm */; };
|
CCE4F9BA1F0DBB5000062E4E /* ASLayoutTestNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = CCE4F9B71F0DBA5000062E4E /* ASLayoutTestNode.mm */; };
|
||||||
CCE4F9BE1F0ECE5200062E4E /* ASTLayoutFixture.mm in Sources */ = {isa = PBXBuildFile; fileRef = CCE4F9BD1F0ECE5200062E4E /* ASTLayoutFixture.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, ); }; };
|
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, ); }; };
|
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 */; };
|
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 = "<group>"; };
|
3917EBD21E9C2FC400D04A01 /* _ASCollectionReusableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _ASCollectionReusableView.h; sourceTree = "<group>"; };
|
||||||
3917EBD31E9C2FC400D04A01 /* _ASCollectionReusableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = _ASCollectionReusableView.m; sourceTree = "<group>"; };
|
3917EBD31E9C2FC400D04A01 /* _ASCollectionReusableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = _ASCollectionReusableView.m; sourceTree = "<group>"; };
|
||||||
3C9C128419E616EF00E942A0 /* ASTableViewTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = ASTableViewTests.mm; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
|
3C9C128419E616EF00E942A0 /* ASTableViewTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = ASTableViewTests.mm; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
|
||||||
|
4496D0721FA9EA6B001CC8D5 /* ASTraitCollectionTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ASTraitCollectionTests.m; sourceTree = "<group>"; };
|
||||||
464052191A3F83C40061C0BA /* ASDataController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = ASDataController.h; sourceTree = "<group>"; };
|
464052191A3F83C40061C0BA /* ASDataController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = ASDataController.h; sourceTree = "<group>"; };
|
||||||
4640521A1A3F83C40061C0BA /* ASDataController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = ASDataController.mm; sourceTree = "<group>"; };
|
4640521A1A3F83C40061C0BA /* ASDataController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = ASDataController.mm; sourceTree = "<group>"; };
|
||||||
4640521B1A3F83C40061C0BA /* ASTableLayoutController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASTableLayoutController.h; sourceTree = "<group>"; };
|
4640521B1A3F83C40061C0BA /* ASTableLayoutController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASTableLayoutController.h; sourceTree = "<group>"; };
|
||||||
@ -895,6 +902,8 @@
|
|||||||
CCCCCCD41EC3EF060087FE10 /* NSParagraphStyle+ASText.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSParagraphStyle+ASText.m"; sourceTree = "<group>"; };
|
CCCCCCD41EC3EF060087FE10 /* NSParagraphStyle+ASText.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSParagraphStyle+ASText.m"; sourceTree = "<group>"; };
|
||||||
CCCCCCE51EC3F0FC0087FE10 /* NSAttributedString+ASText.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSAttributedString+ASText.h"; sourceTree = "<group>"; };
|
CCCCCCE51EC3F0FC0087FE10 /* NSAttributedString+ASText.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSAttributedString+ASText.h"; sourceTree = "<group>"; };
|
||||||
CCCCCCE61EC3F0FC0087FE10 /* NSAttributedString+ASText.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSAttributedString+ASText.m"; sourceTree = "<group>"; };
|
CCCCCCE61EC3F0FC0087FE10 /* NSAttributedString+ASText.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSAttributedString+ASText.m"; sourceTree = "<group>"; };
|
||||||
|
CCDC9B4B200991D10063C1F8 /* ASGraphicsContext.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ASGraphicsContext.h; sourceTree = "<group>"; };
|
||||||
|
CCDC9B4C200991D10063C1F8 /* ASGraphicsContext.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ASGraphicsContext.m; sourceTree = "<group>"; };
|
||||||
CCDD148A1EEDCD9D0020834E /* ASCollectionModernDataSourceTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASCollectionModernDataSourceTests.m; sourceTree = "<group>"; };
|
CCDD148A1EEDCD9D0020834E /* ASCollectionModernDataSourceTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASCollectionModernDataSourceTests.m; sourceTree = "<group>"; };
|
||||||
CCE04B1E1E313EA7006AEBBB /* ASSectionController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASSectionController.h; sourceTree = "<group>"; };
|
CCE04B1E1E313EA7006AEBBB /* ASSectionController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASSectionController.h; sourceTree = "<group>"; };
|
||||||
CCE04B201E313EB9006AEBBB /* IGListAdapter+AsyncDisplayKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "IGListAdapter+AsyncDisplayKit.h"; sourceTree = "<group>"; };
|
CCE04B201E313EB9006AEBBB /* IGListAdapter+AsyncDisplayKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "IGListAdapter+AsyncDisplayKit.h"; sourceTree = "<group>"; };
|
||||||
@ -907,6 +916,9 @@
|
|||||||
CCE4F9BB1F0EA67F00062E4E /* debugbreak.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = debugbreak.h; sourceTree = "<group>"; };
|
CCE4F9BB1F0EA67F00062E4E /* debugbreak.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = debugbreak.h; sourceTree = "<group>"; };
|
||||||
CCE4F9BC1F0ECE5200062E4E /* ASTLayoutFixture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASTLayoutFixture.h; sourceTree = "<group>"; };
|
CCE4F9BC1F0ECE5200062E4E /* ASTLayoutFixture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASTLayoutFixture.h; sourceTree = "<group>"; };
|
||||||
CCE4F9BD1F0ECE5200062E4E /* ASTLayoutFixture.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASTLayoutFixture.mm; sourceTree = "<group>"; };
|
CCE4F9BD1F0ECE5200062E4E /* ASTLayoutFixture.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASTLayoutFixture.mm; sourceTree = "<group>"; };
|
||||||
|
CCED5E3C2020D36800395C40 /* ASNetworkImageLoadInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ASNetworkImageLoadInfo.h; sourceTree = "<group>"; };
|
||||||
|
CCED5E3D2020D36800395C40 /* ASNetworkImageLoadInfo.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ASNetworkImageLoadInfo.m; sourceTree = "<group>"; };
|
||||||
|
CCED5E402020D41600395C40 /* ASNetworkImageLoadInfo+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ASNetworkImageLoadInfo+Private.h"; sourceTree = "<group>"; };
|
||||||
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 = "<group>"; };
|
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 = "<group>"; };
|
||||||
D785F6601A74327E00291744 /* ASScrollNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASScrollNode.h; sourceTree = "<group>"; };
|
D785F6601A74327E00291744 /* ASScrollNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASScrollNode.h; sourceTree = "<group>"; };
|
||||||
D785F6611A74327E00291744 /* ASScrollNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASScrollNode.mm; sourceTree = "<group>"; };
|
D785F6611A74327E00291744 /* ASScrollNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASScrollNode.mm; sourceTree = "<group>"; };
|
||||||
@ -1068,6 +1080,8 @@
|
|||||||
058D09B1195D04C000B7D73C /* Source */ = {
|
058D09B1195D04C000B7D73C /* Source */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
CCED5E3C2020D36800395C40 /* ASNetworkImageLoadInfo.h */,
|
||||||
|
CCED5E3D2020D36800395C40 /* ASNetworkImageLoadInfo.m */,
|
||||||
CCE04B1D1E313E99006AEBBB /* Collection Data Adapter */,
|
CCE04B1D1E313E99006AEBBB /* Collection Data Adapter */,
|
||||||
CC58AA4A1E398E1D002C8CB4 /* ASBlockTypes.h */,
|
CC58AA4A1E398E1D002C8CB4 /* ASBlockTypes.h */,
|
||||||
DBDB83921C6E879900D0098C /* ASPagerFlowLayout.h */,
|
DBDB83921C6E879900D0098C /* ASPagerFlowLayout.h */,
|
||||||
@ -1252,6 +1266,7 @@
|
|||||||
695BE2541DC1245C008E6EA5 /* ASWrapperSpecSnapshotTests.mm */,
|
695BE2541DC1245C008E6EA5 /* ASWrapperSpecSnapshotTests.mm */,
|
||||||
699B83501E3C1BA500433FA4 /* ASLayoutSpecTests.m */,
|
699B83501E3C1BA500433FA4 /* ASLayoutSpecTests.m */,
|
||||||
4E9127681F64157600499623 /* ASRunLoopQueueTests.m */,
|
4E9127681F64157600499623 /* ASRunLoopQueueTests.m */,
|
||||||
|
4496D0721FA9EA6B001CC8D5 /* ASTraitCollectionTests.m */,
|
||||||
);
|
);
|
||||||
path = Tests;
|
path = Tests;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -1270,6 +1285,8 @@
|
|||||||
058D09E1195D050800B7D73C /* Details */ = {
|
058D09E1195D050800B7D73C /* Details */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
CCDC9B4B200991D10063C1F8 /* ASGraphicsContext.h */,
|
||||||
|
CCDC9B4C200991D10063C1F8 /* ASGraphicsContext.m */,
|
||||||
CC5601391F06E9A700DC4FBE /* ASIntegerMap.h */,
|
CC5601391F06E9A700DC4FBE /* ASIntegerMap.h */,
|
||||||
CC56013A1F06E9A700DC4FBE /* ASIntegerMap.mm */,
|
CC56013A1F06E9A700DC4FBE /* ASIntegerMap.mm */,
|
||||||
CC0F885E1E4280B800576FED /* _ASCollectionViewCell.h */,
|
CC0F885E1E4280B800576FED /* _ASCollectionViewCell.h */,
|
||||||
@ -1367,6 +1384,7 @@
|
|||||||
058D0A01195D050800B7D73C /* Private */ = {
|
058D0A01195D050800B7D73C /* Private */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
CCED5E402020D41600395C40 /* ASNetworkImageLoadInfo+Private.h */,
|
||||||
CCA282CE1E9EBF6C0037E8B7 /* ASTipsWindow.h */,
|
CCA282CE1E9EBF6C0037E8B7 /* ASTipsWindow.h */,
|
||||||
CCA282CF1E9EBF6C0037E8B7 /* ASTipsWindow.m */,
|
CCA282CF1E9EBF6C0037E8B7 /* ASTipsWindow.m */,
|
||||||
CCA282C61E9EB64B0037E8B7 /* ASDisplayNodeTipState.h */,
|
CCA282C61E9EB64B0037E8B7 /* ASDisplayNodeTipState.h */,
|
||||||
@ -1836,6 +1854,7 @@
|
|||||||
68EE0DBE1C1B4ED300BA1B99 /* ASMainSerialQueue.h in Headers */,
|
68EE0DBE1C1B4ED300BA1B99 /* ASMainSerialQueue.h in Headers */,
|
||||||
CCCCCCE11EC3EF060087FE10 /* ASTextUtilities.h in Headers */,
|
CCCCCCE11EC3EF060087FE10 /* ASTextUtilities.h in Headers */,
|
||||||
B350624B1B010EFD0018CF92 /* _ASPendingState.h in Headers */,
|
B350624B1B010EFD0018CF92 /* _ASPendingState.h in Headers */,
|
||||||
|
CCDC9B4D200991D10063C1F8 /* ASGraphicsContext.h in Headers */,
|
||||||
E5C347B11ECB3D9200EC4BE4 /* ASBatchFetchingDelegate.h in Headers */,
|
E5C347B11ECB3D9200EC4BE4 /* ASBatchFetchingDelegate.h in Headers */,
|
||||||
CC54A81C1D70079800296A24 /* ASDispatch.h in Headers */,
|
CC54A81C1D70079800296A24 /* ASDispatch.h in Headers */,
|
||||||
B350624D1B010EFD0018CF92 /* _ASScopeTimer.h in Headers */,
|
B350624D1B010EFD0018CF92 /* _ASScopeTimer.h in Headers */,
|
||||||
@ -1903,6 +1922,7 @@
|
|||||||
DE4843DC1C93EAC100A1F33B /* ASLayoutTransition.h in Headers */,
|
DE4843DC1C93EAC100A1F33B /* ASLayoutTransition.h in Headers */,
|
||||||
CC57EAF81E3939450034C595 /* ASTableView+Undeprecated.h in Headers */,
|
CC57EAF81E3939450034C595 /* ASTableView+Undeprecated.h in Headers */,
|
||||||
254C6B781BF94DF4003EC431 /* ASTextKitContext.h in Headers */,
|
254C6B781BF94DF4003EC431 /* ASTextKitContext.h in Headers */,
|
||||||
|
CCED5E412020D49D00395C40 /* ASNetworkImageLoadInfo+Private.h in Headers */,
|
||||||
9CDC18CD1B910E12004965E2 /* ASLayoutElementPrivate.h in Headers */,
|
9CDC18CD1B910E12004965E2 /* ASLayoutElementPrivate.h in Headers */,
|
||||||
B35062201B010EFD0018CF92 /* ASLayoutController.h in Headers */,
|
B35062201B010EFD0018CF92 /* ASLayoutController.h in Headers */,
|
||||||
B35062211B010EFD0018CF92 /* ASLayoutRangeType.h in Headers */,
|
B35062211B010EFD0018CF92 /* ASLayoutRangeType.h in Headers */,
|
||||||
@ -1954,6 +1974,7 @@
|
|||||||
B35062391B010EFD0018CF92 /* ASThread.h in Headers */,
|
B35062391B010EFD0018CF92 /* ASThread.h in Headers */,
|
||||||
2C107F5B1BA9F54500F13DE5 /* AsyncDisplayKit.h in Headers */,
|
2C107F5B1BA9F54500F13DE5 /* AsyncDisplayKit.h in Headers */,
|
||||||
509E68651B3AEDC5009B9150 /* CoreGraphics+ASConvenience.h in Headers */,
|
509E68651B3AEDC5009B9150 /* CoreGraphics+ASConvenience.h in Headers */,
|
||||||
|
CCED5E3E2020D36800395C40 /* ASNetworkImageLoadInfo.h in Headers */,
|
||||||
B350623A1B010EFD0018CF92 /* NSMutableAttributedString+TextKitAdditions.h in Headers */,
|
B350623A1B010EFD0018CF92 /* NSMutableAttributedString+TextKitAdditions.h in Headers */,
|
||||||
044284FF1BAA3BD600D16268 /* UICollectionViewLayout+ASConvenience.h in Headers */,
|
044284FF1BAA3BD600D16268 /* UICollectionViewLayout+ASConvenience.h in Headers */,
|
||||||
B35062431B010EFD0018CF92 /* UIView+ASConvenience.h in Headers */,
|
B35062431B010EFD0018CF92 /* UIView+ASConvenience.h in Headers */,
|
||||||
@ -2159,6 +2180,7 @@
|
|||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
E51B78BF1F028ABF00E32604 /* ASLayoutFlatteningTests.m in Sources */,
|
E51B78BF1F028ABF00E32604 /* ASLayoutFlatteningTests.m in Sources */,
|
||||||
|
4496D0731FA9EA6B001CC8D5 /* ASTraitCollectionTests.m in Sources */,
|
||||||
29CDC2E21AAE70D000833CA4 /* ASBasicImageDownloaderContextTests.m in Sources */,
|
29CDC2E21AAE70D000833CA4 /* ASBasicImageDownloaderContextTests.m in Sources */,
|
||||||
CC583AD71EF9BDC100134156 /* NSInvocation+ASTestHelpers.m in Sources */,
|
CC583AD71EF9BDC100134156 /* NSInvocation+ASTestHelpers.m in Sources */,
|
||||||
CC051F1F1D7A286A006434CB /* ASCALayerTests.m in Sources */,
|
CC051F1F1D7A286A006434CB /* ASCALayerTests.m in Sources */,
|
||||||
@ -2271,6 +2293,7 @@
|
|||||||
E5B078001E69F4EB00C24B5B /* ASElementMap.m in Sources */,
|
E5B078001E69F4EB00C24B5B /* ASElementMap.m in Sources */,
|
||||||
9C8898BC1C738BA800D6B02E /* ASTextKitFontSizeAdjuster.mm in Sources */,
|
9C8898BC1C738BA800D6B02E /* ASTextKitFontSizeAdjuster.mm in Sources */,
|
||||||
690ED59B1E36D118000627C0 /* ASImageNode+tvOS.m in Sources */,
|
690ED59B1E36D118000627C0 /* ASImageNode+tvOS.m in Sources */,
|
||||||
|
CCDC9B4E200991D10063C1F8 /* ASGraphicsContext.m in Sources */,
|
||||||
CCCCCCD81EC3EF060087FE10 /* ASTextInput.m in Sources */,
|
CCCCCCD81EC3EF060087FE10 /* ASTextInput.m in Sources */,
|
||||||
34EFC7621B701CA400AD841F /* ASBackgroundLayoutSpec.mm in Sources */,
|
34EFC7621B701CA400AD841F /* ASBackgroundLayoutSpec.mm in Sources */,
|
||||||
DE8BEAC41C2DF3FC00D57C12 /* ASDelegateProxy.m in Sources */,
|
DE8BEAC41C2DF3FC00D57C12 /* ASDelegateProxy.m in Sources */,
|
||||||
@ -2283,6 +2306,7 @@
|
|||||||
E55D86331CA8A14000A0C26F /* ASLayoutElement.mm in Sources */,
|
E55D86331CA8A14000A0C26F /* ASLayoutElement.mm in Sources */,
|
||||||
68FC85EC1CE29C7D00EDD713 /* ASVisibilityProtocols.m in Sources */,
|
68FC85EC1CE29C7D00EDD713 /* ASVisibilityProtocols.m in Sources */,
|
||||||
CC55A7121E52A0F200594372 /* ASResponderChainEnumerator.m in Sources */,
|
CC55A7121E52A0F200594372 /* ASResponderChainEnumerator.m in Sources */,
|
||||||
|
CCED5E3F2020D36800395C40 /* ASNetworkImageLoadInfo.m in Sources */,
|
||||||
68B8A4E41CBDB958007E4543 /* ASWeakProxy.m in Sources */,
|
68B8A4E41CBDB958007E4543 /* ASWeakProxy.m in Sources */,
|
||||||
E5775B041F16759F00CAC9BC /* ASCollectionLayoutCache.mm in Sources */,
|
E5775B041F16759F00CAC9BC /* ASCollectionLayoutCache.mm in Sources */,
|
||||||
9C70F20A1CDBE949007D6C76 /* ASTableNode.mm in Sources */,
|
9C70F20A1CDBE949007D6C76 /* ASTableNode.mm in Sources */,
|
||||||
|
|||||||
36
CHANGELOG.md
36
CHANGELOG.md
@ -1,9 +1,36 @@
|
|||||||
## master
|
## master
|
||||||
* Add your own contributions to the next release on the line below this with your name.
|
* 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)
|
- [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)
|
- [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)
|
- [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)
|
- [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 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)
|
- [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)
|
- [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)
|
- 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)
|
- [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)
|
- [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
|
## 2.5.1
|
||||||
- [ASVideoNode] Fix unreleased time observer. [Flo Vouin](https://github.com/flovouin)
|
- [ASVideoNode] Fix unreleased time observer. [Flo Vouin](https://github.com/flovouin)
|
||||||
|
|||||||
2
Cartfile
2
Cartfile
@ -1,2 +1,2 @@
|
|||||||
github "pinterest/PINRemoteImage" "3.0.0-beta.13"
|
github "pinterest/PINRemoteImage" "3.0.0-beta.13"
|
||||||
github "pinterest/PINCache"
|
github "pinterest/PINCache" "3.0.1-beta.6"
|
||||||
|
|||||||
10
Dangerfile
10
Dangerfile
@ -52,7 +52,11 @@ def check_file_header(files_to_check, licenses)
|
|||||||
correct_license = false
|
correct_license = false
|
||||||
licenses.each do |license|
|
licenses.each do |license|
|
||||||
license_header = full_license(license, filename)
|
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
|
correct_license = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -67,7 +71,7 @@ end
|
|||||||
|
|
||||||
# Ensure new files have proper header
|
# Ensure new files have proper header
|
||||||
new_source_license_header = <<-HEREDOC
|
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");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
// You may obtain a copy of the License at
|
// 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
|
// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional
|
||||||
// grant of patent rights can be found in the PATENTS file in the same directory.
|
// grant of patent rights can 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");
|
// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
// You may obtain a copy of the License at
|
// You may obtain a copy of the License at
|
||||||
|
|||||||
2
Podfile
2
Podfile
@ -1,6 +1,6 @@
|
|||||||
source 'https://github.com/CocoaPods/Specs.git'
|
source 'https://github.com/CocoaPods/Specs.git'
|
||||||
|
|
||||||
platform :ios, '8.0'
|
platform :ios, '9.0'
|
||||||
|
|
||||||
target :'AsyncDisplayKitTests' do
|
target :'AsyncDisplayKitTests' do
|
||||||
pod 'OCMock', '~> 3.4'
|
pod 'OCMock', '~> 3.4'
|
||||||
|
|||||||
@ -192,6 +192,12 @@ typedef NS_ENUM(NSUInteger, ASCellNodeVisibilityEvent) {
|
|||||||
*/
|
*/
|
||||||
@property (nonatomic) UITableViewCellSelectionStyle selectionStyle;
|
@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.
|
/* @abstract The view used as the background of the cell when it is selected.
|
||||||
* ASTableView uses these properties when configuring UITableViewCells that host ASCellNodes.
|
* ASTableView uses these properties when configuring UITableViewCells that host ASCellNodes.
|
||||||
* ASCollectionView uses these properties when configuring UICollectionViewCells that host ASCellNodes.
|
* ASCollectionView uses these properties when configuring UICollectionViewCells that host ASCellNodes.
|
||||||
|
|||||||
@ -60,6 +60,7 @@
|
|||||||
|
|
||||||
// Use UITableViewCell defaults
|
// Use UITableViewCell defaults
|
||||||
_selectionStyle = UITableViewCellSelectionStyleDefault;
|
_selectionStyle = UITableViewCellSelectionStyleDefault;
|
||||||
|
_focusStyle = UITableViewCellFocusStyleDefault;
|
||||||
self.clipsToBounds = YES;
|
self.clipsToBounds = YES;
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
@ -89,7 +90,7 @@
|
|||||||
if ([_viewController isKindOfClass:[ASViewController class]]) {
|
if ([_viewController isKindOfClass:[ASViewController class]]) {
|
||||||
ASViewController *asViewController = (ASViewController *)_viewController;
|
ASViewController *asViewController = (ASViewController *)_viewController;
|
||||||
_viewControllerNode = asViewController.node;
|
_viewControllerNode = asViewController.node;
|
||||||
[_viewController view];
|
[_viewController loadViewIfNeeded];
|
||||||
} else {
|
} else {
|
||||||
// Careful to avoid retain cycle
|
// Careful to avoid retain cycle
|
||||||
UIViewController *viewController = _viewController;
|
UIViewController *viewController = _viewController;
|
||||||
|
|||||||
@ -632,6 +632,32 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
*/
|
*/
|
||||||
- (NSArray<NSString *> *)collectionNode:(ASCollectionNode *)collectionNode supplementaryElementKindsInSection:(NSInteger)section;
|
- (NSArray<NSString *> *)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:.
|
* Similar to -collectionView:cellForItemAtIndexPath:.
|
||||||
*
|
*
|
||||||
|
|||||||
@ -173,6 +173,18 @@
|
|||||||
return self;
|
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
|
#pragma mark ASDisplayNode
|
||||||
|
|
||||||
- (void)didLoad
|
- (void)didLoad
|
||||||
|
|||||||
@ -101,6 +101,10 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
|
|||||||
NSHashTable<ASCellNode *> *_cellsForLayoutUpdates;
|
NSHashTable<ASCellNode *> *_cellsForLayoutUpdates;
|
||||||
id<ASCollectionViewLayoutFacilitatorProtocol> _layoutFacilitator;
|
id<ASCollectionViewLayoutFacilitatorProtocol> _layoutFacilitator;
|
||||||
CGFloat _leadingScreensForBatching;
|
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;
|
BOOL _inverted;
|
||||||
|
|
||||||
NSUInteger _superBatchUpdateCount;
|
NSUInteger _superBatchUpdateCount;
|
||||||
@ -121,15 +125,6 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
|
|||||||
|
|
||||||
ASCollectionViewInvalidationStyle _nextLayoutInvalidationStyle;
|
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.
|
* 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.
|
* Counter used to keep track of nested batch updates.
|
||||||
*/
|
*/
|
||||||
NSInteger _batchUpdateCount;
|
NSInteger _batchUpdateCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keep a strong reference to node till view is ready to release.
|
||||||
|
*/
|
||||||
|
ASCollectionNode *_keepalive_node;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
unsigned int scrollViewDidScroll:1;
|
unsigned int scrollViewDidScroll:1;
|
||||||
unsigned int scrollViewWillBeginDragging:1;
|
unsigned int scrollViewWillBeginDragging:1;
|
||||||
@ -218,6 +218,8 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
|
|||||||
unsigned int numberOfSectionsInCollectionNode:1;
|
unsigned int numberOfSectionsInCollectionNode:1;
|
||||||
unsigned int collectionNodeNumberOfItemsInSection:1;
|
unsigned int collectionNodeNumberOfItemsInSection:1;
|
||||||
unsigned int collectionNodeContextForSection:1;
|
unsigned int collectionNodeContextForSection:1;
|
||||||
|
unsigned int collectionNodeCanMoveItem:1;
|
||||||
|
unsigned int collectionNodeMoveItem:1;
|
||||||
|
|
||||||
// Whether this data source conforms to ASCollectionDataSourceInterop
|
// Whether this data source conforms to ASCollectionDataSourceInterop
|
||||||
unsigned int interop:1;
|
unsigned int interop:1;
|
||||||
@ -310,10 +312,6 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
|
|||||||
|
|
||||||
[self registerClass:[_ASCollectionViewCell class] forCellWithReuseIdentifier:kReuseIdentifier];
|
[self registerClass:[_ASCollectionViewCell class] forCellWithReuseIdentifier:kReuseIdentifier];
|
||||||
|
|
||||||
if (!AS_AT_LEAST_IOS9) {
|
|
||||||
_retainedLayer = self.layer;
|
|
||||||
}
|
|
||||||
|
|
||||||
[self _configureCollectionViewLayout:layout];
|
[self _configureCollectionViewLayout:layout];
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
@ -454,6 +452,8 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
|
|||||||
_asyncDataSourceFlags.collectionNodeNodeBlockForSupplementaryElement = [_asyncDataSource respondsToSelector:@selector(collectionNode:nodeBlockForSupplementaryElementOfKind:atIndexPath:)];
|
_asyncDataSourceFlags.collectionNodeNodeBlockForSupplementaryElement = [_asyncDataSource respondsToSelector:@selector(collectionNode:nodeBlockForSupplementaryElementOfKind:atIndexPath:)];
|
||||||
_asyncDataSourceFlags.collectionNodeSupplementaryElementKindsInSection = [_asyncDataSource respondsToSelector:@selector(collectionNode:supplementaryElementKindsInSection:)];
|
_asyncDataSourceFlags.collectionNodeSupplementaryElementKindsInSection = [_asyncDataSource respondsToSelector:@selector(collectionNode:supplementaryElementKindsInSection:)];
|
||||||
_asyncDataSourceFlags.nodeModelForItem = [_asyncDataSource respondsToSelector:@selector(collectionNode:nodeModelForItemAtIndexPath:)];
|
_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)];
|
_asyncDataSourceFlags.interop = [_asyncDataSource conformsToProtocol:@protocol(ASCollectionDataSourceInterop)];
|
||||||
if (_asyncDataSourceFlags.interop) {
|
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
|
// Mimic UIKit's gating logic.
|
||||||
ASInterfaceState interfaceState = [self interfaceStateForRangeController:_rangeController];
|
// If the data source doesn't support moving, then all bets are off.
|
||||||
if (ASInterfaceStateIncludesVisible(interfaceState)) {
|
if (!_asyncDataSourceFlags.collectionNodeMoveItem) {
|
||||||
[_rangeController updateCurrentRangeWithMode:ASLayoutRangeModeFull];
|
return NO;
|
||||||
[self _checkForBatchFetching];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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) {
|
for (_ASCollectionViewCell *cell in _cellsForVisibilityUpdates) {
|
||||||
// _cellsForVisibilityUpdates only includes cells for ASCellNode subclasses with overrides of the visibility method.
|
// _cellsForVisibilityUpdates only includes cells for ASCellNode subclasses with overrides of the visibility method.
|
||||||
[cell cellNodeVisibilityEvent:ASCellNodeVisibilityEventVisibleRectChanged inScrollView:scrollView];
|
[cell cellNodeVisibilityEvent:ASCellNodeVisibilityEventVisibleRectChanged inScrollView:scrollView];
|
||||||
@ -1539,6 +1596,10 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
|
|||||||
|
|
||||||
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
|
- (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) {
|
for (_ASCollectionViewCell *cell in _cellsForVisibilityUpdates) {
|
||||||
[cell cellNodeVisibilityEvent:ASCellNodeVisibilityEventWillBeginDragging inScrollView:scrollView];
|
[cell cellNodeVisibilityEvent:ASCellNodeVisibilityEventWillBeginDragging inScrollView:scrollView];
|
||||||
}
|
}
|
||||||
@ -1971,28 +2032,6 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
|
|||||||
return _rangeController;
|
return _rangeController;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The UIKit version of this method is only available on iOS >= 9
|
|
||||||
- (NSArray<NSIndexPath *> *)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<UICollectionViewLayoutAttributes *> *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<ASCollectionElement *> *)visibleElementsForRangeController:(ASRangeController *)rangeController
|
- (NSHashTable<ASCollectionElement *> *)visibleElementsForRangeController:(ASRangeController *)rangeController
|
||||||
{
|
{
|
||||||
return ASPointerTableByFlatMapping(_visibleElements, id element, element);
|
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
|
- (void)rangeController:(ASRangeController *)rangeController updateWithChangeSet:(_ASHierarchyChangeSet *)changeSet updates:(dispatch_block_t)updates
|
||||||
{
|
{
|
||||||
ASDisplayNodeAssertMainThread();
|
ASDisplayNodeAssertMainThread();
|
||||||
if (!self.asyncDataSource || _superIsPendingDataLoad) {
|
if (!self.asyncDataSource || _superIsPendingDataLoad || _updatingInResponseToInteractiveMove) {
|
||||||
updates();
|
updates();
|
||||||
[changeSet executeCompletionHandlerWithFinished:NO];
|
[changeSet executeCompletionHandlerWithFinished:NO];
|
||||||
return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes
|
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
|
#pragma mark ASCALayerExtendedDelegate
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2278,23 +2331,6 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
|
|||||||
return;
|
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
|
@end
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -2,8 +2,13 @@
|
|||||||
// ASDisplayNode+Layout.mm
|
// ASDisplayNode+Layout.mm
|
||||||
// Texture
|
// Texture
|
||||||
//
|
//
|
||||||
// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved.
|
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// 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 not use this file except in compliance with the License.
|
||||||
// You may obtain a copy of the License at
|
// You may obtain a copy of the License at
|
||||||
//
|
//
|
||||||
@ -304,13 +309,24 @@ ASLayoutElementStyleExtensibilityForwarding
|
|||||||
}
|
}
|
||||||
|
|
||||||
CGSize boundsSizeForLayout = ASCeilSizeValues(bounds.size);
|
CGSize boundsSizeForLayout = ASCeilSizeValues(bounds.size);
|
||||||
|
NSUInteger calculatedVersion = _calculatedDisplayNodeLayout->version;
|
||||||
|
|
||||||
// Prefer a newer and not yet applied _pendingDisplayNodeLayout over _calculatedDisplayNodeLayout
|
// 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).
|
// If there is no such _pending, check if _calculated is valid to reuse (avoiding recalculation below).
|
||||||
BOOL pendingLayoutIsPreferred = (_pendingDisplayNodeLayout != nullptr
|
BOOL pendingLayoutIsPreferred = NO;
|
||||||
&& _pendingDisplayNodeLayout->version >= _layoutVersion
|
if (_pendingDisplayNodeLayout != nullptr) {
|
||||||
&& _pendingDisplayNodeLayout->version > _calculatedDisplayNodeLayout->version); // _pending is not yet applied
|
NSUInteger pendingVersion = _pendingDisplayNodeLayout->version;
|
||||||
BOOL calculatedLayoutIsReusable = (_calculatedDisplayNodeLayout->version >= _layoutVersion
|
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
|
&& (_calculatedDisplayNodeLayout->requestedLayoutFromAbove
|
||||||
|| CGSizeEqualToSize(_calculatedDisplayNodeLayout->layout.size, boundsSizeForLayout)));
|
|| CGSizeEqualToSize(_calculatedDisplayNodeLayout->layout.size, boundsSizeForLayout)));
|
||||||
if (!pendingLayoutIsPreferred && calculatedLayoutIsReusable) {
|
if (!pendingLayoutIsPreferred && calculatedLayoutIsReusable) {
|
||||||
|
|||||||
@ -94,12 +94,10 @@
|
|||||||
|
|
||||||
- (void)semanticContentAttributeDidChange:(UISemanticContentAttribute)attribute
|
- (void)semanticContentAttributeDidChange:(UISemanticContentAttribute)attribute
|
||||||
{
|
{
|
||||||
if (AS_AT_LEAST_IOS9) {
|
UIUserInterfaceLayoutDirection layoutDirection =
|
||||||
UIUserInterfaceLayoutDirection layoutDirection =
|
[UIView userInterfaceLayoutDirectionForSemanticContentAttribute:attribute];
|
||||||
[UIView userInterfaceLayoutDirectionForSemanticContentAttribute:attribute];
|
self.style.direction = (layoutDirection == UIUserInterfaceLayoutDirectionLeftToRight
|
||||||
self.style.direction = (layoutDirection == UIUserInterfaceLayoutDirectionLeftToRight
|
? YGDirectionLTR : YGDirectionRTL);
|
||||||
? YGDirectionLTR : YGDirectionRTL);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setYogaParent:(ASDisplayNode *)yogaParent
|
- (void)setYogaParent:(ASDisplayNode *)yogaParent
|
||||||
|
|||||||
@ -666,6 +666,12 @@ extern NSInteger const ASDefaultDrawingPriority;
|
|||||||
* @default ASCornerRoundingTypeDefaultSlowCALayer
|
* @default ASCornerRoundingTypeDefaultSlowCALayer
|
||||||
*/
|
*/
|
||||||
@property (nonatomic, assign) ASCornerRoundingType cornerRoundingType; // default=Slow CALayer .cornerRadius (offscreen rendering)
|
@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) CGFloat cornerRadius; // default=0.0
|
||||||
|
|
||||||
@property (nonatomic, assign) BOOL clipsToBounds; // default==NO
|
@property (nonatomic, assign) BOOL clipsToBounds; // default==NO
|
||||||
|
|||||||
@ -36,6 +36,7 @@
|
|||||||
#import <AsyncDisplayKit/ASDimension.h>
|
#import <AsyncDisplayKit/ASDimension.h>
|
||||||
#import <AsyncDisplayKit/ASDisplayNodeExtras.h>
|
#import <AsyncDisplayKit/ASDisplayNodeExtras.h>
|
||||||
#import <AsyncDisplayKit/ASEqualityHelpers.h>
|
#import <AsyncDisplayKit/ASEqualityHelpers.h>
|
||||||
|
#import <AsyncDisplayKit/ASGraphicsContext.h>
|
||||||
#import <AsyncDisplayKit/ASInternalHelpers.h>
|
#import <AsyncDisplayKit/ASInternalHelpers.h>
|
||||||
#import <AsyncDisplayKit/ASLayoutElementStylePrivate.h>
|
#import <AsyncDisplayKit/ASLayoutElementStylePrivate.h>
|
||||||
#import <AsyncDisplayKit/ASLayoutSpec.h>
|
#import <AsyncDisplayKit/ASLayoutSpec.h>
|
||||||
@ -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
|
// We have to forward declare the protocol as this place otherwise it will not compile compiling with an Base SDK < iOS 10
|
||||||
@protocol CALayerDelegate;
|
@protocol CALayerDelegate;
|
||||||
|
|
||||||
@interface ASDisplayNode () <UIGestureRecognizerDelegate, CALayerDelegate, _ASDisplayLayerDelegate>
|
@interface ASDisplayNode () <UIGestureRecognizerDelegate, CALayerDelegate, _ASDisplayLayerDelegate, ASCATransactionQueueObserving>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See ASDisplayNodeInternal.h for ivars
|
* See ASDisplayNodeInternal.h for ivars
|
||||||
@ -225,12 +226,6 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
|||||||
class_replaceMethod(self, @selector(_staticInitialize), staticInitialize, "v:@");
|
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
|
+ (Class)viewClass
|
||||||
{
|
{
|
||||||
return [_ASDisplayView class];
|
return [_ASDisplayView class];
|
||||||
@ -258,6 +253,11 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
|||||||
|
|
||||||
_viewClass = [self.class viewClass];
|
_viewClass = [self.class viewClass];
|
||||||
_layerClass = [self.class layerClass];
|
_layerClass = [self.class layerClass];
|
||||||
|
BOOL isSynchronous = ![_viewClass isSubclassOfClass:[_ASDisplayView class]]
|
||||||
|
|| ![_layerClass isSubclassOfClass:[_ASDisplayLayer class]];
|
||||||
|
setFlag(Synchronous, isSynchronous);
|
||||||
|
|
||||||
|
|
||||||
_contentsScaleForDisplay = ASScreenScale();
|
_contentsScaleForDisplay = ASScreenScale();
|
||||||
_drawingPriority = ASDefaultDrawingPriority;
|
_drawingPriority = ASDefaultDrawingPriority;
|
||||||
|
|
||||||
@ -1508,7 +1508,7 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
|
|||||||
BOOL isRight = (idx == 1 || idx == 2);
|
BOOL isRight = (idx == 1 || idx == 2);
|
||||||
|
|
||||||
CGSize size = CGSizeMake(radius + 1, radius + 1);
|
CGSize size = CGSizeMake(radius + 1, radius + 1);
|
||||||
UIGraphicsBeginImageContextWithOptions(size, NO, self.contentsScaleForDisplay);
|
ASGraphicsBeginImageContextWithOptions(size, NO, self.contentsScaleForDisplay);
|
||||||
|
|
||||||
CGContextRef ctx = UIGraphicsGetCurrentContext();
|
CGContextRef ctx = UIGraphicsGetCurrentContext();
|
||||||
if (isRight == YES) {
|
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.
|
// No lock needed, as _clipCornerLayers is only modified on the main thread.
|
||||||
CALayer *clipCornerLayer = _clipCornerLayers[idx];
|
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.bounds = CGRectMake(0.0, 0.0, size.width, size.height);
|
||||||
clipCornerLayer.anchorPoint = CGPointMake(isRight ? 1.0 : 0.0, isTop ? 1.0 : 0.0);
|
clipCornerLayer.anchorPoint = CGPointMake(isRight ? 1.0 : 0.0, isTop ? 1.0 : 0.0);
|
||||||
|
|
||||||
UIGraphicsEndImageContext();
|
|
||||||
}
|
}
|
||||||
[self _layoutClipCornersIfNeeded];
|
[self _layoutClipCornersIfNeeded];
|
||||||
});
|
});
|
||||||
@ -2742,7 +2740,7 @@ ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) {
|
|||||||
// Entered or exited range managed state.
|
// Entered or exited range managed state.
|
||||||
if ((newState & ASHierarchyStateRangeManaged) != (oldState & ASHierarchyStateRangeManaged)) {
|
if ((newState & ASHierarchyStateRangeManaged) != (oldState & ASHierarchyStateRangeManaged)) {
|
||||||
if (newState & ASHierarchyStateRangeManaged) {
|
if (newState & ASHierarchyStateRangeManaged) {
|
||||||
[self enterInterfaceState:self.supernode.interfaceState];
|
[self enterInterfaceState:self.supernode.pendingInterfaceState];
|
||||||
} else {
|
} else {
|
||||||
// The case of exiting a range-managed state should be fairly rare. Adding or removing the node
|
// 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),
|
// 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.isExitingHierarchy, @"You should never call -didExitHierarchy directly. Appearance is automatically managed by ASDisplayNode");
|
||||||
ASDisplayNodeAssert(!_flags.isEnteringHierarchy, @"ASDisplayNode inconsistency. __enterHierarchy and __exitHierarchy are mutually exclusive");
|
ASDisplayNodeAssert(!_flags.isEnteringHierarchy, @"ASDisplayNode inconsistency. __enterHierarchy and __exitHierarchy are mutually exclusive");
|
||||||
ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__);
|
ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__);
|
||||||
|
|
||||||
if (![self supportsRangeManagedInterfaceState]) {
|
// 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
|
||||||
self.interfaceState = ASInterfaceStateNone;
|
// 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.
|
||||||
} else {
|
// 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
|
||||||
// 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
|
// 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).
|
||||||
// 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.
|
// 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
|
||||||
// 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
|
// 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.
|
||||||
// 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).
|
if (ASInterfaceStateIncludesVisible(_pendingInterfaceState)) {
|
||||||
// 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
|
void(^exitVisibleInterfaceState)(void) = ^{
|
||||||
// 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.
|
// This block intentionally retains self.
|
||||||
|
__instanceLock__.lock();
|
||||||
if (ASInterfaceStateIncludesVisible(self.interfaceState)) {
|
unsigned isStillInHierarchy = _flags.isInHierarchy;
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
BOOL isVisible = ASInterfaceStateIncludesVisible(_pendingInterfaceState);
|
||||||
// This block intentionally retains self.
|
ASInterfaceState newState = (_pendingInterfaceState & ~ASInterfaceStateVisible);
|
||||||
__instanceLock__.lock();
|
__instanceLock__.unlock();
|
||||||
unsigned isInHierarchy = _flags.isInHierarchy;
|
|
||||||
BOOL isVisible = ASInterfaceStateIncludesVisible(_interfaceState);
|
if (!isStillInHierarchy && isVisible) {
|
||||||
ASInterfaceState newState = (_interfaceState & ~ASInterfaceStateVisible);
|
if (![self supportsRangeManagedInterfaceState]) {
|
||||||
__instanceLock__.unlock();
|
newState = ASInterfaceStateNone;
|
||||||
|
|
||||||
if (!isInHierarchy && isVisible) {
|
|
||||||
self.interfaceState = newState;
|
|
||||||
}
|
}
|
||||||
});
|
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
|
- (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
|
//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.
|
//did(Enter|Exit)(Display|Visible|Preload)State methods currently guarantee calling on main.
|
||||||
ASDisplayNodeAssertMainThread();
|
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
|
// 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__);
|
ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__);
|
||||||
|
|
||||||
ASInterfaceState oldState = ASInterfaceStateNone;
|
ASInterfaceState oldState = ASInterfaceStateNone;
|
||||||
|
ASInterfaceState newState = ASInterfaceStateNone;
|
||||||
{
|
{
|
||||||
ASDN::MutexLocker l(__instanceLock__);
|
ASDN::MutexLocker l(__instanceLock__);
|
||||||
if (_interfaceState == newState) {
|
// newPendingState will not be used when ASCATransactionQueue is enabled
|
||||||
return;
|
// and use _pendingInterfaceState instead for interfaceState update.
|
||||||
|
if ([[ASCATransactionQueue sharedQueue] disabled]) {
|
||||||
|
_pendingInterfaceState = newPendingState;
|
||||||
}
|
}
|
||||||
oldState = _interfaceState;
|
oldState = _interfaceState;
|
||||||
|
newState = _pendingInterfaceState;
|
||||||
|
if (newState == oldState) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
_interfaceState = newState;
|
_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.
|
// TODO: Trigger asynchronous measurement if it is not already cached or being calculated.
|
||||||
// if ((newState & ASInterfaceStateMeasureLayout) != (oldState & ASInterfaceStateMeasureLayout)) {
|
// if ((newState & ASInterfaceStateMeasureLayout) != (oldState & ASInterfaceStateMeasureLayout)) {
|
||||||
// }
|
// }
|
||||||
@ -2984,6 +3014,12 @@ ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) {
|
|||||||
[self interfaceStateDidChange:newState fromState:oldState];
|
[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
|
- (void)interfaceStateDidChange:(ASInterfaceState)newState fromState:(ASInterfaceState)oldState
|
||||||
{
|
{
|
||||||
// Subclass hook
|
// Subclass hook
|
||||||
@ -3081,16 +3117,15 @@ ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) {
|
|||||||
ASDisplayNodeAssertMainThread();
|
ASDisplayNodeAssertMainThread();
|
||||||
ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__);
|
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) {
|
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];
|
[self layoutIfNeeded];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -219,6 +219,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
|
|
||||||
- (BOOL)editableTextNodeShouldPaste:(ASEditableTextNode *)editableTextNode;
|
- (BOOL)editableTextNodeShouldPaste:(ASEditableTextNode *)editableTextNode;
|
||||||
- (ASEditableTextNodeTargetForAction * _Nullable)editableTextNodeTargetForAction:(SEL)action;
|
- (ASEditableTextNodeTargetForAction * _Nullable)editableTextNodeTargetForAction:(SEL)action;
|
||||||
|
- (BOOL)editableTextNodeShouldReturn:(ASEditableTextNode *)editableTextNode;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|||||||
@ -95,6 +95,7 @@
|
|||||||
|
|
||||||
@property (nonatomic, copy) bool (^shouldPaste)();
|
@property (nonatomic, copy) bool (^shouldPaste)();
|
||||||
@property (nonatomic, copy) ASEditableTextNodeTargetForAction *(^targetForActionImpl)(SEL);
|
@property (nonatomic, copy) ASEditableTextNodeTargetForAction *(^targetForActionImpl)(SEL);
|
||||||
|
@property (nonatomic, copy) bool (^shouldReturn)();
|
||||||
|
|
||||||
@end
|
@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
|
#endif
|
||||||
|
|
||||||
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
|
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
|
||||||
@ -305,6 +319,15 @@
|
|||||||
}
|
}
|
||||||
return nil;
|
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 = textView;
|
||||||
_textKitComponents.textView.scrollEnabled = _scrollEnabled;
|
_textKitComponents.textView.scrollEnabled = _scrollEnabled;
|
||||||
_textKitComponents.textView.delegate = self;
|
_textKitComponents.textView.delegate = self;
|
||||||
|
|||||||
@ -25,6 +25,7 @@
|
|||||||
#import <AsyncDisplayKit/ASDisplayNode+FrameworkSubclasses.h>
|
#import <AsyncDisplayKit/ASDisplayNode+FrameworkSubclasses.h>
|
||||||
#import <AsyncDisplayKit/ASDisplayNodeExtras.h>
|
#import <AsyncDisplayKit/ASDisplayNodeExtras.h>
|
||||||
#import <AsyncDisplayKit/ASDisplayNode+Beta.h>
|
#import <AsyncDisplayKit/ASDisplayNode+Beta.h>
|
||||||
|
#import <AsyncDisplayKit/ASGraphicsContext.h>
|
||||||
#import <AsyncDisplayKit/ASLayout.h>
|
#import <AsyncDisplayKit/ASLayout.h>
|
||||||
#import <AsyncDisplayKit/ASTextNode.h>
|
#import <AsyncDisplayKit/ASTextNode.h>
|
||||||
#import <AsyncDisplayKit/ASImageNode+AnimatedImagePrivate.h>
|
#import <AsyncDisplayKit/ASImageNode+AnimatedImagePrivate.h>
|
||||||
@ -218,11 +219,10 @@ typedef void (^ASImageNodeDrawParametersBlock)(ASWeakMapEntry *entry);
|
|||||||
|
|
||||||
ASDN::MutexLocker l(__instanceLock__);
|
ASDN::MutexLocker l(__instanceLock__);
|
||||||
|
|
||||||
UIGraphicsBeginImageContext(size);
|
ASGraphicsBeginImageContextWithOptions(size, NO, 1);
|
||||||
[self.placeholderColor setFill];
|
[self.placeholderColor setFill];
|
||||||
UIRectFill(CGRectMake(0, 0, size.width, size.height));
|
UIRectFill(CGRectMake(0, 0, size.width, size.height));
|
||||||
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
|
UIImage *image = ASGraphicsGetImageAndEndCurrentContext();
|
||||||
UIGraphicsEndImageContext();
|
|
||||||
|
|
||||||
return image;
|
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
|
+ (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.
|
// A5 processor for a 400x800 backingSize.
|
||||||
// Check for cancellation before we call it.
|
// Check for cancellation before we call it.
|
||||||
if (isCancelled()) {
|
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
|
// Use contentsScale of 1.0 and do the contentsScale handling in boundsSizeInPixels so ASCroppedImageBackingSizeAndDrawRectInBounds
|
||||||
// will do its rounding on pixel instead of point boundaries
|
// 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;
|
BOOL contextIsClean = YES;
|
||||||
|
|
||||||
@ -532,16 +532,13 @@ static ASDN::StaticMutex& cacheLock = *new ASDN::StaticMutex;
|
|||||||
key.didDisplayNodeContentWithRenderingContext(context, drawParameters);
|
key.didDisplayNodeContentWithRenderingContext(context, drawParameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The following `UIGraphicsGetImageFromCurrentImageContext` call will commonly take more than 20ms on an
|
// Check cancellation one last time before forming image.
|
||||||
// A5 processor. Check for cancellation before we call it.
|
|
||||||
if (isCancelled()) {
|
if (isCancelled()) {
|
||||||
UIGraphicsEndImageContext();
|
ASGraphicsEndImageContext();
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
UIImage *result = UIGraphicsGetImageFromCurrentImageContext();
|
UIImage *result = ASGraphicsGetImageAndEndCurrentContext();
|
||||||
|
|
||||||
UIGraphicsEndImageContext();
|
|
||||||
|
|
||||||
if (key.imageModificationBlock) {
|
if (key.imageModificationBlock) {
|
||||||
result = key.imageModificationBlock(result);
|
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)
|
extern asimagenode_modification_block_t ASImageNodeRoundBorderModificationBlock(CGFloat borderWidth, UIColor *borderColor)
|
||||||
{
|
{
|
||||||
return ^(UIImage *originalImage) {
|
return ^(UIImage *originalImage) {
|
||||||
UIGraphicsBeginImageContextWithOptions(originalImage.size, NO, originalImage.scale);
|
ASGraphicsBeginImageContextWithOptions(originalImage.size, NO, originalImage.scale);
|
||||||
UIBezierPath *roundOutline = [UIBezierPath bezierPathWithOvalInRect:(CGRect){CGPointZero, originalImage.size}];
|
UIBezierPath *roundOutline = [UIBezierPath bezierPathWithOvalInRect:(CGRect){CGPointZero, originalImage.size}];
|
||||||
|
|
||||||
// Make the image round
|
// Make the image round
|
||||||
@ -768,24 +765,21 @@ extern asimagenode_modification_block_t ASImageNodeRoundBorderModificationBlock(
|
|||||||
[roundOutline stroke];
|
[roundOutline stroke];
|
||||||
}
|
}
|
||||||
|
|
||||||
UIImage *modifiedImage = UIGraphicsGetImageFromCurrentImageContext();
|
return ASGraphicsGetImageAndEndCurrentContext();
|
||||||
UIGraphicsEndImageContext();
|
|
||||||
return modifiedImage;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
extern asimagenode_modification_block_t ASImageNodeTintColorModificationBlock(UIColor *color)
|
extern asimagenode_modification_block_t ASImageNodeTintColorModificationBlock(UIColor *color)
|
||||||
{
|
{
|
||||||
return ^(UIImage *originalImage) {
|
return ^(UIImage *originalImage) {
|
||||||
UIGraphicsBeginImageContextWithOptions(originalImage.size, NO, originalImage.scale);
|
ASGraphicsBeginImageContextWithOptions(originalImage.size, NO, originalImage.scale);
|
||||||
|
|
||||||
// Set color and render template
|
// Set color and render template
|
||||||
[color setFill];
|
[color setFill];
|
||||||
UIImage *templateImage = [originalImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
|
UIImage *templateImage = [originalImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
|
||||||
[templateImage drawAtPoint:CGPointZero blendMode:kCGBlendModeCopy alpha:1];
|
[templateImage drawAtPoint:CGPointZero blendMode:kCGBlendModeCopy alpha:1];
|
||||||
|
|
||||||
UIImage *modifiedImage = UIGraphicsGetImageFromCurrentImageContext();
|
UIImage *modifiedImage = ASGraphicsGetImageAndEndCurrentContext();
|
||||||
UIGraphicsEndImageContext();
|
|
||||||
|
|
||||||
// if the original image was stretchy, keep it stretchy
|
// if the original image was stretchy, keep it stretchy
|
||||||
if (!UIEdgeInsetsEqualToEdgeInsets(originalImage.capInsets, UIEdgeInsetsZero)) {
|
if (!UIEdgeInsetsEqualToEdgeInsets(originalImage.capInsets, UIEdgeInsetsZero)) {
|
||||||
|
|||||||
@ -26,6 +26,7 @@
|
|||||||
|
|
||||||
#import <AsyncDisplayKit/ASDisplayNode+FrameworkSubclasses.h>
|
#import <AsyncDisplayKit/ASDisplayNode+FrameworkSubclasses.h>
|
||||||
#import <AsyncDisplayKit/ASDisplayNodeExtras.h>
|
#import <AsyncDisplayKit/ASDisplayNodeExtras.h>
|
||||||
|
#import <AsyncDisplayKit/ASGraphicsContext.h>
|
||||||
#import <AsyncDisplayKit/ASInsetLayoutSpec.h>
|
#import <AsyncDisplayKit/ASInsetLayoutSpec.h>
|
||||||
#import <AsyncDisplayKit/ASInternalHelpers.h>
|
#import <AsyncDisplayKit/ASInternalHelpers.h>
|
||||||
#import <AsyncDisplayKit/ASLayout.h>
|
#import <AsyncDisplayKit/ASLayout.h>
|
||||||
@ -224,7 +225,7 @@
|
|||||||
|
|
||||||
CGRect finalImageRect = CGRectMake(0, 0, image.size.width, image.size.height);
|
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];
|
[image drawAtPoint:CGPointZero];
|
||||||
|
|
||||||
UIImage *pinImage;
|
UIImage *pinImage;
|
||||||
@ -256,8 +257,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
image = UIGraphicsGetImageFromCurrentImageContext();
|
image = ASGraphicsGetImageAndEndCurrentContext();
|
||||||
UIGraphicsEndImageContext();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
strongSelf.image = image;
|
strongSelf.image = image;
|
||||||
|
|||||||
@ -625,7 +625,7 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
|
|||||||
finishedLoadingBlock(downloadedImage, nextImageIdentifier, error);
|
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]) {
|
else if (ASPhotosFrameworkImageRequest *request = [ASPhotosFrameworkImageRequest requestWithURL:nextImageURL]) {
|
||||||
[self _loadPHAssetWithRequest:request identifier:nextImageIdentifier completion:^(UIImage *image, NSError *error) {
|
[self _loadPHAssetWithRequest:request identifier:nextImageIdentifier completion:^(UIImage *image, NSError *error) {
|
||||||
as_log_verbose(ASImageLoadingLog(), "Acquired image from Photos for %@ %@", weakSelf, nextImageIdentifier);
|
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(imageIdentifier, @"imageIdentifier is required");
|
||||||
ASDisplayNodeAssertNotNil(assetURL, @"assetURL is required");
|
ASDisplayNodeAssertNotNil(assetURL, @"assetURL is required");
|
||||||
ASDisplayNodeAssertNotNil(completionBlock, @"completionBlock 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];
|
ALAssetsLibrary *assetLibrary = [[ALAssetsLibrary alloc] init];
|
||||||
|
|
||||||
[assetLibrary assetForURL:assetURL resultBlock:^(ALAsset *asset) {
|
[assetLibrary assetForURL:assetURL resultBlock:^(ALAsset *asset) {
|
||||||
@ -685,6 +689,7 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
|
|||||||
} failureBlock:^(NSError *error) {
|
} failureBlock:^(NSError *error) {
|
||||||
completionBlock(nil, error);
|
completionBlock(nil, error);
|
||||||
}];
|
}];
|
||||||
|
#pragma clang diagnostic pop
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_loadPHAssetWithRequest:(ASPhotosFrameworkImageRequest *)request identifier:(id)imageIdentifier completion:(void (^)(UIImage *image, NSError *error))completionBlock
|
- (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
|
[self _setDownloadIdentifier:[_downloader downloadImageWithURL:imageURL
|
||||||
callbackQueue:dispatch_get_main_queue()
|
callbackQueue:dispatch_get_main_queue()
|
||||||
downloadProgress:downloadProgressBlock
|
downloadProgress:downloadProgressBlock
|
||||||
completion:^(id <ASImageContainerProtocol> imageContainer, NSError *error, id downloadIdentifier) {
|
completion:^(id <ASImageContainerProtocol> imageContainer, NSError *error, id downloadIdentifier, id userInfo) {
|
||||||
// We dereference iVars directly, so we can't have weakSelf going nil on us.
|
// We dereference iVars directly, so we can't have weakSelf going nil on us.
|
||||||
__typeof__(self) strongSelf = weakSelf;
|
__typeof__(self) strongSelf = weakSelf;
|
||||||
if (!strongSelf)
|
if (!strongSelf)
|
||||||
|
|||||||
39
Source/ASNetworkImageLoadInfo.h
Normal file
39
Source/ASNetworkImageLoadInfo.h
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
//
|
||||||
|
// ASNetworkImageLoadInfo.h
|
||||||
|
// AsyncDisplayKit
|
||||||
|
//
|
||||||
|
// Created by Adlai on 1/30/18.
|
||||||
|
// Copyright © 2018 Facebook. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import <AsyncDisplayKit/ASBaseDefines.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
typedef NS_ENUM(NSInteger, ASNetworkImageSourceType) {
|
||||||
|
ASNetworkImageSourceUnspecified = 0,
|
||||||
|
ASNetworkImageSourceSynchronousCache,
|
||||||
|
ASNetworkImageSourceAsynchronousCache,
|
||||||
|
ASNetworkImageSourceFileURL,
|
||||||
|
ASNetworkImageSourceDownload,
|
||||||
|
};
|
||||||
|
|
||||||
|
AS_SUBCLASSING_RESTRICTED
|
||||||
|
@interface ASNetworkImageLoadInfo : NSObject <NSCopying>
|
||||||
|
|
||||||
|
/// 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
|
||||||
32
Source/ASNetworkImageLoadInfo.m
Normal file
32
Source/ASNetworkImageLoadInfo.m
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
//
|
||||||
|
// ASNetworkImageLoadInfo.m
|
||||||
|
// AsyncDisplayKit
|
||||||
|
//
|
||||||
|
// Created by Adlai on 1/30/18.
|
||||||
|
// Copyright © 2018 Facebook. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <AsyncDisplayKit/ASNetworkImageLoadInfo.h>
|
||||||
|
#import <AsyncDisplayKit/ASNetworkImageLoadInfo+Private.h>
|
||||||
|
|
||||||
|
@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
|
||||||
@ -22,6 +22,7 @@
|
|||||||
NS_ASSUME_NONNULL_BEGIN
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
@protocol ASNetworkImageNodeDelegate, ASImageCacheProtocol, ASImageDownloaderProtocol;
|
@protocol ASNetworkImageNodeDelegate, ASImageCacheProtocol, ASImageDownloaderProtocol;
|
||||||
|
@class ASNetworkImageLoadInfo;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -136,20 +137,6 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
|
|
||||||
#pragma mark -
|
#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
|
* The methods declared by the ASNetworkImageNodeDelegate protocol allow the adopting delegate to respond to
|
||||||
* notifications such as finished decoding and downloading an image.
|
* notifications such as finished decoding and downloading an image.
|
||||||
@ -163,11 +150,11 @@ typedef struct {
|
|||||||
*
|
*
|
||||||
* @param imageNode The sender.
|
* @param imageNode The sender.
|
||||||
* @param image The newly-loaded image.
|
* @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.
|
* @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.
|
* Notification that the image node finished downloading an image.
|
||||||
|
|||||||
@ -28,6 +28,7 @@
|
|||||||
#import <AsyncDisplayKit/ASImageNode+AnimatedImagePrivate.h>
|
#import <AsyncDisplayKit/ASImageNode+AnimatedImagePrivate.h>
|
||||||
#import <AsyncDisplayKit/ASImageContainerProtocolCategories.h>
|
#import <AsyncDisplayKit/ASImageContainerProtocolCategories.h>
|
||||||
#import <AsyncDisplayKit/ASLog.h>
|
#import <AsyncDisplayKit/ASLog.h>
|
||||||
|
#import <AsyncDisplayKit/ASNetworkImageLoadInfo+Private.h>
|
||||||
|
|
||||||
#if AS_PIN_REMOTE_IMAGE
|
#if AS_PIN_REMOTE_IMAGE
|
||||||
#import <AsyncDisplayKit/ASPINRemoteImageDownloader.h>
|
#import <AsyncDisplayKit/ASPINRemoteImageDownloader.h>
|
||||||
@ -335,8 +336,9 @@
|
|||||||
if (asynchronously == NO && _cacheFlags.cacheSupportsSynchronousFetch) {
|
if (asynchronously == NO && _cacheFlags.cacheSupportsSynchronousFetch) {
|
||||||
ASDN::MutexLocker l(__instanceLock__);
|
ASDN::MutexLocker l(__instanceLock__);
|
||||||
|
|
||||||
if (_imageLoaded == NO && _URL && _downloadIdentifier == nil) {
|
NSURL *url = _URL;
|
||||||
UIImage *result = [[_cache synchronouslyFetchedCachedImageWithURL:_URL] asdk_image];
|
if (_imageLoaded == NO && url && _downloadIdentifier == nil) {
|
||||||
|
UIImage *result = [[_cache synchronouslyFetchedCachedImageWithURL:url] asdk_image];
|
||||||
if (result) {
|
if (result) {
|
||||||
[self _locked_setCurrentImageQuality:1.0];
|
[self _locked_setCurrentImageQuality:1.0];
|
||||||
[self _locked__setImage:result];
|
[self _locked__setImage:result];
|
||||||
@ -345,8 +347,7 @@
|
|||||||
// Call out to the delegate.
|
// Call out to the delegate.
|
||||||
if (_delegateFlags.delegateDidLoadImageWithInfo) {
|
if (_delegateFlags.delegateDidLoadImageWithInfo) {
|
||||||
ASDN::MutexUnlocker l(__instanceLock__);
|
ASDN::MutexUnlocker l(__instanceLock__);
|
||||||
ASNetworkImageNodeDidLoadInfo info = {};
|
auto info = [[ASNetworkImageLoadInfo alloc] initWithURL:url sourceType:ASNetworkImageSourceSynchronousCache downloadIdentifier:nil userInfo:nil];
|
||||||
info.imageSource = ASNetworkImageSourceSynchronousCache;
|
|
||||||
[_delegate imageNode:self didLoadImage:result info:info];
|
[_delegate imageNode:self didLoadImage:result info:info];
|
||||||
} else if (_delegateFlags.delegateDidLoadImage) {
|
} else if (_delegateFlags.delegateDidLoadImage) {
|
||||||
ASDN::MutexUnlocker l(__instanceLock__);
|
ASDN::MutexUnlocker l(__instanceLock__);
|
||||||
@ -554,7 +555,7 @@
|
|||||||
_cacheUUID = nil;
|
_cacheUUID = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_downloadImageWithCompletion:(void (^)(id <ASImageContainerProtocol> imageContainer, NSError*, id downloadIdentifier))finished
|
- (void)_downloadImageWithCompletion:(void (^)(id <ASImageContainerProtocol> imageContainer, NSError*, id downloadIdentifier, id userInfo))finished
|
||||||
{
|
{
|
||||||
ASPerformBlockOnBackgroundThread(^{
|
ASPerformBlockOnBackgroundThread(^{
|
||||||
NSURL *url;
|
NSURL *url;
|
||||||
@ -573,9 +574,9 @@
|
|||||||
downloadIdentifier = [_downloader downloadImageWithURL:url
|
downloadIdentifier = [_downloader downloadImageWithURL:url
|
||||||
callbackQueue:dispatch_get_main_queue()
|
callbackQueue:dispatch_get_main_queue()
|
||||||
downloadProgress:NULL
|
downloadProgress:NULL
|
||||||
completion:^(id <ASImageContainerProtocol> _Nullable imageContainer, NSError * _Nullable error, id _Nullable downloadIdentifier) {
|
completion:^(id <ASImageContainerProtocol> _Nullable imageContainer, NSError * _Nullable error, id _Nullable downloadIdentifier, id _Nullable userInfo) {
|
||||||
if (finished != NULL) {
|
if (finished != NULL) {
|
||||||
finished(imageContainer, error, downloadIdentifier);
|
finished(imageContainer, error, downloadIdentifier, userInfo);
|
||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
as_log_verbose(ASImageLoadingLog(), "Downloading image for %@ url: %@", self, url);
|
as_log_verbose(ASImageLoadingLog(), "Downloading image for %@ url: %@", self, url);
|
||||||
@ -630,15 +631,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (_shouldCacheImage) {
|
if (_shouldCacheImage) {
|
||||||
[self _locked__setImage:[UIImage imageNamed:_URL.path.lastPathComponent]];
|
[self _locked__setImage:[UIImage imageNamed:URL.path.lastPathComponent]];
|
||||||
} else {
|
} else {
|
||||||
// First try to load the path directly, for efficiency assuming a developer who
|
// 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.
|
// 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 (nonAnimatedImage == nil) {
|
||||||
// If we couldn't find it, execute an -imageNamed:-like search so we can find resources even if the
|
// 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.
|
// 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) {
|
if (filename != nil) {
|
||||||
nonAnimatedImage = [UIImage imageWithContentsOfFile:filename];
|
nonAnimatedImage = [UIImage imageWithContentsOfFile:filename];
|
||||||
}
|
}
|
||||||
@ -647,7 +648,7 @@
|
|||||||
// If the file may be an animated gif and then created an animated image.
|
// If the file may be an animated gif and then created an animated image.
|
||||||
id<ASAnimatedImageProtocol> animatedImage = nil;
|
id<ASAnimatedImageProtocol> animatedImage = nil;
|
||||||
if (_downloaderFlags.downloaderImplementsAnimatedImage) {
|
if (_downloaderFlags.downloaderImplementsAnimatedImage) {
|
||||||
NSData *data = [NSData dataWithContentsOfURL:_URL];
|
NSData *data = [NSData dataWithContentsOfURL:URL];
|
||||||
if (data != nil) {
|
if (data != nil) {
|
||||||
animatedImage = [_downloader animatedImageWithData:data];
|
animatedImage = [_downloader animatedImageWithData:data];
|
||||||
|
|
||||||
@ -670,8 +671,7 @@
|
|||||||
|
|
||||||
if (_delegateFlags.delegateDidLoadImageWithInfo) {
|
if (_delegateFlags.delegateDidLoadImageWithInfo) {
|
||||||
ASDN::MutexUnlocker u(__instanceLock__);
|
ASDN::MutexUnlocker u(__instanceLock__);
|
||||||
ASNetworkImageNodeDidLoadInfo info = {};
|
auto info = [[ASNetworkImageLoadInfo alloc] initWithURL:URL sourceType:ASNetworkImageSourceFileURL downloadIdentifier:nil userInfo:nil];
|
||||||
info.imageSource = ASNetworkImageSourceFileURL;
|
|
||||||
[delegate imageNode:self didLoadImage:self.image info:info];
|
[delegate imageNode:self didLoadImage:self.image info:info];
|
||||||
} else if (_delegateFlags.delegateDidLoadImage) {
|
} else if (_delegateFlags.delegateDidLoadImage) {
|
||||||
ASDN::MutexUnlocker u(__instanceLock__);
|
ASDN::MutexUnlocker u(__instanceLock__);
|
||||||
@ -680,7 +680,7 @@
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
__weak __typeof__(self) weakSelf = self;
|
__weak __typeof__(self) weakSelf = self;
|
||||||
auto finished = ^(id <ASImageContainerProtocol>imageContainer, NSError *error, id downloadIdentifier, ASNetworkImageSource imageSource) {
|
auto finished = ^(id <ASImageContainerProtocol>imageContainer, NSError *error, id downloadIdentifier, ASNetworkImageSourceType imageSource, id userInfo) {
|
||||||
ASPerformBlockOnBackgroundThread(^{
|
ASPerformBlockOnBackgroundThread(^{
|
||||||
__typeof__(self) strongSelf = weakSelf;
|
__typeof__(self) strongSelf = weakSelf;
|
||||||
if (strongSelf == nil) {
|
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)
|
//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) {
|
if (ASInterfaceStateIncludesPreload(strongSelf->_interfaceState) == NO) {
|
||||||
self->_downloadIdentifier = nil;
|
strongSelf->_downloadIdentifier = nil;
|
||||||
self->_cacheUUID = nil;
|
strongSelf->_cacheUUID = nil;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UIImage *newImage;
|
||||||
if (imageContainer != nil) {
|
if (imageContainer != nil) {
|
||||||
[strongSelf _locked_setCurrentImageQuality:1.0];
|
[strongSelf _locked_setCurrentImageQuality:1.0];
|
||||||
NSData *animatedImageData = [imageContainer asdk_animatedImageData];
|
NSData *animatedImageData = [imageContainer asdk_animatedImageData];
|
||||||
@ -711,7 +712,8 @@
|
|||||||
id animatedImage = [strongSelf->_downloader animatedImageWithData:animatedImageData];
|
id animatedImage = [strongSelf->_downloader animatedImageWithData:animatedImageData];
|
||||||
[strongSelf _locked_setAnimatedImage:animatedImage];
|
[strongSelf _locked_setAnimatedImage:animatedImage];
|
||||||
} else {
|
} else {
|
||||||
[strongSelf _locked__setImage:[imageContainer asdk_image]];
|
newImage = [imageContainer asdk_image];
|
||||||
|
[strongSelf _locked__setImage:newImage];
|
||||||
}
|
}
|
||||||
strongSelf->_imageLoaded = YES;
|
strongSelf->_imageLoaded = YES;
|
||||||
}
|
}
|
||||||
@ -719,30 +721,32 @@
|
|||||||
strongSelf->_downloadIdentifier = nil;
|
strongSelf->_downloadIdentifier = nil;
|
||||||
strongSelf->_cacheUUID = nil;
|
strongSelf->_cacheUUID = nil;
|
||||||
|
|
||||||
ASPerformBlockOnMainThread(^{
|
void (^calloutBlock)(ASNetworkImageNode *inst);
|
||||||
__typeof__(self) strongSelf = weakSelf;
|
|
||||||
if (strongSelf == nil) {
|
if (newImage) {
|
||||||
return;
|
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];
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
} else if (error && _delegateFlags.delegateDidFailWithError) {
|
||||||
// Grab the lock for the rest of the block
|
calloutBlock = ^(ASNetworkImageNode *strongSelf) {
|
||||||
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__);
|
|
||||||
[delegate imageNode:strongSelf didFailWithError:error];
|
[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) {
|
if ([imageContainer asdk_image] == nil && _downloader != nil) {
|
||||||
[self _downloadImageWithCompletion:^(id<ASImageContainerProtocol> imageContainer, NSError *error, id downloadIdentifier) {
|
[self _downloadImageWithCompletion:^(id<ASImageContainerProtocol> imageContainer, NSError *error, id downloadIdentifier, id userInfo) {
|
||||||
finished(imageContainer, error, downloadIdentifier, ASNetworkImageSourceDownload);
|
finished(imageContainer, error, downloadIdentifier, ASNetworkImageSourceDownload, userInfo);
|
||||||
}];
|
}];
|
||||||
} else {
|
} else {
|
||||||
as_log_verbose(ASImageLoadingLog(), "Decached image for %@ img: %@ url: %@", self, [imageContainer asdk_image], URL);
|
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
|
[_cache cachedImageWithURL:URL
|
||||||
callbackQueue:dispatch_get_main_queue()
|
callbackQueue:dispatch_get_main_queue()
|
||||||
completion:completion];
|
completion:completion];
|
||||||
} else {
|
} else {
|
||||||
[self _downloadImageWithCompletion:^(id<ASImageContainerProtocol> imageContainer, NSError *error, id downloadIdentifier) {
|
[self _downloadImageWithCompletion:^(id<ASImageContainerProtocol> imageContainer, NSError *error, id downloadIdentifier, id userInfo) {
|
||||||
finished(imageContainer, error, downloadIdentifier, ASNetworkImageSourceDownload);
|
finished(imageContainer, error, downloadIdentifier, ASNetworkImageSourceDownload, userInfo);
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,8 +20,15 @@
|
|||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@protocol ASCATransactionQueueObserving <NSObject>
|
||||||
|
- (void)prepareForCATransactionCommit;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface ASAbstractRunLoopQueue : NSObject
|
||||||
|
@end
|
||||||
|
|
||||||
AS_SUBCLASSING_RESTRICTED
|
AS_SUBCLASSING_RESTRICTED
|
||||||
@interface ASRunLoopQueue<ObjectType> : NSObject <NSLocking>
|
@interface ASRunLoopQueue<ObjectType> : ASAbstractRunLoopQueue <NSLocking>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new queue with the given run loop and handler.
|
* Create a new queue with the given run loop and handler.
|
||||||
@ -41,13 +48,37 @@ AS_SUBCLASSING_RESTRICTED
|
|||||||
|
|
||||||
- (void)enqueue:(ObjectType)object;
|
- (void)enqueue:(ObjectType)object;
|
||||||
|
|
||||||
@property (nonatomic, readonly) BOOL isEmpty;
|
@property (atomic, readonly) BOOL isEmpty;
|
||||||
|
|
||||||
@property (nonatomic, assign) NSUInteger batchSize; // Default == 1.
|
@property (nonatomic, assign) NSUInteger batchSize; // Default == 1.
|
||||||
@property (nonatomic, assign) BOOL ensureExclusiveMembership; // Default == YES. Set-like behavior.
|
@property (nonatomic, assign) BOOL ensureExclusiveMembership; // Default == YES. Set-like behavior.
|
||||||
|
|
||||||
@end
|
@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<ASCATransactionQueueObserving>)object;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @abstract Apply a node's interfaceState immediately rather than adding to the queue.
|
||||||
|
*/
|
||||||
|
- (void)disable;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
AS_SUBCLASSING_RESTRICTED
|
AS_SUBCLASSING_RESTRICTED
|
||||||
@interface ASDeallocQueue : NSObject
|
@interface ASDeallocQueue : NSObject
|
||||||
|
|
||||||
|
|||||||
@ -57,11 +57,6 @@ static void runLoopSourceCallback(void *info) {
|
|||||||
|
|
||||||
- (void)releaseObjectInBackground:(id _Nullable __strong *)objectPtr
|
- (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) {
|
if (objectPtr != NULL && *objectPtr != nil) {
|
||||||
ASDN::MutexLocker l(_queueLock);
|
ASDN::MutexLocker l(_queueLock);
|
||||||
_queue.push_back(*objectPtr);
|
_queue.push_back(*objectPtr);
|
||||||
@ -186,27 +181,6 @@ static void runLoopSourceCallback(void *info) {
|
|||||||
|
|
||||||
@end
|
@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
|
#if AS_KDEBUG_ENABLE
|
||||||
/**
|
/**
|
||||||
* This is real, private CA API. Valid as of iOS 10.
|
* This is real, private CA API. Valid as of iOS 10.
|
||||||
@ -223,7 +197,23 @@ typedef enum {
|
|||||||
@end
|
@end
|
||||||
#endif
|
#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
|
#if AS_KDEBUG_ENABLE
|
||||||
+ (void)load
|
+ (void)load
|
||||||
@ -270,6 +260,31 @@ typedef enum {
|
|||||||
|
|
||||||
#endif // AS_KDEBUG_ENABLE
|
#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
|
- (instancetype)initWithRunLoop:(CFRunLoopRef)runloop retainObjects:(BOOL)retainsObjects handler:(void (^)(id _Nullable, BOOL))handlerBlock
|
||||||
{
|
{
|
||||||
if (self = [super init]) {
|
if (self = [super init]) {
|
||||||
@ -469,3 +484,232 @@ typedef enum {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@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<id> 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<ASCATransactionQueueObserving>)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
|
||||||
|
|||||||
@ -105,6 +105,18 @@
|
|||||||
return [self initWithStyle:UITableViewStylePlain];
|
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
|
#pragma mark ASDisplayNode
|
||||||
|
|
||||||
- (void)didLoad
|
- (void)didLoad
|
||||||
|
|||||||
@ -115,11 +115,12 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
|||||||
|
|
||||||
if (node) {
|
if (node) {
|
||||||
self.backgroundColor = node.backgroundColor;
|
self.backgroundColor = node.backgroundColor;
|
||||||
self.selectionStyle = node.selectionStyle;
|
|
||||||
self.selectedBackgroundView = node.selectedBackgroundView;
|
self.selectedBackgroundView = node.selectedBackgroundView;
|
||||||
self.separatorInset = node.separatorInset;
|
self.separatorInset = node.separatorInset;
|
||||||
self.selectionStyle = node.selectionStyle;
|
self.selectionStyle = node.selectionStyle;
|
||||||
|
self.focusStyle = node.focusStyle;
|
||||||
self.accessoryType = node.accessoryType;
|
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)
|
// 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
|
// 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;
|
BOOL _automaticallyAdjustsContentOffset;
|
||||||
|
|
||||||
CGPoint _deceleratingVelocity;
|
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;
|
CGFloat _nodesConstrainedWidth;
|
||||||
BOOL _queuedNodeHeightUpdate;
|
BOOL _queuedNodeHeightUpdate;
|
||||||
@ -225,7 +217,12 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
|||||||
* Counter used to keep track of nested batch updates.
|
* Counter used to keep track of nested batch updates.
|
||||||
*/
|
*/
|
||||||
NSInteger _batchUpdateCount;
|
NSInteger _batchUpdateCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keep a strong reference to node till view is ready to release.
|
||||||
|
*/
|
||||||
|
ASTableNode *_keepalive_node;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
unsigned int scrollViewDidScroll:1;
|
unsigned int scrollViewDidScroll:1;
|
||||||
unsigned int scrollViewWillBeginDragging:1;
|
unsigned int scrollViewWillBeginDragging:1;
|
||||||
@ -351,10 +348,6 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
|||||||
|
|
||||||
[self registerClass:_ASTableViewCell.class forCellReuseIdentifier:kCellReuseIdentifier];
|
[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)
|
// iOS 11 automatically uses estimated heights, so disable those (see PR #485)
|
||||||
if (AS_AT_LEAST_IOS11) {
|
if (AS_AT_LEAST_IOS11) {
|
||||||
super.estimatedRowHeight = 0.0;
|
super.estimatedRowHeight = 0.0;
|
||||||
@ -1227,13 +1220,10 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
|||||||
[super scrollViewDidScroll:scrollView];
|
[super scrollViewDidScroll:scrollView];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// If a scroll happenes the current range mode needs to go to full
|
|
||||||
ASInterfaceState interfaceState = [self interfaceStateForRangeController:_rangeController];
|
ASInterfaceState interfaceState = [self interfaceStateForRangeController:_rangeController];
|
||||||
if (ASInterfaceStateIncludesVisible(interfaceState)) {
|
if (ASInterfaceStateIncludesVisible(interfaceState)) {
|
||||||
[_rangeController updateCurrentRangeWithMode:ASLayoutRangeModeFull];
|
|
||||||
[self _checkForBatchFetching];
|
[self _checkForBatchFetching];
|
||||||
}
|
}
|
||||||
|
|
||||||
for (_ASTableViewCell *tableCell in _cellsForVisibilityUpdates) {
|
for (_ASTableViewCell *tableCell in _cellsForVisibilityUpdates) {
|
||||||
[[tableCell node] cellNodeVisibilityEvent:ASCellNodeVisibilityEventVisibleRectChanged
|
[[tableCell node] cellNodeVisibilityEvent:ASCellNodeVisibilityEventVisibleRectChanged
|
||||||
inScrollView:scrollView
|
inScrollView:scrollView
|
||||||
@ -1285,6 +1275,10 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
|||||||
[super scrollViewWillBeginDragging:scrollView];
|
[super scrollViewWillBeginDragging:scrollView];
|
||||||
return;
|
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) {
|
for (_ASTableViewCell *tableViewCell in _cellsForVisibilityUpdates) {
|
||||||
[[tableViewCell node] cellNodeVisibilityEvent:ASCellNodeVisibilityEventWillBeginDragging
|
[[tableViewCell node] cellNodeVisibilityEvent:ASCellNodeVisibilityEventWillBeginDragging
|
||||||
inScrollView:scrollView
|
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
|
@end
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -28,6 +28,7 @@
|
|||||||
#import <AsyncDisplayKit/ASDisplayNode+FrameworkSubclasses.h>
|
#import <AsyncDisplayKit/ASDisplayNode+FrameworkSubclasses.h>
|
||||||
#import <AsyncDisplayKit/ASHighlightOverlayLayer.h>
|
#import <AsyncDisplayKit/ASHighlightOverlayLayer.h>
|
||||||
#import <AsyncDisplayKit/ASDisplayNodeExtras.h>
|
#import <AsyncDisplayKit/ASDisplayNodeExtras.h>
|
||||||
|
#import <AsyncDisplayKit/ASGraphicsContext.h>
|
||||||
|
|
||||||
#import <AsyncDisplayKit/ASTextKitCoreTextAdditions.h>
|
#import <AsyncDisplayKit/ASTextKitCoreTextAdditions.h>
|
||||||
#import <AsyncDisplayKit/ASTextKitRenderer+Positioning.h>
|
#import <AsyncDisplayKit/ASTextKitRenderer+Positioning.h>
|
||||||
@ -229,6 +230,8 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
|||||||
{
|
{
|
||||||
CGColorRelease(_shadowColor);
|
CGColorRelease(_shadowColor);
|
||||||
|
|
||||||
|
// TODO: This may not be necessary post-iOS-9 when most UIKit assign APIs
|
||||||
|
// were changed to weak.
|
||||||
if (_longPressGestureRecognizer) {
|
if (_longPressGestureRecognizer) {
|
||||||
_longPressGestureRecognizer.delegate = nil;
|
_longPressGestureRecognizer.delegate = nil;
|
||||||
[_longPressGestureRecognizer removeTarget:nil action:NULL];
|
[_longPressGestureRecognizer removeTarget:nil action:NULL];
|
||||||
@ -907,7 +910,7 @@ static CGRect ASTextNodeAdjustRenderRectForShadowPadding(CGRect rendererRect, UI
|
|||||||
|
|
||||||
ASDN::MutexLocker l(__instanceLock__);
|
ASDN::MutexLocker l(__instanceLock__);
|
||||||
|
|
||||||
UIGraphicsBeginImageContext(size);
|
ASGraphicsBeginImageContextWithOptions(size, NO, 1.0);
|
||||||
[self.placeholderColor setFill];
|
[self.placeholderColor setFill];
|
||||||
|
|
||||||
ASTextKitRenderer *renderer = [self _locked_renderer];
|
ASTextKitRenderer *renderer = [self _locked_renderer];
|
||||||
@ -926,8 +929,7 @@ static CGRect ASTextNodeAdjustRenderRectForShadowPadding(CGRect rendererRect, UI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
|
UIImage *image = ASGraphicsGetImageAndEndCurrentContext();
|
||||||
UIGraphicsEndImageContext();
|
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -232,7 +232,7 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
|||||||
[self _ensureTruncationText];
|
[self _ensureTruncationText];
|
||||||
|
|
||||||
NSMutableAttributedString *mutableText = [attributedText mutableCopy];
|
NSMutableAttributedString *mutableText = [attributedText mutableCopy];
|
||||||
[self prepareAttributedStringForDrawing:mutableText];
|
[self prepareAttributedString:mutableText];
|
||||||
ASTextLayout *layout = [ASTextNode2 compatibleLayoutWithContainer:container text:mutableText];
|
ASTextLayout *layout = [ASTextNode2 compatibleLayoutWithContainer:container text:mutableText];
|
||||||
|
|
||||||
[self setNeedsDisplay];
|
[self setNeedsDisplay];
|
||||||
@ -319,7 +319,7 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
|||||||
return _textContainer.exclusionPaths;
|
return _textContainer.exclusionPaths;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)prepareAttributedStringForDrawing:(NSMutableAttributedString *)attributedString
|
- (void)prepareAttributedString:(NSMutableAttributedString *)attributedString
|
||||||
{
|
{
|
||||||
ASDN::MutexLocker lock(__instanceLock__);
|
ASDN::MutexLocker lock(__instanceLock__);
|
||||||
|
|
||||||
@ -334,12 +334,6 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
|||||||
[attributedString addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:range];
|
[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
|
// Apply shadow if needed
|
||||||
if (_shadowOpacity > 0 && (_shadowRadius != 0 || !CGSizeEqualToSize(_shadowOffset, CGSizeZero)) && CGColorGetAlpha(_shadowColor) > 0) {
|
if (_shadowOpacity > 0 && (_shadowRadius != 0 || !CGSizeEqualToSize(_shadowOffset, CGSizeZero)) && CGColorGetAlpha(_shadowColor) > 0) {
|
||||||
NSShadow *shadow = [[NSShadow alloc] init];
|
NSShadow *shadow = [[NSShadow alloc] init];
|
||||||
@ -362,11 +356,19 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
|||||||
ASTextContainer *copiedContainer = [_textContainer copy];
|
ASTextContainer *copiedContainer = [_textContainer copy];
|
||||||
copiedContainer.size = self.bounds.size;
|
copiedContainer.size = self.bounds.size;
|
||||||
NSMutableAttributedString *mutableText = [self.attributedText mutableCopy] ?: [[NSMutableAttributedString alloc] init];
|
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 @{
|
return @{
|
||||||
@"container": copiedContainer,
|
@"container": copiedContainer,
|
||||||
@"text": mutableText
|
@"text": mutableText
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -35,6 +35,7 @@
|
|||||||
#import <AsyncDisplayKit/ASBasicImageDownloader.h>
|
#import <AsyncDisplayKit/ASBasicImageDownloader.h>
|
||||||
#import <AsyncDisplayKit/ASPINRemoteImageDownloader.h>
|
#import <AsyncDisplayKit/ASPINRemoteImageDownloader.h>
|
||||||
#import <AsyncDisplayKit/ASMultiplexImageNode.h>
|
#import <AsyncDisplayKit/ASMultiplexImageNode.h>
|
||||||
|
#import <AsyncDisplayKit/ASNetworkImageLoadInfo.h>
|
||||||
#import <AsyncDisplayKit/ASNetworkImageNode.h>
|
#import <AsyncDisplayKit/ASNetworkImageNode.h>
|
||||||
#import <AsyncDisplayKit/ASPhotosFrameworkImageRequest.h>
|
#import <AsyncDisplayKit/ASPhotosFrameworkImageRequest.h>
|
||||||
|
|
||||||
@ -119,6 +120,7 @@
|
|||||||
#import <AsyncDisplayKit/UICollectionViewLayout+ASConvenience.h>
|
#import <AsyncDisplayKit/UICollectionViewLayout+ASConvenience.h>
|
||||||
#import <AsyncDisplayKit/UIView+ASConvenience.h>
|
#import <AsyncDisplayKit/UIView+ASConvenience.h>
|
||||||
#import <AsyncDisplayKit/UIImage+ASConvenience.h>
|
#import <AsyncDisplayKit/UIImage+ASConvenience.h>
|
||||||
|
#import <AsyncDisplayKit/ASGraphicsContext.h>
|
||||||
#import <AsyncDisplayKit/NSArray+Diffing.h>
|
#import <AsyncDisplayKit/NSArray+Diffing.h>
|
||||||
#import <AsyncDisplayKit/ASObjectDescriptionHelpers.h>
|
#import <AsyncDisplayKit/ASObjectDescriptionHelpers.h>
|
||||||
#import <AsyncDisplayKit/UIResponder+AsyncDisplayKit.h>
|
#import <AsyncDisplayKit/UIResponder+AsyncDisplayKit.h>
|
||||||
|
|||||||
@ -19,10 +19,6 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifndef kCFCoreFoundationVersionNumber_iOS_9_0
|
|
||||||
#define kCFCoreFoundationVersionNumber_iOS_9_0 1240.10
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef kCFCoreFoundationVersionNumber_iOS_10_0
|
#ifndef kCFCoreFoundationVersionNumber_iOS_10_0
|
||||||
#define kCFCoreFoundationVersionNumber_iOS_10_0 1348.00
|
#define kCFCoreFoundationVersionNumber_iOS_10_0 1348.00
|
||||||
#endif
|
#endif
|
||||||
@ -35,7 +31,6 @@
|
|||||||
#define __IPHONE_11_0 110000
|
#define __IPHONE_11_0 110000
|
||||||
#endif
|
#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_IOS10 (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_10_0)
|
||||||
#define AS_AT_LEAST_IOS11 (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_11_0)
|
#define AS_AT_LEAST_IOS11 (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_11_0)
|
||||||
|
|
||||||
|
|||||||
@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
#import <AsyncDisplayKit/AsyncDisplayKit+Debug.h>
|
#import <AsyncDisplayKit/AsyncDisplayKit+Debug.h>
|
||||||
#import <AsyncDisplayKit/ASAbstractLayoutController.h>
|
#import <AsyncDisplayKit/ASAbstractLayoutController.h>
|
||||||
|
#import <AsyncDisplayKit/ASGraphicsContext.h>
|
||||||
#import <AsyncDisplayKit/ASLayout.h>
|
#import <AsyncDisplayKit/ASLayout.h>
|
||||||
#import <AsyncDisplayKit/ASWeakSet.h>
|
#import <AsyncDisplayKit/ASWeakSet.h>
|
||||||
#import <AsyncDisplayKit/UIImage+ASConvenience.h>
|
#import <AsyncDisplayKit/UIImage+ASConvenience.h>
|
||||||
@ -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];
|
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);
|
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];
|
[fillColor setFill];
|
||||||
UIRectFill(imgRect);
|
UIRectFill(imgRect);
|
||||||
@ -156,8 +157,7 @@ static BOOL __enableHitTestDebug = NO;
|
|||||||
[self drawEdgeIfClippedWithEdges:clippedEdges color:clipsBorderColor borderWidth:borderWidth imgRect:imgRect];
|
[self drawEdgeIfClippedWithEdges:clippedEdges color:clipsBorderColor borderWidth:borderWidth imgRect:imgRect];
|
||||||
[self drawEdgeIfClippedWithEdges:clipsToBoundsClippedEdges color:borderColor borderWidth:borderWidth imgRect:imgRect];
|
[self drawEdgeIfClippedWithEdges:clipsToBoundsClippedEdges color:borderColor borderWidth:borderWidth imgRect:imgRect];
|
||||||
|
|
||||||
UIImage *debugHighlightImage = UIGraphicsGetImageFromCurrentImageContext();
|
UIImage *debugHighlightImage = ASGraphicsGetImageAndEndCurrentContext();
|
||||||
UIGraphicsEndImageContext();
|
|
||||||
|
|
||||||
UIEdgeInsets edgeInsets = UIEdgeInsetsMake(borderWidth, borderWidth, borderWidth, borderWidth);
|
UIEdgeInsets edgeInsets = UIEdgeInsetsMake(borderWidth, borderWidth, borderWidth, borderWidth);
|
||||||
debugOverlay.image = [debugHighlightImage resizableImageWithCapInsets:edgeInsets resizingMode:UIImageResizingModeStretch];
|
debugOverlay.image = [debugHighlightImage resizableImageWithCapInsets:edgeInsets resizingMode:UIImageResizingModeStretch];
|
||||||
|
|||||||
@ -27,14 +27,15 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
@interface ASBasicImageDownloader : NSObject <ASImageDownloaderProtocol>
|
@interface ASBasicImageDownloader : NSObject <ASImageDownloaderProtocol>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
* 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
|
* 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.
|
* @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)new __attribute__((unavailable("+[ASBasicImageDownloader sharedImageDownloader] must be used.")));
|
||||||
- (instancetype)init __attribute__((unavailable("+[ASBasicImageDownloader sharedImageDownloader] must be used.")));
|
- (instancetype)init __attribute__((unavailable("+[ASBasicImageDownloader sharedImageDownloader] must be used.")));
|
||||||
|
|||||||
@ -130,7 +130,7 @@ static ASDN::StaticMutex& currentRequestsLock = *new ASDN::StaticMutex;
|
|||||||
|
|
||||||
if (completionBlock) {
|
if (completionBlock) {
|
||||||
dispatch_async(callbackQueue, ^{
|
dispatch_async(callbackQueue, ^{
|
||||||
completionBlock(image, error, nil);
|
completionBlock(image, error, nil, nil);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -206,7 +206,7 @@ static const char *kContextKey = NSStringFromClass(ASBasicImageDownloaderContext
|
|||||||
|
|
||||||
@implementation ASBasicImageDownloader
|
@implementation ASBasicImageDownloader
|
||||||
|
|
||||||
+ (instancetype)sharedImageDownloader
|
+ (ASBasicImageDownloader *)sharedImageDownloader
|
||||||
{
|
{
|
||||||
static ASBasicImageDownloader *sharedImageDownloader = nil;
|
static ASBasicImageDownloader *sharedImageDownloader = nil;
|
||||||
static dispatch_once_t once = 0;
|
static dispatch_once_t once = 0;
|
||||||
@ -235,9 +235,9 @@ static const char *kContextKey = NSStringFromClass(ASBasicImageDownloaderContext
|
|||||||
#pragma mark ASImageDownloaderProtocol.
|
#pragma mark ASImageDownloaderProtocol.
|
||||||
|
|
||||||
- (id)downloadImageWithURL:(NSURL *)URL
|
- (id)downloadImageWithURL:(NSURL *)URL
|
||||||
callbackQueue:(dispatch_queue_t)callbackQueue
|
callbackQueue:(dispatch_queue_t)callbackQueue
|
||||||
downloadProgress:(nullable ASImageDownloaderProgress)downloadProgress
|
downloadProgress:(nullable ASImageDownloaderProgress)downloadProgress
|
||||||
completion:(ASImageDownloaderCompletion)completion
|
completion:(ASImageDownloaderCompletion)completion
|
||||||
{
|
{
|
||||||
ASBasicImageDownloaderContext *context = [ASBasicImageDownloaderContext contextForURL:URL];
|
ASBasicImageDownloaderContext *context = [ASBasicImageDownloaderContext contextForURL:URL];
|
||||||
|
|
||||||
|
|||||||
@ -528,7 +528,7 @@ typedef dispatch_block_t ASDataControllerCompletionBlock;
|
|||||||
|
|
||||||
BOOL canDelegate = (self.layoutDelegate != nil);
|
BOOL canDelegate = (self.layoutDelegate != nil);
|
||||||
ASElementMap *newMap;
|
ASElementMap *newMap;
|
||||||
id layoutContext;
|
ASCollectionLayoutContext *layoutContext;
|
||||||
{
|
{
|
||||||
as_activity_scope(as_activity_create("Latch new data for collection update", changeSet.rootActivity, OS_ACTIVITY_FLAG_DEFAULT));
|
as_activity_scope(as_activity_create("Latch new data for collection update", changeSet.rootActivity, OS_ACTIVITY_FLAG_DEFAULT));
|
||||||
|
|
||||||
|
|||||||
65
Source/Details/ASGraphicsContext.h
Normal file
65
Source/Details/ASGraphicsContext.h
Normal file
@ -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 <Foundation/Foundation.h>
|
||||||
|
#import <AsyncDisplayKit/ASBaseDefines.h>
|
||||||
|
#import <CoreGraphics/CoreGraphics.h>
|
||||||
|
|
||||||
|
@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
|
||||||
194
Source/Details/ASGraphicsContext.m
Normal file
194
Source/Details/ASGraphicsContext.m
Normal file
@ -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 <AsyncDisplayKit/ASAssert.h>
|
||||||
|
#import <AsyncDisplayKit/ASInternalHelpers.h>
|
||||||
|
#import <UIKit/UIGraphics.h>
|
||||||
|
#import <UIKit/UIImage.h>
|
||||||
|
#import <stdatomic.h>
|
||||||
|
#import <objc/runtime.h>
|
||||||
|
|
||||||
|
#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();
|
||||||
|
}
|
||||||
@ -72,8 +72,9 @@ typedef void(^ASImageCacherCompletion)(id <ASImageContainerProtocol> _Nullable i
|
|||||||
@param image The image that was downloaded, if the image could be successfully downloaded; nil otherwise.
|
@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 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 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 <ASImageContainerProtocol> _Nullable image, NSError * _Nullable error, id _Nullable downloadIdentifier);
|
typedef void(^ASImageDownloaderCompletion)(id <ASImageContainerProtocol> _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.
|
@param progress The progress of the download, in the range of (0.0, 1.0), inclusive.
|
||||||
|
|||||||
@ -30,12 +30,13 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
@interface ASPINRemoteImageDownloader : NSObject <ASImageCacheProtocol, ASImageDownloaderProtocol>
|
@interface ASPINRemoteImageDownloader : NSObject <ASImageCacheProtocol, ASImageDownloaderProtocol>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
* 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.
|
* available. It uses PINRemoteImage's features to provide caching and progressive image downloads.
|
||||||
*/
|
*/
|
||||||
+ (ASPINRemoteImageDownloader *)sharedDownloader;
|
@property (class, readonly) ASPINRemoteImageDownloader *sharedDownloader;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -116,7 +116,7 @@ static ASPINRemoteImageDownloader *sharedDownloader = nil;
|
|||||||
|
|
||||||
@implementation ASPINRemoteImageDownloader
|
@implementation ASPINRemoteImageDownloader
|
||||||
|
|
||||||
+ (instancetype)sharedDownloader
|
+ (ASPINRemoteImageDownloader *)sharedDownloader
|
||||||
{
|
{
|
||||||
|
|
||||||
static dispatch_once_t onceToken = 0;
|
static dispatch_once_t onceToken = 0;
|
||||||
@ -237,12 +237,12 @@ static ASPINRemoteImageDownloader *sharedDownloader = nil;
|
|||||||
[ASPINRemoteImageDownloader _performWithCallbackQueue:callbackQueue work:^{
|
[ASPINRemoteImageDownloader _performWithCallbackQueue:callbackQueue work:^{
|
||||||
#if PIN_ANIMATED_AVAILABLE
|
#if PIN_ANIMATED_AVAILABLE
|
||||||
if (result.alternativeRepresentation) {
|
if (result.alternativeRepresentation) {
|
||||||
completion(result.alternativeRepresentation, result.error, result.UUID);
|
completion(result.alternativeRepresentation, result.error, result.UUID, result);
|
||||||
} else {
|
} else {
|
||||||
completion(result.image, result.error, result.UUID);
|
completion(result.image, result.error, result.UUID, result);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
completion(result.image, result.error, result.UUID);
|
completion(result.image, result.error, result.UUID, result);
|
||||||
#endif
|
#endif
|
||||||
}];
|
}];
|
||||||
};
|
};
|
||||||
|
|||||||
@ -102,6 +102,11 @@ AS_SUBCLASSING_RESTRICTED
|
|||||||
*/
|
*/
|
||||||
@property (nonatomic, weak) id<ASRangeControllerDelegate> delegate;
|
@property (nonatomic, weak) id<ASRangeControllerDelegate> delegate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Property that indicates whether the scroll view for this range controller has ever changed its contentOffset.
|
||||||
|
*/
|
||||||
|
@property (nonatomic, assign) BOOL contentHasBeenScrolled;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -46,6 +46,7 @@
|
|||||||
NSSet<NSIndexPath *> *_allPreviousIndexPaths;
|
NSSet<NSIndexPath *> *_allPreviousIndexPaths;
|
||||||
NSHashTable<ASCellNode *> *_visibleNodes;
|
NSHashTable<ASCellNode *> *_visibleNodes;
|
||||||
ASLayoutRangeMode _currentRangeMode;
|
ASLayoutRangeMode _currentRangeMode;
|
||||||
|
BOOL _contentHasBeenScrolled;
|
||||||
BOOL _preserveCurrentRangeMode;
|
BOOL _preserveCurrentRangeMode;
|
||||||
BOOL _didRegisterForNodeDisplayNotifications;
|
BOOL _didRegisterForNodeDisplayNotifications;
|
||||||
CFTimeInterval _pendingDisplayNodesTimestamp;
|
CFTimeInterval _pendingDisplayNodesTimestamp;
|
||||||
@ -78,6 +79,7 @@ static UIApplicationState __ApplicationState = UIApplicationStateActive;
|
|||||||
|
|
||||||
_rangeIsValid = YES;
|
_rangeIsValid = YES;
|
||||||
_currentRangeMode = ASLayoutRangeModeUnspecified;
|
_currentRangeMode = ASLayoutRangeModeUnspecified;
|
||||||
|
_contentHasBeenScrolled = NO;
|
||||||
_preserveCurrentRangeMode = NO;
|
_preserveCurrentRangeMode = NO;
|
||||||
_previousScrollDirection = ASScrollDirectionDown | ASScrollDirectionRight;
|
_previousScrollDirection = ASScrollDirectionDown | ASScrollDirectionRight;
|
||||||
|
|
||||||
@ -223,10 +225,6 @@ static UIApplicationState __ApplicationState = UIApplicationStateActive;
|
|||||||
auto visibleElements = [_dataSource visibleElementsForRangeController:self];
|
auto visibleElements = [_dataSource visibleElementsForRangeController:self];
|
||||||
NSHashTable *newVisibleNodes = [NSHashTable hashTableWithOptions:NSHashTableObjectPointerPersonality];
|
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);
|
ASSignpostStart(ASSignpostRangeControllerUpdate);
|
||||||
|
|
||||||
// Get the scroll direction. Default to using the previous one, if they're not scrolling.
|
// 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;
|
scrollDirection = _previousScrollDirection;
|
||||||
}
|
}
|
||||||
_previousScrollDirection = scrollDirection;
|
_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];
|
ASInterfaceState selfInterfaceState = [self interfaceState];
|
||||||
ASLayoutRangeMode rangeMode = _currentRangeMode;
|
ASLayoutRangeMode rangeMode = _currentRangeMode;
|
||||||
// If the range mode is explicitly set via updateCurrentRangeWithMode: it will last in that mode until the
|
BOOL updateRangeMode = (!_preserveCurrentRangeMode && _contentHasBeenScrolled);
|
||||||
// range controller becomes visible again or explicitly changes the range mode again
|
|
||||||
if ((!_preserveCurrentRangeMode && ASInterfaceStateIncludesVisible(selfInterfaceState)) || [[self class] isFirstRangeUpdateForRangeMode:rangeMode]) {
|
// 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];
|
rangeMode = [ASRangeController rangeModeForInterfaceState:selfInterfaceState currentRangeMode:_currentRangeMode];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -413,7 +427,7 @@ static UIApplicationState __ApplicationState = UIApplicationStateActive;
|
|||||||
// NSLog(@"custom: %@", visibleNodePathsSet);
|
// NSLog(@"custom: %@", visibleNodePathsSet);
|
||||||
// }
|
// }
|
||||||
[modifiedIndexPaths sortUsingSelector:@selector(compare:)];
|
[modifiedIndexPaths sortUsingSelector:@selector(compare:)];
|
||||||
NSLog(@"Range update complete; modifiedIndexPaths: %@", [self descriptionWithIndexPaths:modifiedIndexPaths]);
|
NSLog(@"Range update complete; modifiedIndexPaths: %@, rangeMode: %d", [self descriptionWithIndexPaths:modifiedIndexPaths], rangeMode);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ASSignpostEnd(ASSignpostRangeControllerUpdate);
|
ASSignpostEnd(ASSignpostRangeControllerUpdate);
|
||||||
|
|||||||
@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
#import <AsyncDisplayKit/ASBaseDefines.h>
|
#import <AsyncDisplayKit/ASBaseDefines.h>
|
||||||
|
|
||||||
@class ASTraitCollection;
|
@class ASTraitCollection;
|
||||||
@ -27,14 +28,51 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
|
|
||||||
ASDISPLAYNODE_EXTERN_C_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
|
#pragma mark - ASPrimitiveTraitCollection
|
||||||
|
|
||||||
typedef struct ASPrimitiveTraitCollection {
|
typedef struct ASPrimitiveTraitCollection {
|
||||||
CGFloat displayScale;
|
|
||||||
UIUserInterfaceSizeClass horizontalSizeClass;
|
UIUserInterfaceSizeClass horizontalSizeClass;
|
||||||
UIUserInterfaceIdiom userInterfaceIdiom;
|
|
||||||
UIUserInterfaceSizeClass verticalSizeClass;
|
UIUserInterfaceSizeClass verticalSizeClass;
|
||||||
|
|
||||||
|
CGFloat displayScale;
|
||||||
|
UIDisplayGamut displayGamut;
|
||||||
|
|
||||||
|
UIUserInterfaceIdiom userInterfaceIdiom;
|
||||||
UIForceTouchCapability forceTouchCapability;
|
UIForceTouchCapability forceTouchCapability;
|
||||||
|
UITraitEnvironmentLayoutDirection layoutDirection;
|
||||||
|
#if TARGET_OS_TV
|
||||||
|
UIUserInterfaceStyle userInterfaceStyle;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ASPrimitiveContentSizeCategory preferredContentSizeCategory;
|
||||||
|
|
||||||
CGSize containerSize;
|
CGSize containerSize;
|
||||||
} ASPrimitiveTraitCollection;
|
} ASPrimitiveTraitCollection;
|
||||||
@ -124,11 +162,21 @@ ASDISPLAYNODE_EXTERN_C_END
|
|||||||
AS_SUBCLASSING_RESTRICTED
|
AS_SUBCLASSING_RESTRICTED
|
||||||
@interface ASTraitCollection : NSObject
|
@interface ASTraitCollection : NSObject
|
||||||
|
|
||||||
@property (nonatomic, assign, readonly) CGFloat displayScale;
|
|
||||||
@property (nonatomic, assign, readonly) UIUserInterfaceSizeClass horizontalSizeClass;
|
@property (nonatomic, assign, readonly) UIUserInterfaceSizeClass horizontalSizeClass;
|
||||||
@property (nonatomic, assign, readonly) UIUserInterfaceIdiom userInterfaceIdiom;
|
|
||||||
@property (nonatomic, assign, readonly) UIUserInterfaceSizeClass verticalSizeClass;
|
@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) 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;
|
@property (nonatomic, assign, readonly) CGSize containerSize;
|
||||||
|
|
||||||
+ (ASTraitCollection *)traitCollectionWithASPrimitiveTraitCollection:(ASPrimitiveTraitCollection)traits;
|
+ (ASTraitCollection *)traitCollectionWithASPrimitiveTraitCollection:(ASPrimitiveTraitCollection)traits;
|
||||||
@ -136,18 +184,48 @@ AS_SUBCLASSING_RESTRICTED
|
|||||||
+ (ASTraitCollection *)traitCollectionWithUITraitCollection:(UITraitCollection *)traitCollection
|
+ (ASTraitCollection *)traitCollectionWithUITraitCollection:(UITraitCollection *)traitCollection
|
||||||
containerSize:(CGSize)windowSize;
|
containerSize:(CGSize)windowSize;
|
||||||
|
|
||||||
|
+ (ASTraitCollection *)traitCollectionWithUITraitCollection:(UITraitCollection *)traitCollection
|
||||||
|
containerSize:(CGSize)windowSize
|
||||||
|
fallbackContentSizeCategory:(UIContentSizeCategory)fallbackContentSizeCategory;
|
||||||
|
|
||||||
+ (ASTraitCollection *)traitCollectionWithDisplayScale:(CGFloat)displayScale
|
#if TARGET_OS_TV
|
||||||
userInterfaceIdiom:(UIUserInterfaceIdiom)userInterfaceIdiom
|
+ (ASTraitCollection *)traitCollectionWithHorizontalSizeClass:(UIUserInterfaceSizeClass)horizontalSizeClass
|
||||||
horizontalSizeClass:(UIUserInterfaceSizeClass)horizontalSizeClass
|
verticalSizeClass:(UIUserInterfaceSizeClass)verticalSizeClass
|
||||||
verticalSizeClass:(UIUserInterfaceSizeClass)verticalSizeClass
|
displayScale:(CGFloat)displayScale
|
||||||
forceTouchCapability:(UIForceTouchCapability)forceTouchCapability
|
displayGamut:(UIDisplayGamut)displayGamut
|
||||||
containerSize:(CGSize)windowSize;
|
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;
|
- (ASPrimitiveTraitCollection)primitiveTraitCollection;
|
||||||
- (BOOL)isEqualToTraitCollection:(ASTraitCollection *)traitCollection;
|
- (BOOL)isEqualToTraitCollection:(ASTraitCollection *)traitCollection;
|
||||||
|
|
||||||
@end
|
@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
|
NS_ASSUME_NONNULL_END
|
||||||
|
|||||||
@ -20,6 +20,60 @@
|
|||||||
#import <AsyncDisplayKit/ASObjectDescriptionHelpers.h>
|
#import <AsyncDisplayKit/ASObjectDescriptionHelpers.h>
|
||||||
#import <AsyncDisplayKit/ASLayoutElement.h>
|
#import <AsyncDisplayKit/ASLayoutElement.h>
|
||||||
|
|
||||||
|
#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
|
#pragma mark - ASPrimitiveTraitCollection
|
||||||
|
|
||||||
extern void ASTraitCollectionPropagateDown(id<ASLayoutElement> element, ASPrimitiveTraitCollection traitCollection) {
|
extern void ASTraitCollectionPropagateDown(id<ASLayoutElement> element, ASPrimitiveTraitCollection traitCollection) {
|
||||||
@ -32,63 +86,73 @@ extern void ASTraitCollectionPropagateDown(id<ASLayoutElement> element, ASPrimit
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ASPrimitiveTraitCollection ASPrimitiveTraitCollectionMakeDefault()
|
ASPrimitiveTraitCollection ASPrimitiveTraitCollectionMakeDefault() {
|
||||||
{
|
|
||||||
return (ASPrimitiveTraitCollection) {
|
return (ASPrimitiveTraitCollection) {
|
||||||
// Default values can be defined in here
|
// Default values can be defined in here
|
||||||
|
.displayGamut = UIDisplayGamutUnspecified,
|
||||||
.userInterfaceIdiom = UIUserInterfaceIdiomUnspecified,
|
.userInterfaceIdiom = UIUserInterfaceIdiomUnspecified,
|
||||||
|
.layoutDirection = UITraitEnvironmentLayoutDirectionUnspecified,
|
||||||
|
.preferredContentSizeCategory = ASPrimitiveContentSizeCategoryMake(AS_UIContentSizeCategoryUnspecified()),
|
||||||
.containerSize = CGSizeZero,
|
.containerSize = CGSizeZero,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
ASPrimitiveTraitCollection ASPrimitiveTraitCollectionFromUITraitCollection(UITraitCollection *traitCollection)
|
ASPrimitiveTraitCollection ASPrimitiveTraitCollectionFromUITraitCollection(UITraitCollection *traitCollection) {
|
||||||
{
|
|
||||||
ASPrimitiveTraitCollection environmentTraitCollection = ASPrimitiveTraitCollectionMakeDefault();
|
ASPrimitiveTraitCollection environmentTraitCollection = ASPrimitiveTraitCollectionMakeDefault();
|
||||||
environmentTraitCollection.displayScale = traitCollection.displayScale;
|
|
||||||
environmentTraitCollection.horizontalSizeClass = traitCollection.horizontalSizeClass;
|
environmentTraitCollection.horizontalSizeClass = traitCollection.horizontalSizeClass;
|
||||||
environmentTraitCollection.verticalSizeClass = traitCollection.verticalSizeClass;
|
environmentTraitCollection.verticalSizeClass = traitCollection.verticalSizeClass;
|
||||||
|
environmentTraitCollection.displayScale = traitCollection.displayScale;
|
||||||
environmentTraitCollection.userInterfaceIdiom = traitCollection.userInterfaceIdiom;
|
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;
|
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
|
return
|
||||||
lhs.verticalSizeClass == rhs.verticalSizeClass &&
|
lhs.verticalSizeClass == rhs.verticalSizeClass &&
|
||||||
lhs.horizontalSizeClass == rhs.horizontalSizeClass &&
|
lhs.horizontalSizeClass == rhs.horizontalSizeClass &&
|
||||||
lhs.displayScale == rhs.displayScale &&
|
lhs.displayScale == rhs.displayScale &&
|
||||||
|
lhs.displayGamut == rhs.displayGamut &&
|
||||||
lhs.userInterfaceIdiom == rhs.userInterfaceIdiom &&
|
lhs.userInterfaceIdiom == rhs.userInterfaceIdiom &&
|
||||||
lhs.forceTouchCapability == rhs.forceTouchCapability &&
|
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);
|
CGSizeEqualToSize(lhs.containerSize, rhs.containerSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Named so as not to conflict with a hidden Apple function, in case compiler decides not to inline
|
// 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) {
|
ASDISPLAYNODE_INLINE NSString *AS_NSStringFromUIUserInterfaceIdiom(UIUserInterfaceIdiom idiom) {
|
||||||
if (AS_AVAILABLE_IOS(9)) {
|
switch (idiom) {
|
||||||
switch (idiom) {
|
case UIUserInterfaceIdiomTV:
|
||||||
case UIUserInterfaceIdiomTV:
|
return @"TV";
|
||||||
return @"TV";
|
case UIUserInterfaceIdiomPad:
|
||||||
case UIUserInterfaceIdiomPad:
|
return @"Pad";
|
||||||
return @"Pad";
|
case UIUserInterfaceIdiomPhone:
|
||||||
case UIUserInterfaceIdiomPhone:
|
return @"Phone";
|
||||||
return @"Phone";
|
case UIUserInterfaceIdiomCarPlay:
|
||||||
case UIUserInterfaceIdiomCarPlay:
|
return @"CarPlay";
|
||||||
return @"CarPlay";
|
default:
|
||||||
default:
|
return @"Unspecified";
|
||||||
return @"Unspecified";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
switch (idiom) {
|
|
||||||
case UIUserInterfaceIdiomPad:
|
|
||||||
return @"Pad";
|
|
||||||
case UIUserInterfaceIdiomPhone:
|
|
||||||
return @"Phone";
|
|
||||||
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<NSDictionary *> *props = [NSMutableArray array];
|
NSMutableArray<NSDictionary *> *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:@{ @"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:@{ @"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);
|
return ASObjectDescriptionMakeWithoutObject(props);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,73 +239,238 @@ NSString *NSStringFromASPrimitiveTraitCollection(ASPrimitiveTraitCollection trai
|
|||||||
|
|
||||||
@implementation ASTraitCollection
|
@implementation ASTraitCollection
|
||||||
|
|
||||||
- (instancetype)initWithDisplayScale:(CGFloat)displayScale
|
#if TARGET_OS_TV
|
||||||
userInterfaceIdiom:(UIUserInterfaceIdiom)userInterfaceIdiom
|
|
||||||
horizontalSizeClass:(UIUserInterfaceSizeClass)horizontalSizeClass
|
- (instancetype)initWithHorizontalSizeClass:(UIUserInterfaceSizeClass)horizontalSizeClass
|
||||||
verticalSizeClass:(UIUserInterfaceSizeClass)verticalSizeClass
|
verticalSizeClass:(UIUserInterfaceSizeClass)verticalSizeClass
|
||||||
forceTouchCapability:(UIForceTouchCapability)forceTouchCapability
|
displayScale:(CGFloat)displayScale
|
||||||
containerSize:(CGSize)windowSize
|
displayGamut:(UIDisplayGamut)displayGamut
|
||||||
|
userInterfaceIdiom:(UIUserInterfaceIdiom)userInterfaceIdiom
|
||||||
|
forceTouchCapability:(UIForceTouchCapability)forceTouchCapability
|
||||||
|
layoutDirection:(UITraitEnvironmentLayoutDirection)layoutDirection
|
||||||
|
userInterfaceStyle:(UIUserInterfaceStyle)userInterfaceStyle
|
||||||
|
preferredContentSizeCategory:(UIContentSizeCategory)preferredContentSizeCategory
|
||||||
|
containerSize:(CGSize)windowSize
|
||||||
{
|
{
|
||||||
self = [super init];
|
self = [super init];
|
||||||
if (self) {
|
if (self) {
|
||||||
_displayScale = displayScale;
|
|
||||||
_userInterfaceIdiom = userInterfaceIdiom;
|
|
||||||
_horizontalSizeClass = horizontalSizeClass;
|
_horizontalSizeClass = horizontalSizeClass;
|
||||||
_verticalSizeClass = verticalSizeClass;
|
_verticalSizeClass = verticalSizeClass;
|
||||||
|
_displayScale = displayScale;
|
||||||
|
_displayGamut = displayGamut;
|
||||||
|
_userInterfaceIdiom = userInterfaceIdiom;
|
||||||
_forceTouchCapability = forceTouchCapability;
|
_forceTouchCapability = forceTouchCapability;
|
||||||
|
_layoutDirection = layoutDirection;
|
||||||
|
_userInterfaceStyle = userInterfaceStyle;
|
||||||
|
_preferredContentSizeCategory = preferredContentSizeCategory;
|
||||||
_containerSize = windowSize;
|
_containerSize = windowSize;
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (instancetype)traitCollectionWithDisplayScale:(CGFloat)displayScale
|
+ (instancetype)traitCollectionWithHorizontalSizeClass:(UIUserInterfaceSizeClass)horizontalSizeClass
|
||||||
userInterfaceIdiom:(UIUserInterfaceIdiom)userInterfaceIdiom
|
verticalSizeClass:(UIUserInterfaceSizeClass)verticalSizeClass
|
||||||
horizontalSizeClass:(UIUserInterfaceSizeClass)horizontalSizeClass
|
displayScale:(CGFloat)displayScale
|
||||||
verticalSizeClass:(UIUserInterfaceSizeClass)verticalSizeClass
|
displayGamut:(UIDisplayGamut)displayGamut
|
||||||
forceTouchCapability:(UIForceTouchCapability)forceTouchCapability
|
userInterfaceIdiom:(UIUserInterfaceIdiom)userInterfaceIdiom
|
||||||
containerSize:(CGSize)windowSize
|
forceTouchCapability:(UIForceTouchCapability)forceTouchCapability
|
||||||
|
layoutDirection:(UITraitEnvironmentLayoutDirection)layoutDirection
|
||||||
|
userInterfaceStyle:(UIUserInterfaceStyle)userInterfaceStyle
|
||||||
|
preferredContentSizeCategory:(UIContentSizeCategory)preferredContentSizeCategory
|
||||||
|
containerSize:(CGSize)windowSize
|
||||||
{
|
{
|
||||||
return [[self alloc] initWithDisplayScale:displayScale
|
return [[self alloc] initWithHorizontalSizeClass:horizontalSizeClass
|
||||||
userInterfaceIdiom:userInterfaceIdiom
|
verticalSizeClass:verticalSizeClass
|
||||||
horizontalSizeClass:horizontalSizeClass
|
displayScale:displayScale
|
||||||
verticalSizeClass:verticalSizeClass
|
displayGamut:displayGamut
|
||||||
forceTouchCapability:forceTouchCapability
|
userInterfaceIdiom:userInterfaceIdiom
|
||||||
containerSize:windowSize];
|
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
|
+ (instancetype)traitCollectionWithASPrimitiveTraitCollection:(ASPrimitiveTraitCollection)traits
|
||||||
{
|
{
|
||||||
return [self traitCollectionWithDisplayScale:traits.displayScale
|
#if TARGET_OS_TV
|
||||||
userInterfaceIdiom:traits.userInterfaceIdiom
|
return [self traitCollectionWithHorizontalSizeClass:traits.horizontalSizeClass
|
||||||
horizontalSizeClass:traits.horizontalSizeClass
|
verticalSizeClass:traits.verticalSizeClass
|
||||||
verticalSizeClass:traits.verticalSizeClass
|
displayScale:traits.displayScale
|
||||||
forceTouchCapability:traits.forceTouchCapability
|
displayGamut:traits.displayGamut
|
||||||
containerSize:traits.containerSize];
|
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
|
+ (instancetype)traitCollectionWithUITraitCollection:(UITraitCollection *)traitCollection
|
||||||
containerSize:(CGSize)windowSize
|
containerSize:(CGSize)windowSize
|
||||||
{
|
{
|
||||||
UIForceTouchCapability forceTouch = UIForceTouchCapabilityUnknown;
|
return [self traitCollectionWithUITraitCollection:traitCollection
|
||||||
if(AS_AVAILABLE_IOS(9)) {
|
containerSize:windowSize
|
||||||
forceTouch = traitCollection.forceTouchCapability;
|
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
|
#if TARGET_OS_TV
|
||||||
horizontalSizeClass:traitCollection.horizontalSizeClass
|
return [self traitCollectionWithHorizontalSizeClass:traitCollection.horizontalSizeClass
|
||||||
verticalSizeClass:traitCollection.verticalSizeClass
|
verticalSizeClass:traitCollection.verticalSizeClass
|
||||||
forceTouchCapability:forceTouch
|
displayScale:traitCollection.displayScale
|
||||||
containerSize:windowSize];
|
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
|
- (ASPrimitiveTraitCollection)primitiveTraitCollection
|
||||||
{
|
{
|
||||||
return (ASPrimitiveTraitCollection) {
|
return (ASPrimitiveTraitCollection) {
|
||||||
.displayScale = self.displayScale,
|
|
||||||
.horizontalSizeClass = self.horizontalSizeClass,
|
.horizontalSizeClass = self.horizontalSizeClass,
|
||||||
.userInterfaceIdiom = self.userInterfaceIdiom,
|
|
||||||
.verticalSizeClass = self.verticalSizeClass,
|
.verticalSizeClass = self.verticalSizeClass,
|
||||||
|
.displayScale = self.displayScale,
|
||||||
|
.displayGamut = self.displayGamut,
|
||||||
|
.userInterfaceIdiom = self.userInterfaceIdiom,
|
||||||
.forceTouchCapability = self.forceTouchCapability,
|
.forceTouchCapability = self.forceTouchCapability,
|
||||||
|
.layoutDirection = self.layoutDirection,
|
||||||
|
#if TARGET_OS_TV
|
||||||
|
.userInterfaceStyle = self.userInterfaceStyle,
|
||||||
|
#endif
|
||||||
|
.preferredContentSizeCategory = ASPrimitiveContentSizeCategoryMake(self.preferredContentSizeCategory),
|
||||||
.containerSize = self.containerSize,
|
.containerSize = self.containerSize,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -208,12 +481,19 @@ NSString *NSStringFromASPrimitiveTraitCollection(ASPrimitiveTraitCollection trai
|
|||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.displayScale == traitCollection.displayScale &&
|
return
|
||||||
self.horizontalSizeClass == traitCollection.horizontalSizeClass &&
|
self.horizontalSizeClass == traitCollection.horizontalSizeClass &&
|
||||||
self.verticalSizeClass == traitCollection.verticalSizeClass &&
|
self.verticalSizeClass == traitCollection.verticalSizeClass &&
|
||||||
self.userInterfaceIdiom == traitCollection.userInterfaceIdiom &&
|
self.displayScale == traitCollection.displayScale &&
|
||||||
CGSizeEqualToSize(self.containerSize, traitCollection.containerSize) &&
|
self.displayGamut == traitCollection.displayGamut &&
|
||||||
self.forceTouchCapability == traitCollection.forceTouchCapability;
|
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
|
@end
|
||||||
|
|||||||
@ -19,6 +19,8 @@
|
|||||||
#import <AsyncDisplayKit/ASBaseDefines.h>
|
#import <AsyncDisplayKit/ASBaseDefines.h>
|
||||||
#import <AsyncDisplayKit/ASBlockTypes.h>
|
#import <AsyncDisplayKit/ASBlockTypes.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
@class ASDisplayNode;
|
@class ASDisplayNode;
|
||||||
@protocol _ASDisplayLayerDelegate;
|
@protocol _ASDisplayLayerDelegate;
|
||||||
|
|
||||||
@ -28,7 +30,7 @@
|
|||||||
@discussion This property overrides the CALayer category method which implements this via associated objects.
|
@discussion This property overrides the CALayer category method which implements this via associated objects.
|
||||||
This should result in much better performance for _ASDisplayLayers.
|
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.
|
@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.
|
@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.
|
@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 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.
|
@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.
|
@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.
|
@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.
|
@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<NSObject>)parameters isCancelled:(AS_NOESCAPE asdisplaynode_iscancelled_block_t)isCancelledBlock;
|
+ (UIImage *)displayWithParameters:(nullable id<NSObject>)parameters
|
||||||
|
isCancelled:(AS_NOESCAPE asdisplaynode_iscancelled_block_t)isCancelledBlock;
|
||||||
|
|
||||||
// Called on the main thread only
|
// Called on the main thread only
|
||||||
|
|
||||||
@ -147,3 +153,5 @@
|
|||||||
- (void)cancelDisplayAsyncLayer:(_ASDisplayLayer *)asyncLayer;
|
- (void)cancelDisplayAsyncLayer:(_ASDisplayLayer *)asyncLayer;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
|
|||||||
@ -17,11 +17,21 @@
|
|||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
// This class is only for use by ASDisplayNode and should never be subclassed or used directly.
|
// 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.
|
// Note that the "node" property is added to UIView directly via a category in ASDisplayNode.
|
||||||
|
|
||||||
|
@class ASDisplayNode;
|
||||||
|
|
||||||
@interface _ASDisplayView : UIView
|
@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
|
// 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
|
// Some UIKit mechanisms, like UITableView and UICollectionView selection handling, require this to work
|
||||||
- (void)__forwardTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
|
- (void)__forwardTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
|
||||||
@ -30,3 +40,5 @@
|
|||||||
- (void)__forwardTouchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;
|
- (void)__forwardTouchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
|
|||||||
@ -27,7 +27,6 @@
|
|||||||
#import <AsyncDisplayKit/ASLayout.h>
|
#import <AsyncDisplayKit/ASLayout.h>
|
||||||
|
|
||||||
@interface _ASDisplayView ()
|
@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
|
// 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.
|
// the view, the layer retains the view to prevent a crash. This replicates this behaviour for the node abstraction.
|
||||||
|
|||||||
@ -192,7 +192,7 @@ extern NSString * const ASLayoutElementStyleLayoutPositionProperty;
|
|||||||
#pragma mark - Sizing
|
#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.
|
* The minWidth and maxWidth properties override width.
|
||||||
* Defaults to ASDimensionAuto
|
* Defaults to ASDimensionAuto
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -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<UIDataSourceTranslating> 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
|
- (CGSize)collectionViewContentSize
|
||||||
{
|
{
|
||||||
ASDisplayNodeAssertMainThread();
|
ASDisplayNodeAssertMainThread();
|
||||||
|
|||||||
@ -21,168 +21,12 @@
|
|||||||
#import <AsyncDisplayKit/ASAssert.h>
|
#import <AsyncDisplayKit/ASAssert.h>
|
||||||
#import <AsyncDisplayKit/ASDisplayNodeInternal.h>
|
#import <AsyncDisplayKit/ASDisplayNodeInternal.h>
|
||||||
#import <AsyncDisplayKit/ASDisplayNode+FrameworkSubclasses.h>
|
#import <AsyncDisplayKit/ASDisplayNode+FrameworkSubclasses.h>
|
||||||
|
#import <AsyncDisplayKit/ASGraphicsContext.h>
|
||||||
#import <AsyncDisplayKit/ASInternalHelpers.h>
|
#import <AsyncDisplayKit/ASInternalHelpers.h>
|
||||||
#import <AsyncDisplayKit/ASSignpost.h>
|
#import <AsyncDisplayKit/ASSignpost.h>
|
||||||
#import <AsyncDisplayKit/ASDisplayNodeExtras.h>
|
#import <AsyncDisplayKit/ASDisplayNodeExtras.h>
|
||||||
|
|
||||||
|
|
||||||
@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>
|
@interface ASDisplayNode () <_ASDisplayLayerDelegate>
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@ -375,25 +219,14 @@ static void DrawingContextDataProviderReleaseDataCallback(void *info, __unused c
|
|||||||
displayBlock = ^id{
|
displayBlock = ^id{
|
||||||
CHECK_CANCELLED_AND_RETURN_NIL();
|
CHECK_CANCELLED_AND_RETURN_NIL();
|
||||||
|
|
||||||
ASDrawingContext *context = [[ASDrawingContext alloc] initWithSize:bounds.size scale:contentsScaleForDisplay opaque:opaque];
|
ASGraphicsBeginImageContextWithOptions(bounds.size, opaque, contentsScaleForDisplay);
|
||||||
[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);
|
|
||||||
|
|
||||||
for (dispatch_block_t block in displayBlocks) {
|
for (dispatch_block_t block in displayBlocks) {
|
||||||
CHECK_CANCELLED_AND_RETURN_NIL(UIGraphicsEndImageContext());
|
CHECK_CANCELLED_AND_RETURN_NIL(ASGraphicsEndImageContext());
|
||||||
block();
|
block();
|
||||||
}
|
}
|
||||||
|
|
||||||
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
|
UIImage *image = ASGraphicsGetImageAndEndCurrentContext();
|
||||||
UIGraphicsEndImageContext();*/
|
|
||||||
|
|
||||||
ASDN_DELAY_FOR_DISPLAY();
|
ASDN_DELAY_FOR_DISPLAY();
|
||||||
return image;
|
return image;
|
||||||
@ -402,13 +235,9 @@ static void DrawingContextDataProviderReleaseDataCallback(void *info, __unused c
|
|||||||
displayBlock = ^id{
|
displayBlock = ^id{
|
||||||
CHECK_CANCELLED_AND_RETURN_NIL();
|
CHECK_CANCELLED_AND_RETURN_NIL();
|
||||||
|
|
||||||
ASDrawingContext *context = nil;
|
|
||||||
if (shouldCreateGraphicsContext) {
|
if (shouldCreateGraphicsContext) {
|
||||||
//UIGraphicsBeginImageContextWithOptions(bounds.size, opaque, contentsScaleForDisplay);
|
ASGraphicsBeginImageContextWithOptions(bounds.size, opaque, contentsScaleForDisplay);
|
||||||
//CHECK_CANCELLED_AND_RETURN_NIL( UIGraphicsEndImageContext(); );
|
CHECK_CANCELLED_AND_RETURN_NIL( ASGraphicsEndImageContext(); );
|
||||||
context = [[ASDrawingContext alloc] initWithSize:bounds.size scale:contentsScaleForDisplay opaque:opaque];
|
|
||||||
[context pushCurrent];
|
|
||||||
CHECK_CANCELLED_AND_RETURN_NIL( [context popCurrent]; );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CGContextRef currentContext = UIGraphicsGetCurrentContext();
|
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];
|
[self __didDisplayNodeContentWithRenderingContext:currentContext image:&image drawParameters:drawParameters backgroundColor:backgroundColor borderWidth:borderWidth borderColor:borderColor];
|
||||||
|
|
||||||
if (shouldCreateGraphicsContext) {
|
if (shouldCreateGraphicsContext) {
|
||||||
CHECK_CANCELLED_AND_RETURN_NIL( [context popCurrent]; );
|
CHECK_CANCELLED_AND_RETURN_NIL( ASGraphicsEndImageContext(); );
|
||||||
[context popCurrent];
|
image = ASGraphicsGetImageAndEndCurrentContext();
|
||||||
image = [context generateImage];
|
|
||||||
context = nil;
|
|
||||||
//CHECK_CANCELLED_AND_RETURN_NIL( UIGraphicsEndImageContext(); );
|
|
||||||
//image = UIGraphicsGetImageFromCurrentImageContext();
|
|
||||||
//UIGraphicsEndImageContext();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ASDN_DELAY_FOR_DISPLAY();
|
ASDN_DELAY_FOR_DISPLAY();
|
||||||
@ -507,7 +331,7 @@ static void DrawingContextDataProviderReleaseDataCallback(void *info, __unused c
|
|||||||
bounds.size.height *= contentsScale;
|
bounds.size.height *= contentsScale;
|
||||||
CGFloat white = 0.0f, alpha = 0.0f;
|
CGFloat white = 0.0f, alpha = 0.0f;
|
||||||
[backgroundColor getWhite:&white alpha:&alpha];
|
[backgroundColor getWhite:&white alpha:&alpha];
|
||||||
UIGraphicsBeginImageContextWithOptions(bounds.size, (alpha == 1.0f), contentsScale);
|
ASGraphicsBeginImageContextWithOptions(bounds.size, (alpha == 1.0f), contentsScale);
|
||||||
[*image drawInRect:bounds];
|
[*image drawInRect:bounds];
|
||||||
} else {
|
} else {
|
||||||
bounds = CGContextGetClipBoundingBox(context);
|
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.
|
[roundedPath stroke]; // Won't do anything if borderWidth is 0 and roundedPath is nil.
|
||||||
|
|
||||||
if (*image) {
|
if (*image) {
|
||||||
*image = UIGraphicsGetImageFromCurrentImageContext();
|
*image = ASGraphicsGetImageAndEndCurrentContext();
|
||||||
UIGraphicsEndImageContext();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -141,6 +141,10 @@ __unused static NSString * _Nonnull NSStringFromASHierarchyStateChange(ASHierarc
|
|||||||
// delegate to inform of ASInterfaceState changes (used by ASNodeController)
|
// delegate to inform of ASInterfaceState changes (used by ASNodeController)
|
||||||
@property (nonatomic, weak) id<ASInterfaceStateDelegate> interfaceStateDelegate;
|
@property (nonatomic, weak) id<ASInterfaceStateDelegate> 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.
|
// These methods are recursive, and either union or remove the provided interfaceState to all sub-elements.
|
||||||
- (void)enterInterfaceState:(ASInterfaceState)interfaceState;
|
- (void)enterInterfaceState:(ASInterfaceState)interfaceState;
|
||||||
- (void)exitInterfaceState:(ASInterfaceState)interfaceState;
|
- (void)exitInterfaceState:(ASInterfaceState)interfaceState;
|
||||||
|
|||||||
@ -186,17 +186,12 @@ if (shouldApply) { _layer.layerProperty = (layerValueExpr); } else { ASDisplayNo
|
|||||||
- (CGFloat)cornerRadius
|
- (CGFloat)cornerRadius
|
||||||
{
|
{
|
||||||
ASDN::MutexLocker l(__instanceLock__);
|
ASDN::MutexLocker l(__instanceLock__);
|
||||||
if (_cornerRoundingType == ASCornerRoundingTypeDefaultSlowCALayer) {
|
return _cornerRadius;
|
||||||
return self.layerCornerRadius;
|
|
||||||
} else {
|
|
||||||
return _cornerRadius;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setCornerRadius:(CGFloat)newCornerRadius
|
- (void)setCornerRadius:(CGFloat)newCornerRadius
|
||||||
{
|
{
|
||||||
ASDN::MutexLocker l(__instanceLock__);
|
[self updateCornerRoundingWithType:self.cornerRoundingType cornerRadius:newCornerRadius];
|
||||||
[self updateCornerRoundingWithType:_cornerRoundingType cornerRadius:newCornerRadius];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (ASCornerRoundingType)cornerRoundingType
|
- (ASCornerRoundingType)cornerRoundingType
|
||||||
@ -207,8 +202,7 @@ if (shouldApply) { _layer.layerProperty = (layerValueExpr); } else { ASDisplayNo
|
|||||||
|
|
||||||
- (void)setCornerRoundingType:(ASCornerRoundingType)newRoundingType
|
- (void)setCornerRoundingType:(ASCornerRoundingType)newRoundingType
|
||||||
{
|
{
|
||||||
ASDN::MutexLocker l(__instanceLock__);
|
[self updateCornerRoundingWithType:newRoundingType cornerRadius:self.cornerRadius];
|
||||||
[self updateCornerRoundingWithType:newRoundingType cornerRadius:_cornerRadius];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)contentsGravity
|
- (NSString *)contentsGravity
|
||||||
@ -857,21 +851,16 @@ if (shouldApply) { _layer.layerProperty = (layerValueExpr); } else { ASDisplayNo
|
|||||||
- (UISemanticContentAttribute)semanticContentAttribute
|
- (UISemanticContentAttribute)semanticContentAttribute
|
||||||
{
|
{
|
||||||
_bridge_prologue_read;
|
_bridge_prologue_read;
|
||||||
if (AS_AT_LEAST_IOS9) {
|
return _getFromViewOnly(semanticContentAttribute);
|
||||||
return _getFromViewOnly(semanticContentAttribute);
|
|
||||||
}
|
|
||||||
return UISemanticContentAttributeUnspecified;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setSemanticContentAttribute:(UISemanticContentAttribute)semanticContentAttribute
|
- (void)setSemanticContentAttribute:(UISemanticContentAttribute)semanticContentAttribute
|
||||||
{
|
{
|
||||||
_bridge_prologue_write;
|
_bridge_prologue_write;
|
||||||
if (AS_AT_LEAST_IOS9) {
|
_setToViewOnly(semanticContentAttribute, semanticContentAttribute);
|
||||||
_setToViewOnly(semanticContentAttribute, semanticContentAttribute);
|
|
||||||
#if YOGA
|
#if YOGA
|
||||||
[self semanticContentAttributeDidChange:semanticContentAttribute];
|
[self semanticContentAttributeDidChange:semanticContentAttribute];
|
||||||
#endif
|
#endif
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@ -77,7 +77,7 @@ FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBefo
|
|||||||
{
|
{
|
||||||
@package
|
@package
|
||||||
_ASPendingState *_pendingViewState;
|
_ASPendingState *_pendingViewState;
|
||||||
|
ASInterfaceState _pendingInterfaceState;
|
||||||
UIView *_view;
|
UIView *_view;
|
||||||
CALayer *_layer;
|
CALayer *_layer;
|
||||||
|
|
||||||
|
|||||||
@ -102,6 +102,14 @@ typedef struct {
|
|||||||
[self.delegate scrollViewWillBeginDragging:scrollView];
|
[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
|
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
|
||||||
{
|
{
|
||||||
[self.delegate scrollViewDidEndDragging:scrollView willDecelerate:decelerate];
|
[self.delegate scrollViewDidEndDragging:scrollView willDecelerate:decelerate];
|
||||||
|
|||||||
@ -143,8 +143,9 @@ CGFloat ASScreenScale()
|
|||||||
static CGFloat __scale = 0.0;
|
static CGFloat __scale = 0.0;
|
||||||
static dispatch_once_t onceToken;
|
static dispatch_once_t onceToken;
|
||||||
dispatch_once(&onceToken, ^{
|
dispatch_once(&onceToken, ^{
|
||||||
ASDisplayNodeCAssertMainThread();
|
UIGraphicsBeginImageContextWithOptions(CGSizeMake(1, 1), YES, 0);
|
||||||
__scale = [[UIScreen mainScreen] scale];
|
__scale = CGContextGetCTM(UIGraphicsGetCurrentContext()).a;
|
||||||
|
UIGraphicsEndImageContext();
|
||||||
});
|
});
|
||||||
return __scale;
|
return __scale;
|
||||||
}
|
}
|
||||||
|
|||||||
22
Source/Private/ASNetworkImageLoadInfo+Private.h
Normal file
22
Source/Private/ASNetworkImageLoadInfo+Private.h
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
//
|
||||||
|
// ASNetworkImageLoadInfo+Private.h
|
||||||
|
// AsyncDisplayKit
|
||||||
|
//
|
||||||
|
// Created by Adlai on 1/30/18.
|
||||||
|
// Copyright © 2018 Facebook. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <AsyncDisplayKit/ASNetworkImageLoadInfo.h>
|
||||||
|
|
||||||
|
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
|
||||||
@ -405,25 +405,9 @@ dispatch_semaphore_signal(_lock);
|
|||||||
container->_readonly = YES;
|
container->_readonly = YES;
|
||||||
maximumNumberOfRows = container.maximumNumberOfRows;
|
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
|
// It may use larger constraint size when create CTFrame with
|
||||||
// CTFramesetterCreateFrame in iOS 10.
|
// CTFramesetterCreateFrame in iOS 10.
|
||||||
static BOOL needFixLayoutSizeBug = NO;
|
BOOL needFixLayoutSizeBug = AS_AT_LEAST_IOS10;
|
||||||
static dispatch_once_t onceToken;
|
|
||||||
dispatch_once(&onceToken, ^{
|
|
||||||
double systemVersionDouble = [UIDevice currentDevice].systemVersion.doubleValue;
|
|
||||||
if (8.3 <= systemVersionDouble && systemVersionDouble < 9) {
|
|
||||||
needFixJoinedEmojiBug = YES;
|
|
||||||
}
|
|
||||||
if (systemVersionDouble >= 10) {
|
|
||||||
needFixLayoutSizeBug = YES;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (needFixJoinedEmojiBug) {
|
|
||||||
[((NSMutableAttributedString *)text) as_setClearColorToJoinedEmoji];
|
|
||||||
}
|
|
||||||
|
|
||||||
layout = [[ASTextLayout alloc] _init];
|
layout = [[ASTextLayout alloc] _init];
|
||||||
layout.text = text;
|
layout.text = text;
|
||||||
|
|||||||
@ -63,7 +63,10 @@ ASTextAttributeType ASTextAttributeGetType(NSString *name){
|
|||||||
dic[(id)kCTSuperscriptAttributeName] = UIKit; //it's a CoreText attrubite, but only supported by UIKit...
|
dic[(id)kCTSuperscriptAttributeName] = UIKit; //it's a CoreText attrubite, but only supported by UIKit...
|
||||||
dic[NSVerticalGlyphFormAttributeName] = All;
|
dic[NSVerticalGlyphFormAttributeName] = All;
|
||||||
dic[(id)kCTGlyphInfoAttributeName] = CoreText_ASText;
|
dic[(id)kCTGlyphInfoAttributeName] = CoreText_ASText;
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||||
dic[(id)kCTCharacterShapeAttributeName] = CoreText_ASText;
|
dic[(id)kCTCharacterShapeAttributeName] = CoreText_ASText;
|
||||||
|
#pragma clang diagnostic pop
|
||||||
dic[(id)kCTRunDelegateAttributeName] = CoreText_ASText;
|
dic[(id)kCTRunDelegateAttributeName] = CoreText_ASText;
|
||||||
dic[(id)kCTBaselineClassAttributeName] = CoreText_ASText;
|
dic[(id)kCTBaselineClassAttributeName] = CoreText_ASText;
|
||||||
dic[(id)kCTBaselineInfoAttributeName] = CoreText_ASText;
|
dic[(id)kCTBaselineInfoAttributeName] = CoreText_ASText;
|
||||||
|
|||||||
@ -1357,21 +1357,6 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
*/
|
*/
|
||||||
- (void)as_appendString:(NSString *)string;
|
- (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.
|
Removes all discontinuous attributes in a specified range.
|
||||||
See `allDiscontinuousAttributeKeys`.
|
See `allDiscontinuousAttributeKeys`.
|
||||||
|
|||||||
@ -600,7 +600,10 @@ return style. _attr_;
|
|||||||
dispatch_once(&onceToken, ^{
|
dispatch_once(&onceToken, ^{
|
||||||
failSet = [NSMutableSet new];
|
failSet = [NSMutableSet new];
|
||||||
[failSet addObject:(id)kCTGlyphInfoAttributeName];
|
[failSet addObject:(id)kCTGlyphInfoAttributeName];
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||||
[failSet addObject:(id)kCTCharacterShapeAttributeName];
|
[failSet addObject:(id)kCTCharacterShapeAttributeName];
|
||||||
|
#pragma clang diagnostic pop
|
||||||
[failSet addObject:(id)kCTLanguageAttributeName];
|
[failSet addObject:(id)kCTLanguageAttributeName];
|
||||||
[failSet addObject:(id)kCTRunDelegateAttributeName];
|
[failSet addObject:(id)kCTRunDelegateAttributeName];
|
||||||
[failSet addObject:(id)kCTBaselineClassAttributeName];
|
[failSet addObject:(id)kCTBaselineClassAttributeName];
|
||||||
@ -1049,7 +1052,10 @@ style. _attr_ = _attr_; \
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)as_setCharacterShape:(NSNumber *)characterShape range:(NSRange)range {
|
- (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];
|
[self as_setAttribute:(id)kCTCharacterShapeAttributeName value:characterShape range:range];
|
||||||
|
#pragma clang diagnostic pop
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)as_setRunDelegate:(CTRunDelegateRef)runDelegate range:(NSRange)range {
|
- (void)as_setRunDelegate:(CTRunDelegateRef)runDelegate range:(NSRange)range {
|
||||||
@ -1178,51 +1184,6 @@ style. _attr_ = _attr_; \
|
|||||||
[self as_removeDiscontinuousAttributesInRange:NSMakeRange(length, string.length)];
|
[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 {
|
- (void)as_removeDiscontinuousAttributesInRange:(NSRange)range {
|
||||||
NSArray *keys = [NSMutableAttributedString as_allDiscontinuousAttributeKeys];
|
NSArray *keys = [NSMutableAttributedString as_allDiscontinuousAttributeKeys];
|
||||||
for (NSString *key in keys) {
|
for (NSString *key in keys) {
|
||||||
|
|||||||
@ -295,9 +295,7 @@ static BOOL defaultAllowsEdgeAntialiasing = NO;
|
|||||||
accessibilityActivationPoint = CGPointZero;
|
accessibilityActivationPoint = CGPointZero;
|
||||||
accessibilityPath = nil;
|
accessibilityPath = nil;
|
||||||
edgeAntialiasingMask = (kCALayerLeftEdge | kCALayerRightEdge | kCALayerTopEdge | kCALayerBottomEdge);
|
edgeAntialiasingMask = (kCALayerLeftEdge | kCALayerRightEdge | kCALayerTopEdge | kCALayerBottomEdge);
|
||||||
if (AS_AVAILABLE_IOS(9)) {
|
semanticContentAttribute = UISemanticContentAttributeUnspecified;
|
||||||
semanticContentAttribute = UISemanticContentAttributeUnspecified;
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
@ -1051,10 +1049,8 @@ static BOOL defaultAllowsEdgeAntialiasing = NO;
|
|||||||
if (flags.setOpaque)
|
if (flags.setOpaque)
|
||||||
ASDisplayNodeAssert(layer.opaque == opaque, @"Didn't set opaque as desired");
|
ASDisplayNodeAssert(layer.opaque == opaque, @"Didn't set opaque as desired");
|
||||||
|
|
||||||
if (AS_AVAILABLE_IOS(9)) {
|
if (flags.setSemanticContentAttribute) {
|
||||||
if (flags.setSemanticContentAttribute) {
|
view.semanticContentAttribute = semanticContentAttribute;
|
||||||
view.semanticContentAttribute = semanticContentAttribute;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flags.setIsAccessibilityElement)
|
if (flags.setIsAccessibilityElement)
|
||||||
@ -1215,9 +1211,7 @@ static BOOL defaultAllowsEdgeAntialiasing = NO;
|
|||||||
pendingState.allowsGroupOpacity = layer.allowsGroupOpacity;
|
pendingState.allowsGroupOpacity = layer.allowsGroupOpacity;
|
||||||
pendingState.allowsEdgeAntialiasing = layer.allowsEdgeAntialiasing;
|
pendingState.allowsEdgeAntialiasing = layer.allowsEdgeAntialiasing;
|
||||||
pendingState.edgeAntialiasingMask = layer.edgeAntialiasingMask;
|
pendingState.edgeAntialiasingMask = layer.edgeAntialiasingMask;
|
||||||
if (AS_AVAILABLE_IOS(9)) {
|
pendingState.semanticContentAttribute = view.semanticContentAttribute;
|
||||||
pendingState.semanticContentAttribute = view.semanticContentAttribute;
|
|
||||||
}
|
|
||||||
pendingState.isAccessibilityElement = view.isAccessibilityElement;
|
pendingState.isAccessibilityElement = view.isAccessibilityElement;
|
||||||
pendingState.accessibilityLabel = view.accessibilityLabel;
|
pendingState.accessibilityLabel = view.accessibilityLabel;
|
||||||
pendingState.accessibilityHint = view.accessibilityHint;
|
pendingState.accessibilityHint = view.accessibilityHint;
|
||||||
|
|||||||
@ -16,6 +16,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#import <AsyncDisplayKit/UIImage+ASConvenience.h>
|
#import <AsyncDisplayKit/UIImage+ASConvenience.h>
|
||||||
|
#import <AsyncDisplayKit/ASGraphicsContext.h>
|
||||||
#import <AsyncDisplayKit/ASInternalHelpers.h>
|
#import <AsyncDisplayKit/ASInternalHelpers.h>
|
||||||
#import <AsyncDisplayKit/ASAssert.h>
|
#import <AsyncDisplayKit/ASAssert.h>
|
||||||
|
|
||||||
@ -138,7 +139,7 @@ UIImage *cachedImageNamed(NSString *imageName, UITraitCollection *traitCollectio
|
|||||||
|
|
||||||
// We should probably check if the background color has any alpha component but that
|
// 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.
|
// 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;
|
BOOL contextIsClean = YES;
|
||||||
if (cornerColor) {
|
if (cornerColor) {
|
||||||
@ -168,8 +169,7 @@ UIImage *cachedImageNamed(NSString *imageName, UITraitCollection *traitCollectio
|
|||||||
[strokePath strokeWithBlendMode:(canUseCopy ? kCGBlendModeCopy : kCGBlendModeNormal) alpha:1];
|
[strokePath strokeWithBlendMode:(canUseCopy ? kCGBlendModeCopy : kCGBlendModeNormal) alpha:1];
|
||||||
}
|
}
|
||||||
|
|
||||||
UIImage *result = UIGraphicsGetImageFromCurrentImageContext();
|
UIImage *result = ASGraphicsGetImageAndEndCurrentContext();
|
||||||
UIGraphicsEndImageContext();
|
|
||||||
|
|
||||||
UIEdgeInsets capInsets = UIEdgeInsetsMake(cornerRadius, cornerRadius, cornerRadius, cornerRadius);
|
UIEdgeInsets capInsets = UIEdgeInsetsMake(cornerRadius, cornerRadius, cornerRadius, cornerRadius);
|
||||||
result = [result resizableImageWithCapInsets:capInsets resizingMode:UIImageResizingModeStretch];
|
result = [result resizableImageWithCapInsets:capInsets resizingMode:UIImageResizingModeStretch];
|
||||||
|
|||||||
@ -283,14 +283,14 @@
|
|||||||
CC55321D1E16EB7A0011C01F /* Debug */ = {
|
CC55321D1E16EB7A0011C01F /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||||
};
|
};
|
||||||
name = Debug;
|
name = Debug;
|
||||||
};
|
};
|
||||||
CC55321E1E16EB7A0011C01F /* Release */ = {
|
CC55321E1E16EB7A0011C01F /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
};
|
};
|
||||||
@ -336,7 +336,7 @@
|
|||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
INFOPLIST_FILE = ASDKListKitTests/Info.plist;
|
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";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||||
MTL_ENABLE_DEBUG_INFO = YES;
|
MTL_ENABLE_DEBUG_INFO = YES;
|
||||||
ONLY_ACTIVE_ARCH = YES;
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
@ -382,7 +382,7 @@
|
|||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
INFOPLIST_FILE = ASDKListKitTests/Info.plist;
|
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";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = asyncdisplaykit.ASDKListKitTests;
|
PRODUCT_BUNDLE_IDENTIFIER = asyncdisplaykit.ASDKListKitTests;
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
source 'https://github.com/CocoaPods/Specs.git'
|
source 'https://github.com/CocoaPods/Specs.git'
|
||||||
|
|
||||||
platform :ios, '8.0'
|
platform :ios, '9.0'
|
||||||
target 'ASDKListKitTests' do
|
target 'ASDKListKitTests' do
|
||||||
pod 'Texture/IGListKit', :path => '../..'
|
pod 'Texture/IGListKit', :path => '../..'
|
||||||
pod 'OCMock', '~> 3.4'
|
pod 'OCMock', '~> 3.4'
|
||||||
|
|||||||
@ -38,14 +38,14 @@
|
|||||||
[downloader downloadImageWithURL:URL
|
[downloader downloadImageWithURL:URL
|
||||||
callbackQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
|
callbackQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
|
||||||
downloadProgress:nil
|
downloadProgress:nil
|
||||||
completion:^(id<ASImageContainerProtocol> _Nullable image, NSError * _Nullable error, id _Nullable downloadIdentifier) {
|
completion:^(id<ASImageContainerProtocol> _Nullable image, NSError * _Nullable error, id _Nullable downloadIdentifier, id _Nullable userInfo) {
|
||||||
[firstExpectation fulfill];
|
[firstExpectation fulfill];
|
||||||
}];
|
}];
|
||||||
|
|
||||||
[downloader downloadImageWithURL:URL
|
[downloader downloadImageWithURL:URL
|
||||||
callbackQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
|
callbackQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
|
||||||
downloadProgress:nil
|
downloadProgress:nil
|
||||||
completion:^(id<ASImageContainerProtocol> _Nullable image, NSError * _Nullable error, id _Nullable downloadIdentifier) {
|
completion:^(id<ASImageContainerProtocol> _Nullable image, NSError * _Nullable error, id _Nullable downloadIdentifier, id _Nullable userInfo) {
|
||||||
[secondExpectation fulfill];
|
[secondExpectation fulfill];
|
||||||
}];
|
}];
|
||||||
|
|
||||||
|
|||||||
@ -23,6 +23,8 @@
|
|||||||
#import <vector>
|
#import <vector>
|
||||||
#import <OCMock/OCMock.h>
|
#import <OCMock/OCMock.h>
|
||||||
#import <AsyncDisplayKit/ASCollectionView+Undeprecated.h>
|
#import <AsyncDisplayKit/ASCollectionView+Undeprecated.h>
|
||||||
|
#import <AsyncDisplayKit/ASDisplayNode+FrameworkPrivate.h>
|
||||||
|
#import "ASDisplayNodeTestsHelper.h"
|
||||||
|
|
||||||
@interface ASTextCellNodeWithSetSelectedCounter : ASTextCellNode
|
@interface ASTextCellNodeWithSetSelectedCounter : ASTextCellNode
|
||||||
|
|
||||||
@ -860,6 +862,7 @@
|
|||||||
[cn waitUntilAllUpdatesAreProcessed];
|
[cn waitUntilAllUpdatesAreProcessed];
|
||||||
[cn.view layoutIfNeeded];
|
[cn.view layoutIfNeeded];
|
||||||
ASCellNode *node = [cn nodeForItemAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]];
|
ASCellNode *node = [cn nodeForItemAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]];
|
||||||
|
ASCATransactionQueueWait();
|
||||||
XCTAssertTrue(node.visible);
|
XCTAssertTrue(node.visible);
|
||||||
testController.asyncDelegate->_itemCounts = {0};
|
testController.asyncDelegate->_itemCounts = {0};
|
||||||
[cn deleteItemsAtIndexPaths: @[[NSIndexPath indexPathForItem:0 inSection:0]]];
|
[cn deleteItemsAtIndexPaths: @[[NSIndexPath indexPathForItem:0 inSection:0]]];
|
||||||
@ -1047,7 +1050,7 @@
|
|||||||
window.rootViewController = testController;
|
window.rootViewController = testController;
|
||||||
|
|
||||||
[window makeKeyAndVisible];
|
[window makeKeyAndVisible];
|
||||||
// Trigger the initial reload to start
|
// Trigger the initial reload to start
|
||||||
[window layoutIfNeeded];
|
[window layoutIfNeeded];
|
||||||
|
|
||||||
// Test the APIs that monitor ASCollectionNode update handling
|
// Test the APIs that monitor ASCollectionNode update handling
|
||||||
@ -1071,6 +1074,7 @@
|
|||||||
for (NSInteger i = 0; i < c; i++) {
|
for (NSInteger i = 0; i < c; i++) {
|
||||||
NSIndexPath *ip = [NSIndexPath indexPathForItem:i inSection:s];
|
NSIndexPath *ip = [NSIndexPath indexPathForItem:i inSection:s];
|
||||||
ASCellNode *node = [cn nodeForItemAtIndexPath:ip];
|
ASCellNode *node = [cn nodeForItemAtIndexPath:ip];
|
||||||
|
ASCATransactionQueueWait();
|
||||||
if (node.inPreloadState) {
|
if (node.inPreloadState) {
|
||||||
CGRect frame = [cn.view layoutAttributesForItemAtIndexPath:ip].frame;
|
CGRect frame = [cn.view layoutAttributesForItemAtIndexPath:ip].frame;
|
||||||
r = CGRectUnion(r, frame);
|
r = CGRectUnion(r, frame);
|
||||||
@ -1097,7 +1101,7 @@
|
|||||||
[window layoutIfNeeded];
|
[window layoutIfNeeded];
|
||||||
|
|
||||||
// The initial reload is async, changing the trait collection here should be "mid-update"
|
// 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.displayScale = cn.primitiveTraitCollection.displayScale + 1; // Just a dummy change
|
||||||
traitCollection.containerSize = screenBounds.size;
|
traitCollection.containerSize = screenBounds.size;
|
||||||
cn.primitiveTraitCollection = traitCollection;
|
cn.primitiveTraitCollection = traitCollection;
|
||||||
|
|||||||
@ -114,6 +114,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
ASSpecTestDisplayNode *node = [[ASSpecTestDisplayNode alloc] init];
|
ASSpecTestDisplayNode *node = [[ASSpecTestDisplayNode alloc] init];
|
||||||
|
[node setHierarchyState:ASHierarchyStateRangeManaged];
|
||||||
node.automaticallyManagesSubnodes = YES;
|
node.automaticallyManagesSubnodes = YES;
|
||||||
node.layoutSpecBlock = ^(ASDisplayNode *weakNode, ASSizeRange constrainedSize) {
|
node.layoutSpecBlock = ^(ASDisplayNode *weakNode, ASSizeRange constrainedSize) {
|
||||||
ASAbsoluteLayoutSpec *absoluteLayout = [ASAbsoluteLayoutSpec absoluteLayoutSpecWithChildren:@[subnodes[3]]];
|
ASAbsoluteLayoutSpec *absoluteLayout = [ASAbsoluteLayoutSpec absoluteLayoutSpecWithChildren:@[subnodes[3]]];
|
||||||
@ -130,6 +131,7 @@
|
|||||||
ASDisplayNodeSizeToFitSizeRange(node, ASSizeRangeMake(CGSizeZero, CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX)));
|
ASDisplayNodeSizeToFitSizeRange(node, ASSizeRangeMake(CGSizeZero, CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX)));
|
||||||
[node recursivelySetInterfaceState:ASInterfaceStatePreload];
|
[node recursivelySetInterfaceState:ASInterfaceStatePreload];
|
||||||
|
|
||||||
|
ASCATransactionQueueWait();
|
||||||
// No premature view allocation
|
// No premature view allocation
|
||||||
XCTAssertFalse(node.isNodeLoaded);
|
XCTAssertFalse(node.isNodeLoaded);
|
||||||
// Subnodes should be inserted, laid out and entered preload state
|
// Subnodes should be inserted, laid out and entered preload state
|
||||||
|
|||||||
@ -91,7 +91,7 @@ for (ASDisplayNode *n in @[ nodes ]) {\
|
|||||||
@interface ASDisplayNode (HackForTests)
|
@interface ASDisplayNode (HackForTests)
|
||||||
- (id)initWithViewClass:(Class)viewClass;
|
- (id)initWithViewClass:(Class)viewClass;
|
||||||
- (id)initWithLayerClass:(Class)layerClass;
|
- (id)initWithLayerClass:(Class)layerClass;
|
||||||
|
- (void)setInterfaceState:(ASInterfaceState)state;
|
||||||
// FIXME: Importing ASDisplayNodeInternal.h causes a heap of problems.
|
// FIXME: Importing ASDisplayNodeInternal.h causes a heap of problems.
|
||||||
- (void)enterInterfaceState:(ASInterfaceState)interfaceState;
|
- (void)enterInterfaceState:(ASInterfaceState)interfaceState;
|
||||||
@end
|
@end
|
||||||
@ -127,6 +127,12 @@ for (ASDisplayNode *n in @[ nodes ]) {\
|
|||||||
|
|
||||||
@implementation ASTestDisplayNode
|
@implementation ASTestDisplayNode
|
||||||
|
|
||||||
|
- (void)setInterfaceState:(ASInterfaceState)state
|
||||||
|
{
|
||||||
|
[super setInterfaceState:state];
|
||||||
|
ASCATransactionQueueWait();
|
||||||
|
}
|
||||||
|
|
||||||
- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize
|
- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize
|
||||||
{
|
{
|
||||||
return _calculateSizeBlock ? _calculateSizeBlock(self, constrainedSize) : CGSizeZero;
|
return _calculateSizeBlock ? _calculateSizeBlock(self, constrainedSize) : CGSizeZero;
|
||||||
@ -178,6 +184,28 @@ for (ASDisplayNode *n in @[ nodes ]) {\
|
|||||||
|
|
||||||
@end
|
@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
|
@interface UIDisplayNodeTestView : UIView
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@ -2041,9 +2069,9 @@ static bool stringContainsPointer(NSString *description, id p) {
|
|||||||
// Underlying issue for: https://github.com/facebook/AsyncDisplayKit/issues/2205
|
// Underlying issue for: https://github.com/facebook/AsyncDisplayKit/issues/2205
|
||||||
- (void)testThatRasterizedNodesGetInterfaceStateUpdatesWhenContainerEntersHierarchy
|
- (void)testThatRasterizedNodesGetInterfaceStateUpdatesWhenContainerEntersHierarchy
|
||||||
{
|
{
|
||||||
ASDisplayNode *supernode = [[ASDisplayNode alloc] init];
|
ASDisplayNode *supernode = [[ASTestDisplayNode alloc] init];
|
||||||
[supernode enableSubtreeRasterization];
|
[supernode enableSubtreeRasterization];
|
||||||
ASDisplayNode *subnode = [[ASDisplayNode alloc] init];
|
ASDisplayNode *subnode = [[ASTestDisplayNode alloc] init];
|
||||||
ASSetDebugNames(supernode, subnode);
|
ASSetDebugNames(supernode, subnode);
|
||||||
UIWindow *window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
|
UIWindow *window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
|
||||||
[supernode addSubnode:subnode];
|
[supernode addSubnode:subnode];
|
||||||
@ -2059,9 +2087,9 @@ static bool stringContainsPointer(NSString *description, id p) {
|
|||||||
// Underlying issue for: https://github.com/facebook/AsyncDisplayKit/issues/2205
|
// Underlying issue for: https://github.com/facebook/AsyncDisplayKit/issues/2205
|
||||||
- (void)testThatRasterizedNodesGetInterfaceStateUpdatesWhenAddedToContainerThatIsInHierarchy
|
- (void)testThatRasterizedNodesGetInterfaceStateUpdatesWhenAddedToContainerThatIsInHierarchy
|
||||||
{
|
{
|
||||||
ASDisplayNode *supernode = [[ASDisplayNode alloc] init];
|
ASDisplayNode *supernode = [[ASTestDisplayNode alloc] init];
|
||||||
[supernode enableSubtreeRasterization];
|
[supernode enableSubtreeRasterization];
|
||||||
ASDisplayNode *subnode = [[ASDisplayNode alloc] init];
|
ASDisplayNode *subnode = [[ASTestDisplayNode alloc] init];
|
||||||
ASSetDebugNames(supernode, subnode);
|
ASSetDebugNames(supernode, subnode);
|
||||||
|
|
||||||
UIWindow *window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
|
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 view]; // Node needs to be loaded
|
||||||
|
|
||||||
[node enterInterfaceState:ASInterfaceStatePreload];
|
[node enterInterfaceState:ASInterfaceStatePreload];
|
||||||
|
|
||||||
|
|
||||||
XCTAssertTrue((node.interfaceState & ASInterfaceStatePreload) == ASInterfaceStatePreload);
|
XCTAssertTrue((node.interfaceState & ASInterfaceStatePreload) == ASInterfaceStatePreload);
|
||||||
XCTAssertTrue((subnode.interfaceState & ASInterfaceStatePreload) == ASInterfaceStatePreload);
|
XCTAssertTrue((subnode.interfaceState & ASInterfaceStatePreload) == ASInterfaceStatePreload);
|
||||||
XCTAssertTrue(node.hasPreloaded);
|
XCTAssertTrue(node.hasPreloaded);
|
||||||
@ -2354,4 +2381,21 @@ static bool stringContainsPointer(NSString *description, id p) {
|
|||||||
XCTAssert(hasVC);
|
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
|
@end
|
||||||
|
|||||||
@ -28,5 +28,6 @@ BOOL ASDisplayNodeRunRunLoopUntilBlockIsTrue(as_condition_block_t block);
|
|||||||
|
|
||||||
void ASDisplayNodeSizeToFitSize(ASDisplayNode *node, CGSize size);
|
void ASDisplayNodeSizeToFitSize(ASDisplayNode *node, CGSize size);
|
||||||
void ASDisplayNodeSizeToFitSizeRange(ASDisplayNode *node, ASSizeRange sizeRange);
|
void ASDisplayNodeSizeToFitSizeRange(ASDisplayNode *node, ASSizeRange sizeRange);
|
||||||
|
void ASCATransactionQueueWait(void);
|
||||||
|
|
||||||
ASDISPLAYNODE_EXTERN_C_END
|
ASDISPLAYNODE_EXTERN_C_END
|
||||||
|
|||||||
@ -18,6 +18,7 @@
|
|||||||
#import "ASDisplayNodeTestsHelper.h"
|
#import "ASDisplayNodeTestsHelper.h"
|
||||||
#import <AsyncDisplayKit/ASDisplayNode.h>
|
#import <AsyncDisplayKit/ASDisplayNode.h>
|
||||||
#import <AsyncDisplayKit/ASLayout.h>
|
#import <AsyncDisplayKit/ASLayout.h>
|
||||||
|
#import <AsyncDisplayKit/ASRunLoopQueue.h>
|
||||||
|
|
||||||
#import <QuartzCore/QuartzCore.h>
|
#import <QuartzCore/QuartzCore.h>
|
||||||
|
|
||||||
@ -62,3 +63,14 @@ void ASDisplayNodeSizeToFitSizeRange(ASDisplayNode *node, ASSizeRange sizeRange)
|
|||||||
CGSize sizeThatFits = [node layoutThatFits:sizeRange].size;
|
CGSize sizeThatFits = [node layoutThatFits:sizeRange].size;
|
||||||
node.bounds = (CGRect){.origin = CGPointZero, .size = sizeThatFits};
|
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]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -238,7 +238,7 @@
|
|||||||
|
|
||||||
// Simulate completion.
|
// Simulate completion.
|
||||||
ASImageDownloaderCompletion completionBlock = [inv as_argumentAtIndexAsObject:5];
|
ASImageDownloaderCompletion completionBlock = [inv as_argumentAtIndexAsObject:5];
|
||||||
completionBlock([self _testImage], nil, nil);
|
completionBlock([self _testImage], nil, nil, nil);
|
||||||
});
|
});
|
||||||
|
|
||||||
NSNumber *imageIdentifier = @1;
|
NSNumber *imageIdentifier = @1;
|
||||||
|
|||||||
@ -2,8 +2,8 @@
|
|||||||
// ASRunLoopQueueTests.m
|
// ASRunLoopQueueTests.m
|
||||||
// Texture
|
// Texture
|
||||||
//
|
//
|
||||||
// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved.
|
// Copyright (c) 2017-present,
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
// You may obtain a copy of the License at
|
// You may obtain a copy of the License at
|
||||||
//
|
//
|
||||||
@ -12,9 +12,21 @@
|
|||||||
|
|
||||||
#import <XCTest/XCTest.h>
|
#import <XCTest/XCTest.h>
|
||||||
#import <AsyncDisplayKit/ASRunLoopQueue.h>
|
#import <AsyncDisplayKit/ASRunLoopQueue.h>
|
||||||
|
#import "ASDisplayNodeTestsHelper.h"
|
||||||
|
|
||||||
static NSTimeInterval const kRunLoopRunTime = 0.001; // Allow the RunLoop to run for one millisecond each time.
|
static NSTimeInterval const kRunLoopRunTime = 0.001; // Allow the RunLoop to run for one millisecond each time.
|
||||||
|
|
||||||
|
@interface QueueObject : NSObject <ASCATransactionQueueObserving>
|
||||||
|
@property (nonatomic, assign) BOOL queueObjectProcessed;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation QueueObject
|
||||||
|
- (void)prepareForCATransactionCommit
|
||||||
|
{
|
||||||
|
self.queueObjectProcessed = YES;
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
@interface ASRunLoopQueueTests : XCTestCase
|
@interface ASRunLoopQueueTests : XCTestCase
|
||||||
|
|
||||||
@end
|
@end
|
||||||
@ -157,4 +169,24 @@ static NSTimeInterval const kRunLoopRunTime = 0.001; // Allow the RunLoop to run
|
|||||||
XCTAssertTrue(queue.isEmpty);
|
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
|
@end
|
||||||
|
|||||||
34
Tests/ASTraitCollectionTests.m
Normal file
34
Tests/ASTraitCollectionTests.m
Normal file
@ -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 <XCTest/XCTest.h>
|
||||||
|
#import <AsyncDisplayKit/ASTraitCollection.h>
|
||||||
|
|
||||||
|
@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
|
||||||
@ -21,6 +21,7 @@
|
|||||||
#import <XCTest/XCTest.h>
|
#import <XCTest/XCTest.h>
|
||||||
#import <AVFoundation/AVFoundation.h>
|
#import <AVFoundation/AVFoundation.h>
|
||||||
#import <AsyncDisplayKit/AsyncDisplayKit.h>
|
#import <AsyncDisplayKit/AsyncDisplayKit.h>
|
||||||
|
#import "ASDisplayNodeTestsHelper.h"
|
||||||
|
|
||||||
@interface ASVideoNodeTests : XCTestCase <ASVideoNodeDelegate>
|
@interface ASVideoNodeTests : XCTestCase <ASVideoNodeDelegate>
|
||||||
{
|
{
|
||||||
@ -351,9 +352,9 @@
|
|||||||
|
|
||||||
[_videoNode setInterfaceState:ASInterfaceStateVisible | ASInterfaceStateDisplay | ASInterfaceStatePreload];
|
[_videoNode setInterfaceState:ASInterfaceStateVisible | ASInterfaceStateDisplay | ASInterfaceStatePreload];
|
||||||
[_videoNode prepareToPlayAsset:assetMock withKeys:_requestedKeys];
|
[_videoNode prepareToPlayAsset:assetMock withKeys:_requestedKeys];
|
||||||
|
ASCATransactionQueueWait();
|
||||||
[_videoNode pause];
|
[_videoNode pause];
|
||||||
_videoNode.shouldBePlaying = YES;
|
_videoNode.shouldBePlaying = YES;
|
||||||
|
|
||||||
XCTAssertFalse(_videoNode.isPlaying);
|
XCTAssertFalse(_videoNode.isPlaying);
|
||||||
|
|
||||||
[_videoNode observeValueForKeyPath:@"playbackLikelyToKeepUp" ofObject:[_videoNode currentItem] change:@{NSKeyValueChangeNewKey : @YES} context:NULL];
|
[_videoNode observeValueForKeyPath:@"playbackLikelyToKeepUp" ofObject:[_videoNode currentItem] change:@{NSKeyValueChangeNewKey : @YES} context:NULL];
|
||||||
|
|||||||
@ -14,7 +14,7 @@ Pod::Spec.new do |spec|
|
|||||||
spec.weak_frameworks = 'Photos','MapKit','AssetsLibrary'
|
spec.weak_frameworks = 'Photos','MapKit','AssetsLibrary'
|
||||||
spec.requires_arc = true
|
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
|
# Uncomment when fixed: issues with tvOS build for release 2.0
|
||||||
# spec.tvos.deployment_target = '9.0'
|
# spec.tvos.deployment_target = '9.0'
|
||||||
@ -51,7 +51,7 @@ Pod::Spec.new do |spec|
|
|||||||
end
|
end
|
||||||
|
|
||||||
spec.subspec 'IGListKit' do |igl|
|
spec.subspec 'IGListKit' do |igl|
|
||||||
igl.dependency 'IGListKit', '3.0.0'
|
igl.dependency 'IGListKit', '~> 3.0'
|
||||||
igl.dependency 'Texture/Core'
|
igl.dependency 'Texture/Core'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -137,7 +137,7 @@ optional public func editableTextNodeDidUpdateText(_ editableTextNode: ASEditabl
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
-- Indicates to the delegate that teh text node has finished editing.
|
-- Indicates to the delegate that the text node has finished editing.
|
||||||
|
|
||||||
<div class = "highlight-group">
|
<div class = "highlight-group">
|
||||||
<span class="language-toggle"><a data-lang="swift" class="swiftButton">Swift</a><a data-lang="objective-c" class = "active objcButton">Objective-C</a></span>
|
<span class="language-toggle"><a data-lang="swift" class="swiftButton">Swift</a><a data-lang="objective-c" class = "active objcButton">Objective-C</a></span>
|
||||||
|
|||||||
@ -394,17 +394,17 @@ Within `ASAbsoluteLayoutSpec` you can specify exact locations (x/y coordinates)
|
|||||||
CGSize maxConstrainedSize = constrainedSize.max;
|
CGSize maxConstrainedSize = constrainedSize.max;
|
||||||
|
|
||||||
// Layout all nodes absolute in a static layout spec
|
// Layout all nodes absolute in a static layout spec
|
||||||
guitarVideoNode.layoutPosition = CGPointMake(0, 0);
|
guitarVideoNode.style.layoutPosition = CGPointMake(0, 0);
|
||||||
guitarVideoNode.size = ASSizeMakeFromCGSize(CGSizeMake(maxConstrainedSize.width, maxConstrainedSize.height / 3.0));
|
guitarVideoNode.style.size = ASSizeMakeFromCGSize(CGSizeMake(maxConstrainedSize.width, maxConstrainedSize.height / 3.0));
|
||||||
|
|
||||||
nicCageVideoNode.layoutPosition = CGPointMake(maxConstrainedSize.width / 2.0, maxConstrainedSize.height / 3.0);
|
nicCageVideoNode.style.layoutPosition = CGPointMake(maxConstrainedSize.width / 2.0, maxConstrainedSize.height / 3.0);
|
||||||
nicCageVideoNode.size = ASSizeMakeFromCGSize(CGSizeMake(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.style.layoutPosition = CGPointMake(0.0, maxConstrainedSize.height - (maxConstrainedSize.height / 3.0));
|
||||||
simonVideoNode.size = ASSizeMakeFromCGSize(CGSizeMake(maxConstrainedSize.width/2, 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.style.layoutPosition = CGPointMake(0.0, maxConstrainedSize.height / 3.0);
|
||||||
hlsVideoNode.size = ASSizeMakeFromCGSize(CGSizeMake(maxConstrainedSize.width / 2.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]];
|
return [ASAbsoluteLayoutSpec absoluteLayoutSpecWithChildren:@[guitarVideoNode, nicCageVideoNode, simonVideoNode, hlsVideoNode]];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -51,7 +51,7 @@ An `ASViewController` is a regular `UIViewController` subclass that has special
|
|||||||
|
|
||||||
### `-init`
|
### `-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.
|
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.
|
||||||
|
|
||||||
|
|||||||
@ -229,6 +229,16 @@ permalink: /showcase.html
|
|||||||
|
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
|
||||||
|
<td align="center" valign="top">
|
||||||
|
<a href="https://itunes.apple.com/in/app/mensxp-fashion-grooming-tips/id1253494246?mt=8"><img class="roundrect" src="http://is5.mzstatic.com/image/thumb/Purple118/v4/04/78/65/04786577-67dc-ea87-2063-b97a89492e18/source/175x175bb.jpg" style="width:100px;height:100px;"></a>
|
||||||
|
<br />
|
||||||
|
<b>MensXP</b>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
source 'https://github.com/CocoaPods/Specs.git'
|
source 'https://github.com/CocoaPods/Specs.git'
|
||||||
platform :ios, '8.0'
|
platform :ios, '9.0'
|
||||||
target 'Sample' do
|
target 'Sample' do
|
||||||
pod 'Texture', :path => '../..'
|
pod 'Texture', :path => '../..'
|
||||||
end
|
end
|
||||||
|
|||||||
@ -240,13 +240,16 @@
|
|||||||
files = (
|
files = (
|
||||||
);
|
);
|
||||||
inputPaths = (
|
inputPaths = (
|
||||||
|
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||||
|
"${PODS_ROOT}/Manifest.lock",
|
||||||
);
|
);
|
||||||
name = "[CP] Check Pods Manifest.lock";
|
name = "[CP] Check Pods Manifest.lock";
|
||||||
outputPaths = (
|
outputPaths = (
|
||||||
|
"$(DERIVED_FILE_DIR)/Pods-Sample-checkManifestLockResult.txt",
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
shellPath = /bin/sh;
|
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;
|
showEnvVarsInLog = 0;
|
||||||
};
|
};
|
||||||
/* End PBXShellScriptBuildPhase section */
|
/* End PBXShellScriptBuildPhase section */
|
||||||
@ -302,7 +305,7 @@
|
|||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 8.1;
|
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||||
MTL_ENABLE_DEBUG_INFO = YES;
|
MTL_ENABLE_DEBUG_INFO = YES;
|
||||||
ONLY_ACTIVE_ARCH = YES;
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
@ -338,7 +341,7 @@
|
|||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 8.1;
|
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
@ -353,7 +356,6 @@
|
|||||||
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
|
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
|
||||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
INFOPLIST_FILE = Sample/Info.plist;
|
INFOPLIST_FILE = Sample/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 8.1;
|
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
TARGETED_DEVICE_FAMILY = 1;
|
TARGETED_DEVICE_FAMILY = 1;
|
||||||
@ -367,7 +369,6 @@
|
|||||||
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
|
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
|
||||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
INFOPLIST_FILE = Sample/Info.plist;
|
INFOPLIST_FILE = Sample/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 8.1;
|
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
TARGETED_DEVICE_FAMILY = 1;
|
TARGETED_DEVICE_FAMILY = 1;
|
||||||
|
|||||||
@ -23,10 +23,13 @@
|
|||||||
|
|
||||||
#define ASYNC_COLLECTION_LAYOUT 0
|
#define ASYNC_COLLECTION_LAYOUT 0
|
||||||
|
|
||||||
|
static CGSize const kItemSize = (CGSize){180, 90};
|
||||||
|
|
||||||
@interface ViewController () <ASCollectionDataSource, ASCollectionDelegateFlowLayout, ASCollectionGalleryLayoutPropertiesProviding>
|
@interface ViewController () <ASCollectionDataSource, ASCollectionDelegateFlowLayout, ASCollectionGalleryLayoutPropertiesProviding>
|
||||||
|
|
||||||
@property (nonatomic, strong) ASCollectionNode *collectionNode;
|
@property (nonatomic, strong) ASCollectionNode *collectionNode;
|
||||||
@property (nonatomic, strong) NSArray *data;
|
@property (nonatomic, strong) NSMutableArray<NSMutableArray<NSString *> *> *data;
|
||||||
|
@property (nonatomic, strong) UILongPressGestureRecognizer *moveRecognizer;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@ -34,18 +37,13 @@
|
|||||||
|
|
||||||
#pragma mark - Lifecycle
|
#pragma mark - Lifecycle
|
||||||
|
|
||||||
- (void)dealloc
|
|
||||||
{
|
|
||||||
self.collectionNode.dataSource = nil;
|
|
||||||
self.collectionNode.delegate = nil;
|
|
||||||
|
|
||||||
NSLog(@"ViewController is deallocing");
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)viewDidLoad
|
- (void)viewDidLoad
|
||||||
{
|
{
|
||||||
[super viewDidLoad];
|
[super viewDidLoad];
|
||||||
|
|
||||||
|
self.moveRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress)];
|
||||||
|
[self.view addGestureRecognizer:self.moveRecognizer];
|
||||||
|
|
||||||
#if ASYNC_COLLECTION_LAYOUT
|
#if ASYNC_COLLECTION_LAYOUT
|
||||||
ASCollectionGalleryLayoutDelegate *layoutDelegate = [[ASCollectionGalleryLayoutDelegate alloc] initWithScrollableDirections:ASScrollDirectionVerticalDirections];
|
ASCollectionGalleryLayoutDelegate *layoutDelegate = [[ASCollectionGalleryLayoutDelegate alloc] initWithScrollableDirections:ASScrollDirectionVerticalDirections];
|
||||||
layoutDelegate.propertiesProvider = self;
|
layoutDelegate.propertiesProvider = self;
|
||||||
@ -54,6 +52,7 @@
|
|||||||
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
|
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
|
||||||
layout.headerReferenceSize = CGSizeMake(50.0, 50.0);
|
layout.headerReferenceSize = CGSizeMake(50.0, 50.0);
|
||||||
layout.footerReferenceSize = 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 = [[ASCollectionNode alloc] initWithFrame:self.view.bounds collectionViewLayout:layout];
|
||||||
[self.collectionNode registerSupplementaryNodeOfKind:UICollectionElementKindSectionHeader];
|
[self.collectionNode registerSupplementaryNodeOfKind:UICollectionElementKindSectionHeader];
|
||||||
[self.collectionNode registerSupplementaryNodeOfKind:UICollectionElementKindSectionFooter];
|
[self.collectionNode registerSupplementaryNodeOfKind:UICollectionElementKindSectionFooter];
|
||||||
@ -73,34 +72,37 @@
|
|||||||
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh
|
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh
|
||||||
target:self
|
target:self
|
||||||
action:@selector(reloadTapped)];
|
action:@selector(reloadTapped)];
|
||||||
#endif
|
[self loadData];
|
||||||
|
#else
|
||||||
#if SIMULATE_WEB_RESPONSE
|
|
||||||
__weak typeof(self) weakSelf = self;
|
__weak typeof(self) weakSelf = self;
|
||||||
void(^mockWebService)() = ^{
|
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||||
NSLog(@"ViewController \"got data from a web service\"");
|
[weakSelf handleSimulatedWebResponse];
|
||||||
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];
|
|
||||||
});
|
});
|
||||||
#endif
|
#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
|
#pragma mark - Button Actions
|
||||||
|
|
||||||
- (void)reloadTapped
|
- (void)reloadTapped
|
||||||
@ -115,14 +117,42 @@
|
|||||||
- (CGSize)galleryLayoutDelegate:(ASCollectionGalleryLayoutDelegate *)delegate sizeForElements:(ASElementMap *)elements
|
- (CGSize)galleryLayoutDelegate:(ASCollectionGalleryLayoutDelegate *)delegate sizeForElements:(ASElementMap *)elements
|
||||||
{
|
{
|
||||||
ASDisplayNodeAssertMainThread();
|
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;
|
- (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 ^{
|
||||||
return [[ItemNode alloc] initWithString:text];
|
return [[ItemNode alloc] initWithString:text];
|
||||||
};
|
};
|
||||||
@ -139,18 +169,29 @@
|
|||||||
|
|
||||||
- (NSInteger)collectionNode:(ASCollectionNode *)collectionNode numberOfItemsInSection:(NSInteger)section
|
- (NSInteger)collectionNode:(ASCollectionNode *)collectionNode numberOfItemsInSection:(NSInteger)section
|
||||||
{
|
{
|
||||||
return 10;
|
return self.data[section].count;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSInteger)numberOfSectionsInCollectionNode:(ASCollectionNode *)collectionNode
|
- (NSInteger)numberOfSectionsInCollectionNode:(ASCollectionNode *)collectionNode
|
||||||
{
|
{
|
||||||
#if SIMULATE_WEB_RESPONSE
|
return self.data.count;
|
||||||
return _data == nil ? 0 : 100;
|
|
||||||
#else
|
|
||||||
return 100;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (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
|
- (void)collectionNode:(ASCollectionNode *)collectionNode willBeginBatchFetchWithContext:(ASBatchContext *)context
|
||||||
{
|
{
|
||||||
NSLog(@"fetch additional content");
|
NSLog(@"fetch additional content");
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
source 'https://github.com/CocoaPods/Specs.git'
|
source 'https://github.com/CocoaPods/Specs.git'
|
||||||
platform :ios, '8.0'
|
platform :ios, '9.0'
|
||||||
target 'Sample' do
|
target 'Sample' do
|
||||||
pod 'Texture', :path => '../..'
|
pod 'Texture', :path => '../..'
|
||||||
pod 'Texture/Yoga', :path => '../..'
|
pod 'Texture/Yoga', :path => '../..'
|
||||||
|
|||||||
@ -196,13 +196,16 @@
|
|||||||
files = (
|
files = (
|
||||||
);
|
);
|
||||||
inputPaths = (
|
inputPaths = (
|
||||||
|
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||||
|
"${PODS_ROOT}/Manifest.lock",
|
||||||
);
|
);
|
||||||
name = "[CP] Check Pods Manifest.lock";
|
name = "[CP] Check Pods Manifest.lock";
|
||||||
outputPaths = (
|
outputPaths = (
|
||||||
|
"$(DERIVED_FILE_DIR)/Pods-Sample-checkManifestLockResult.txt",
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
shellPath = /bin/sh;
|
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;
|
showEnvVarsInLog = 0;
|
||||||
};
|
};
|
||||||
F012A6F39E0149F18F564F50 /* [CP] Copy Pods Resources */ = {
|
F012A6F39E0149F18F564F50 /* [CP] Copy Pods Resources */ = {
|
||||||
@ -271,7 +274,7 @@
|
|||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||||
MTL_ENABLE_DEBUG_INFO = YES;
|
MTL_ENABLE_DEBUG_INFO = YES;
|
||||||
ONLY_ACTIVE_ARCH = YES;
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
@ -306,7 +309,7 @@
|
|||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
VALIDATE_PRODUCT = YES;
|
VALIDATE_PRODUCT = YES;
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
source 'https://github.com/CocoaPods/Specs.git'
|
source 'https://github.com/CocoaPods/Specs.git'
|
||||||
platform :ios, '8.0'
|
platform :ios, '9.0'
|
||||||
target 'Sample' do
|
target 'Sample' do
|
||||||
pod 'Texture', :path => '../..'
|
pod 'Texture', :path => '../..'
|
||||||
end
|
end
|
||||||
|
|||||||
@ -277,13 +277,16 @@
|
|||||||
files = (
|
files = (
|
||||||
);
|
);
|
||||||
inputPaths = (
|
inputPaths = (
|
||||||
|
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||||
|
"${PODS_ROOT}/Manifest.lock",
|
||||||
);
|
);
|
||||||
name = "[CP] Check Pods Manifest.lock";
|
name = "[CP] Check Pods Manifest.lock";
|
||||||
outputPaths = (
|
outputPaths = (
|
||||||
|
"$(DERIVED_FILE_DIR)/Pods-Sample-checkManifestLockResult.txt",
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
shellPath = /bin/sh;
|
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;
|
showEnvVarsInLog = 0;
|
||||||
};
|
};
|
||||||
F012A6F39E0149F18F564F50 /* [CP] Copy Pods Resources */ = {
|
F012A6F39E0149F18F564F50 /* [CP] Copy Pods Resources */ = {
|
||||||
@ -356,7 +359,7 @@
|
|||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||||
MTL_ENABLE_DEBUG_INFO = YES;
|
MTL_ENABLE_DEBUG_INFO = YES;
|
||||||
ONLY_ACTIVE_ARCH = YES;
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
@ -391,7 +394,7 @@
|
|||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
VALIDATE_PRODUCT = YES;
|
VALIDATE_PRODUCT = YES;
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
source 'https://github.com/CocoaPods/Specs.git'
|
source 'https://github.com/CocoaPods/Specs.git'
|
||||||
platform :ios, '8.0'
|
platform :ios, '9.0'
|
||||||
target 'Sample' do
|
target 'Sample' do
|
||||||
pod 'Texture/IGListKit', :path => '../..'
|
pod 'Texture/IGListKit', :path => '../..'
|
||||||
pod 'Texture/PINRemoteImage', :path => '../..'
|
pod 'Texture/PINRemoteImage', :path => '../..'
|
||||||
|
|||||||
@ -381,13 +381,16 @@
|
|||||||
files = (
|
files = (
|
||||||
);
|
);
|
||||||
inputPaths = (
|
inputPaths = (
|
||||||
|
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||||
|
"${PODS_ROOT}/Manifest.lock",
|
||||||
);
|
);
|
||||||
name = "[CP] Check Pods Manifest.lock";
|
name = "[CP] Check Pods Manifest.lock";
|
||||||
outputPaths = (
|
outputPaths = (
|
||||||
|
"$(DERIVED_FILE_DIR)/Pods-Sample-checkManifestLockResult.txt",
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
shellPath = /bin/sh;
|
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;
|
showEnvVarsInLog = 0;
|
||||||
};
|
};
|
||||||
F012A6F39E0149F18F564F50 /* [CP] Copy Pods Resources */ = {
|
F012A6F39E0149F18F564F50 /* [CP] Copy Pods Resources */ = {
|
||||||
@ -477,7 +480,7 @@
|
|||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||||
MTL_ENABLE_DEBUG_INFO = YES;
|
MTL_ENABLE_DEBUG_INFO = YES;
|
||||||
ONLY_ACTIVE_ARCH = YES;
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
@ -512,7 +515,7 @@
|
|||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
VALIDATE_PRODUCT = YES;
|
VALIDATE_PRODUCT = YES;
|
||||||
|
|||||||
@ -22,6 +22,8 @@
|
|||||||
#import "WindowWithStatusBarUnderlay.h"
|
#import "WindowWithStatusBarUnderlay.h"
|
||||||
#import "Utilities.h"
|
#import "Utilities.h"
|
||||||
|
|
||||||
|
#import <AsyncDisplayKit/ASGraphicsContext.h>
|
||||||
|
|
||||||
#define WEAVER 0
|
#define WEAVER 0
|
||||||
|
|
||||||
#if WEAVER
|
#if WEAVER
|
||||||
@ -38,6 +40,8 @@
|
|||||||
|
|
||||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
|
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
|
||||||
|
|
||||||
|
ASEnableNoCopyRendering();
|
||||||
|
|
||||||
// this UIWindow subclass is neccessary to make the status bar opaque
|
// this UIWindow subclass is neccessary to make the status bar opaque
|
||||||
_window = [[WindowWithStatusBarUnderlay alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
|
_window = [[WindowWithStatusBarUnderlay alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
|
||||||
_window.backgroundColor = [UIColor whiteColor];
|
_window.backgroundColor = [UIColor whiteColor];
|
||||||
|
|||||||
@ -44,6 +44,9 @@
|
|||||||
#define InsetForHeader UIEdgeInsetsMake(0, HORIZONTAL_BUFFER, 0, HORIZONTAL_BUFFER)
|
#define InsetForHeader UIEdgeInsetsMake(0, HORIZONTAL_BUFFER, 0, HORIZONTAL_BUFFER)
|
||||||
#define InsetForFooter UIEdgeInsetsMake(VERTICAL_BUFFER, HORIZONTAL_BUFFER, VERTICAL_BUFFER, HORIZONTAL_BUFFER)
|
#define InsetForFooter UIEdgeInsetsMake(VERTICAL_BUFFER, HORIZONTAL_BUFFER, VERTICAL_BUFFER, HORIZONTAL_BUFFER)
|
||||||
|
|
||||||
|
@interface PhotoCellNode () <ASNetworkImageNodeDelegate>
|
||||||
|
@end
|
||||||
|
|
||||||
@implementation PhotoCellNode
|
@implementation PhotoCellNode
|
||||||
{
|
{
|
||||||
PhotoModel *_photoModel;
|
PhotoModel *_photoModel;
|
||||||
@ -77,6 +80,7 @@
|
|||||||
}];
|
}];
|
||||||
|
|
||||||
_photoImageNode = [[ASNetworkImageNode alloc] init];
|
_photoImageNode = [[ASNetworkImageNode alloc] init];
|
||||||
|
_photoImageNode.delegate = self;
|
||||||
_photoImageNode.URL = photo.URL;
|
_photoImageNode.URL = photo.URL;
|
||||||
_photoImageNode.layerBacked = YES;
|
_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
|
#pragma mark - Helper Methods
|
||||||
|
|
||||||
- (ASTextNode *)createLayerBackedTextNodeWithString:(NSAttributedString *)attributedString
|
- (ASTextNode *)createLayerBackedTextNodeWithString:(NSAttributedString *)attributedString
|
||||||
|
|||||||
@ -1,20 +1,18 @@
|
|||||||
//
|
//
|
||||||
// PhotoFeedModel.m
|
// PhotoFeedModel.m
|
||||||
// Sample
|
// Texture
|
||||||
//
|
|
||||||
// Created by Hannah Troisi on 2/28/16.
|
|
||||||
//
|
//
|
||||||
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
|
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
|
||||||
// This source code is licensed under the BSD-style license found in the
|
// This source code is licensed under the BSD-style license found in the
|
||||||
// LICENSE file in the root directory of this source tree. An additional grant
|
// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional
|
||||||
// of patent rights can be found in the PATENTS file in the same directory.
|
// 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
|
// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present,
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
// you may not use this file except in compliance with the License.
|
||||||
// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
// You may obtain a copy of the License at
|
||||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
//
|
||||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
//
|
//
|
||||||
|
|
||||||
#import "PhotoFeedModel.h"
|
#import "PhotoFeedModel.h"
|
||||||
@ -184,9 +182,11 @@
|
|||||||
// early return if reached end of pages
|
// early return if reached end of pages
|
||||||
if (_totalPages) {
|
if (_totalPages) {
|
||||||
if (_currentPage == _totalPages) {
|
if (_currentPage == _totalPages) {
|
||||||
if (block){
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
block(@[]);
|
if (block) {
|
||||||
}
|
block(@[]);
|
||||||
|
}
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
source 'https://github.com/CocoaPods/Specs.git'
|
source 'https://github.com/CocoaPods/Specs.git'
|
||||||
platform :ios, '8.0'
|
platform :ios, '9.0'
|
||||||
target 'Sample' do
|
target 'Sample' do
|
||||||
pod 'Texture', :path => '../..'
|
pod 'Texture', :path => '../..'
|
||||||
end
|
end
|
||||||
|
|||||||
@ -214,13 +214,16 @@
|
|||||||
files = (
|
files = (
|
||||||
);
|
);
|
||||||
inputPaths = (
|
inputPaths = (
|
||||||
|
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||||
|
"${PODS_ROOT}/Manifest.lock",
|
||||||
);
|
);
|
||||||
name = "[CP] Check Pods Manifest.lock";
|
name = "[CP] Check Pods Manifest.lock";
|
||||||
outputPaths = (
|
outputPaths = (
|
||||||
|
"$(DERIVED_FILE_DIR)/Pods-Sample-checkManifestLockResult.txt",
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
shellPath = /bin/sh;
|
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;
|
showEnvVarsInLog = 0;
|
||||||
};
|
};
|
||||||
/* End PBXShellScriptBuildPhase section */
|
/* End PBXShellScriptBuildPhase section */
|
||||||
@ -288,7 +291,7 @@
|
|||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||||
MTL_ENABLE_DEBUG_INFO = YES;
|
MTL_ENABLE_DEBUG_INFO = YES;
|
||||||
ONLY_ACTIVE_ARCH = YES;
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
@ -325,7 +328,7 @@
|
|||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
VALIDATE_PRODUCT = YES;
|
VALIDATE_PRODUCT = YES;
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
source 'https://github.com/CocoaPods/Specs.git'
|
source 'https://github.com/CocoaPods/Specs.git'
|
||||||
platform :ios, '8.0'
|
platform :ios, '9.0'
|
||||||
target 'Sample' do
|
target 'Sample' do
|
||||||
pod 'Texture', :path => '../..'
|
pod 'Texture', :path => '../..'
|
||||||
end
|
end
|
||||||
|
|||||||
@ -219,13 +219,16 @@
|
|||||||
files = (
|
files = (
|
||||||
);
|
);
|
||||||
inputPaths = (
|
inputPaths = (
|
||||||
|
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||||
|
"${PODS_ROOT}/Manifest.lock",
|
||||||
);
|
);
|
||||||
name = "[CP] Check Pods Manifest.lock";
|
name = "[CP] Check Pods Manifest.lock";
|
||||||
outputPaths = (
|
outputPaths = (
|
||||||
|
"$(DERIVED_FILE_DIR)/Pods-Sample-checkManifestLockResult.txt",
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
shellPath = /bin/sh;
|
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;
|
showEnvVarsInLog = 0;
|
||||||
};
|
};
|
||||||
/* End PBXShellScriptBuildPhase section */
|
/* End PBXShellScriptBuildPhase section */
|
||||||
@ -294,7 +297,7 @@
|
|||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||||
MTL_ENABLE_DEBUG_INFO = YES;
|
MTL_ENABLE_DEBUG_INFO = YES;
|
||||||
ONLY_ACTIVE_ARCH = YES;
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
@ -331,7 +334,7 @@
|
|||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
VALIDATE_PRODUCT = YES;
|
VALIDATE_PRODUCT = YES;
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
source 'https://github.com/CocoaPods/Specs.git'
|
source 'https://github.com/CocoaPods/Specs.git'
|
||||||
platform :ios, '8.0'
|
platform :ios, '9.0'
|
||||||
target 'Sample' do
|
target 'Sample' do
|
||||||
pod 'Texture', :path => '../..'
|
pod 'Texture', :path => '../..'
|
||||||
pod 'PINRemoteImage/WebP'
|
pod 'PINRemoteImage/WebP'
|
||||||
|
|||||||
@ -214,13 +214,16 @@
|
|||||||
files = (
|
files = (
|
||||||
);
|
);
|
||||||
inputPaths = (
|
inputPaths = (
|
||||||
|
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||||
|
"${PODS_ROOT}/Manifest.lock",
|
||||||
);
|
);
|
||||||
name = "[CP] Check Pods Manifest.lock";
|
name = "[CP] Check Pods Manifest.lock";
|
||||||
outputPaths = (
|
outputPaths = (
|
||||||
|
"$(DERIVED_FILE_DIR)/Pods-Sample-checkManifestLockResult.txt",
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
shellPath = /bin/sh;
|
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;
|
showEnvVarsInLog = 0;
|
||||||
};
|
};
|
||||||
/* End PBXShellScriptBuildPhase section */
|
/* End PBXShellScriptBuildPhase section */
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
# Uncomment this line to define a global platform for your project
|
# 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
|
# Uncomment this line if you're using Swift
|
||||||
# use_frameworks!
|
# use_frameworks!
|
||||||
|
|||||||
@ -217,13 +217,16 @@
|
|||||||
files = (
|
files = (
|
||||||
);
|
);
|
||||||
inputPaths = (
|
inputPaths = (
|
||||||
|
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||||
|
"${PODS_ROOT}/Manifest.lock",
|
||||||
);
|
);
|
||||||
name = "[CP] Check Pods Manifest.lock";
|
name = "[CP] Check Pods Manifest.lock";
|
||||||
outputPaths = (
|
outputPaths = (
|
||||||
|
"$(DERIVED_FILE_DIR)/Pods-Sample-checkManifestLockResult.txt",
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
shellPath = /bin/sh;
|
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;
|
showEnvVarsInLog = 0;
|
||||||
};
|
};
|
||||||
84F93825AFB1CA7FBB116BA4 /* [CP] Copy Pods Resources */ = {
|
84F93825AFB1CA7FBB116BA4 /* [CP] Copy Pods Resources */ = {
|
||||||
@ -309,7 +312,7 @@
|
|||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||||
MTL_ENABLE_DEBUG_INFO = YES;
|
MTL_ENABLE_DEBUG_INFO = YES;
|
||||||
ONLY_ACTIVE_ARCH = YES;
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
@ -348,7 +351,7 @@
|
|||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
source 'https://github.com/CocoaPods/Specs.git'
|
source 'https://github.com/CocoaPods/Specs.git'
|
||||||
platform :ios, '8.0'
|
platform :ios, '9.0'
|
||||||
target 'Sample' do
|
target 'Sample' do
|
||||||
pod 'Texture', :path => '../..'
|
pod 'Texture', :path => '../..'
|
||||||
end
|
end
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user