Merge commit '89156ebbc750a2d28b8d4594ccad62ef4c73127e'

This commit is contained in:
Peter
2017-01-18 02:13:01 +03:00
68 changed files with 1460 additions and 603 deletions

22
.buckconfig Normal file
View File

@@ -0,0 +1,22 @@
[cxx]
default_platform = iphonesimulator-x86_64
combined_preprocess_and_compile = true
[apple]
iphonesimulator_target_sdk_version = 8.0
iphoneos_target_sdk_version = 8.0
xctool_default_destination_specifier = platform=iOS Simulator, name=iPhone 6, OS=10.2
[alias]
lib = //:AsyncDisplayKit
tests = //:Tests
[httpserver]
port = 8080
[project]
ide = xcode
ignore = .buckd, \
.hg, \
.git, \
buck-out, \

1
.buckversion Normal file
View File

@@ -0,0 +1 @@
c948c20ebb155904909af05cfd16428a6992b98d

5
.gitignore vendored
View File

@@ -29,3 +29,8 @@ build
timeline.xctimeline timeline.xctimeline
playground.xcworkspace playground.xcworkspace
# Buck
/buck-out
/.buckconfig.local
/.buckd

View File

@@ -26,7 +26,7 @@ Pod::Spec.new do |spec|
'AsyncDisplayKit/Layout/*.h', 'AsyncDisplayKit/Layout/*.h',
'Base/*.h', 'Base/*.h',
'AsyncDisplayKit/Debug/ASLayoutElementInspectorNode.h', 'AsyncDisplayKit/Debug/ASLayoutElementInspectorNode.h',
'AsyncDisplayKit/TextKit/ASTextNodeTypes.h', 'AsyncDisplayKit/TextKit/ASTextNodeTypes.h',
'AsyncDisplayKit/TextKit/ASTextKitComponents.h' 'AsyncDisplayKit/TextKit/ASTextKitComponents.h'
] ]

View File

@@ -93,7 +93,7 @@
254C6B7D1BF94DF4003EC431 /* ASTextKitShadower.h in Headers */ = {isa = PBXBuildFile; fileRef = 2577549F1BEE44CD00737CA5 /* ASTextKitShadower.h */; }; 254C6B7D1BF94DF4003EC431 /* ASTextKitShadower.h in Headers */ = {isa = PBXBuildFile; fileRef = 2577549F1BEE44CD00737CA5 /* ASTextKitShadower.h */; };
254C6B7E1BF94DF4003EC431 /* ASTextKitTailTruncater.h in Headers */ = {isa = PBXBuildFile; fileRef = 257754A11BEE44CD00737CA5 /* ASTextKitTailTruncater.h */; }; 254C6B7E1BF94DF4003EC431 /* ASTextKitTailTruncater.h in Headers */ = {isa = PBXBuildFile; fileRef = 257754A11BEE44CD00737CA5 /* ASTextKitTailTruncater.h */; };
254C6B7F1BF94DF4003EC431 /* ASTextKitTruncating.h in Headers */ = {isa = PBXBuildFile; fileRef = 257754A31BEE44CD00737CA5 /* ASTextKitTruncating.h */; }; 254C6B7F1BF94DF4003EC431 /* ASTextKitTruncating.h in Headers */ = {isa = PBXBuildFile; fileRef = 257754A31BEE44CD00737CA5 /* ASTextKitTruncating.h */; };
254C6B821BF94F8A003EC431 /* ASTextKitComponents.m in Sources */ = {isa = PBXBuildFile; fileRef = 257754B71BEE458D00737CA5 /* ASTextKitComponents.m */; }; 254C6B821BF94F8A003EC431 /* ASTextKitComponents.mm in Sources */ = {isa = PBXBuildFile; fileRef = 257754B71BEE458D00737CA5 /* ASTextKitComponents.mm */; };
254C6B831BF94F8A003EC431 /* ASTextKitCoreTextAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 257754B81BEE458E00737CA5 /* ASTextKitCoreTextAdditions.m */; }; 254C6B831BF94F8A003EC431 /* ASTextKitCoreTextAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 257754B81BEE458E00737CA5 /* ASTextKitCoreTextAdditions.m */; };
254C6B841BF94F8A003EC431 /* ASTextNodeWordKerner.m in Sources */ = {isa = PBXBuildFile; fileRef = 257754BD1BEE458E00737CA5 /* ASTextNodeWordKerner.m */; }; 254C6B841BF94F8A003EC431 /* ASTextNodeWordKerner.m in Sources */ = {isa = PBXBuildFile; fileRef = 257754BD1BEE458E00737CA5 /* ASTextNodeWordKerner.m */; };
254C6B851BF94F8A003EC431 /* ASTextKitAttributes.mm in Sources */ = {isa = PBXBuildFile; fileRef = 257754941BEE44CD00737CA5 /* ASTextKitAttributes.mm */; }; 254C6B851BF94F8A003EC431 /* ASTextKitAttributes.mm in Sources */ = {isa = PBXBuildFile; fileRef = 257754941BEE44CD00737CA5 /* ASTextKitAttributes.mm */; };
@@ -112,7 +112,7 @@
257754B01BEE44CD00737CA5 /* ASTextKitRenderer+TextChecking.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2577549E1BEE44CD00737CA5 /* ASTextKitRenderer+TextChecking.mm */; }; 257754B01BEE44CD00737CA5 /* ASTextKitRenderer+TextChecking.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2577549E1BEE44CD00737CA5 /* ASTextKitRenderer+TextChecking.mm */; };
257754B21BEE44CD00737CA5 /* ASTextKitShadower.mm in Sources */ = {isa = PBXBuildFile; fileRef = 257754A01BEE44CD00737CA5 /* ASTextKitShadower.mm */; }; 257754B21BEE44CD00737CA5 /* ASTextKitShadower.mm in Sources */ = {isa = PBXBuildFile; fileRef = 257754A01BEE44CD00737CA5 /* ASTextKitShadower.mm */; };
257754B41BEE44CD00737CA5 /* ASTextKitTailTruncater.mm in Sources */ = {isa = PBXBuildFile; fileRef = 257754A21BEE44CD00737CA5 /* ASTextKitTailTruncater.mm */; }; 257754B41BEE44CD00737CA5 /* ASTextKitTailTruncater.mm in Sources */ = {isa = PBXBuildFile; fileRef = 257754A21BEE44CD00737CA5 /* ASTextKitTailTruncater.mm */; };
257754BE1BEE458E00737CA5 /* ASTextKitComponents.m in Sources */ = {isa = PBXBuildFile; fileRef = 257754B71BEE458D00737CA5 /* ASTextKitComponents.m */; }; 257754BE1BEE458E00737CA5 /* ASTextKitComponents.mm in Sources */ = {isa = PBXBuildFile; fileRef = 257754B71BEE458D00737CA5 /* ASTextKitComponents.mm */; };
257754BF1BEE458E00737CA5 /* ASTextKitCoreTextAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 257754B81BEE458E00737CA5 /* ASTextKitCoreTextAdditions.m */; }; 257754BF1BEE458E00737CA5 /* ASTextKitCoreTextAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 257754B81BEE458E00737CA5 /* ASTextKitCoreTextAdditions.m */; };
257754C41BEE458E00737CA5 /* ASTextNodeWordKerner.m in Sources */ = {isa = PBXBuildFile; fileRef = 257754BD1BEE458E00737CA5 /* ASTextNodeWordKerner.m */; }; 257754C41BEE458E00737CA5 /* ASTextNodeWordKerner.m in Sources */ = {isa = PBXBuildFile; fileRef = 257754BD1BEE458E00737CA5 /* ASTextNodeWordKerner.m */; };
25E327571C16819500A2170C /* ASPagerNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 25E327541C16819500A2170C /* ASPagerNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; 25E327571C16819500A2170C /* ASPagerNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 25E327541C16819500A2170C /* ASPagerNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -203,8 +203,16 @@
6907C2581DC4ECFE00374C66 /* ASObjectDescriptionHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = 6907C2561DC4ECFE00374C66 /* ASObjectDescriptionHelpers.h */; settings = {ATTRIBUTES = (Public, ); }; }; 6907C2581DC4ECFE00374C66 /* ASObjectDescriptionHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = 6907C2561DC4ECFE00374C66 /* ASObjectDescriptionHelpers.h */; settings = {ATTRIBUTES = (Public, ); }; };
6907C2591DC4ECFE00374C66 /* ASObjectDescriptionHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = 6907C2571DC4ECFE00374C66 /* ASObjectDescriptionHelpers.m */; }; 6907C2591DC4ECFE00374C66 /* ASObjectDescriptionHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = 6907C2571DC4ECFE00374C66 /* ASObjectDescriptionHelpers.m */; };
6907C25A1DC4ECFE00374C66 /* ASObjectDescriptionHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = 6907C2571DC4ECFE00374C66 /* ASObjectDescriptionHelpers.m */; }; 6907C25A1DC4ECFE00374C66 /* ASObjectDescriptionHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = 6907C2571DC4ECFE00374C66 /* ASObjectDescriptionHelpers.m */; };
690C35611E055C5D00069B91 /* ASDimensionInternal.mm in Sources */ = {isa = PBXBuildFile; fileRef = 690C35601E055C5D00069B91 /* ASDimensionInternal.mm */; };
690C35621E055C5D00069B91 /* ASDimensionInternal.mm in Sources */ = {isa = PBXBuildFile; fileRef = 690C35601E055C5D00069B91 /* ASDimensionInternal.mm */; };
690C35641E055C7B00069B91 /* ASDimensionInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 690C35631E055C7B00069B91 /* ASDimensionInternal.h */; settings = {ATTRIBUTES = (Public, ); }; };
690C35661E0567C600069B91 /* ASDimensionDeprecated.mm in Sources */ = {isa = PBXBuildFile; fileRef = 690C35651E0567C600069B91 /* ASDimensionDeprecated.mm */; };
690C35671E0567C600069B91 /* ASDimensionDeprecated.mm in Sources */ = {isa = PBXBuildFile; fileRef = 690C35651E0567C600069B91 /* ASDimensionDeprecated.mm */; };
690C356B1E05680300069B91 /* ASDimensionDeprecated.h in Headers */ = {isa = PBXBuildFile; fileRef = 690C356A1E05680300069B91 /* ASDimensionDeprecated.h */; settings = {ATTRIBUTES = (Public, ); }; };
69127CFE1DD2B387004BF6E2 /* ASEventLog.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 696F01EA1DD2AF450049FBD5 /* ASEventLog.h */; }; 69127CFE1DD2B387004BF6E2 /* ASEventLog.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 696F01EA1DD2AF450049FBD5 /* ASEventLog.h */; };
693117CE1DC7C72700DE4784 /* ASDisplayNode+Deprecated.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 683489271D70DE3400327501 /* ASDisplayNode+Deprecated.h */; }; 693117CE1DC7C72700DE4784 /* ASDisplayNode+Deprecated.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 683489271D70DE3400327501 /* ASDisplayNode+Deprecated.h */; };
693DA50F1E2536A600F66DF4 /* ASDimensionInternal.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 690C35631E055C7B00069B91 /* ASDimensionInternal.h */; };
693DA5141E25373100F66DF4 /* ASDimensionDeprecated.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 690C356A1E05680300069B91 /* ASDimensionDeprecated.h */; };
69527B121DC84292004785FB /* ASLayoutElementStylePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 69527B111DC84292004785FB /* ASLayoutElementStylePrivate.h */; }; 69527B121DC84292004785FB /* ASLayoutElementStylePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 69527B111DC84292004785FB /* ASLayoutElementStylePrivate.h */; };
6959433E1D70815300B0EE1F /* ASDisplayNodeLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6959433C1D70815300B0EE1F /* ASDisplayNodeLayout.mm */; }; 6959433E1D70815300B0EE1F /* ASDisplayNodeLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6959433C1D70815300B0EE1F /* ASDisplayNodeLayout.mm */; };
6959433F1D70815300B0EE1F /* ASDisplayNodeLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6959433C1D70815300B0EE1F /* ASDisplayNodeLayout.mm */; }; 6959433F1D70815300B0EE1F /* ASDisplayNodeLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6959433C1D70815300B0EE1F /* ASDisplayNodeLayout.mm */; };
@@ -673,6 +681,8 @@
dstPath = "include/$(PRODUCT_NAME)"; dstPath = "include/$(PRODUCT_NAME)";
dstSubfolderSpec = 16; dstSubfolderSpec = 16;
files = ( files = (
693DA5141E25373100F66DF4 /* ASDimensionDeprecated.h in CopyFiles */,
693DA50F1E2536A600F66DF4 /* ASDimensionInternal.h in CopyFiles */,
68C2155C1DE11AA80019C4BC /* ASObjectDescriptionHelpers.h in CopyFiles */, 68C2155C1DE11AA80019C4BC /* ASObjectDescriptionHelpers.h in CopyFiles */,
68C2155B1DE11A790019C4BC /* ASCollectionViewLayoutInspector.h in CopyFiles */, 68C2155B1DE11A790019C4BC /* ASCollectionViewLayoutInspector.h in CopyFiles */,
DEB8ED7E1DD007F400DBDE55 /* ASLayoutElementInspectorNode.h in CopyFiles */, DEB8ED7E1DD007F400DBDE55 /* ASLayoutElementInspectorNode.h in CopyFiles */,
@@ -956,7 +966,7 @@
257754A11BEE44CD00737CA5 /* ASTextKitTailTruncater.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASTextKitTailTruncater.h; path = TextKit/ASTextKitTailTruncater.h; sourceTree = "<group>"; }; 257754A11BEE44CD00737CA5 /* ASTextKitTailTruncater.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASTextKitTailTruncater.h; path = TextKit/ASTextKitTailTruncater.h; sourceTree = "<group>"; };
257754A21BEE44CD00737CA5 /* ASTextKitTailTruncater.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASTextKitTailTruncater.mm; path = TextKit/ASTextKitTailTruncater.mm; sourceTree = "<group>"; }; 257754A21BEE44CD00737CA5 /* ASTextKitTailTruncater.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASTextKitTailTruncater.mm; path = TextKit/ASTextKitTailTruncater.mm; sourceTree = "<group>"; };
257754A31BEE44CD00737CA5 /* ASTextKitTruncating.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASTextKitTruncating.h; path = TextKit/ASTextKitTruncating.h; sourceTree = "<group>"; }; 257754A31BEE44CD00737CA5 /* ASTextKitTruncating.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASTextKitTruncating.h; path = TextKit/ASTextKitTruncating.h; sourceTree = "<group>"; };
257754B71BEE458D00737CA5 /* ASTextKitComponents.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ASTextKitComponents.m; path = TextKit/ASTextKitComponents.m; sourceTree = "<group>"; }; 257754B71BEE458D00737CA5 /* ASTextKitComponents.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASTextKitComponents.mm; path = TextKit/ASTextKitComponents.mm; sourceTree = "<group>"; };
257754B81BEE458E00737CA5 /* ASTextKitCoreTextAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ASTextKitCoreTextAdditions.m; path = TextKit/ASTextKitCoreTextAdditions.m; sourceTree = "<group>"; }; 257754B81BEE458E00737CA5 /* ASTextKitCoreTextAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ASTextKitCoreTextAdditions.m; path = TextKit/ASTextKitCoreTextAdditions.m; sourceTree = "<group>"; };
257754B91BEE458E00737CA5 /* ASTextNodeWordKerner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASTextNodeWordKerner.h; path = TextKit/ASTextNodeWordKerner.h; sourceTree = "<group>"; }; 257754B91BEE458E00737CA5 /* ASTextNodeWordKerner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASTextNodeWordKerner.h; path = TextKit/ASTextNodeWordKerner.h; sourceTree = "<group>"; };
257754BA1BEE458E00737CA5 /* ASTextKitComponents.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASTextKitComponents.h; path = TextKit/ASTextKitComponents.h; sourceTree = "<group>"; }; 257754BA1BEE458E00737CA5 /* ASTextKitComponents.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASTextKitComponents.h; path = TextKit/ASTextKitComponents.h; sourceTree = "<group>"; };
@@ -1003,6 +1013,10 @@
68FC85E81CE29C7D00EDD713 /* ASVisibilityProtocols.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASVisibilityProtocols.m; sourceTree = "<group>"; }; 68FC85E81CE29C7D00EDD713 /* ASVisibilityProtocols.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASVisibilityProtocols.m; sourceTree = "<group>"; };
6907C2561DC4ECFE00374C66 /* ASObjectDescriptionHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASObjectDescriptionHelpers.h; sourceTree = "<group>"; }; 6907C2561DC4ECFE00374C66 /* ASObjectDescriptionHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASObjectDescriptionHelpers.h; sourceTree = "<group>"; };
6907C2571DC4ECFE00374C66 /* ASObjectDescriptionHelpers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASObjectDescriptionHelpers.m; sourceTree = "<group>"; }; 6907C2571DC4ECFE00374C66 /* ASObjectDescriptionHelpers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASObjectDescriptionHelpers.m; sourceTree = "<group>"; };
690C35601E055C5D00069B91 /* ASDimensionInternal.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASDimensionInternal.mm; path = AsyncDisplayKit/Layout/ASDimensionInternal.mm; sourceTree = "<group>"; };
690C35631E055C7B00069B91 /* ASDimensionInternal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASDimensionInternal.h; path = AsyncDisplayKit/Layout/ASDimensionInternal.h; sourceTree = "<group>"; };
690C35651E0567C600069B91 /* ASDimensionDeprecated.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASDimensionDeprecated.mm; path = AsyncDisplayKit/Layout/ASDimensionDeprecated.mm; sourceTree = "<group>"; };
690C356A1E05680300069B91 /* ASDimensionDeprecated.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASDimensionDeprecated.h; path = AsyncDisplayKit/Layout/ASDimensionDeprecated.h; sourceTree = "<group>"; };
69527B111DC84292004785FB /* ASLayoutElementStylePrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASLayoutElementStylePrivate.h; path = AsyncDisplayKit/Layout/ASLayoutElementStylePrivate.h; sourceTree = SOURCE_ROOT; }; 69527B111DC84292004785FB /* ASLayoutElementStylePrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASLayoutElementStylePrivate.h; path = AsyncDisplayKit/Layout/ASLayoutElementStylePrivate.h; sourceTree = SOURCE_ROOT; };
6959433C1D70815300B0EE1F /* ASDisplayNodeLayout.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASDisplayNodeLayout.mm; sourceTree = "<group>"; }; 6959433C1D70815300B0EE1F /* ASDisplayNodeLayout.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASDisplayNodeLayout.mm; sourceTree = "<group>"; };
6959433D1D70815300B0EE1F /* ASDisplayNodeLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASDisplayNodeLayout.h; sourceTree = "<group>"; }; 6959433D1D70815300B0EE1F /* ASDisplayNodeLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASDisplayNodeLayout.h; sourceTree = "<group>"; };
@@ -1658,7 +1672,7 @@
B30BF6501C5964B0004FCD53 /* ASLayoutManager.h */, B30BF6501C5964B0004FCD53 /* ASLayoutManager.h */,
B30BF6511C5964B0004FCD53 /* ASLayoutManager.m */, B30BF6511C5964B0004FCD53 /* ASLayoutManager.m */,
257754BA1BEE458E00737CA5 /* ASTextKitComponents.h */, 257754BA1BEE458E00737CA5 /* ASTextKitComponents.h */,
257754B71BEE458D00737CA5 /* ASTextKitComponents.m */, 257754B71BEE458D00737CA5 /* ASTextKitComponents.mm */,
257754BB1BEE458E00737CA5 /* ASTextKitCoreTextAdditions.h */, 257754BB1BEE458E00737CA5 /* ASTextKitCoreTextAdditions.h */,
257754B81BEE458E00737CA5 /* ASTextKitCoreTextAdditions.m */, 257754B81BEE458E00737CA5 /* ASTextKitCoreTextAdditions.m */,
257754B91BEE458E00737CA5 /* ASTextNodeWordKerner.h */, 257754B91BEE458E00737CA5 /* ASTextNodeWordKerner.h */,
@@ -1731,6 +1745,10 @@
ACF6ED041B17843500DA7C62 /* ASCenterLayoutSpec.mm */, ACF6ED041B17843500DA7C62 /* ASCenterLayoutSpec.mm */,
ACF6ED071B17843500DA7C62 /* ASDimension.h */, ACF6ED071B17843500DA7C62 /* ASDimension.h */,
ACF6ED081B17843500DA7C62 /* ASDimension.mm */, ACF6ED081B17843500DA7C62 /* ASDimension.mm */,
690C356A1E05680300069B91 /* ASDimensionDeprecated.h */,
690C35651E0567C600069B91 /* ASDimensionDeprecated.mm */,
690C35631E055C7B00069B91 /* ASDimensionInternal.h */,
690C35601E055C5D00069B91 /* ASDimensionInternal.mm */,
ACF6ED091B17843500DA7C62 /* ASInsetLayoutSpec.h */, ACF6ED091B17843500DA7C62 /* ASInsetLayoutSpec.h */,
ACF6ED0A1B17843500DA7C62 /* ASInsetLayoutSpec.mm */, ACF6ED0A1B17843500DA7C62 /* ASInsetLayoutSpec.mm */,
ACF6ED0B1B17843500DA7C62 /* ASLayout.h */, ACF6ED0B1B17843500DA7C62 /* ASLayout.h */,
@@ -1808,6 +1826,8 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
696F01EC1DD2AF450049FBD5 /* ASEventLog.h in Headers */, 696F01EC1DD2AF450049FBD5 /* ASEventLog.h in Headers */,
690C35641E055C7B00069B91 /* ASDimensionInternal.h in Headers */,
690C356B1E05680300069B91 /* ASDimensionDeprecated.h in Headers */,
683489281D70DE3400327501 /* ASDisplayNode+Deprecated.h in Headers */, 683489281D70DE3400327501 /* ASDisplayNode+Deprecated.h in Headers */,
6907C2581DC4ECFE00374C66 /* ASObjectDescriptionHelpers.h in Headers */, 6907C2581DC4ECFE00374C66 /* ASObjectDescriptionHelpers.h in Headers */,
69E0E8A71D356C9400627613 /* ASEqualityHelpers.h in Headers */, 69E0E8A71D356C9400627613 /* ASEqualityHelpers.h in Headers */,
@@ -2195,6 +2215,7 @@
8B0768B41CE752EC002E1453 /* ASDefaultPlaybackButton.m in Sources */, 8B0768B41CE752EC002E1453 /* ASDefaultPlaybackButton.m in Sources */,
E55D86321CA8A14000A0C26F /* ASLayoutElement.mm in Sources */, E55D86321CA8A14000A0C26F /* ASLayoutElement.mm in Sources */,
68FC85E41CE29B7E00EDD713 /* ASTabBarController.m in Sources */, 68FC85E41CE29B7E00EDD713 /* ASTabBarController.m in Sources */,
690C35661E0567C600069B91 /* ASDimensionDeprecated.mm in Sources */,
058D0A23195D050800B7D73C /* _ASAsyncTransactionContainer.m in Sources */, 058D0A23195D050800B7D73C /* _ASAsyncTransactionContainer.m in Sources */,
058D0A24195D050800B7D73C /* _ASAsyncTransactionGroup.m in Sources */, 058D0A24195D050800B7D73C /* _ASAsyncTransactionGroup.m in Sources */,
68355B3A1CB57A5A001D4E68 /* ASPINRemoteImageDownloader.m in Sources */, 68355B3A1CB57A5A001D4E68 /* ASPINRemoteImageDownloader.m in Sources */,
@@ -2287,7 +2308,7 @@
9C8221971BA237B80037F19A /* ASStackBaselinePositionedLayout.mm in Sources */, 9C8221971BA237B80037F19A /* ASStackBaselinePositionedLayout.mm in Sources */,
251B8EF81BBB3D690087C538 /* ASCollectionDataController.mm in Sources */, 251B8EF81BBB3D690087C538 /* ASCollectionDataController.mm in Sources */,
ACF6ED301B17843500DA7C62 /* ASStackLayoutSpec.mm in Sources */, ACF6ED301B17843500DA7C62 /* ASStackLayoutSpec.mm in Sources */,
257754BE1BEE458E00737CA5 /* ASTextKitComponents.m in Sources */, 257754BE1BEE458E00737CA5 /* ASTextKitComponents.mm in Sources */,
257754A91BEE44CD00737CA5 /* ASTextKitContext.mm in Sources */, 257754A91BEE44CD00737CA5 /* ASTextKitContext.mm in Sources */,
ACF6ED501B17847A00DA7C62 /* ASStackPositionedLayout.mm in Sources */, ACF6ED501B17847A00DA7C62 /* ASStackPositionedLayout.mm in Sources */,
ACF6ED521B17847A00DA7C62 /* ASStackUnpositionedLayout.mm in Sources */, ACF6ED521B17847A00DA7C62 /* ASStackUnpositionedLayout.mm in Sources */,
@@ -2299,6 +2320,7 @@
92074A691CC8BADA00918F75 /* ASControlNode+tvOS.m in Sources */, 92074A691CC8BADA00918F75 /* ASControlNode+tvOS.m in Sources */,
ACF6ED321B17843500DA7C62 /* ASAbsoluteLayoutSpec.mm in Sources */, ACF6ED321B17843500DA7C62 /* ASAbsoluteLayoutSpec.mm in Sources */,
AC026B6B1BD57D6F00BBC17E /* ASChangeSetDataController.mm in Sources */, AC026B6B1BD57D6F00BBC17E /* ASChangeSetDataController.mm in Sources */,
690C35611E055C5D00069B91 /* ASDimensionInternal.mm in Sources */,
68355B311CB5799E001D4E68 /* ASImageNode+AnimatedImage.mm in Sources */, 68355B311CB5799E001D4E68 /* ASImageNode+AnimatedImage.mm in Sources */,
68C215591DE10D330019C4BC /* ASCollectionViewLayoutInspector.m in Sources */, 68C215591DE10D330019C4BC /* ASCollectionViewLayoutInspector.m in Sources */,
9CFFC6C01CCAC73C006A6476 /* ASViewController.mm in Sources */, 9CFFC6C01CCAC73C006A6476 /* ASViewController.mm in Sources */,
@@ -2386,6 +2408,7 @@
9C70F2091CDABA36007D6C76 /* ASViewController.mm in Sources */, 9C70F2091CDABA36007D6C76 /* ASViewController.mm in Sources */,
8BBBAB8D1CEBAF1E00107FC6 /* ASDefaultPlaybackButton.m in Sources */, 8BBBAB8D1CEBAF1E00107FC6 /* ASDefaultPlaybackButton.m in Sources */,
B30BF6541C59D889004FCD53 /* ASLayoutManager.m in Sources */, B30BF6541C59D889004FCD53 /* ASLayoutManager.m in Sources */,
690C35671E0567C600069B91 /* ASDimensionDeprecated.mm in Sources */,
92DD2FE71BF4D0850074C9DD /* ASMapNode.mm in Sources */, 92DD2FE71BF4D0850074C9DD /* ASMapNode.mm in Sources */,
636EA1A51C7FF4EF00EE152F /* ASDefaultPlayButton.m in Sources */, 636EA1A51C7FF4EF00EE152F /* ASDefaultPlayButton.m in Sources */,
9B92C8861BC2EB7600EE46B2 /* ASCollectionViewFlowLayoutInspector.m in Sources */, 9B92C8861BC2EB7600EE46B2 /* ASCollectionViewFlowLayoutInspector.m in Sources */,
@@ -2449,7 +2472,7 @@
B35062541B010EFD0018CF92 /* ASImageNode+CGExtras.m in Sources */, B35062541B010EFD0018CF92 /* ASImageNode+CGExtras.m in Sources */,
68355B401CB57A69001D4E68 /* ASImageContainerProtocolCategories.m in Sources */, 68355B401CB57A69001D4E68 /* ASImageContainerProtocolCategories.m in Sources */,
B35062031B010EFD0018CF92 /* ASImageNode.mm in Sources */, B35062031B010EFD0018CF92 /* ASImageNode.mm in Sources */,
254C6B821BF94F8A003EC431 /* ASTextKitComponents.m in Sources */, 254C6B821BF94F8A003EC431 /* ASTextKitComponents.mm in Sources */,
430E7C921B4C23F100697A4C /* ASIndexPath.m in Sources */, 430E7C921B4C23F100697A4C /* ASIndexPath.m in Sources */,
34EFC7601B701C8B00AD841F /* ASInsetLayoutSpec.mm in Sources */, 34EFC7601B701C8B00AD841F /* ASInsetLayoutSpec.mm in Sources */,
AC6145441D8AFD4F003D62A2 /* ASSection.m in Sources */, AC6145441D8AFD4F003D62A2 /* ASSection.m in Sources */,
@@ -2490,6 +2513,7 @@
AC026B6C1BD57D6F00BBC17E /* ASChangeSetDataController.mm in Sources */, AC026B6C1BD57D6F00BBC17E /* ASChangeSetDataController.mm in Sources */,
34EFC7741B701D0A00AD841F /* ASAbsoluteLayoutSpec.mm in Sources */, 34EFC7741B701D0A00AD841F /* ASAbsoluteLayoutSpec.mm in Sources */,
92074A6A1CC8BADA00918F75 /* ASControlNode+tvOS.m in Sources */, 92074A6A1CC8BADA00918F75 /* ASControlNode+tvOS.m in Sources */,
690C35621E055C5D00069B91 /* ASDimensionInternal.mm in Sources */,
68C2155A1DE10D330019C4BC /* ASCollectionViewLayoutInspector.m in Sources */, 68C2155A1DE10D330019C4BC /* ASCollectionViewLayoutInspector.m in Sources */,
DB78412E1C6BCE1600A9E2B4 /* _ASTransitionContext.m in Sources */, DB78412E1C6BCE1600A9E2B4 /* _ASTransitionContext.m in Sources */,
B350620B1B010EFD0018CF92 /* ASTableView.mm in Sources */, B350620B1B010EFD0018CF92 /* ASTableView.mm in Sources */,

View File

@@ -14,7 +14,7 @@
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
@class ASCellNode; @class ASCellNode, ASTextNode;
typedef NSUInteger ASCellNodeAnimation; typedef NSUInteger ASCellNodeAnimation;
@@ -92,12 +92,6 @@ typedef NS_ENUM(NSUInteger, ASCellNodeVisibilityEvent) {
*/ */
@property (nonatomic, strong, readonly, nullable) UICollectionViewLayoutAttributes *layoutAttributes; @property (nonatomic, strong, readonly, nullable) UICollectionViewLayoutAttributes *layoutAttributes;
/*
* ASTableView uses these properties when configuring UITableViewCells that host ASCellNodes.
*/
//@property (nonatomic, retain) UIColor *backgroundColor;
@property (nonatomic) UITableViewCellSelectionStyle selectionStyle;
/** /**
* A Boolean value that is synchronized with the underlying collection or tableView cell property. * A Boolean value that is synchronized with the underlying collection or tableView cell property.
* Setting this value is equivalent to calling selectItem / deselectItem on the collection or table. * Setting this value is equivalent to calling selectItem / deselectItem on the collection or table.
@@ -169,6 +163,25 @@ typedef NS_ENUM(NSUInteger, ASCellNodeVisibilityEvent) {
*/ */
- (void)cellNodeVisibilityEvent:(ASCellNodeVisibilityEvent)event inScrollView:(nullable UIScrollView *)scrollView withCellFrame:(CGRect)cellFrame; - (void)cellNodeVisibilityEvent:(ASCellNodeVisibilityEvent)event inScrollView:(nullable UIScrollView *)scrollView withCellFrame:(CGRect)cellFrame;
#pragma mark - UITableViewCell specific passthrough properties
/* @abstract The selection style when a tap on a cell occurs
* @default UITableViewCellSelectionStyleDefault
* ASTableView uses these properties when configuring UITableViewCells that host ASCellNodes.
*/
@property (nonatomic) UITableViewCellSelectionStyle selectionStyle;
/* @abstract The accessory type view on the right side of the cell. Please take care of your ASLayoutSpec so that doesn't overlay the accessoryView
* @default UITableViewCellAccessoryNone
* ASTableView uses these properties when configuring UITableViewCells that host ASCellNodes.
*/
@property (nonatomic) UITableViewCellAccessoryType accessoryType;
/* @abstract The seperator inset of the cell seperator line
* ASTableView uses these properties when configuring UITableViewCells that host ASCellNodes.
*/
@property (nonatomic) UIEdgeInsets seperatorInset;
@end @end
@interface ASCellNode (Unavailable) @interface ASCellNode (Unavailable)
@@ -207,6 +220,11 @@ typedef NS_ENUM(NSUInteger, ASCellNodeVisibilityEvent) {
*/ */
@property (nonatomic, assign) UIEdgeInsets textInsets; @property (nonatomic, assign) UIEdgeInsets textInsets;
/**
* The text node used by this cell node.
*/
@property (nonatomic, strong, readonly) ASTextNode *textNode;
@end @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END

View File

@@ -397,13 +397,6 @@ static NSMutableSet *__cellClassesForVisibilityNotifications = nil; // See +init
#pragma mark - #pragma mark -
#pragma mark ASTextCellNode #pragma mark ASTextCellNode
@interface ASTextCellNode ()
@property (nonatomic, strong) ASTextNode *textNode;
@end
@implementation ASTextCellNode @implementation ASTextCellNode
static const CGFloat kASTextCellNodeDefaultFontSize = 18.0f; static const CGFloat kASTextCellNodeDefaultFontSize = 18.0f;
@@ -422,7 +415,7 @@ static const CGFloat kASTextCellNodeDefaultVerticalPadding = 11.0f;
_textInsets = textInsets; _textInsets = textInsets;
_textAttributes = [textAttributes copy]; _textAttributes = [textAttributes copy];
_textNode = [[ASTextNode alloc] init]; _textNode = [[ASTextNode alloc] init];
[self addSubnode:_textNode]; self.automaticallyManagesSubnodes = YES;
} }
return self; return self;
} }

View File

@@ -173,7 +173,7 @@ NS_ASSUME_NONNULL_BEGIN
* Boolean parameter that contains the value YES if all of the related animations completed successfully or * Boolean parameter that contains the value YES if all of the related animations completed successfully or
* NO if they were interrupted. This parameter may be nil. If supplied, the block is run on the main thread. * NO if they were interrupted. This parameter may be nil. If supplied, the block is run on the main thread.
*/ */
- (void)performBatchAnimated:(BOOL)animated updates:(nullable __attribute((noescape)) void (^)())updates completion:(nullable void (^)(BOOL finished))completion; - (void)performBatchAnimated:(BOOL)animated updates:(nullable AS_NOESCAPE void (^)())updates completion:(nullable void (^)(BOOL finished))completion;
/** /**
* Perform a batch of updates asynchronously, optionally disabling all animations in the batch. This method must be called from the main thread. * Perform a batch of updates asynchronously, optionally disabling all animations in the batch. This method must be called from the main thread.
@@ -184,7 +184,7 @@ NS_ASSUME_NONNULL_BEGIN
* Boolean parameter that contains the value YES if all of the related animations completed successfully or * Boolean parameter that contains the value YES if all of the related animations completed successfully or
* NO if they were interrupted. This parameter may be nil. If supplied, the block is run on the main thread. * NO if they were interrupted. This parameter may be nil. If supplied, the block is run on the main thread.
*/ */
- (void)performBatchUpdates:(nullable __attribute((noescape)) void (^)())updates completion:(nullable void (^)(BOOL finished))completion; - (void)performBatchUpdates:(nullable AS_NOESCAPE void (^)())updates completion:(nullable void (^)(BOOL finished))completion;
/** /**
* Blocks execution of the main thread until all section and item updates are committed to the view. This method must be called from the main thread. * Blocks execution of the main thread until all section and item updates are committed to the view. This method must be called from the main thread.

View File

@@ -227,7 +227,7 @@ NS_ASSUME_NONNULL_BEGIN
* Boolean parameter that contains the value YES if all of the related animations completed successfully or * Boolean parameter that contains the value YES if all of the related animations completed successfully or
* NO if they were interrupted. This parameter may be nil. If supplied, the block is run on the main thread. * NO if they were interrupted. This parameter may be nil. If supplied, the block is run on the main thread.
*/ */
- (void)performBatchAnimated:(BOOL)animated updates:(nullable __attribute((noescape)) void (^)())updates completion:(nullable void (^)(BOOL finished))completion ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); - (void)performBatchAnimated:(BOOL)animated updates:(nullable AS_NOESCAPE void (^)())updates completion:(nullable void (^)(BOOL finished))completion ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead.");
/** /**
* Perform a batch of updates asynchronously. This method must be called from the main thread. * Perform a batch of updates asynchronously. This method must be called from the main thread.
@@ -238,7 +238,7 @@ NS_ASSUME_NONNULL_BEGIN
* Boolean parameter that contains the value YES if all of the related animations completed successfully or * Boolean parameter that contains the value YES if all of the related animations completed successfully or
* NO if they were interrupted. This parameter may be nil. If supplied, the block is run on the main thread. * NO if they were interrupted. This parameter may be nil. If supplied, the block is run on the main thread.
*/ */
- (void)performBatchUpdates:(nullable __attribute((noescape)) void (^)())updates completion:(nullable void (^)(BOOL finished))completion ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); - (void)performBatchUpdates:(nullable AS_NOESCAPE void (^)())updates completion:(nullable void (^)(BOOL finished))completion ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead.");
/** /**
* Reload everything from scratch, destroying the working range and all cached nodes. * Reload everything from scratch, destroying the working range and all cached nodes.

View File

@@ -78,6 +78,8 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertMainThread();
node.layoutAttributes = _layoutAttributes; node.layoutAttributes = _layoutAttributes;
_node = node; _node = node;
self.backgroundColor = node.backgroundColor;
self.clipsToBounds = node.clipsToBounds;
[node __setSelectedFromUIKit:self.selected]; [node __setSelectedFromUIKit:self.selected];
[node __setHighlightedFromUIKit:self.highlighted]; [node __setHighlightedFromUIKit:self.highlighted];
} }
@@ -121,6 +123,15 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
self.layoutAttributes = layoutAttributes; self.layoutAttributes = layoutAttributes;
} }
/**
* Keep our node filling our content view.
*/
- (void)layoutSubviews
{
[super layoutSubviews];
self.node.frame = self.contentView.bounds;
}
@end @end
#pragma mark - #pragma mark -
@@ -282,7 +293,15 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
{ {
if (!(self = [super initWithFrame:frame collectionViewLayout:layout])) if (!(self = [super initWithFrame:frame collectionViewLayout:layout]))
return nil; return nil;
// Disable UICollectionView prefetching.
// Experiments done by Instagram show that this option being YES (default)
// when unused causes a significant hit to scroll performance.
// https://github.com/Instagram/IGListKit/issues/318
if (AS_AT_LEAST_IOS10) {
self.prefetchingEnabled = NO;
}
_layoutController = [[ASCollectionViewLayoutController alloc] initWithCollectionView:self]; _layoutController = [[ASCollectionViewLayoutController alloc] initWithCollectionView:self];
_rangeController = [[ASRangeController alloc] init]; _rangeController = [[ASRangeController alloc] init];
@@ -614,6 +633,10 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
- (NSIndexPath *)convertIndexPathFromCollectionNode:(NSIndexPath *)indexPath waitingIfNeeded:(BOOL)wait - (NSIndexPath *)convertIndexPathFromCollectionNode:(NSIndexPath *)indexPath waitingIfNeeded:(BOOL)wait
{ {
if (indexPath == nil) {
return nil;
}
// If this is a section index path, we don't currently have a method // If this is a section index path, we don't currently have a method
// to do a mapping. // to do a mapping.
if (indexPath.item == NSNotFound) { if (indexPath.item == NSNotFound) {
@@ -751,7 +774,8 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
- (void)performBatchUpdates:(void (^)())updates completion:(void (^)(BOOL))completion - (void)performBatchUpdates:(void (^)())updates completion:(void (^)(BOOL))completion
{ {
[self performBatchAnimated:YES updates:updates completion:completion]; // We capture the current state of whether animations are enabled if they don't provide us with one.
[self performBatchAnimated:[UIView areAnimationsEnabled] updates:updates completion:completion];
} }
- (void)registerSupplementaryNodeOfKind:(NSString *)elementKind - (void)registerSupplementaryNodeOfKind:(NSString *)elementKind

View File

@@ -63,6 +63,14 @@ id<NSCopying> _ASControlNodeEventKeyForControlEvent(ASControlNodeEvent controlEv
*/ */
void _ASEnumerateControlEventsIncludedInMaskWithBlock(ASControlNodeEvent mask, void (^block)(ASControlNodeEvent anEvent)); void _ASEnumerateControlEventsIncludedInMaskWithBlock(ASControlNodeEvent mask, void (^block)(ASControlNodeEvent anEvent));
/**
@abstract Returns the expanded bounds used to determine if a touch is considered 'inside' during tracking.
@param controlNode A control node.
@result The expanded bounds of the node.
*/
CGRect _ASControlNodeGetExpandedBounds(ASControlNode *controlNode);
@end @end
@implementation ASControlNode @implementation ASControlNode
@@ -158,7 +166,7 @@ void _ASEnumerateControlEventsIncludedInMaskWithBlock(ASControlNodeEvent mask, v
BOOL dragIsInsideBounds = [self pointInside:touchLocation withEvent:nil]; BOOL dragIsInsideBounds = [self pointInside:touchLocation withEvent:nil];
// Update our highlighted state. // Update our highlighted state.
CGRect expandedBounds = CGRectInset(self.view.bounds, kASControlNodeExpandedInset, kASControlNodeExpandedInset); CGRect expandedBounds = _ASControlNodeGetExpandedBounds(self);
BOOL dragIsInsideExpandedBounds = CGRectContainsPoint(expandedBounds, touchLocation); BOOL dragIsInsideExpandedBounds = CGRectContainsPoint(expandedBounds, touchLocation);
self.touchInside = dragIsInsideExpandedBounds; self.touchInside = dragIsInsideExpandedBounds;
self.highlighted = dragIsInsideExpandedBounds; self.highlighted = dragIsInsideExpandedBounds;
@@ -216,7 +224,7 @@ void _ASEnumerateControlEventsIncludedInMaskWithBlock(ASControlNodeEvent mask, v
[self endTrackingWithTouch:theTouch withEvent:event]; [self endTrackingWithTouch:theTouch withEvent:event];
// Send the appropriate touch-up control event. // Send the appropriate touch-up control event.
CGRect expandedBounds = CGRectInset(self.view.bounds, kASControlNodeExpandedInset, kASControlNodeExpandedInset); CGRect expandedBounds = _ASControlNodeGetExpandedBounds(self);
BOOL touchUpIsInsideExpandedBounds = CGRectContainsPoint(expandedBounds, touchLocation); BOOL touchUpIsInsideExpandedBounds = CGRectContainsPoint(expandedBounds, touchLocation);
[self sendActionsForControlEvents:(touchUpIsInsideExpandedBounds ? ASControlNodeEventTouchUpInside : ASControlNodeEventTouchUpOutside) [self sendActionsForControlEvents:(touchUpIsInsideExpandedBounds ? ASControlNodeEventTouchUpInside : ASControlNodeEventTouchUpOutside)
@@ -428,6 +436,10 @@ void _ASEnumerateControlEventsIncludedInMaskWithBlock(ASControlNodeEvent mask, v
} }
} }
CGRect _ASControlNodeGetExpandedBounds(ASControlNode *controlNode) {
return CGRectInset(UIEdgeInsetsInsetRect(controlNode.view.bounds, controlNode.hitTestSlop), kASControlNodeExpandedInset, kASControlNodeExpandedInset);
}
#pragma mark - For Subclasses #pragma mark - For Subclasses
- (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)touchEvent - (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)touchEvent
{ {

View File

@@ -195,7 +195,7 @@ NS_ASSUME_NONNULL_BEGIN
* @note Called on the display queue and/or main queue (MUST BE THREAD SAFE) * @note Called on the display queue and/or main queue (MUST BE THREAD SAFE)
*/ */
+ (void)drawRect:(CGRect)bounds withParameters:(nullable id <NSObject>)parameters + (void)drawRect:(CGRect)bounds withParameters:(nullable id <NSObject>)parameters
isCancelled:(__attribute((noescape)) asdisplaynode_iscancelled_block_t)isCancelledBlock isCancelled:(AS_NOESCAPE asdisplaynode_iscancelled_block_t)isCancelledBlock
isRasterizing:(BOOL)isRasterizing; isRasterizing:(BOOL)isRasterizing;
/** /**
@@ -212,7 +212,7 @@ NS_ASSUME_NONNULL_BEGIN
* @note Called on the display queue and/or main queue (MUST BE THREAD SAFE) * @note Called on the display queue and/or main queue (MUST BE THREAD SAFE)
*/ */
+ (nullable UIImage *)displayWithParameters:(nullable id<NSObject>)parameters + (nullable UIImage *)displayWithParameters:(nullable id<NSObject>)parameters
isCancelled:(__attribute((noescape)) asdisplaynode_iscancelled_block_t)isCancelledBlock; isCancelled:(AS_NOESCAPE asdisplaynode_iscancelled_block_t)isCancelledBlock;
/** /**
* @abstract Delegate override for drawParameters * @abstract Delegate override for drawParameters

View File

@@ -1805,6 +1805,21 @@ static inline CATransform3D _calculateTransformFromReferenceToTarget(ASDisplayNo
- (CGPoint)convertPoint:(CGPoint)point fromNode:(ASDisplayNode *)node - (CGPoint)convertPoint:(CGPoint)point fromNode:(ASDisplayNode *)node
{ {
ASDisplayNodeAssertThreadAffinity(self); ASDisplayNodeAssertThreadAffinity(self);
/**
* When passed node=nil, all methods in this family use the UIView-style
* behavior that is, convert from/to window coordinates if there's a window,
* otherwise return the point untransformed.
*/
if (node == nil && self.nodeLoaded) {
CALayer *layer = self.layer;
if (UIWindow *window = ASFindWindowOfLayer(layer)) {
return [layer convertPoint:point fromLayer:window.layer];
} else {
return point;
}
}
// Get root node of the accessible node hierarchy, if node not specified // Get root node of the accessible node hierarchy, if node not specified
node = node ? : ASDisplayNodeUltimateParentOfNode(self); node = node ? : ASDisplayNodeUltimateParentOfNode(self);
@@ -1820,6 +1835,16 @@ static inline CATransform3D _calculateTransformFromReferenceToTarget(ASDisplayNo
- (CGPoint)convertPoint:(CGPoint)point toNode:(ASDisplayNode *)node - (CGPoint)convertPoint:(CGPoint)point toNode:(ASDisplayNode *)node
{ {
ASDisplayNodeAssertThreadAffinity(self); ASDisplayNodeAssertThreadAffinity(self);
if (node == nil && self.nodeLoaded) {
CALayer *layer = self.layer;
if (UIWindow *window = ASFindWindowOfLayer(layer)) {
return [layer convertPoint:point toLayer:window.layer];
} else {
return point;
}
}
// Get root node of the accessible node hierarchy, if node not specified // Get root node of the accessible node hierarchy, if node not specified
node = node ? : ASDisplayNodeUltimateParentOfNode(self); node = node ? : ASDisplayNodeUltimateParentOfNode(self);
@@ -1835,6 +1860,16 @@ static inline CATransform3D _calculateTransformFromReferenceToTarget(ASDisplayNo
- (CGRect)convertRect:(CGRect)rect fromNode:(ASDisplayNode *)node - (CGRect)convertRect:(CGRect)rect fromNode:(ASDisplayNode *)node
{ {
ASDisplayNodeAssertThreadAffinity(self); ASDisplayNodeAssertThreadAffinity(self);
if (node == nil && self.nodeLoaded) {
CALayer *layer = self.layer;
if (UIWindow *window = ASFindWindowOfLayer(layer)) {
return [layer convertRect:rect fromLayer:window.layer];
} else {
return rect;
}
}
// Get root node of the accessible node hierarchy, if node not specified // Get root node of the accessible node hierarchy, if node not specified
node = node ? : ASDisplayNodeUltimateParentOfNode(self); node = node ? : ASDisplayNodeUltimateParentOfNode(self);
@@ -1850,6 +1885,16 @@ static inline CATransform3D _calculateTransformFromReferenceToTarget(ASDisplayNo
- (CGRect)convertRect:(CGRect)rect toNode:(ASDisplayNode *)node - (CGRect)convertRect:(CGRect)rect toNode:(ASDisplayNode *)node
{ {
ASDisplayNodeAssertThreadAffinity(self); ASDisplayNodeAssertThreadAffinity(self);
if (node == nil && self.nodeLoaded) {
CALayer *layer = self.layer;
if (UIWindow *window = ASFindWindowOfLayer(layer)) {
return [layer convertRect:rect toLayer:window.layer];
} else {
return rect;
}
}
// Get root node of the accessible node hierarchy, if node not specified // Get root node of the accessible node hierarchy, if node not specified
node = node ? : ASDisplayNodeUltimateParentOfNode(self); node = node ? : ASDisplayNodeUltimateParentOfNode(self);

View File

@@ -125,6 +125,11 @@ extern ASDisplayNode * _Nullable ASDisplayNodeFindFirstSupernode(ASDisplayNode *
*/ */
extern __kindof ASDisplayNode * _Nullable ASDisplayNodeFindFirstSupernodeOfClass(ASDisplayNode *start, Class c) AS_WARN_UNUSED_RESULT; extern __kindof ASDisplayNode * _Nullable ASDisplayNodeFindFirstSupernodeOfClass(ASDisplayNode *start, Class c) AS_WARN_UNUSED_RESULT;
/**
* Given a layer, find the window it lives in, if any.
*/
extern UIWindow * _Nullable ASFindWindowOfLayer(CALayer *layer) AS_WARN_UNUSED_RESULT;
/** /**
* Given two nodes, finds their most immediate common parent. Used for geometry conversion methods. * Given two nodes, finds their most immediate common parent. Used for geometry conversion methods.
* NOTE: It is an error to try to convert between nodes which do not share a common ancestor. This behavior is * NOTE: It is an error to try to convert between nodes which do not share a common ancestor. This behavior is

View File

@@ -249,6 +249,21 @@ static inline BOOL _ASDisplayNodeIsAncestorOfDisplayNode(ASDisplayNode *possible
return NO; return NO;
} }
extern UIWindow * _Nullable ASFindWindowOfLayer(CALayer *layer)
{
while (layer != nil) {
if (UIView *view = ASDynamicCast(layer.delegate, UIView)) {
if ([view isKindOfClass:[UIWindow class]]) {
return (UIWindow *)view;
} else {
return view.window;
}
}
layer = layer.superlayer;
}
return nil;
}
extern ASDisplayNode *ASDisplayNodeFindClosestCommonAncestor(ASDisplayNode *node1, ASDisplayNode *node2) extern ASDisplayNode *ASDisplayNodeFindClosestCommonAncestor(ASDisplayNode *node1, ASDisplayNode *node2)
{ {
ASDisplayNode *possibleAncestor = node1; ASDisplayNode *possibleAncestor = node1;

View File

@@ -93,16 +93,10 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, readwrite) UIEdgeInsets textContainerInset; @property (nonatomic, readwrite) UIEdgeInsets textContainerInset;
/** /**
@abstract <UITextInputTraits> properties. @abstract The maximum number of lines to display. Additional lines will require scrolling.
@default 0 (No limit)
*/ */
@property(nonatomic, readwrite, assign) UITextAutocapitalizationType autocapitalizationType; // default is UITextAutocapitalizationTypeSentences @property (nonatomic, assign) NSUInteger maximumLinesToDisplay;
@property(nonatomic, readwrite, assign) UITextAutocorrectionType autocorrectionType; // default is UITextAutocorrectionTypeDefault
@property(nonatomic, readwrite, assign) UITextSpellCheckingType spellCheckingType; // default is UITextSpellCheckingTypeDefault;
@property(nonatomic, readwrite, assign) UIKeyboardType keyboardType; // default is UIKeyboardTypeDefault
@property(nonatomic, readwrite, assign) UIKeyboardAppearance keyboardAppearance; // default is UIKeyboardAppearanceDefault
@property(nonatomic, readwrite, assign) UIReturnKeyType returnKeyType; // default is UIReturnKeyDefault (See note under UIReturnKeyType enum)
@property(nonatomic, readwrite, assign) BOOL enablesReturnKeyAutomatically; // default is NO (when YES, will automatically disable return key when text widget has zero-length contents, and will automatically enable when text widget has non-zero-length contents)
@property(nonatomic, readwrite, assign, getter=isSecureTextEntry) BOOL secureTextEntry; // default is NO
/** /**
@abstract Indicates whether the receiver's text view is the first responder, and thus has the keyboard visible and is prepared for editing by the user. @abstract Indicates whether the receiver's text view is the first responder, and thus has the keyboard visible and is prepared for editing by the user.
@@ -125,6 +119,18 @@ NS_ASSUME_NONNULL_BEGIN
*/ */
- (CGRect)frameForTextRange:(NSRange)textRange AS_WARN_UNUSED_RESULT; - (CGRect)frameForTextRange:(NSRange)textRange AS_WARN_UNUSED_RESULT;
/**
@abstract <UITextInputTraits> properties.
*/
@property(nonatomic, readwrite, assign) UITextAutocapitalizationType autocapitalizationType; // default is UITextAutocapitalizationTypeSentences
@property(nonatomic, readwrite, assign) UITextAutocorrectionType autocorrectionType; // default is UITextAutocorrectionTypeDefault
@property(nonatomic, readwrite, assign) UITextSpellCheckingType spellCheckingType; // default is UITextSpellCheckingTypeDefault;
@property(nonatomic, readwrite, assign) UIKeyboardType keyboardType; // default is UIKeyboardTypeDefault
@property(nonatomic, readwrite, assign) UIKeyboardAppearance keyboardAppearance; // default is UIKeyboardAppearanceDefault
@property(nonatomic, readwrite, assign) UIReturnKeyType returnKeyType; // default is UIReturnKeyDefault (See note under UIReturnKeyType enum)
@property(nonatomic, readwrite, assign) BOOL enablesReturnKeyAutomatically; // default is NO (when YES, will automatically disable return key when text widget has zero-length contents, and will automatically enable when text widget has non-zero-length contents)
@property(nonatomic, readwrite, assign, getter=isSecureTextEntry) BOOL secureTextEntry; // default is NO
@end @end
@interface ASEditableTextNode (Unavailable) @interface ASEditableTextNode (Unavailable)

View File

@@ -238,7 +238,16 @@
- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize - (CGSize)calculateSizeThatFits:(CGSize)constrainedSize
{ {
ASTextKitComponents *displayedComponents = [self isDisplayingPlaceholder] ? _placeholderTextKitComponents : _textKitComponents; ASTextKitComponents *displayedComponents = [self isDisplayingPlaceholder] ? _placeholderTextKitComponents : _textKitComponents;
CGSize textSize = [displayedComponents sizeForConstrainedWidth:constrainedSize.width];
CGSize textSize;
if (_maximumLinesToDisplay > 0) {
textSize = [displayedComponents sizeForConstrainedWidth:constrainedSize.width
forMaxNumberOfLines: _maximumLinesToDisplay];
} else {
textSize = [displayedComponents sizeForConstrainedWidth:constrainedSize.width];
}
CGFloat width = std::ceil(textSize.width + _textContainerInset.left + _textContainerInset.right); CGFloat width = std::ceil(textSize.width + _textContainerInset.left + _textContainerInset.right);
CGFloat height = std::ceil(textSize.height + _textContainerInset.top + _textContainerInset.bottom); CGFloat height = std::ceil(textSize.height + _textContainerInset.top + _textContainerInset.bottom);
return CGSizeMake(std::fmin(width, constrainedSize.width), std::fmin(height, constrainedSize.height)); return CGSizeMake(std::fmin(width, constrainedSize.width), std::fmin(height, constrainedSize.height));
@@ -313,6 +322,12 @@
return _textKitComponents.textView; return _textKitComponents.textView;
} }
- (void)setMaximumLinesToDisplay:(NSUInteger)maximumLines
{
_maximumLinesToDisplay = maximumLines;
[self setNeedsLayout];
}
#pragma mark - #pragma mark -
@dynamic typingAttributes; @dynamic typingAttributes;

View File

@@ -27,6 +27,7 @@
#import "ASEqualityHelpers.h" #import "ASEqualityHelpers.h"
#import "ASEqualityHashHelpers.h" #import "ASEqualityHashHelpers.h"
#import "ASWeakMap.h" #import "ASWeakMap.h"
#import "CoreGraphics+ASConvenience.h"
// TODO: It would be nice to remove this dependency; it's the only subclass using more than +FrameworkSubclasses.h // TODO: It would be nice to remove this dependency; it's the only subclass using more than +FrameworkSubclasses.h
#import "ASDisplayNodeInternal.h" #import "ASDisplayNodeInternal.h"
@@ -183,6 +184,26 @@ struct ASImageNodeDrawParameters {
[self invalidateAnimatedImage]; [self invalidateAnimatedImage];
} }
- (UIImage *)placeholderImage
{
// FIXME: Replace this implementation with reusable CALayers that have .backgroundColor set.
// This would completely eliminate the memory and performance cost of the backing store.
CGSize size = self.calculatedSize;
if ((size.width * size.height) < CGFLOAT_EPSILON) {
return nil;
}
ASDN::MutexLocker l(__instanceLock__);
UIGraphicsBeginImageContext(size);
[self.placeholderColor setFill];
UIRectFill(CGRectMake(0, 0, size.width, size.height));
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
#pragma mark - Layout and Sizing #pragma mark - Layout and Sizing
- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize - (CGSize)calculateSizeThatFits:(CGSize)constrainedSize

View File

@@ -27,9 +27,6 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
@interface ASNetworkImageNode () @interface ASNetworkImageNode ()
{ {
__weak id<ASImageCacheProtocol> _cache;
__weak id<ASImageDownloaderProtocol> _downloader;
// Only access any of these with __instanceLock__. // Only access any of these with __instanceLock__.
__weak id<ASNetworkImageNodeDelegate> _delegate; __weak id<ASNetworkImageNodeDelegate> _delegate;
@@ -55,6 +52,9 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
} _delegateFlags; } _delegateFlags;
//set on init only //set on init only
__weak id<ASImageDownloaderProtocol> _downloader;
__weak id<ASImageCacheProtocol> _cache;
struct { struct {
unsigned int downloaderImplementsSetProgress:1; unsigned int downloaderImplementsSetProgress:1;
unsigned int downloaderImplementsSetPriority:1; unsigned int downloaderImplementsSetPriority:1;
@@ -238,11 +238,13 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
- (void)setShouldRenderProgressImages:(BOOL)shouldRenderProgressImages - (void)setShouldRenderProgressImages:(BOOL)shouldRenderProgressImages
{ {
ASDN::MutexLocker l(__instanceLock__); {
if (shouldRenderProgressImages == _shouldRenderProgressImages) { ASDN::MutexLocker l(__instanceLock__);
return; if (shouldRenderProgressImages == _shouldRenderProgressImages) {
return;
}
_shouldRenderProgressImages = shouldRenderProgressImages;
} }
_shouldRenderProgressImages = shouldRenderProgressImages;
[self _updateProgressImageBlockOnDownloaderIfNeeded]; [self _updateProgressImageBlockOnDownloaderIfNeeded];
} }
@@ -295,28 +297,41 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
- (void)didEnterVisibleState - (void)didEnterVisibleState
{ {
[super didEnterVisibleState]; [super didEnterVisibleState];
ASDN::MutexLocker l(__instanceLock__);
id downloadIdentifier = nil;
{
ASDN::MutexLocker l(__instanceLock__);
if (_downloaderFlags.downloaderImplementsSetPriority) { if (_downloaderFlags.downloaderImplementsSetPriority) {
if (_downloadIdentifier != nil) { downloadIdentifier = _downloadIdentifier;
[_downloader setPriority:ASImageDownloaderPriorityVisible withDownloadIdentifier:_downloadIdentifier];
} }
} }
if (downloadIdentifier != nil) {
[_downloader setPriority:ASImageDownloaderPriorityVisible withDownloadIdentifier:downloadIdentifier];
}
[self _updateProgressImageBlockOnDownloaderIfNeeded]; [self _updateProgressImageBlockOnDownloaderIfNeeded];
} }
- (void)didExitVisibleState - (void)didExitVisibleState
{ {
[super didExitVisibleState]; [super didExitVisibleState];
ASDN::MutexLocker l(__instanceLock__);
id downloadIdentifier = nil;
if (_downloaderFlags.downloaderImplementsSetPriority) { {
if (_downloadIdentifier != nil) { ASDN::MutexLocker l(__instanceLock__);
[_downloader setPriority:ASImageDownloaderPriorityPreload withDownloadIdentifier:_downloadIdentifier]; if (_downloaderFlags.downloaderImplementsSetPriority) {
downloadIdentifier = _downloadIdentifier;
} }
} }
if (downloadIdentifier != nil) {
[_downloader setPriority:ASImageDownloaderPriorityPreload withDownloadIdentifier:downloadIdentifier];
}
[self _updateProgressImageBlockOnDownloaderIfNeeded]; [self _updateProgressImageBlockOnDownloaderIfNeeded];
} }
@@ -372,9 +387,16 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
} }
// Read state. // Read state.
BOOL shouldRender = _shouldRenderProgressImages && ASInterfaceStateIncludesVisible(_interfaceState); BOOL shouldRender;
id oldDownloadIDForProgressBlock = _downloadIdentifierForProgressBlock; id oldDownloadIDForProgressBlock;
id newDownloadIDForProgressBlock = shouldRender ? _downloadIdentifier : nil; id newDownloadIDForProgressBlock;
BOOL clearAndReattempt = NO;
{
ASDN::MutexLocker l(__instanceLock__);
shouldRender = _shouldRenderProgressImages && ASInterfaceStateIncludesVisible(_interfaceState);
oldDownloadIDForProgressBlock = _downloadIdentifierForProgressBlock;
newDownloadIDForProgressBlock = shouldRender ? _downloadIdentifier : nil;
}
// If we're already bound to the correct download, we're done. // If we're already bound to the correct download, we're done.
if (ASObjectIsEqual(oldDownloadIDForProgressBlock, newDownloadIDForProgressBlock)) { if (ASObjectIsEqual(oldDownloadIDForProgressBlock, newDownloadIDForProgressBlock)) {
@@ -395,7 +417,19 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
} }
// Update state. // Update state.
_downloadIdentifierForProgressBlock = newDownloadIDForProgressBlock; {
ASDN::MutexLocker l(__instanceLock__);
if (_downloadIdentifierForProgressBlock == oldDownloadIDForProgressBlock) {
_downloadIdentifierForProgressBlock = newDownloadIDForProgressBlock;
} else {
clearAndReattempt = YES;
}
}
if (clearAndReattempt) {
[_downloader setProgressImageBlock:nil callbackQueue:dispatch_get_main_queue() withDownloadIdentifier:newDownloadIDForProgressBlock];
[self _updateProgressImageBlockOnDownloaderIfNeeded];
}
} }
- (void)_cancelDownloadAndClearImage - (void)_cancelDownloadAndClearImage
@@ -445,19 +479,46 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
- (void)_downloadImageWithCompletion:(void (^)(id <ASImageContainerProtocol> imageContainer, NSError*, id downloadIdentifier))finished - (void)_downloadImageWithCompletion:(void (^)(id <ASImageContainerProtocol> imageContainer, NSError*, id downloadIdentifier))finished
{ {
ASPerformBlockOnBackgroundThread(^{ ASPerformBlockOnBackgroundThread(^{
NSURL *url;
id downloadIdentifier;
BOOL cancelAndReattempt = NO;
ASDN::MutexLocker l(__instanceLock__); // Below, to avoid performance issues, we're calling downloadImageWithURL without holding the lock. This is a bit ugly because
_downloadIdentifier = [_downloader downloadImageWithURL:_URL // We need to reobtain the lock after and ensure that the task we've kicked off still matches our URL. If not, we need to cancel
callbackQueue:dispatch_get_main_queue() // it and try again.
downloadProgress:NULL {
completion:^(id <ASImageContainerProtocol> _Nullable imageContainer, NSError * _Nullable error, id _Nullable downloadIdentifier) { ASDN::MutexLocker l(__instanceLock__);
if (finished != NULL) { url = _URL;
finished(imageContainer, error, downloadIdentifier); }
}
}]; downloadIdentifier = [_downloader downloadImageWithURL:url
callbackQueue:dispatch_get_main_queue()
downloadProgress:NULL
completion:^(id <ASImageContainerProtocol> _Nullable imageContainer, NSError * _Nullable error, id _Nullable downloadIdentifier) {
if (finished != NULL) {
finished(imageContainer, error, downloadIdentifier);
}
}];
{
ASDN::MutexLocker l(__instanceLock__);
if ([_URL isEqual:url]) {
// The download we kicked off is correct, no need to do any more work.
_downloadIdentifier = downloadIdentifier;
} else {
// The URL changed since we kicked off our download task. This shouldn't happen often so we'll pay the cost and
// cancel that request and kick off a new one.
cancelAndReattempt = YES;
}
}
if (cancelAndReattempt) {
[_downloader cancelImageDownloadForIdentifier:downloadIdentifier];
[self _downloadImageWithCompletion:finished];
return;
}
[self _updateProgressImageBlockOnDownloaderIfNeeded]; [self _updateProgressImageBlockOnDownloaderIfNeeded];
}); });
} }

View File

@@ -60,19 +60,21 @@ static void runLoopSourceCallback(void *info) {
// 100ms timer. No resources are wasted in between, as the thread sleeps, and each check is fast. // 100ms timer. No resources are wasted in between, as the thread sleeps, and each check is fast.
// This time is fast enough for most use cases without excessive churn. // This time is fast enough for most use cases without excessive churn.
CFRunLoopTimerRef timer = CFRunLoopTimerCreateWithHandler(NULL, -1, 0.1, 0, 0, ^(CFRunLoopTimerRef timer) { CFRunLoopTimerRef timer = CFRunLoopTimerCreateWithHandler(NULL, -1, 0.1, 0, 0, ^(CFRunLoopTimerRef timer) {
@autoreleasepool {
#if ASRunLoopQueueLoggingEnabled #if ASRunLoopQueueLoggingEnabled
NSLog(@"ASDeallocQueue Processing: %d objects destroyed", weakSelf->_queue.size()); NSLog(@"ASDeallocQueue Processing: %d objects destroyed", weakSelf->_queue.size());
#endif #endif
weakSelf->_queueLock.lock(); weakSelf->_queueLock.lock();
std::deque<id> currentQueue = weakSelf->_queue; std::deque<id> currentQueue = weakSelf->_queue;
if (currentQueue.size() == 0) { if (currentQueue.size() == 0) {
weakSelf->_queueLock.unlock();
return;
}
// Sometimes we release 10,000 objects at a time. Don't hold the lock while releasing.
weakSelf->_queue = std::deque<id>();
weakSelf->_queueLock.unlock(); weakSelf->_queueLock.unlock();
return; currentQueue.clear();
} }
// Sometimes we release 10,000 objects at a time. Don't hold the lock while releasing.
weakSelf->_queue = std::deque<id>();
weakSelf->_queueLock.unlock();
currentQueue.clear();
}); });
CFRunLoopRef runloop = CFRunLoopGetCurrent(); CFRunLoopRef runloop = CFRunLoopGetCurrent();

View File

@@ -92,7 +92,7 @@
} }
// Don't provide a position, as that should be set by the parent. // Don't provide a position, as that should be set by the parent.
layout = [ASLayout layoutWithLayoutElement:self layout = [ASLayout layoutWithLayoutElement:self
size:parentSize size:selfSize
sublayouts:layout.sublayouts]; sublayouts:layout.sublayouts];
} }
return layout; return layout;

View File

@@ -150,7 +150,7 @@ NS_ASSUME_NONNULL_BEGIN
* Boolean parameter that contains the value YES if all of the related animations completed successfully or * Boolean parameter that contains the value YES if all of the related animations completed successfully or
* NO if they were interrupted. This parameter may be nil. If supplied, the block is run on the main thread. * NO if they were interrupted. This parameter may be nil. If supplied, the block is run on the main thread.
*/ */
- (void)performBatchAnimated:(BOOL)animated updates:(nullable __attribute((noescape)) void (^)())updates completion:(nullable void (^)(BOOL finished))completion; - (void)performBatchAnimated:(BOOL)animated updates:(nullable AS_NOESCAPE void (^)())updates completion:(nullable void (^)(BOOL finished))completion;
/** /**
* Perform a batch of updates asynchronously, optionally disabling all animations in the batch. This method must be called from the main thread. * Perform a batch of updates asynchronously, optionally disabling all animations in the batch. This method must be called from the main thread.
@@ -161,7 +161,7 @@ NS_ASSUME_NONNULL_BEGIN
* Boolean parameter that contains the value YES if all of the related animations completed successfully or * Boolean parameter that contains the value YES if all of the related animations completed successfully or
* NO if they were interrupted. This parameter may be nil. If supplied, the block is run on the main thread. * NO if they were interrupted. This parameter may be nil. If supplied, the block is run on the main thread.
*/ */
- (void)performBatchUpdates:(nullable __attribute((noescape)) void (^)())updates completion:(nullable void (^)(BOOL finished))completion; - (void)performBatchUpdates:(nullable AS_NOESCAPE void (^)())updates completion:(nullable void (^)(BOOL finished))completion;
/** /**
* Blocks execution of the main thread until all section and row updates are committed. This method must be called from the main thread. * Blocks execution of the main thread until all section and row updates are committed. This method must be called from the main thread.

View File

@@ -76,6 +76,13 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
- (void)setNode:(ASCellNode *)node - (void)setNode:(ASCellNode *)node
{ {
_node = node; _node = node;
self.backgroundColor = node.backgroundColor;
self.selectionStyle = node.selectionStyle;
self.accessoryType = node.accessoryType;
self.separatorInset = node.seperatorInset;
self.clipsToBounds = node.clipsToBounds;
[node __setSelectedFromUIKit:self.selected]; [node __setSelectedFromUIKit:self.selected];
[node __setHighlightedFromUIKit:self.highlighted]; [node __setHighlightedFromUIKit:self.highlighted];
} }
@@ -593,6 +600,10 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
- (nullable NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode waitingIfNeeded:(BOOL)wait - (nullable NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode waitingIfNeeded:(BOOL)wait
{ {
if (cellNode == nil) {
return nil;
}
NSIndexPath *indexPath = [_dataController completedIndexPathForNode:cellNode]; NSIndexPath *indexPath = [_dataController completedIndexPathForNode:cellNode];
indexPath = [self validateIndexPath:indexPath]; indexPath = [self validateIndexPath:indexPath];
if (indexPath == nil && wait) { if (indexPath == nil && wait) {
@@ -627,7 +638,8 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
- (void)endUpdates - (void)endUpdates
{ {
[self endUpdatesAnimated:YES completion:nil]; // We capture the current state of whether animations are enabled if they don't provide us with one.
[self endUpdatesAnimated:[UIView areAnimationsEnabled] completion:nil];
} }
- (void)endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL completed))completion; - (void)endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL completed))completion;
@@ -801,13 +813,6 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
[_rangeController configureContentView:cell.contentView forCellNode:node]; [_rangeController configureContentView:cell.contentView forCellNode:node];
cell.node = node; cell.node = node;
cell.backgroundColor = node.backgroundColor;
cell.selectionStyle = node.selectionStyle;
// the following ensures that we clip the entire cell to it's bounds if node.clipsToBounds is set (the default)
// This is actually a workaround for a bug we are seeing in some rare cases (selected background view
// overlaps other cells if size of ASCellNode has changed.)
cell.clipsToBounds = node.clipsToBounds;
} }
return cell; return cell;
@@ -816,7 +821,18 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{ {
ASCellNode *node = [_dataController nodeAtIndexPath:indexPath]; ASCellNode *node = [_dataController nodeAtIndexPath:indexPath];
return node.calculatedSize.height; CGFloat height = node.calculatedSize.height;
/**
* Weirdly enough, Apple expects the return value here to _include_ the height
* of the separator, if there is one! So if our node wants to be 43.5, we need
* to return 44. UITableView will make a cell of height 44 with a content view
* of height 43.5.
*/
if (tableView.separatorStyle != UITableViewCellSeparatorStyleNone) {
height += 1.0 / ASScreenScale();
}
return height;
} }
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView

View File

@@ -73,7 +73,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, assign) int32_t periodicTimeObserverTimescale; @property (nonatomic, assign) int32_t periodicTimeObserverTimescale;
//! Defaults to AVLayerVideoGravityResizeAspect //! Defaults to AVLayerVideoGravityResizeAspect
@property (nonatomic) NSString *gravity; @property (nonatomic, copy) NSString *gravity;
@property (nullable, nonatomic, weak, readwrite) id<ASVideoNodeDelegate, ASNetworkImageNodeDelegate> delegate; @property (nullable, nonatomic, weak, readwrite) id<ASVideoNodeDelegate, ASNetworkImageNodeDelegate> delegate;

View File

@@ -43,7 +43,6 @@ static NSString * const kRate = @"rate";
@interface ASVideoNode () @interface ASVideoNode ()
{ {
__weak id<ASVideoNodeDelegate> _delegate;
struct { struct {
unsigned int delegateVideNodeShouldChangePlayerStateTo:1; unsigned int delegateVideNodeShouldChangePlayerStateTo:1;
unsigned int delegateVideoDidPlayToEnd:1; unsigned int delegateVideoDidPlayToEnd:1;
@@ -88,6 +87,8 @@ static NSString * const kRate = @"rate";
@implementation ASVideoNode @implementation ASVideoNode
@dynamic delegate;
// TODO: Support preview images with HTTP Live Streaming videos. // TODO: Support preview images with HTTP Live Streaming videos.
#pragma mark - Construction and Layout #pragma mark - Construction and Layout
@@ -163,7 +164,7 @@ static NSString * const kRate = @"rate";
} }
if (_delegateFlags.delegateVideoNodeDidSetCurrentItem) { if (_delegateFlags.delegateVideoNodeDidSetCurrentItem) {
[_delegate videoNode:self didSetCurrentItem:playerItem]; [self.delegate videoNode:self didSetCurrentItem:playerItem];
} }
if (self.image == nil && self.URL == nil) { if (self.image == nil && self.URL == nil) {
@@ -316,6 +317,9 @@ static NSString * const kRate = @"rate";
if ([change[NSKeyValueChangeNewKey] integerValue] == AVPlayerItemStatusReadyToPlay) { if ([change[NSKeyValueChangeNewKey] integerValue] == AVPlayerItemStatusReadyToPlay) {
if (self.playerState != ASVideoNodePlayerStatePlaying) { if (self.playerState != ASVideoNodePlayerStatePlaying) {
self.playerState = ASVideoNodePlayerStateReadyToPlay; self.playerState = ASVideoNodePlayerStateReadyToPlay;
if (_shouldBePlaying && ASInterfaceStateIncludesVisible(self.interfaceState)) {
[self play];
}
} }
// If we don't yet have a placeholder image update it now that we should have data available for it // If we don't yet have a placeholder image update it now that we should have data available for it
if (self.image == nil && self.URL == nil) { if (self.image == nil && self.URL == nil) {
@@ -334,7 +338,7 @@ static NSString * const kRate = @"rate";
} }
if (_shouldBePlaying && (_shouldAggressivelyRecoverFromStall || likelyToKeepUp) && ASInterfaceStateIncludesVisible(self.interfaceState)) { if (_shouldBePlaying && (_shouldAggressivelyRecoverFromStall || likelyToKeepUp) && ASInterfaceStateIncludesVisible(self.interfaceState)) {
if (self.playerState == ASVideoNodePlayerStateLoading && _delegateFlags.delegateVideoNodeDidRecoverFromStall) { if (self.playerState == ASVideoNodePlayerStateLoading && _delegateFlags.delegateVideoNodeDidRecoverFromStall) {
[_delegate videoNodeDidRecoverFromStall:self]; [self.delegate videoNodeDidRecoverFromStall:self];
} }
[self play]; // autoresume after buffer catches up [self play]; // autoresume after buffer catches up
} }
@@ -359,7 +363,7 @@ static NSString * const kRate = @"rate";
- (void)tapped - (void)tapped
{ {
if (_delegateFlags.delegateDidTapVideoNode) { if (_delegateFlags.delegateDidTapVideoNode) {
[_delegate didTapVideoNode:self]; [self.delegate didTapVideoNode:self];
} else { } else {
if (_shouldBePlaying) { if (_shouldBePlaying) {
@@ -383,14 +387,14 @@ static NSString * const kRate = @"rate";
self.playerState = ASVideoNodePlayerStateLoading; self.playerState = ASVideoNodePlayerStateLoading;
if (_delegateFlags.delegateVideoNodeDidStartInitialLoading) { if (_delegateFlags.delegateVideoNodeDidStartInitialLoading) {
[_delegate videoNodeDidStartInitialLoading:self]; [self.delegate videoNodeDidStartInitialLoading:self];
} }
NSArray<NSString *> *requestedKeys = @[@"playable"]; NSArray<NSString *> *requestedKeys = @[@"playable"];
[asset loadValuesAsynchronouslyForKeys:requestedKeys completionHandler:^{ [asset loadValuesAsynchronouslyForKeys:requestedKeys completionHandler:^{
ASPerformBlockOnMainThread(^{ ASPerformBlockOnMainThread(^{
if (_delegateFlags.delegateVideoNodeDidFinishInitialLoading) { if (_delegateFlags.delegateVideoNodeDidFinishInitialLoading) {
[_delegate videoNodeDidFinishInitialLoading:self]; [self.delegate videoNodeDidFinishInitialLoading:self];
} }
[self prepareToPlayAsset:asset withKeys:requestedKeys]; [self prepareToPlayAsset:asset withKeys:requestedKeys];
}); });
@@ -405,7 +409,7 @@ static NSString * const kRate = @"rate";
} }
if (_delegateFlags.delegateVideoNodeDidPlayToTimeInterval) { if (_delegateFlags.delegateVideoNodeDidPlayToTimeInterval) {
[_delegate videoNode:self didPlayToTimeInterval:timeInSeconds]; [self.delegate videoNode:self didPlayToTimeInterval:timeInSeconds];
} }
} }
@@ -465,7 +469,7 @@ static NSString * const kRate = @"rate";
} }
if (_delegateFlags.delegateVideoNodeWillChangePlayerStateToState) { if (_delegateFlags.delegateVideoNodeWillChangePlayerStateToState) {
[_delegate videoNode:self willChangePlayerState:oldState toState:playerState]; [self.delegate videoNode:self willChangePlayerState:oldState toState:playerState];
} }
_playerState = playerState; _playerState = playerState;
@@ -564,28 +568,23 @@ static NSString * const kRate = @"rate";
return (AVPlayerLayer *)_playerNode.layer; return (AVPlayerLayer *)_playerNode.layer;
} }
- (id<ASVideoNodeDelegate>)delegate{
return _delegate;
}
- (void)setDelegate:(id<ASVideoNodeDelegate>)delegate - (void)setDelegate:(id<ASVideoNodeDelegate>)delegate
{ {
[super setDelegate:delegate]; [super setDelegate:delegate];
_delegate = delegate;
if (_delegate == nil) { if (delegate == nil) {
memset(&_delegateFlags, 0, sizeof(_delegateFlags)); memset(&_delegateFlags, 0, sizeof(_delegateFlags));
} else { } else {
_delegateFlags.delegateVideNodeShouldChangePlayerStateTo = [_delegate respondsToSelector:@selector(videoNode:shouldChangePlayerStateTo:)]; _delegateFlags.delegateVideNodeShouldChangePlayerStateTo = [delegate respondsToSelector:@selector(videoNode:shouldChangePlayerStateTo:)];
_delegateFlags.delegateVideoDidPlayToEnd = [_delegate respondsToSelector:@selector(videoDidPlayToEnd:)]; _delegateFlags.delegateVideoDidPlayToEnd = [delegate respondsToSelector:@selector(videoDidPlayToEnd:)];
_delegateFlags.delegateDidTapVideoNode = [_delegate respondsToSelector:@selector(didTapVideoNode:)]; _delegateFlags.delegateDidTapVideoNode = [delegate respondsToSelector:@selector(didTapVideoNode:)];
_delegateFlags.delegateVideoNodeWillChangePlayerStateToState = [_delegate respondsToSelector:@selector(videoNode:willChangePlayerState:toState:)]; _delegateFlags.delegateVideoNodeWillChangePlayerStateToState = [delegate respondsToSelector:@selector(videoNode:willChangePlayerState:toState:)];
_delegateFlags.delegateVideoNodeDidPlayToTimeInterval = [_delegate respondsToSelector:@selector(videoNode:didPlayToTimeInterval:)]; _delegateFlags.delegateVideoNodeDidPlayToTimeInterval = [delegate respondsToSelector:@selector(videoNode:didPlayToTimeInterval:)];
_delegateFlags.delegateVideoNodeDidStartInitialLoading = [_delegate respondsToSelector:@selector(videoNodeDidStartInitialLoading:)]; _delegateFlags.delegateVideoNodeDidStartInitialLoading = [delegate respondsToSelector:@selector(videoNodeDidStartInitialLoading:)];
_delegateFlags.delegateVideoNodeDidFinishInitialLoading = [_delegate respondsToSelector:@selector(videoNodeDidFinishInitialLoading:)]; _delegateFlags.delegateVideoNodeDidFinishInitialLoading = [delegate respondsToSelector:@selector(videoNodeDidFinishInitialLoading:)];
_delegateFlags.delegateVideoNodeDidSetCurrentItem = [_delegate respondsToSelector:@selector(videoNode:didSetCurrentItem:)]; _delegateFlags.delegateVideoNodeDidSetCurrentItem = [delegate respondsToSelector:@selector(videoNode:didSetCurrentItem:)];
_delegateFlags.delegateVideoNodeDidStallAtTimeInterval = [_delegate respondsToSelector:@selector(videoNode:didStallAtTimeInterval:)]; _delegateFlags.delegateVideoNodeDidStallAtTimeInterval = [delegate respondsToSelector:@selector(videoNode:didStallAtTimeInterval:)];
_delegateFlags.delegateVideoNodeDidRecoverFromStall = [_delegate respondsToSelector:@selector(videoNodeDidRecoverFromStall:)]; _delegateFlags.delegateVideoNodeDidRecoverFromStall = [delegate respondsToSelector:@selector(videoNodeDidRecoverFromStall:)];
} }
} }
@@ -677,7 +676,7 @@ static NSString * const kRate = @"rate";
- (BOOL)isStateChangeValid:(ASVideoNodePlayerState)state - (BOOL)isStateChangeValid:(ASVideoNodePlayerState)state
{ {
if (_delegateFlags.delegateVideNodeShouldChangePlayerStateTo) { if (_delegateFlags.delegateVideNodeShouldChangePlayerStateTo) {
if (![_delegate videoNode:self shouldChangePlayerStateTo:state]) { if (![self.delegate videoNode:self shouldChangePlayerStateTo:state]) {
return NO; return NO;
} }
} }
@@ -704,7 +703,7 @@ static NSString * const kRate = @"rate";
{ {
self.playerState = ASVideoNodePlayerStateFinished; self.playerState = ASVideoNodePlayerStateFinished;
if (_delegateFlags.delegateVideoDidPlayToEnd) { if (_delegateFlags.delegateVideoDidPlayToEnd) {
[_delegate videoDidPlayToEnd:self]; [self.delegate videoDidPlayToEnd:self];
} }
if (_shouldAutorepeat) { if (_shouldAutorepeat) {
@@ -719,7 +718,7 @@ static NSString * const kRate = @"rate";
{ {
self.playerState = ASVideoNodePlayerStateLoading; self.playerState = ASVideoNodePlayerStateLoading;
if (_delegateFlags.delegateVideoNodeDidStallAtTimeInterval) { if (_delegateFlags.delegateVideoNodeDidStallAtTimeInterval) {
[_delegate videoNode:self didStallAtTimeInterval:CMTimeGetSeconds(_player.currentItem.currentTime)]; [self.delegate videoNode:self didStallAtTimeInterval:CMTimeGetSeconds(_player.currentItem.currentTime)];
} }
} }

View File

@@ -58,7 +58,7 @@ NS_ASSUME_NONNULL_BEGIN
//! Defaults to 100 //! Defaults to 100
@property (nonatomic, assign) int32_t periodicTimeObserverTimescale; @property (nonatomic, assign) int32_t periodicTimeObserverTimescale;
//! Defaults to AVLayerVideoGravityResizeAspect //! Defaults to AVLayerVideoGravityResizeAspect
@property (nonatomic) NSString *gravity; @property (nonatomic, copy) NSString *gravity;
- (instancetype)initWithUrl:(NSURL*)url; - (instancetype)initWithUrl:(NSURL*)url;
- (instancetype)initWithAsset:(AVAsset*)asset; - (instancetype)initWithAsset:(AVAsset*)asset;

View File

@@ -48,6 +48,8 @@
#import <AsyncDisplayKit/ASLayout.h> #import <AsyncDisplayKit/ASLayout.h>
#import <AsyncDisplayKit/ASDimension.h> #import <AsyncDisplayKit/ASDimension.h>
#import <AsyncDisplayKit/ASDimensionInternal.h>
#import <AsyncDisplayKit/ASDimensionDeprecated.h>
#import <AsyncDisplayKit/ASEnvironment.h> #import <AsyncDisplayKit/ASEnvironment.h>
#import <AsyncDisplayKit/ASLayoutElement.h> #import <AsyncDisplayKit/ASLayoutElement.h>
#import <AsyncDisplayKit/ASLayoutSpec.h> #import <AsyncDisplayKit/ASLayoutSpec.h>

View File

@@ -76,12 +76,11 @@ extern NSString * const ASCollectionInvalidUpdateException;
*/ */
@protocol ASDataControllerDelegate <NSObject> @protocol ASDataControllerDelegate <NSObject>
@optional
/** /**
Called for batch update. Called for batch update.
*/ */
- (void)dataControllerBeginUpdates:(ASDataController *)dataController; - (void)dataControllerBeginUpdates:(ASDataController *)dataController;
- (void)dataControllerWillDeleteAllData:(ASDataController *)dataController;
- (void)dataController:(ASDataController *)dataController endUpdatesAnimated:(BOOL)animated completion:(void (^ _Nullable)(BOOL))completion; - (void)dataController:(ASDataController *)dataController endUpdatesAnimated:(BOOL)animated completion:(void (^ _Nullable)(BOOL))completion;
/** /**

View File

@@ -454,6 +454,7 @@ NSString * const ASCollectionInvalidUpdateException = @"ASCollectionInvalidUpdat
// -beginUpdates // -beginUpdates
[_mainSerialQueue performBlockOnMainThread:^{ [_mainSerialQueue performBlockOnMainThread:^{
[_delegate dataControllerBeginUpdates:self]; [_delegate dataControllerBeginUpdates:self];
[_delegate dataControllerWillDeleteAllData:self];
}]; }];
// deleteSections: // deleteSections:
@@ -673,40 +674,7 @@ NSString * const ASCollectionInvalidUpdateException = @"ASCollectionInvalidUpdat
- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions - (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
{ {
ASDisplayNodeAssertMainThread(); ASDisplayNodeAssert(NO, @"ASDataController does not support %@. Call this on ASChangeSetDataController and the move will be processed along with the current batch of updates.", NSStringFromSelector(_cmd));
LOG(@"Edit Command - moveSection");
if (!_initialReloadDataHasBeenCalled) {
return;
}
NSMutableArray *rowContexts = _nodeContexts[ASDataControllerRowNodeKind];
NSArray *contexts = rowContexts[section];
[rowContexts removeObjectAtIndex:section];
[rowContexts insertObject:contexts atIndex:section];
dispatch_group_wait(_editingTransactionGroup, DISPATCH_TIME_FOREVER);
dispatch_group_async(_editingTransactionGroup, _editingTransactionQueue, ^{
[self willMoveSection:section toSection:newSection];
// remove elements
LOG(@"Edit Transaction - moveSection");
NSMutableArray *editingRows = _editingNodes[ASDataControllerRowNodeKind];
NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(editingRows, [NSIndexSet indexSetWithIndex:section]);
NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(editingRows, indexPaths);
[self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions];
// update the section of indexpaths
NSMutableArray *updatedIndexPaths = [[NSMutableArray alloc] initWithCapacity:indexPaths.count];
for (NSIndexPath *indexPath in indexPaths) {
NSIndexPath *updatedIndexPath = [NSIndexPath indexPathForItem:indexPath.item inSection:newSection];
[updatedIndexPaths addObject:updatedIndexPath];
}
// Don't re-calculate size for moving
[self _insertNodes:nodes atIndexPaths:updatedIndexPaths withAnimationOptions:animationOptions];
});
} }
@@ -879,29 +847,7 @@ NSString * const ASCollectionInvalidUpdateException = @"ASCollectionInvalidUpdat
- (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions - (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
{ {
ASDisplayNodeAssertMainThread(); ASDisplayNodeAssert(NO, @"ASDataController does not support %@. Call this on ASChangeSetDataController and the move will be processed along with the current batch of updates.", NSStringFromSelector(_cmd));
if (!_initialReloadDataHasBeenCalled) {
return;
}
NSMutableArray *contexts = _nodeContexts[ASDataControllerRowNodeKind];
ASIndexedNodeContext *context = contexts[indexPath.section][indexPath.item];
[contexts[indexPath.section] removeObjectAtIndex:indexPath.item];
[contexts[newIndexPath.section] insertObject:context atIndex:newIndexPath.item];
LOG(@"Edit Command - moveRow: %@ > %@", indexPath, newIndexPath);
dispatch_group_wait(_editingTransactionGroup, DISPATCH_TIME_FOREVER);
dispatch_group_async(_editingTransactionGroup, _editingTransactionQueue, ^{
LOG(@"Edit Transaction - moveRow: %@ > %@", indexPath, newIndexPath);
NSArray *indexPaths = @[indexPath];
NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_editingNodes[ASDataControllerRowNodeKind], indexPaths);
[self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions];
// Don't re-calculate size for moving
NSArray *newIndexPaths = @[newIndexPath];
[self _insertNodes:nodes atIndexPaths:newIndexPaths withAnimationOptions:animationOptions];
});
} }
#pragma mark - Data Querying (Subclass API) #pragma mark - Data Querying (Subclass API)
@@ -953,6 +899,9 @@ NSString * const ASCollectionInvalidUpdateException = @"ASCollectionInvalidUpdat
- (ASCellNode *)nodeAtIndexPath:(NSIndexPath *)indexPath - (ASCellNode *)nodeAtIndexPath:(NSIndexPath *)indexPath
{ {
ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertMainThread();
if (indexPath == nil) {
return nil;
}
NSArray *contexts = _nodeContexts[ASDataControllerRowNodeKind]; NSArray *contexts = _nodeContexts[ASDataControllerRowNodeKind];
NSInteger section = indexPath.section; NSInteger section = indexPath.section;
@@ -972,6 +921,9 @@ NSString * const ASCollectionInvalidUpdateException = @"ASCollectionInvalidUpdat
- (ASCellNode *)nodeAtCompletedIndexPath:(NSIndexPath *)indexPath - (ASCellNode *)nodeAtCompletedIndexPath:(NSIndexPath *)indexPath
{ {
ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertMainThread();
if (indexPath == nil) {
return nil;
}
NSArray *completedNodes = [self completedNodes]; NSArray *completedNodes = [self completedNodes];
NSInteger section = indexPath.section; NSInteger section = indexPath.section;

View File

@@ -59,17 +59,9 @@
ASRangeTuningParameters tuningParameters = [self tuningParametersForRangeMode:rangeMode rangeType:rangeType]; ASRangeTuningParameters tuningParameters = [self tuningParametersForRangeMode:rangeMode rangeType:rangeType];
if (_layoutDirection == ASFlowLayoutDirectionHorizontal) { if (_layoutDirection == ASFlowLayoutDirectionHorizontal) {
ASDisplayNodeAssert(scrollDirection == ASScrollDirectionNone ||
scrollDirection == ASScrollDirectionLeft ||
scrollDirection == ASScrollDirectionRight, @"Invalid scroll direction");
viewportDirectionalSize = viewportSize.width; viewportDirectionalSize = viewportSize.width;
directionalBuffer = ASDirectionalScreenfulBufferHorizontal(scrollDirection, tuningParameters); directionalBuffer = ASDirectionalScreenfulBufferHorizontal(scrollDirection, tuningParameters);
} else { } else {
ASDisplayNodeAssert(scrollDirection == ASScrollDirectionNone ||
scrollDirection == ASScrollDirectionUp ||
scrollDirection == ASScrollDirectionDown, @"Invalid scroll direction");
viewportDirectionalSize = viewportSize.height; viewportDirectionalSize = viewportSize.height;
directionalBuffer = ASDirectionalScreenfulBufferVertical(scrollDirection, tuningParameters); directionalBuffer = ASDirectionalScreenfulBufferVertical(scrollDirection, tuningParameters);
} }

View File

@@ -41,6 +41,12 @@
BOOL _preserveCurrentRangeMode; BOOL _preserveCurrentRangeMode;
BOOL _didRegisterForNodeDisplayNotifications; BOOL _didRegisterForNodeDisplayNotifications;
CFTimeInterval _pendingDisplayNodesTimestamp; CFTimeInterval _pendingDisplayNodesTimestamp;
// If the user is not currently scrolling, we will keep our ranges
// configured to match their previous scroll direction. Defaults
// to [.right, .down] so that when the user first opens a screen
// the ranges point down into the content.
ASScrollDirection _previousScrollDirection;
#if AS_RANGECONTROLLER_LOG_UPDATE_FREQ #if AS_RANGECONTROLLER_LOG_UPDATE_FREQ
NSUInteger _updateCountThisFrame; NSUInteger _updateCountThisFrame;
@@ -65,6 +71,7 @@ static UIApplicationState __ApplicationState = UIApplicationStateActive;
_rangeIsValid = YES; _rangeIsValid = YES;
_currentRangeMode = ASLayoutRangeModeInvalid; _currentRangeMode = ASLayoutRangeModeInvalid;
_preserveCurrentRangeMode = NO; _preserveCurrentRangeMode = NO;
_previousScrollDirection = ASScrollDirectionDown | ASScrollDirectionRight;
[[[self class] allRangeControllersWeakSet] addObject:self]; [[[self class] allRangeControllersWeakSet] addObject:self];
@@ -216,7 +223,13 @@ static UIApplicationState __ApplicationState = UIApplicationStateActive;
} }
ASProfilingSignpostStart(1, self); ASProfilingSignpostStart(1, self);
// Get the scroll direction. Default to using the previous one, if they're not scrolling.
ASScrollDirection scrollDirection = [_dataSource scrollDirectionForRangeController:self]; ASScrollDirection scrollDirection = [_dataSource scrollDirectionForRangeController:self];
if (scrollDirection == ASScrollDirectionNone) {
scrollDirection = _previousScrollDirection;
}
_previousScrollDirection = scrollDirection;
if (_layoutControllerImplementsSetViewportSize) { if (_layoutControllerImplementsSetViewportSize) {
[_layoutController setViewportSize:[_dataSource viewportSizeForRangeController:self]]; [_layoutController setViewportSize:[_dataSource viewportSizeForRangeController:self]];
} }
@@ -478,6 +491,11 @@ static UIApplicationState __ApplicationState = UIApplicationStateActive;
[_delegate didBeginUpdatesInRangeController:self]; [_delegate didBeginUpdatesInRangeController:self];
} }
- (void)dataControllerWillDeleteAllData:(ASDataController *)dataController
{
[self _setVisibleNodes:nil];
}
- (void)dataController:(ASDataController *)dataController endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL))completion - (void)dataController:(ASDataController *)dataController endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL))completion
{ {
ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertMainThread();

View File

@@ -9,6 +9,7 @@
// //
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASBaseDefines.h>
@class ASDisplayNode; @class ASDisplayNode;
@protocol _ASDisplayLayerDelegate; @protocol _ASDisplayLayerDelegate;
@@ -103,7 +104,7 @@ typedef BOOL(^asdisplaynode_iscancelled_block_t)(void);
@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<NSObject>)parameters isCancelled:(__attribute((noescape)) asdisplaynode_iscancelled_block_t)isCancelledBlock isRasterizing:(BOOL)isRasterizing; + (void)drawRect:(CGRect)bounds withParameters:(id<NSObject>)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.
@@ -111,19 +112,19 @@ typedef BOOL(^asdisplaynode_iscancelled_block_t)(void);
@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:(__attribute((noescape)) asdisplaynode_iscancelled_block_t)isCancelledBlock; + (UIImage *)displayWithParameters:(id<NSObject>)parameters isCancelled:(AS_NOESCAPE asdisplaynode_iscancelled_block_t)isCancelledBlock;
/** /**
* @abstract instance version of drawRect class method * @abstract instance version of drawRect class method
* @see drawRect:withParameters:isCancelled:isRasterizing class method * @see drawRect:withParameters:isCancelled:isRasterizing class method
*/ */
- (void)drawRect:(CGRect)bounds withParameters:(id <NSObject>)parameters isCancelled:(__attribute((noescape)) asdisplaynode_iscancelled_block_t)isCancelledBlock isRasterizing:(BOOL)isRasterizing; - (void)drawRect:(CGRect)bounds withParameters:(id <NSObject>)parameters isCancelled:(AS_NOESCAPE asdisplaynode_iscancelled_block_t)isCancelledBlock isRasterizing:(BOOL)isRasterizing;
/** /**
* @abstract instance version of display class method * @abstract instance version of display class method
* @see displayWithParameters:isCancelled class method * @see displayWithParameters:isCancelled class method
*/ */
- (UIImage *)displayWithParameters:(id <NSObject>)parameters isCancelled:(__attribute((noescape)) asdisplaynode_iscancelled_block_t)isCancelled; - (UIImage *)displayWithParameters:(id <NSObject>)parameters isCancelled:(AS_NOESCAPE asdisplaynode_iscancelled_block_t)isCancelled;
// Called on the main thread only // Called on the main thread only

View File

@@ -9,7 +9,7 @@
// //
#import <AsyncDisplayKit/ASBaseDefines.h> #import <AsyncDisplayKit/ASBaseDefines.h>
#import <AsyncDisplayKit/ASDimension.h> #import <AsyncDisplayKit/ASDimensionDeprecated.h>
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN

View File

@@ -13,6 +13,11 @@
#import <AsyncDisplayKit/ASBaseDefines.h> #import <AsyncDisplayKit/ASBaseDefines.h>
#import <AsyncDisplayKit/ASAssert.h> #import <AsyncDisplayKit/ASAssert.h>
ASDISPLAYNODE_EXTERN_C_BEGIN
NS_ASSUME_NONNULL_BEGIN
#pragma mark -
ASDISPLAYNODE_INLINE BOOL AS_WARN_UNUSED_RESULT ASPointsValidForLayout(CGFloat points) ASDISPLAYNODE_INLINE BOOL AS_WARN_UNUSED_RESULT ASPointsValidForLayout(CGFloat points)
{ {
return ((isnormal(points) || points == 0.0) && points >= 0.0 && points < (CGFLOAT_MAX / 2.0)); return ((isnormal(points) || points == 0.0) && points >= 0.0 && points < (CGFLOAT_MAX / 2.0));
@@ -33,6 +38,8 @@ ASDISPLAYNODE_INLINE BOOL AS_WARN_UNUSED_RESULT ASIsCGSizeValidForSize(CGSize si
return (ASPointsValidForSize(size.width) && ASPointsValidForSize(size.height)); return (ASPointsValidForSize(size.width) && ASPointsValidForSize(size.height));
} }
#pragma mark - ASDimension
/** /**
* A dimension relative to constraints to be provided in the future. * A dimension relative to constraints to be provided in the future.
* A ASDimension can be one of three types: * A ASDimension can be one of three types:
@@ -58,42 +65,10 @@ typedef struct {
} ASDimension; } ASDimension;
/** /**
* Expresses an inclusive range of sizes. Used to provide a simple constraint to layout. * Represents auto as ASDimension
*/ */
typedef struct {
CGSize min;
CGSize max;
} ASSizeRange;
/**
* A struct specifying a ASLayoutElement's size. Example:
*
* ASLayoutElementSize size = (ASLayoutElementSize){
* .width = ASDimensionMakeWithFraction(0.25),
* .maxWidth = ASDimensionMakeWithPoints(200),
* .minHeight = ASDimensionMakeWithFraction(0.50)
* };
*
* Description: <ASLayoutElementSize: exact={25%, Auto}, min={Auto, 50%}, max={200pt, Auto}>
*
*/
typedef struct {
ASDimension width;
ASDimension height;
ASDimension minWidth;
ASDimension maxWidth;
ASDimension minHeight;
ASDimension maxHeight;
} ASLayoutElementSize;
extern ASDimension const ASDimensionAuto; extern ASDimension const ASDimensionAuto;
ASDISPLAYNODE_EXTERN_C_BEGIN
NS_ASSUME_NONNULL_BEGIN
#pragma mark - ASDimension
/** /**
* Returns a dimension with the specified type and value. * Returns a dimension with the specified type and value.
*/ */
@@ -173,15 +148,6 @@ ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT CGFloat ASDimensionResolve(ASDimensio
} }
} }
#pragma mark - NSNumber+ASDimension
@interface NSNumber (ASDimension)
@property (nonatomic, readonly) ASDimension as_pointDimension;
@property (nonatomic, readonly) ASDimension as_fractionDimension;
@end
#pragma mark - ASLayoutSize #pragma mark - ASLayoutSize
/** /**
@@ -205,6 +171,15 @@ ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT ASLayoutSize ASLayoutSizeMake(ASDimen
return size; return size;
} }
/**
* Resolve this relative size relative to a parent size.
*/
ASDISPLAYNODE_INLINE CGSize ASLayoutSizeResolveSize(ASLayoutSize layoutSize, CGSize parentSize, CGSize autoSize)
{
return CGSizeMake(ASDimensionResolve(layoutSize.width, parentSize.width, autoSize.width),
ASDimensionResolve(layoutSize.height, parentSize.height, autoSize.height));
}
/* /*
* Returns a string representation of a relative size. * Returns a string representation of a relative size.
*/ */
@@ -217,6 +192,14 @@ ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT NSString *NSStringFromASLayoutSize(AS
#pragma mark - ASSizeRange #pragma mark - ASSizeRange
/**
* Expresses an inclusive range of sizes. Used to provide a simple constraint to layout.
*/
typedef struct {
CGSize min;
CGSize max;
} ASSizeRange;
/** /**
* Creates an ASSizeRange with provided min and max size. * Creates an ASSizeRange with provided min and max size.
*/ */
@@ -272,149 +255,5 @@ ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT BOOL ASSizeRangeEqualToSizeRange(ASSi
*/ */
extern AS_WARN_UNUSED_RESULT NSString *NSStringFromASSizeRange(ASSizeRange sizeRange); extern AS_WARN_UNUSED_RESULT NSString *NSStringFromASSizeRange(ASSizeRange sizeRange);
#pragma mark - ASLayoutElementSize
/**
* Returns an ASLayoutElementSize with default values.
*/
ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT ASLayoutElementSize ASLayoutElementSizeMake()
{
return (ASLayoutElementSize){
.width = ASDimensionAuto,
.height = ASDimensionAuto,
.minWidth = ASDimensionAuto,
.maxWidth = ASDimensionAuto,
.minHeight = ASDimensionAuto,
.maxHeight = ASDimensionAuto
};
}
/**
* Returns an ASLayoutElementSize with the specified CGSize values as width and height.
*/
ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT ASLayoutElementSize ASLayoutElementSizeMakeFromCGSize(CGSize size)
{
ASLayoutElementSize s = ASLayoutElementSizeMake();
s.width = ASDimensionMakeWithPoints(size.width);
s.height = ASDimensionMakeWithPoints(size.height);
return s;
}
/**
* Returns whether two sizes are equal.
*/
ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT BOOL ASLayoutElementSizeEqualToLayoutElementSize(ASLayoutElementSize lhs, ASLayoutElementSize rhs)
{
return (ASDimensionEqualToDimension(lhs.width, rhs.width)
&& ASDimensionEqualToDimension(lhs.height, rhs.height)
&& ASDimensionEqualToDimension(lhs.minWidth, rhs.minWidth)
&& ASDimensionEqualToDimension(lhs.maxWidth, rhs.maxWidth)
&& ASDimensionEqualToDimension(lhs.minHeight, rhs.minHeight)
&& ASDimensionEqualToDimension(lhs.maxHeight, rhs.maxHeight));
}
/**
* Returns a string formatted to contain the data from an ASLayoutElementSize.
*/
extern AS_WARN_UNUSED_RESULT NSString *NSStringFromASLayoutElementSize(ASLayoutElementSize size);
/**
* Resolve the given size relative to a parent size and an auto size.
* From the given size uses width, height to resolve the exact size constraint, uses the minHeight and minWidth to
* resolve the min size constraint and the maxHeight and maxWidth to resolve the max size constraint. For every
* dimension with unit ASDimensionUnitAuto the given autoASSizeRange value will be used.
* Based on the calculated exact, min and max size constraints the final size range will be calculated.
*/
extern AS_WARN_UNUSED_RESULT ASSizeRange ASLayoutElementSizeResolveAutoSize(ASLayoutElementSize size, const CGSize parentSize, ASSizeRange autoASSizeRange);
/**
* Resolve the given size to a parent size. Uses internally ASLayoutElementSizeResolveAutoSize with {INFINITY, INFINITY} as
* as autoASSizeRange. For more information look at ASLayoutElementSizeResolveAutoSize.
*/
ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT ASSizeRange ASLayoutElementSizeResolve(ASLayoutElementSize size, const CGSize parentSize)
{
return ASLayoutElementSizeResolveAutoSize(size, parentSize, ASSizeRangeMake(CGSizeZero, CGSizeMake(INFINITY, INFINITY)));
}
#pragma mark - Deprecated
/**
* A dimension relative to constraints to be provided in the future.
* A ASDimension can be one of three types:
*
* "Auto" - This indicated "I have no opinion" and may be resolved in whatever way makes most sense given the circumstances.
*
* "Points" - Just a number. It will always resolve to exactly this amount.
*
* "Percent" - Multiplied to a provided parent amount to resolve a final amount.
*/
typedef NS_ENUM(NSInteger, ASRelativeDimensionType) {
/** This indicates "I have no opinion" and may be resolved in whatever way makes most sense given the circumstances. */
ASRelativeDimensionTypeAuto,
/** Just a number. It will always resolve to exactly this amount. This is the default type. */
ASRelativeDimensionTypePoints,
/** Multiplied to a provided parent amount to resolve a final amount. */
ASRelativeDimensionTypeFraction,
};
#define ASRelativeDimension ASDimension
#define ASRelativeSize ASLayoutSize
#define ASRelativeDimensionMakeWithPoints ASDimensionMakeWithPoints
#define ASRelativeDimensionMakeWithFraction ASDimensionMakeWithFraction
/**
* Function is deprecated. Use ASSizeRangeMake instead.
*/
extern AS_WARN_UNUSED_RESULT ASSizeRange ASSizeRangeMakeExactSize(CGSize size) ASDISPLAYNODE_DEPRECATED_MSG("Use ASSizeRangeMake instead.");
/**
Expresses an inclusive range of relative sizes. Used to provide additional constraint to layout.
Used by ASStaticLayoutSpec.
*/
typedef struct {
ASLayoutSize min;
ASLayoutSize max;
} ASRelativeSizeRange;
extern ASRelativeSizeRange const ASRelativeSizeRangeUnconstrained;
#pragma mark - ASRelativeDimension
extern ASDimension ASRelativeDimensionMake(ASRelativeDimensionType type, CGFloat value) ASDISPLAYNODE_DEPRECATED;
#pragma mark - ASRelativeSize
extern ASLayoutSize ASRelativeSizeMake(ASRelativeDimension width, ASRelativeDimension height) ASDISPLAYNODE_DEPRECATED;
/** Convenience constructor to provide size in points. */
extern ASLayoutSize ASRelativeSizeMakeWithCGSize(CGSize size) ASDISPLAYNODE_DEPRECATED;
/** Convenience constructor to provide size as a fraction. */
extern ASLayoutSize ASRelativeSizeMakeWithFraction(CGFloat fraction) ASDISPLAYNODE_DEPRECATED;
extern BOOL ASRelativeSizeEqualToRelativeSize(ASLayoutSize lhs, ASLayoutSize rhs) ASDISPLAYNODE_DEPRECATED;
extern NSString *NSStringFromASRelativeSize(ASLayoutSize size) ASDISPLAYNODE_DEPRECATED;
#pragma mark - ASRelativeSizeRange
extern ASRelativeSizeRange ASRelativeSizeRangeMake(ASLayoutSize min, ASLayoutSize max) ASDISPLAYNODE_DEPRECATED;
#pragma mark Convenience constructors to provide an exact size (min == max).
extern ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactRelativeSize(ASLayoutSize exact) ASDISPLAYNODE_DEPRECATED;
extern ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactCGSize(CGSize exact) ASDISPLAYNODE_DEPRECATED;
extern ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactFraction(CGFloat fraction) ASDISPLAYNODE_DEPRECATED;
extern ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactRelativeDimensions(ASRelativeDimension exactWidth, ASRelativeDimension exactHeight) ASDISPLAYNODE_DEPRECATED;
extern BOOL ASRelativeSizeRangeEqualToRelativeSizeRange(ASRelativeSizeRange lhs, ASRelativeSizeRange rhs) ASDISPLAYNODE_DEPRECATED;
/** Provided a parent size, compute final dimensions for this RelativeSizeRange to arrive at a SizeRange. */
extern ASSizeRange ASRelativeSizeRangeResolve(ASRelativeSizeRange relativeSizeRange, CGSize parentSize) ASDISPLAYNODE_DEPRECATED;
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END
ASDISPLAYNODE_EXTERN_C_END ASDISPLAYNODE_EXTERN_C_END

View File

@@ -52,91 +52,10 @@ NSString *NSStringFromASDimension(ASDimension dimension)
} }
} }
#pragma mark - NSNumber+ASDimension
@implementation NSNumber (ASDimension)
- (ASDimension)as_pointDimension
{
return ASDimensionMake(ASDimensionUnitPoints, ASCGFloatFromNumber(self));
}
- (ASDimension)as_fractionDimension
{
return ASDimensionMake(ASDimensionUnitFraction, ASCGFloatFromNumber(self));
}
@end
#pragma mark - ASLayoutSize #pragma mark - ASLayoutSize
ASLayoutSize const ASLayoutSizeAuto = {ASDimensionAuto, ASDimensionAuto}; ASLayoutSize const ASLayoutSizeAuto = {ASDimensionAuto, ASDimensionAuto};
// ** Resolve this relative size relative to a parent size. */
ASDISPLAYNODE_INLINE CGSize ASLayoutSizeResolveSize(ASLayoutSize layoutSize, CGSize parentSize, CGSize autoSize)
{
return CGSizeMake(ASDimensionResolve(layoutSize.width, parentSize.width, autoSize.width),
ASDimensionResolve(layoutSize.height, parentSize.height, autoSize.height));
}
#pragma mark - ASLayoutElementSize
NSString *NSStringFromASLayoutElementSize(ASLayoutElementSize size)
{
return [NSString stringWithFormat:
@"<ASLayoutElementSize: exact=%@, min=%@, max=%@>",
NSStringFromASLayoutSize(ASLayoutSizeMake(size.width, size.height)),
NSStringFromASLayoutSize(ASLayoutSizeMake(size.minWidth, size.minHeight)),
NSStringFromASLayoutSize(ASLayoutSizeMake(size.maxWidth, size.maxHeight))];
}
ASDISPLAYNODE_INLINE void ASLayoutElementSizeConstrain(CGFloat minVal, CGFloat exactVal, CGFloat maxVal, CGFloat *outMin, CGFloat *outMax)
{
NSCAssert(!isnan(minVal), @"minVal must not be NaN");
NSCAssert(!isnan(maxVal), @"maxVal must not be NaN");
// Avoid use of min/max primitives since they're harder to reason
// about in the presence of NaN (in exactVal)
// Follow CSS: min overrides max overrides exact.
// Begin with the min/max range
*outMin = minVal;
*outMax = maxVal;
if (maxVal <= minVal) {
// min overrides max and exactVal is irrelevant
*outMax = minVal;
return;
}
if (isnan(exactVal)) {
// no exact value, so leave as a min/max range
return;
}
if (exactVal > maxVal) {
// clip to max value
*outMin = maxVal;
} else if (exactVal < minVal) {
// clip to min value
*outMax = minVal;
} else {
// use exact value
*outMin = *outMax = exactVal;
}
}
ASSizeRange ASLayoutElementSizeResolveAutoSize(ASLayoutElementSize size, const CGSize parentSize, ASSizeRange autoASSizeRange)
{
CGSize resolvedExact = ASLayoutSizeResolveSize(ASLayoutSizeMake(size.width, size.height), parentSize, {NAN, NAN});
CGSize resolvedMin = ASLayoutSizeResolveSize(ASLayoutSizeMake(size.minWidth, size.minHeight), parentSize, autoASSizeRange.min);
CGSize resolvedMax = ASLayoutSizeResolveSize(ASLayoutSizeMake(size.maxWidth, size.maxHeight), parentSize, autoASSizeRange.max);
CGSize rangeMin, rangeMax;
ASLayoutElementSizeConstrain(resolvedMin.width, resolvedExact.width, resolvedMax.width, &rangeMin.width, &rangeMax.width);
ASLayoutElementSizeConstrain(resolvedMin.height, resolvedExact.height, resolvedMax.height, &rangeMin.height, &rangeMax.height);
return {rangeMin, rangeMax};
}
#pragma mark - ASSizeRange #pragma mark - ASSizeRange
@@ -178,90 +97,3 @@ NSString *NSStringFromASSizeRange(ASSizeRange sizeRange)
NSStringFromCGSize(sizeRange.min), NSStringFromCGSize(sizeRange.min),
NSStringFromCGSize(sizeRange.max)]; NSStringFromCGSize(sizeRange.max)];
} }
#pragma mark - Deprecated
ASDimension ASRelativeDimensionMake(ASRelativeDimensionType type, CGFloat value)
{
if (type == ASRelativeDimensionTypePoints) {
return ASDimensionMakeWithPoints(value);
} else if (type == ASRelativeDimensionTypeFraction) {
return ASDimensionMakeWithFraction(value);
}
ASDisplayNodeCAssert(NO, @"ASRelativeDimensionMake does not support the given ASRelativeDimensionType");
return ASDimensionMakeWithPoints(0);
}
ASSizeRange ASSizeRangeMakeExactSize(CGSize size)
{
return ASSizeRangeMake(size);
}
ASRelativeSizeRange const ASRelativeSizeRangeUnconstrained = {};
#pragma mark - ASRelativeSize
ASLayoutSize ASRelativeSizeMake(ASRelativeDimension width, ASRelativeDimension height)
{
return ASLayoutSizeMake(width, height);
}
ASLayoutSize ASRelativeSizeMakeWithCGSize(CGSize size)
{
return ASRelativeSizeMake(ASRelativeDimensionMakeWithPoints(size.width),
ASRelativeDimensionMakeWithPoints(size.height));
}
ASLayoutSize ASRelativeSizeMakeWithFraction(CGFloat fraction)
{
return ASRelativeSizeMake(ASRelativeDimensionMakeWithFraction(fraction),
ASRelativeDimensionMakeWithFraction(fraction));
}
BOOL ASRelativeSizeEqualToRelativeSize(ASLayoutSize lhs, ASLayoutSize rhs)
{
return ASDimensionEqualToDimension(lhs.width, rhs.width)
&& ASDimensionEqualToDimension(lhs.height, rhs.height);
}
#pragma mark - ASRelativeSizeRange
ASRelativeSizeRange ASRelativeSizeRangeMake(ASLayoutSize min, ASLayoutSize max)
{
ASRelativeSizeRange sizeRange; sizeRange.min = min; sizeRange.max = max; return sizeRange;
}
ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactRelativeSize(ASLayoutSize exact)
{
return ASRelativeSizeRangeMake(exact, exact);
}
ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactCGSize(CGSize exact)
{
return ASRelativeSizeRangeMakeWithExactRelativeSize(ASRelativeSizeMakeWithCGSize(exact));
}
ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactFraction(CGFloat fraction)
{
return ASRelativeSizeRangeMakeWithExactRelativeSize(ASRelativeSizeMakeWithFraction(fraction));
}
ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactRelativeDimensions(ASRelativeDimension exactWidth, ASRelativeDimension exactHeight)
{
return ASRelativeSizeRangeMakeWithExactRelativeSize(ASRelativeSizeMake(exactWidth, exactHeight));
}
BOOL ASRelativeSizeRangeEqualToRelativeSizeRange(ASRelativeSizeRange lhs, ASRelativeSizeRange rhs)
{
return ASRelativeSizeEqualToRelativeSize(lhs.min, rhs.min) && ASRelativeSizeEqualToRelativeSize(lhs.max, rhs.max);
}
ASSizeRange ASRelativeSizeRangeResolve(ASRelativeSizeRange relativeSizeRange,
CGSize parentSize)
{
return ASSizeRangeMake(ASLayoutSizeResolveSize(relativeSizeRange.min, parentSize, parentSize),
ASLayoutSizeResolveSize(relativeSizeRange.max, parentSize, parentSize));
}

View File

@@ -0,0 +1,95 @@
//
// ASDimensionDeprecated.h
// AsyncDisplayKit
//
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.
//
#pragma once
#import <AsyncDisplayKit/ASBaseDefines.h>
#import <AsyncDisplayKit/ASDimension.h>
ASDISPLAYNODE_EXTERN_C_BEGIN
NS_ASSUME_NONNULL_BEGIN
/**
* A dimension relative to constraints to be provided in the future.
* A ASDimension can be one of three types:
*
* "Auto" - This indicated "I have no opinion" and may be resolved in whatever way makes most sense given the circumstances.
*
* "Points" - Just a number. It will always resolve to exactly this amount.
*
* "Percent" - Multiplied to a provided parent amount to resolve a final amount.
*/
typedef NS_ENUM(NSInteger, ASRelativeDimensionType) {
/** This indicates "I have no opinion" and may be resolved in whatever way makes most sense given the circumstances. */
ASRelativeDimensionTypeAuto,
/** Just a number. It will always resolve to exactly this amount. This is the default type. */
ASRelativeDimensionTypePoints,
/** Multiplied to a provided parent amount to resolve a final amount. */
ASRelativeDimensionTypeFraction,
};
#define ASRelativeDimension ASDimension
#define ASRelativeSize ASLayoutSize
#define ASRelativeDimensionMakeWithPoints ASDimensionMakeWithPoints
#define ASRelativeDimensionMakeWithFraction ASDimensionMakeWithFraction
/**
* Function is deprecated. Use ASSizeRangeMake instead.
*/
extern AS_WARN_UNUSED_RESULT ASSizeRange ASSizeRangeMakeExactSize(CGSize size) ASDISPLAYNODE_DEPRECATED_MSG("Use ASSizeRangeMake instead.");
/**
Expresses an inclusive range of relative sizes. Used to provide additional constraint to layout.
Used by ASStaticLayoutSpec.
*/
typedef struct {
ASLayoutSize min;
ASLayoutSize max;
} ASRelativeSizeRange;
extern ASRelativeSizeRange const ASRelativeSizeRangeUnconstrained;
#pragma mark - ASRelativeDimension
extern ASDimension ASRelativeDimensionMake(ASRelativeDimensionType type, CGFloat value) ASDISPLAYNODE_DEPRECATED;
#pragma mark - ASRelativeSize
extern ASLayoutSize ASRelativeSizeMake(ASRelativeDimension width, ASRelativeDimension height) ASDISPLAYNODE_DEPRECATED;
/** Convenience constructor to provide size in points. */
extern ASLayoutSize ASRelativeSizeMakeWithCGSize(CGSize size) ASDISPLAYNODE_DEPRECATED;
/** Convenience constructor to provide size as a fraction. */
extern ASLayoutSize ASRelativeSizeMakeWithFraction(CGFloat fraction) ASDISPLAYNODE_DEPRECATED;
extern BOOL ASRelativeSizeEqualToRelativeSize(ASLayoutSize lhs, ASLayoutSize rhs) ASDISPLAYNODE_DEPRECATED;
extern NSString *NSStringFromASRelativeSize(ASLayoutSize size) ASDISPLAYNODE_DEPRECATED;
#pragma mark - ASRelativeSizeRange
extern ASRelativeSizeRange ASRelativeSizeRangeMake(ASLayoutSize min, ASLayoutSize max) ASDISPLAYNODE_DEPRECATED;
#pragma mark Convenience constructors to provide an exact size (min == max).
extern ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactRelativeSize(ASLayoutSize exact) ASDISPLAYNODE_DEPRECATED;
extern ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactCGSize(CGSize exact) ASDISPLAYNODE_DEPRECATED;
extern ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactFraction(CGFloat fraction) ASDISPLAYNODE_DEPRECATED;
extern ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactRelativeDimensions(ASRelativeDimension exactWidth, ASRelativeDimension exactHeight) ASDISPLAYNODE_DEPRECATED;
extern BOOL ASRelativeSizeRangeEqualToRelativeSizeRange(ASRelativeSizeRange lhs, ASRelativeSizeRange rhs) ASDISPLAYNODE_DEPRECATED;
/** Provided a parent size, compute final dimensions for this RelativeSizeRange to arrive at a SizeRange. */
extern ASSizeRange ASRelativeSizeRangeResolve(ASRelativeSizeRange relativeSizeRange, CGSize parentSize) ASDISPLAYNODE_DEPRECATED;
NS_ASSUME_NONNULL_END
ASDISPLAYNODE_EXTERN_C_END

View File

@@ -0,0 +1,95 @@
//
// ASDimensionDeprecated.mm
// AsyncDisplayKit
//
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.
//
#import "ASDimensionDeprecated.h"
ASDimension ASRelativeDimensionMake(ASRelativeDimensionType type, CGFloat value)
{
if (type == ASRelativeDimensionTypePoints) {
return ASDimensionMakeWithPoints(value);
} else if (type == ASRelativeDimensionTypeFraction) {
return ASDimensionMakeWithFraction(value);
}
ASDisplayNodeCAssert(NO, @"ASRelativeDimensionMake does not support the given ASRelativeDimensionType");
return ASDimensionMakeWithPoints(0);
}
ASSizeRange ASSizeRangeMakeExactSize(CGSize size)
{
return ASSizeRangeMake(size);
}
ASRelativeSizeRange const ASRelativeSizeRangeUnconstrained = {};
#pragma mark - ASRelativeSize
ASLayoutSize ASRelativeSizeMake(ASRelativeDimension width, ASRelativeDimension height)
{
return ASLayoutSizeMake(width, height);
}
ASLayoutSize ASRelativeSizeMakeWithCGSize(CGSize size)
{
return ASRelativeSizeMake(ASRelativeDimensionMakeWithPoints(size.width),
ASRelativeDimensionMakeWithPoints(size.height));
}
ASLayoutSize ASRelativeSizeMakeWithFraction(CGFloat fraction)
{
return ASRelativeSizeMake(ASRelativeDimensionMakeWithFraction(fraction),
ASRelativeDimensionMakeWithFraction(fraction));
}
BOOL ASRelativeSizeEqualToRelativeSize(ASLayoutSize lhs, ASLayoutSize rhs)
{
return ASDimensionEqualToDimension(lhs.width, rhs.width)
&& ASDimensionEqualToDimension(lhs.height, rhs.height);
}
#pragma mark - ASRelativeSizeRange
ASRelativeSizeRange ASRelativeSizeRangeMake(ASLayoutSize min, ASLayoutSize max)
{
ASRelativeSizeRange sizeRange; sizeRange.min = min; sizeRange.max = max; return sizeRange;
}
ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactRelativeSize(ASLayoutSize exact)
{
return ASRelativeSizeRangeMake(exact, exact);
}
ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactCGSize(CGSize exact)
{
return ASRelativeSizeRangeMakeWithExactRelativeSize(ASRelativeSizeMakeWithCGSize(exact));
}
ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactFraction(CGFloat fraction)
{
return ASRelativeSizeRangeMakeWithExactRelativeSize(ASRelativeSizeMakeWithFraction(fraction));
}
ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactRelativeDimensions(ASRelativeDimension exactWidth, ASRelativeDimension exactHeight)
{
return ASRelativeSizeRangeMakeWithExactRelativeSize(ASRelativeSizeMake(exactWidth, exactHeight));
}
BOOL ASRelativeSizeRangeEqualToRelativeSizeRange(ASRelativeSizeRange lhs, ASRelativeSizeRange rhs)
{
return ASRelativeSizeEqualToRelativeSize(lhs.min, rhs.min) && ASRelativeSizeEqualToRelativeSize(lhs.max, rhs.max);
}
ASSizeRange ASRelativeSizeRangeResolve(ASRelativeSizeRange relativeSizeRange,
CGSize parentSize)
{
return ASSizeRangeMake(ASLayoutSizeResolveSize(relativeSizeRange.min, parentSize, parentSize),
ASLayoutSizeResolveSize(relativeSizeRange.max, parentSize, parentSize));
}

View File

@@ -0,0 +1,105 @@
//
// ASDimensionInternal.h
// AsyncDisplayKit
//
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.
//
#pragma once
#import <AsyncDisplayKit/ASBaseDefines.h>
#import <AsyncDisplayKit/ASDimension.h>
ASDISPLAYNODE_EXTERN_C_BEGIN
NS_ASSUME_NONNULL_BEGIN
#pragma mark - ASLayoutElementSize
/**
* A struct specifying a ASLayoutElement's size. Example:
*
* ASLayoutElementSize size = (ASLayoutElementSize){
* .width = ASDimensionMakeWithFraction(0.25),
* .maxWidth = ASDimensionMakeWithPoints(200),
* .minHeight = ASDimensionMakeWithFraction(0.50)
* };
*
* Description: <ASLayoutElementSize: exact={25%, Auto}, min={Auto, 50%}, max={200pt, Auto}>
*
*/
typedef struct {
ASDimension width;
ASDimension height;
ASDimension minWidth;
ASDimension maxWidth;
ASDimension minHeight;
ASDimension maxHeight;
} ASLayoutElementSize;
/**
* Returns an ASLayoutElementSize with default values.
*/
ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT ASLayoutElementSize ASLayoutElementSizeMake()
{
return (ASLayoutElementSize){
.width = ASDimensionAuto,
.height = ASDimensionAuto,
.minWidth = ASDimensionAuto,
.maxWidth = ASDimensionAuto,
.minHeight = ASDimensionAuto,
.maxHeight = ASDimensionAuto
};
}
/**
* Returns an ASLayoutElementSize with the specified CGSize values as width and height.
*/
ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT ASLayoutElementSize ASLayoutElementSizeMakeFromCGSize(CGSize size)
{
ASLayoutElementSize s = ASLayoutElementSizeMake();
s.width = ASDimensionMakeWithPoints(size.width);
s.height = ASDimensionMakeWithPoints(size.height);
return s;
}
/**
* Returns whether two sizes are equal.
*/
ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT BOOL ASLayoutElementSizeEqualToLayoutElementSize(ASLayoutElementSize lhs, ASLayoutElementSize rhs)
{
return (ASDimensionEqualToDimension(lhs.width, rhs.width)
&& ASDimensionEqualToDimension(lhs.height, rhs.height)
&& ASDimensionEqualToDimension(lhs.minWidth, rhs.minWidth)
&& ASDimensionEqualToDimension(lhs.maxWidth, rhs.maxWidth)
&& ASDimensionEqualToDimension(lhs.minHeight, rhs.minHeight)
&& ASDimensionEqualToDimension(lhs.maxHeight, rhs.maxHeight));
}
/**
* Returns a string formatted to contain the data from an ASLayoutElementSize.
*/
extern AS_WARN_UNUSED_RESULT NSString *NSStringFromASLayoutElementSize(ASLayoutElementSize size);
/**
* Resolve the given size relative to a parent size and an auto size.
* From the given size uses width, height to resolve the exact size constraint, uses the minHeight and minWidth to
* resolve the min size constraint and the maxHeight and maxWidth to resolve the max size constraint. For every
* dimension with unit ASDimensionUnitAuto the given autoASSizeRange value will be used.
* Based on the calculated exact, min and max size constraints the final size range will be calculated.
*/
extern AS_WARN_UNUSED_RESULT ASSizeRange ASLayoutElementSizeResolveAutoSize(ASLayoutElementSize size, const CGSize parentSize, ASSizeRange autoASSizeRange);
/**
* Resolve the given size to a parent size. Uses internally ASLayoutElementSizeResolveAutoSize with {INFINITY, INFINITY} as
* as autoASSizeRange. For more information look at ASLayoutElementSizeResolveAutoSize.
*/
ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT ASSizeRange ASLayoutElementSizeResolve(ASLayoutElementSize size, const CGSize parentSize)
{
return ASLayoutElementSizeResolveAutoSize(size, parentSize, ASSizeRangeMake(CGSizeZero, CGSizeMake(INFINITY, INFINITY)));
}
NS_ASSUME_NONNULL_END
ASDISPLAYNODE_EXTERN_C_END

View File

@@ -0,0 +1,66 @@
//
// ASDimensionInternal.mm
// AsyncDisplayKit
//
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.
//
#import "ASDimensionInternal.h"
#pragma mark - ASLayoutElementSize
NSString *NSStringFromASLayoutElementSize(ASLayoutElementSize size)
{
return [NSString stringWithFormat:
@"<ASLayoutElementSize: exact=%@, min=%@, max=%@>",
NSStringFromASLayoutSize(ASLayoutSizeMake(size.width, size.height)),
NSStringFromASLayoutSize(ASLayoutSizeMake(size.minWidth, size.minHeight)),
NSStringFromASLayoutSize(ASLayoutSizeMake(size.maxWidth, size.maxHeight))];
}
ASDISPLAYNODE_INLINE void ASLayoutElementSizeConstrain(CGFloat minVal, CGFloat exactVal, CGFloat maxVal, CGFloat *outMin, CGFloat *outMax)
{
NSCAssert(!isnan(minVal), @"minVal must not be NaN");
NSCAssert(!isnan(maxVal), @"maxVal must not be NaN");
// Avoid use of min/max primitives since they're harder to reason
// about in the presence of NaN (in exactVal)
// Follow CSS: min overrides max overrides exact.
// Begin with the min/max range
*outMin = minVal;
*outMax = maxVal;
if (maxVal <= minVal) {
// min overrides max and exactVal is irrelevant
*outMax = minVal;
return;
}
if (isnan(exactVal)) {
// no exact value, so leave as a min/max range
return;
}
if (exactVal > maxVal) {
// clip to max value
*outMin = maxVal;
} else if (exactVal < minVal) {
// clip to min value
*outMax = minVal;
} else {
// use exact value
*outMin = *outMax = exactVal;
}
}
ASSizeRange ASLayoutElementSizeResolveAutoSize(ASLayoutElementSize size, const CGSize parentSize, ASSizeRange autoASSizeRange)
{
CGSize resolvedExact = ASLayoutSizeResolveSize(ASLayoutSizeMake(size.width, size.height), parentSize, {NAN, NAN});
CGSize resolvedMin = ASLayoutSizeResolveSize(ASLayoutSizeMake(size.minWidth, size.minHeight), parentSize, autoASSizeRange.min);
CGSize resolvedMax = ASLayoutSizeResolveSize(ASLayoutSizeMake(size.maxWidth, size.maxHeight), parentSize, autoASSizeRange.max);
CGSize rangeMin, rangeMax;
ASLayoutElementSizeConstrain(resolvedMin.width, resolvedExact.width, resolvedMax.width, &rangeMin.width, &rangeMax.width);
ASLayoutElementSizeConstrain(resolvedMin.height, resolvedExact.height, resolvedMax.height, &rangeMin.height, &rangeMax.height);
return {rangeMin, rangeMax};
}

View File

@@ -8,7 +8,7 @@
// of patent rights can be found in the PATENTS file in the same directory. // of patent rights can be found in the PATENTS file in the same directory.
// //
#import <AsyncDisplayKit/ASDimension.h> #import <AsyncDisplayKit/ASDimensionInternal.h>
#import <AsyncDisplayKit/ASStackLayoutDefines.h> #import <AsyncDisplayKit/ASStackLayoutDefines.h>
#import <AsyncDisplayKit/ASStackLayoutElement.h> #import <AsyncDisplayKit/ASStackLayoutElement.h>
#import <AsyncDisplayKit/ASAbsoluteLayoutElement.h> #import <AsyncDisplayKit/ASAbsoluteLayoutElement.h>
@@ -315,7 +315,7 @@ extern NSString * const ASLayoutElementStyleLayoutPositionProperty;
@protocol ASLayoutElementStylability @protocol ASLayoutElementStylability
- (instancetype)styledWithBlock:(__attribute__((noescape)) void (^)(__kindof ASLayoutElementStyle *style))styleBlock; - (instancetype)styledWithBlock:(AS_NOESCAPE void (^)(__kindof ASLayoutElementStyle *style))styleBlock;
@end @end

View File

@@ -133,7 +133,7 @@ NS_ASSUME_NONNULL_BEGIN
* Boolean parameter that contains the value YES if all of the related animations completed successfully or * Boolean parameter that contains the value YES if all of the related animations completed successfully or
* NO if they were interrupted. This parameter may be nil. If supplied, the block is run on the main thread. * NO if they were interrupted. This parameter may be nil. If supplied, the block is run on the main thread.
*/ */
- (void)performBatchAnimated:(BOOL)animated updates:(nullable __attribute((noescape)) void (^)())updates completion:(nullable void (^)(BOOL finished))completion; - (void)performBatchAnimated:(BOOL)animated updates:(nullable AS_NOESCAPE void (^)())updates completion:(nullable void (^)(BOOL finished))completion;
/** /**
* Perform a batch of updates asynchronously. This method must be called from the main thread. * Perform a batch of updates asynchronously. This method must be called from the main thread.
@@ -144,7 +144,7 @@ NS_ASSUME_NONNULL_BEGIN
* Boolean parameter that contains the value YES if all of the related animations completed successfully or * Boolean parameter that contains the value YES if all of the related animations completed successfully or
* NO if they were interrupted. This parameter may be nil. If supplied, the block is run on the main thread. * NO if they were interrupted. This parameter may be nil. If supplied, the block is run on the main thread.
*/ */
- (void)performBatchUpdates:(nullable __attribute((noescape)) void (^)())updates completion:(nullable void (^)(BOOL finished))completion; - (void)performBatchUpdates:(nullable AS_NOESCAPE void (^)())updates completion:(nullable void (^)(BOOL finished))completion;
/** /**
* Reload everything from scratch, destroying the working range and all cached nodes. * Reload everything from scratch, destroying the working range and all cached nodes.

View File

@@ -39,14 +39,12 @@ static CGFloat crossOffset(const ASStackLayoutSpecStyle &style,
* @param style The layout style of the overall stack layout * @param style The layout style of the overall stack layout
* @param firstChildOffset Offset of the first child * @param firstChildOffset Offset of the first child
* @param extraSpacing Spacing between children, in addition to spacing set to the stack's layout style * @param extraSpacing Spacing between children, in addition to spacing set to the stack's layout style
* @param lastChildOffset Offset of the last child
* @param unpositionedLayout Unpositioned children of the stack * @param unpositionedLayout Unpositioned children of the stack
* @param constrainedSize Constrained size of the stack * @param constrainedSize Constrained size of the stack
*/ */
static ASStackPositionedLayout stackedLayout(const ASStackLayoutSpecStyle &style, static ASStackPositionedLayout stackedLayout(const ASStackLayoutSpecStyle &style,
const CGFloat firstChildOffset, const CGFloat firstChildOffset,
const CGFloat extraSpacing, const CGFloat extraSpacing,
const CGFloat lastChildOffset,
const ASStackUnpositionedLayout &unpositionedLayout, const ASStackUnpositionedLayout &unpositionedLayout,
const ASSizeRange &constrainedSize) const ASSizeRange &constrainedSize)
{ {
@@ -63,14 +61,11 @@ static ASStackPositionedLayout stackedLayout(const ASStackLayoutSpecStyle &style
CGPoint p = directionPoint(style.direction, firstChildOffset, 0); CGPoint p = directionPoint(style.direction, firstChildOffset, 0);
BOOL first = YES; BOOL first = YES;
const auto lastChild = unpositionedLayout.items.back().child;
CGFloat offset = 0;
// Adjust the position of the unpositioned layouts to be positioned // Adjust the position of the unpositioned layouts to be positioned
const auto stackedChildren = unpositionedLayout.items; const auto stackedChildren = unpositionedLayout.items;
for (const auto &l : stackedChildren) { for (const auto &l : stackedChildren) {
offset = (l.child.element == lastChild.element) ? lastChildOffset : 0; p = p + directionPoint(style.direction, l.child.style.spacingBefore, 0);
p = p + directionPoint(style.direction, l.child.style.spacingBefore + offset, 0);
if (!first) { if (!first) {
p = p + directionPoint(style.direction, style.spacing + extraSpacing, 0); p = p + directionPoint(style.direction, style.spacing + extraSpacing, 0);
} }
@@ -83,14 +78,6 @@ static ASStackPositionedLayout stackedLayout(const ASStackLayoutSpecStyle &style
return {std::move(stackedChildren), crossSize}; return {std::move(stackedChildren), crossSize};
} }
static ASStackPositionedLayout stackedLayout(const ASStackLayoutSpecStyle &style,
const CGFloat firstChildOffset,
const ASStackUnpositionedLayout &unpositionedLayout,
const ASSizeRange &constrainedSize)
{
return stackedLayout(style, firstChildOffset, 0, 0, unpositionedLayout, constrainedSize);
}
ASStackPositionedLayout ASStackPositionedLayout::compute(const ASStackUnpositionedLayout &unpositionedLayout, ASStackPositionedLayout ASStackPositionedLayout::compute(const ASStackUnpositionedLayout &unpositionedLayout,
const ASStackLayoutSpecStyle &style, const ASStackLayoutSpecStyle &style,
const ASSizeRange &constrainedSize) const ASSizeRange &constrainedSize)
@@ -109,23 +96,23 @@ ASStackPositionedLayout ASStackPositionedLayout::compute(const ASStackUnposition
switch (justifyContent) { switch (justifyContent) {
case ASStackLayoutJustifyContentStart: { case ASStackLayoutJustifyContentStart: {
return stackedLayout(style, 0, unpositionedLayout, constrainedSize); return stackedLayout(style, 0, 0, unpositionedLayout, constrainedSize);
} }
case ASStackLayoutJustifyContentCenter: { case ASStackLayoutJustifyContentCenter: {
return stackedLayout(style, std::floor(violation / 2), unpositionedLayout, constrainedSize); return stackedLayout(style, std::floor(violation / 2), 0, unpositionedLayout, constrainedSize);
} }
case ASStackLayoutJustifyContentEnd: { case ASStackLayoutJustifyContentEnd: {
return stackedLayout(style, violation, unpositionedLayout, constrainedSize); return stackedLayout(style, violation, 0, unpositionedLayout, constrainedSize);
} }
case ASStackLayoutJustifyContentSpaceBetween: { case ASStackLayoutJustifyContentSpaceBetween: {
// Spacing between the items, no spaces at the edges, evenly distributed // Spacing between the items, no spaces at the edges, evenly distributed
const auto numOfSpacings = numOfItems - 1; const auto numOfSpacings = numOfItems - 1;
return stackedLayout(style, 0, violation / numOfSpacings, 0, unpositionedLayout, constrainedSize); return stackedLayout(style, 0, violation / numOfSpacings, unpositionedLayout, constrainedSize);
} }
case ASStackLayoutJustifyContentSpaceAround: { case ASStackLayoutJustifyContentSpaceAround: {
// Spacing between items are twice the spacing on the edges // Spacing between items are twice the spacing on the edges
CGFloat spacingUnit = violation / (numOfItems * 2); CGFloat spacingUnit = violation / (numOfItems * 2);
return stackedLayout(style, spacingUnit, spacingUnit * 2, 0, unpositionedLayout, constrainedSize); return stackedLayout(style, spacingUnit, spacingUnit * 2, unpositionedLayout, constrainedSize);
} }
} }
} }

View File

@@ -44,6 +44,10 @@ NS_ASSUME_NONNULL_BEGIN
*/ */
- (CGSize)sizeForConstrainedWidth:(CGFloat)constrainedWidth; - (CGSize)sizeForConstrainedWidth:(CGFloat)constrainedWidth;
- (CGSize)sizeForConstrainedWidth:(CGFloat)constrainedWidth
forMaxNumberOfLines:(NSInteger)numberOfLines;
@property (nonatomic, strong, readonly) NSTextStorage *textStorage; @property (nonatomic, strong, readonly) NSTextStorage *textStorage;
@property (nonatomic, strong, readonly) NSTextContainer *textContainer; @property (nonatomic, strong, readonly) NSTextContainer *textContainer;
@property (nonatomic, strong, readonly) NSLayoutManager *layoutManager; @property (nonatomic, strong, readonly) NSLayoutManager *layoutManager;

View File

@@ -10,6 +10,8 @@
#import "ASTextKitComponents.h" #import "ASTextKitComponents.h"
#import <tgmath.h>
@interface ASTextKitComponents () @interface ASTextKitComponents ()
// read-write redeclarations // read-write redeclarations
@@ -66,4 +68,55 @@
return textSize; return textSize;
} }
- (CGSize)sizeForConstrainedWidth:(CGFloat)constrainedWidth
forMaxNumberOfLines:(NSInteger)maxNumberOfLines
{
if (maxNumberOfLines == 0) {
return [self sizeForConstrainedWidth:constrainedWidth];
}
ASTextKitComponents *components = self;
// Always use temporary stack in case of threading issues
components = [ASTextKitComponents componentsWithAttributedSeedString:components.textStorage textContainerSize:CGSizeMake(constrainedWidth, CGFLOAT_MAX)];
// Force glyph generation and layout, which may not have happened yet (and isn't triggered by - usedRectForTextContainer:).
[components.layoutManager ensureLayoutForTextContainer:components.textContainer];
CGFloat width = [components.layoutManager usedRectForTextContainer:components.textContainer].size.width;
// Calculate height based on line fragments
// Based on calculating number of lines from: http://asciiwwdc.com/2013/sessions/220
NSRange glyphRange, lineRange = NSMakeRange(0, 0);
CGRect rect = CGRectZero;
CGFloat height = 0;
CGFloat lastOriginY = -1.0;
NSUInteger numberOfLines = 0;
glyphRange = [components.layoutManager glyphRangeForTextContainer:components.textContainer];
while (lineRange.location < NSMaxRange(glyphRange)) {
rect = [components.layoutManager lineFragmentRectForGlyphAtIndex:lineRange.location
effectiveRange:&lineRange];
if (CGRectGetMinY(rect) > lastOriginY) {
++numberOfLines;
if (numberOfLines == maxNumberOfLines) {
height = rect.origin.y + rect.size.height;
break;
}
}
lastOriginY = CGRectGetMinY(rect);
lineRange.location = NSMaxRange(lineRange);
}
CGFloat fragmentHeight = rect.origin.y + rect.size.height;
CGFloat finalHeight = std::ceil(std::fmax(height, fragmentHeight));
CGSize size = CGSizeMake(width, finalHeight);
return size;
}
@end @end

View File

@@ -70,6 +70,27 @@ NS_ASSUME_NONNULL_BEGIN
roundedCorners:(UIRectCorner)roundedCorners roundedCorners:(UIRectCorner)roundedCorners
scale:(CGFloat)scale AS_WARN_UNUSED_RESULT; scale:(CGFloat)scale AS_WARN_UNUSED_RESULT;
/**
* A version of imageNamed that caches results because loading an image is expensive.
* Calling with the same name value will usually return the same object. A UIImage,
* after creation, is immutable and thread-safe so it's fine to share these objects across multiple threads.
*
* @param imageName The name of the image to load
* @return The loaded image or nil
*/
+ (UIImage *)as_imageNamed:(NSString *)imageName;
/**
* A version of imageNamed that caches results because loading an image is expensive.
* Calling with the same name value will usually return the same object. A UIImage,
* after creation, is immutable and thread-safe so it's fine to share these objects across multiple threads.
*
* @param imageName The name of the image to load
* @param traitCollection The traits associated with the intended environment for the image.
* @return The loaded image or nil
*/
+ (UIImage *)as_imageNamed:(NSString *)imageName compatibleWithTraitCollection:(nullable UITraitCollection *)traitCollection;
@end @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END

View File

@@ -122,4 +122,47 @@
return result; return result;
} }
#pragma mark - as_imageNamed
UIImage *cachedImageNamed(NSString *imageName, UITraitCollection *traitCollection)
{
static NSCache *imageCache = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// Because NSCache responds to memory warnings, we do not need an explicit limit.
// all of these objects contain compressed image data and are relatively small
// compared to the backing stores of text and image views.
imageCache = [[NSCache alloc] init];
});
UIImage *image = nil;
if ([imageName length] > 0) {
NSString *imageKey = imageName;
if (traitCollection) {
char imageKeyBuffer[256];
snprintf(imageKeyBuffer, sizeof(imageKeyBuffer), "%s|%ld|%ld", imageName.UTF8String, (long)traitCollection.horizontalSizeClass, (long)traitCollection.verticalSizeClass);
imageKey = [NSString stringWithUTF8String:imageKeyBuffer];
}
image = [imageCache objectForKey:imageKey];
if (!image) {
image = [UIImage imageNamed:imageName inBundle:nil compatibleWithTraitCollection:traitCollection];
if (image) {
[imageCache setObject:image forKey:imageKey];
}
}
}
return image;
}
+ (UIImage *)as_imageNamed:(NSString *)imageName
{
return cachedImageNamed(imageName, nil);
}
+ (UIImage *)as_imageNamed:(NSString *)imageName compatibleWithTraitCollection:(UITraitCollection *)traitCollection
{
return cachedImageNamed(imageName, traitCollection);
}
@end @end

View File

@@ -94,16 +94,6 @@
}; };
} }
- (void)collectionView:(ASCollectionView *)collectionView willDisplayNode:(ASCellNode *)node forItemAtIndexPath:(NSIndexPath *)indexPath
{
ASDisplayNodeAssertNotNil(node.layoutAttributes, @"Expected layout attributes for node in %@ to be non-nil.", NSStringFromSelector(_cmd));
}
- (void)collectionView:(ASCollectionView *)collectionView didEndDisplayingNode:(ASCellNode *)node forItemAtIndexPath:(NSIndexPath *)indexPath
{
ASDisplayNodeAssertNotNil(node.layoutAttributes, @"Expected layout attributes for node in %@ to be non-nil.", NSStringFromSelector(_cmd));
}
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
return _itemCounts.size(); return _itemCounts.size();
} }
@@ -890,7 +880,6 @@
ASCollectionViewTestController *testController = [[ASCollectionViewTestController alloc] initWithNibName:nil bundle:nil]; ASCollectionViewTestController *testController = [[ASCollectionViewTestController alloc] initWithNibName:nil bundle:nil];
window.rootViewController = testController; window.rootViewController = testController;
// Start with 1 item so that our content does not fill bounds.
testController.asyncDelegate->_itemCounts = {}; testController.asyncDelegate->_itemCounts = {};
[window makeKeyAndVisible]; [window makeKeyAndVisible];
[window layoutIfNeeded]; [window layoutIfNeeded];
@@ -1002,4 +991,65 @@
XCTAssertLessThanOrEqual(contentHeight, requiredContentHeight + 2 * itemHeight, @"Loaded too much content."); XCTAssertLessThanOrEqual(contentHeight, requiredContentHeight + 2 * itemHeight, @"Loaded too much content.");
} }
- (void)testInitialRangeBounds
{
UIWindow *window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
ASCollectionViewTestController *testController = [[ASCollectionViewTestController alloc] initWithNibName:nil bundle:nil];
ASCollectionNode *cn = testController.collectionNode;
[cn setTuningParameters:{ .leadingBufferScreenfuls = 2, .trailingBufferScreenfuls = 0 } forRangeMode:ASLayoutRangeModeMinimum rangeType:ASLayoutRangeTypePreload];
window.rootViewController = testController;
[window makeKeyAndVisible];
[window layoutIfNeeded];
[cn waitUntilAllUpdatesAreCommitted];
CGRect preloadBounds = ({
CGRect r = CGRectNull;
for (NSInteger s = 0; s < cn.numberOfSections; s++) {
NSInteger c = [cn numberOfItemsInSection:s];
for (NSInteger i = 0; i < c; i++) {
NSIndexPath *ip = [NSIndexPath indexPathForItem:i inSection:s];
ASCellNode *node = [cn nodeForItemAtIndexPath:ip];
if (node.inPreloadState) {
CGRect frame = [cn.view layoutAttributesForItemAtIndexPath:ip].frame;
r = CGRectUnion(r, frame);
}
}
}
r;
});
CGFloat expectedHeight = cn.bounds.size.height * 3;
XCTAssertEqualWithAccuracy(CGRectGetHeight(preloadBounds), expectedHeight, 100);
XCTAssertEqual([[cn valueForKeyPath:@"rangeController.currentRangeMode"] integerValue], ASLayoutRangeModeMinimum, @"Expected range mode to be minimum before scrolling begins.");
}
/**
* This tests an issue where, since subnode insertions aren't applied until the UIKit layout pass,
* which we trigger during the display phase, subnodes like network image nodes are not preloading
* until this layout pass happens which is too late.
*/
- (void)DISABLED_testThatAutomaticallyManagedSubnodesGetPreloadCallBeforeDisplay
{
UIWindow *window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
ASCollectionViewTestController *testController = [[ASCollectionViewTestController alloc] initWithNibName:nil bundle:nil];
window.rootViewController = testController;
ASCollectionNode *cn = testController.collectionNode;
__block NSInteger itemCount = 100;
testController.asyncDelegate->_itemCounts = {itemCount};
[window makeKeyAndVisible];
[window layoutIfNeeded];
[cn waitUntilAllUpdatesAreCommitted];
for (NSInteger i = 0; i < itemCount; i++) {
ASTextCellNodeWithSetSelectedCounter *node = [cn nodeForItemAtIndexPath:[NSIndexPath indexPathForItem:i inSection:0]];
XCTAssert(node.automaticallyManagesSubnodes, @"Expected test cell node to use automatic subnode management. Can modify the test with a different class if needed.");
ASDisplayNode *subnode = node.textNode;
XCTAssertEqualObjects(NSStringFromASInterfaceState(subnode.interfaceState), NSStringFromASInterfaceState(node.interfaceState), @"Subtree interface state should match cell node interface state for ASM nodes.");
XCTAssert(node.inDisplayState || !node.nodeLoaded, @"Only nodes in the display range should be loaded.");
}
}
@end @end

View File

@@ -2283,4 +2283,25 @@ static bool stringContainsPointer(NSString *description, id p) {
XCTAssertLessThan(underlayIndex, overlayIndex); XCTAssertLessThan(underlayIndex, overlayIndex);
} }
- (void)testThatConvertPointGoesToWindowWhenPassedNil
{
UIWindow *window = [[UIWindow alloc] initWithFrame:CGRectMake(0, 0, 20, 20)];
ASDisplayNode *node = [[ASDisplayNode alloc] init];
node.frame = CGRectMake(10, 10, 10, 10);
[window addSubnode:node];
CGPoint expectedOrigin = CGPointMake(10, 10);
ASXCTAssertEqualPoints([node convertPoint:node.bounds.origin toNode:nil], expectedOrigin);
}
- (void)testThatConvertPointGoesToWindowWhenPassedNil_layerBacked
{
UIWindow *window = [[UIWindow alloc] initWithFrame:CGRectMake(0, 0, 20, 20)];
ASDisplayNode *node = [[ASDisplayNode alloc] init];
node.layerBacked = YES;
node.frame = CGRectMake(10, 10, 10, 10);
[window addSubnode:node];
CGPoint expectedOrigin = CGPointMake(10, 10);
ASXCTAssertEqualPoints([node convertPoint:node.bounds.origin toNode:nil], expectedOrigin);
}
@end @end

View File

@@ -8,6 +8,7 @@
// of patent rights can be found in the PATENTS file in the same directory. // of patent rights can be found in the PATENTS file in the same directory.
// //
#import <XCTest/XCTest.h>
#import "ASXCTExtensions.h" #import "ASXCTExtensions.h"
#import "ASLayoutElement.h" #import "ASLayoutElement.h"

View File

@@ -93,7 +93,8 @@
return testController; return testController;
} }
- (void)testThatRootPagerNodeDoesGetTheRightInsetWhilePoppingBack // Disabled due to flakiness https://github.com/facebook/AsyncDisplayKit/issues/2818
- (void)DISABLED_testThatRootPagerNodeDoesGetTheRightInsetWhilePoppingBack
{ {
UICollectionViewCell *cell = nil; UICollectionViewCell *cell = nil;

View File

@@ -8,6 +8,7 @@
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#import <XCTest/XCTestAssertionsImpl.h> #import <XCTest/XCTestAssertionsImpl.h>
#import <AsyncDisplayKit/ASBaseDefines.h>
#define ASXCTAssertRelativePerformanceInRange(test, caseName, min, max) \ #define ASXCTAssertRelativePerformanceInRange(test, caseName, min, max) \
_XCTPrimitiveAssertLessThanOrEqual(self, test.results[caseName].relativePerformance, @#caseName, max, @#max);\ _XCTPrimitiveAssertLessThanOrEqual(self, test.results[caseName].relativePerformance, @#caseName, max, @#max);\
@@ -32,7 +33,7 @@ typedef void (^ASTestPerformanceCaseBlock)(NSUInteger i, dispatch_block_t startM
/** /**
* The first case you add here will be considered the reference case. * The first case you add here will be considered the reference case.
*/ */
- (void)addCaseWithName:(NSString *)caseName block:(__attribute((noescape)) ASTestPerformanceCaseBlock)block; - (void)addCaseWithName:(NSString *)caseName block:(AS_NOESCAPE ASTestPerformanceCaseBlock)block;
@property (nonatomic, copy, readonly) NSDictionary<NSString *, ASPerformanceTestResult *> *results; @property (nonatomic, copy, readonly) NSDictionary<NSString *, ASPerformanceTestResult *> *results;

View File

@@ -74,7 +74,7 @@
return YES; return YES;
} }
- (void)addCaseWithName:(NSString *)caseName block:(__attribute((noescape)) ASTestPerformanceCaseBlock)block - (void)addCaseWithName:(NSString *)caseName block:(AS_NOESCAPE ASTestPerformanceCaseBlock)block
{ {
ASDisplayNodeAssert(_results[caseName] == nil, @"Already have a case named %@", caseName); ASDisplayNodeAssert(_results[caseName] == nil, @"Already have a case named %@", caseName);
ASPerformanceTestResult *result = [[ASPerformanceTestResult alloc] init]; ASPerformanceTestResult *result = [[ASPerformanceTestResult alloc] init];
@@ -91,7 +91,7 @@
} }
/// Returns total work time /// Returns total work time
- (CFTimeInterval)_testPerformanceForCaseWithBlock:(__attribute((noescape)) ASTestPerformanceCaseBlock)block - (CFTimeInterval)_testPerformanceForCaseWithBlock:(AS_NOESCAPE ASTestPerformanceCaseBlock)block
{ {
__block CFTimeInterval time = 0; __block CFTimeInterval time = 0;
for (NSInteger i = 0; i < _iterationCount; i++) { for (NSInteger i = 0; i < _iterationCount; i++) {

View File

@@ -19,6 +19,7 @@
#import "ASTableView+Undeprecated.h" #import "ASTableView+Undeprecated.h"
#import <JGMethodSwizzler/JGMethodSwizzler.h> #import <JGMethodSwizzler/JGMethodSwizzler.h>
#import "ASXCTExtensions.h" #import "ASXCTExtensions.h"
#import "ASInternalHelpers.h"
#define NumberOfSections 10 #define NumberOfSections 10
#define NumberOfReloadIterations 50 #define NumberOfReloadIterations 50
@@ -234,7 +235,7 @@
- (void)testConstrainedSizeForRowAtIndexPath - (void)testConstrainedSizeForRowAtIndexPath
{ {
// Initial width of the table view is non-zero and all nodes are measured with this size. // Initial width of the table view is non-zero and all nodes are measured with this size.
// Any subsequence size change must trigger a relayout. // Any subsequent size change must trigger a relayout.
// Width and height are swapped so that a later size change will simulate a rotation // Width and height are swapped so that a later size change will simulate a rotation
ASTestTableView *tableView = [[ASTestTableView alloc] __initWithFrame:CGRectMake(0, 0, 100, 400) ASTestTableView *tableView = [[ASTestTableView alloc] __initWithFrame:CGRectMake(0, 0, 100, 400)
style:UITableViewStylePlain]; style:UITableViewStylePlain];
@@ -249,12 +250,13 @@
[tableView setNeedsLayout]; [tableView setNeedsLayout];
[tableView layoutIfNeeded]; [tableView layoutIfNeeded];
CGFloat separatorHeight = 1.0 / ASScreenScale();
for (int section = 0; section < NumberOfSections; section++) { for (int section = 0; section < NumberOfSections; section++) {
for (int row = 0; row < [tableView numberOfRowsInSection:section]; row++) { for (int row = 0; row < [tableView numberOfRowsInSection:section]; row++) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section]; NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section];
CGRect rect = [tableView rectForRowAtIndexPath:indexPath]; CGRect rect = [tableView rectForRowAtIndexPath:indexPath];
XCTAssertEqual(rect.size.width, 100); // specified width should be ignored for table XCTAssertEqual(rect.size.width, 100); // specified width should be ignored for table
XCTAssertEqual(rect.size.height, 42); XCTAssertEqual(rect.size.height, 42 + separatorHeight);
} }
} }
} }
@@ -775,7 +777,7 @@
[window layoutIfNeeded]; [window layoutIfNeeded];
[node waitUntilAllUpdatesAreCommitted]; [node waitUntilAllUpdatesAreCommitted];
XCTAssertEqual(node.view.numberOfSections, NumberOfSections); XCTAssertEqual(node.view.numberOfSections, NumberOfSections);
ASXCTAssertEqualRects(CGRectMake(0, 32, 375, 43.5), [node rectForRowAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]], @"This text requires very specific geometry. The rect for the first row should match up."); ASXCTAssertEqualRects(CGRectMake(0, 32, 375, 44), [node rectForRowAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]], @"This text requires very specific geometry. The rect for the first row should match up.");
__unused XCTestExpectation *e = [self expectationWithDescription:@"Did a bunch of rounds of updates."]; __unused XCTestExpectation *e = [self expectationWithDescription:@"Did a bunch of rounds of updates."];
NSInteger totalCount = 20; NSInteger totalCount = 20;

View File

@@ -6,7 +6,7 @@
// Copyright © 2016 Facebook. All rights reserved. // Copyright © 2016 Facebook. All rights reserved.
// //
@import XCTest; #import <XCTest/XCTest.h>
#import <AsyncDisplayKit/AsyncDisplayKit.h> #import <AsyncDisplayKit/AsyncDisplayKit.h>
#import "ASTableViewInternal.h" #import "ASTableViewInternal.h"
#import "ASTableView+Undeprecated.h" #import "ASTableView+Undeprecated.h"

View File

@@ -25,12 +25,6 @@
} }
@end @end
@interface ASNetworkImageNode () {
@public __weak id<ASNetworkImageNodeDelegate> _delegate;
}
@end
@interface ASVideoNode () { @interface ASVideoNode () {
ASDisplayNode *_playerNode; ASDisplayNode *_playerNode;
AVPlayer *_player; AVPlayer *_player;
@@ -419,11 +413,9 @@
XCTAssertTrue([_videoNode.delegate conformsToProtocol:@protocol(ASVideoNodeDelegate)]); XCTAssertTrue([_videoNode.delegate conformsToProtocol:@protocol(ASVideoNodeDelegate)]);
XCTAssertTrue([_videoNode.delegate conformsToProtocol:@protocol(ASNetworkImageNodeDelegate)]); XCTAssertTrue([_videoNode.delegate conformsToProtocol:@protocol(ASNetworkImageNodeDelegate)]);
XCTAssertTrue([((ASNetworkImageNode*)_videoNode).delegate conformsToProtocol:@protocol(ASNetworkImageNodeDelegate)]); XCTAssertTrue([((ASNetworkImageNode*)_videoNode).delegate conformsToProtocol:@protocol(ASNetworkImageNodeDelegate)]);
XCTAssertTrue([((ASNetworkImageNode*)_videoNode)->_delegate conformsToProtocol:@protocol(ASNetworkImageNodeDelegate)]);
XCTAssertEqual(_videoNode.delegate, self); XCTAssertEqual(_videoNode.delegate, self);
XCTAssertEqual(((ASNetworkImageNode*)_videoNode).delegate, self); XCTAssertEqual(((ASNetworkImageNode*)_videoNode).delegate, self);
XCTAssertEqual(((ASNetworkImageNode*)_videoNode)->_delegate, self);
} }
@end @end

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

204
BUCK Executable file
View File

@@ -0,0 +1,204 @@
#####################################
# Defines
#####################################
COMMON_PREPROCESSOR_FLAGS = [
'-fobjc-arc',
'-DDEBUG=1',
]
COMMON_LANG_PREPROCESSOR_FLAGS = {
'C': ['-std=gnu99'],
'CXX': ['-std=c++11', '-stdlib=libc++'],
'OBJCXX': ['-std=c++11', '-stdlib=libc++'],
}
COMMON_LINKER_FLAGS = ['-ObjC++']
ASYNCDISPLAYKIT_EXPORTED_HEADERS = glob([
'AsyncDisplayKit/*.h',
'AsyncDisplayKit/Details/**/*.h',
'AsyncDisplayKit/Layout/*.h',
'Base/*.h',
'AsyncDisplayKit/Debug/ASLayoutElementInspectorNode.h',
# Most TextKit components are not public because the C++ content
# in the headers will cause build errors when using
# `use_frameworks!` on 0.39.0 & Swift 2.1.
# See https://github.com/facebook/AsyncDisplayKit/issues/1153
'AsyncDisplayKit/TextKit/ASTextNodeTypes.h',
'AsyncDisplayKit/TextKit/ASTextKitComponents.h'
])
ASYNCDISPLAYKIT_PRIVATE_HEADERS = glob([
'AsyncDisplayKit/**/*.h'
],
excludes = ASYNCDISPLAYKIT_EXPORTED_HEADERS,
)
def asyncdisplaykit_library(
name,
additional_preprocessor_flags = [],
deps = [],
additional_frameworks = []):
apple_library(
name = name,
prefix_header = 'AsyncDisplayKit/AsyncDisplayKit-Prefix.pch',
header_path_prefix = 'AsyncDisplayKit',
exported_headers = ASYNCDISPLAYKIT_EXPORTED_HEADERS,
headers = ASYNCDISPLAYKIT_PRIVATE_HEADERS,
srcs = glob([
'AsyncDisplayKit/**/*.m',
'AsyncDisplayKit/**/*.mm',
'Base/*.m'
]),
preprocessor_flags = COMMON_PREPROCESSOR_FLAGS + additional_preprocessor_flags,
lang_preprocessor_flags = COMMON_LANG_PREPROCESSOR_FLAGS,
linker_flags = COMMON_LINKER_FLAGS + [
'-weak_framework',
'Photos',
'-weak_framework',
'MapKit',
],
deps = deps,
frameworks = [
'$SDKROOT/System/Library/Frameworks/Foundation.framework',
'$SDKROOT/System/Library/Frameworks/UIKit.framework',
'$SDKROOT/System/Library/Frameworks/AssetsLibrary.framework',
'$SDKROOT/System/Library/Frameworks/QuartzCore.framework',
'$SDKROOT/System/Library/Frameworks/CoreMedia.framework',
'$SDKROOT/System/Library/Frameworks/CoreText.framework',
'$SDKROOT/System/Library/Frameworks/CoreGraphics.framework',
'$SDKROOT/System/Library/Frameworks/CoreLocation.framework',
'$SDKROOT/System/Library/Frameworks/AVFoundation.framework',
] + additional_frameworks,
visibility = ['PUBLIC'],
)
#####################################
# AsyncDisplayKit targets
#####################################
asyncdisplaykit_library(
name = 'AsyncDisplayKit-Core',
)
# (Default) AsyncDisplayKit and AsyncDisplayKit-PINRemoteImage targets are basically the same library with different names
for name in ['AsyncDisplayKit', 'AsyncDisplayKit-PINRemoteImage']:
asyncdisplaykit_library(
name = name,
additional_preprocessor_flags = ['-DPIN_REMOTE_IMAGE=1'],
deps = [
'//Pods/PINRemoteImage:PINRemoteImage-PINCache',
],
additional_frameworks = [
'$SDKROOT/System/Library/Frameworks/MobileCoreServices.framework',
]
)
#####################################
# Test Host
# TODO: Split to smaller BUCK files and parse in parallel
#####################################
apple_resource(
name = 'TestHostResources',
files = ['Default-568h@2x.png'],
dirs = [],
)
apple_bundle(
name = 'TestHost',
binary = ':TestHostBinary',
extension = 'app',
info_plist = 'AsyncDisplayKitTestHost/Info.plist',
info_plist_substitutions = {
'PRODUCT_BUNDLE_IDENTIFIER': 'com.facebook.AsyncDisplayKitTestHost',
},
tests = [':Tests'],
)
apple_binary(
name = 'TestHostBinary',
headers = glob(['AsyncDisplayKitTestHost/*.h']),
srcs = glob([
'AsyncDisplayKitTestHost/*.m',
'AsyncDisplayKitTestHost/*.mm',
]),
lang_preprocessor_flags = COMMON_LANG_PREPROCESSOR_FLAGS,
linker_flags = COMMON_LINKER_FLAGS,
deps = [
':TestHostResources',
':AsyncDisplayKit-Core',
],
frameworks = [
'$SDKROOT/System/Library/Frameworks/Photos.framework',
'$SDKROOT/System/Library/Frameworks/MapKit.framework',
],
)
apple_package(
name = 'TestHostPackage',
bundle = ':TestHost',
)
#####################################
# Tests
#####################################
apple_resource(
name = 'TestsResources',
files = ['AsyncDisplayKitTests/en.lproj/InfoPlist.strings'],
dirs = ['AsyncDisplayKitTests/TestResources'],
)
apple_resource(
name = 'SnapshotTestsResources',
files = [],
dirs = [
'AsyncDisplayKitTests/ReferenceImages_32',
'AsyncDisplayKitTests/ReferenceImages_64',
'AsyncDisplayKitTests/ReferenceImages_iOS_10',
],
)
apple_test(
name = 'Tests',
test_host_app = ':TestHost',
info_plist = 'AsyncDisplayKitTests/AsyncDisplayKitTests-Info.plist',
info_plist_substitutions = {
'PRODUCT_BUNDLE_IDENTIFIER': 'com.facebook.AsyncDisplayKitTests',
},
prefix_header = 'AsyncDisplayKitTests/AsyncDisplayKitTests-Prefix.pch',
# Expose all ASDK headers to tests
headers = ASYNCDISPLAYKIT_EXPORTED_HEADERS + ASYNCDISPLAYKIT_PRIVATE_HEADERS + glob([
'AsyncDisplayKitTests/*.h',
]),
srcs = glob([
'AsyncDisplayKitTests/*.m',
'AsyncDisplayKitTests/*.mm'
],
# ASTextNodePerformanceTests are excluded (#2173)
excludes = ['AsyncDisplayKitTests/ASTextNodePerformanceTests.m*']
),
preprocessor_flags = COMMON_PREPROCESSOR_FLAGS + [
'-Wno-implicit-function-declaration',
],
lang_preprocessor_flags = COMMON_LANG_PREPROCESSOR_FLAGS,
linker_flags = COMMON_LINKER_FLAGS,
deps = [
':TestsResources',
':SnapshotTestsResources',
'//Pods/OCMock:OCMock',
'//Pods/FBSnapshotTestCase:FBSnapshotTestCase',
'//Pods/JGMethodSwizzler:JGMethodSwizzler',
],
frameworks = [
'$SDKROOT/System/Library/Frameworks/Foundation.framework',
'$SDKROOT/System/Library/Frameworks/UIKit.framework',
'$SDKROOT/System/Library/Frameworks/CoreMedia.framework',
'$SDKROOT/System/Library/Frameworks/CoreText.framework',
'$SDKROOT/System/Library/Frameworks/CoreGraphics.framework',
'$SDKROOT/System/Library/Frameworks/AVFoundation.framework',
'$PLATFORM_DIR/Developer/Library/Frameworks/XCTest.framework',
],
)

View File

@@ -183,5 +183,15 @@
#define ASOVERLOADABLE __attribute__((overloadable)) #define ASOVERLOADABLE __attribute__((overloadable))
#if __has_attribute(noescape)
#define AS_NOESCAPE __attribute__((noescape))
#else
#define AS_NOESCAPE
#endif
/// Ensure that class is of certain kind /// Ensure that class is of certain kind
#define ASDynamicCast(x, c) ((c *) ([x isKindOfClass:[c class]] ? x : nil)) #define ASDynamicCast(x, c) ({ \
id __val = x;\
((c *) ([__val isKindOfClass:[c class]] ? __val : nil));\
})

22
Podfile
View File

@@ -6,4 +6,26 @@ target :'AsyncDisplayKitTests' do
pod 'OCMock', '~> 2.2' pod 'OCMock', '~> 2.2'
pod 'FBSnapshotTestCase/Core', '~> 2.1' pod 'FBSnapshotTestCase/Core', '~> 2.1'
pod 'JGMethodSwizzler', :git => 'https://github.com/JonasGessner/JGMethodSwizzler', :branch => 'master' pod 'JGMethodSwizzler', :git => 'https://github.com/JonasGessner/JGMethodSwizzler', :branch => 'master'
# Only for buck build
pod 'PINRemoteImage', '3.0.0-beta.7'
#TODO CocoaPods plugin instead?
post_install do |installer|
require 'fileutils'
# Assuming we're at the root dir
buck_files_dir = 'buck-files'
if File.directory?(buck_files_dir)
installer.pod_targets.flat_map do |pod_target|
pod_name = pod_target.pod_name
# Copy the file at buck-files/BUCK_pod_name to Pods/pod_name/BUCK,
# override existing file if needed
buck_file = buck_files_dir + '/BUCK_' + pod_name
if File.file?(buck_file)
FileUtils.cp(buck_file, 'Pods/' + pod_name + '/BUCK', :preserve => false)
end
end
end
end
end end

View File

@@ -1,7 +1,7 @@
![AsyncDisplayKit](https://github.com/AsyncDisplayKit/Documentation/raw/master/docs/static/images/logo.png) ![AsyncDisplayKit](https://github.com/AsyncDisplayKit/Documentation/raw/master/docs/static/images/logo.png)
[![Apps Using](https://img.shields.io/badge/Apps%20Using%20ASDK-%3E4,974-28B9FE.svg)](http://cocoapods.org/pods/AsyncDisplayKit) [![Apps Using](https://img.shields.io/cocoapods/at/AsyncDisplayKit.svg?label=Apps%20Using%20ASDK&colorB=28B9FE)](http://cocoapods.org/pods/AsyncDisplayKit)
[![Downloads](https://img.shields.io/badge/Total%20Downloads-%3E512,000-28B9FE.svg)](http://cocoapods.org/pods/AsyncDisplayKit) [![Downloads](https://img.shields.io/cocoapods/dt/AsyncDisplayKit.svg?label=Total%20Downloads&colorB=28B9FE)](http://cocoapods.org/pods/AsyncDisplayKit)
[![Platform](https://img.shields.io/badge/platforms-iOS%20%7C%20tvOS-orange.svg)](http://AsyncDisplayKit.org) [![Platform](https://img.shields.io/badge/platforms-iOS%20%7C%20tvOS-orange.svg)](http://AsyncDisplayKit.org)
[![Languages](https://img.shields.io/badge/languages-ObjC%20%7C%20Swift-orange.svg)](http://AsyncDisplayKit.org) [![Languages](https://img.shields.io/badge/languages-ObjC%20%7C%20Swift-orange.svg)](http://AsyncDisplayKit.org)

View File

@@ -0,0 +1,12 @@
apple_library(
name = 'FBSnapshotTestCase',
exported_headers = glob(['FBSnapshotTestCase' + '/**/*.h']),
srcs = glob(['FBSnapshotTestCase' + '/**/*.m']),
frameworks = [
'$SDKROOT/System/Library/Frameworks/Foundation.framework',
'$SDKROOT/System/Library/Frameworks/UIKit.framework',
'$SDKROOT/System/Library/Frameworks/QuartzCore.framework',
'$PLATFORM_DIR/Developer/Library/Frameworks/XCTest.framework',
],
visibility = ['PUBLIC'],
)

18
buck-files/BUCK_FLAnimatedImage Executable file
View File

@@ -0,0 +1,18 @@
apple_library(
name = 'FLAnimatedImage',
exported_headers = glob(['FLAnimatedImage/*.h']),
srcs = glob(['FLAnimatedImage/*.m']),
preprocessor_flags = ['-fobjc-arc', '-Wno-deprecated-declarations'],
lang_preprocessor_flags = {
'C': ['-std=gnu99'],
'CXX': ['-std=gnu++11', '-stdlib=libc++'],
},
frameworks = [
'$SDKROOT/System/Library/Frameworks/Foundation.framework',
'$SDKROOT/System/Library/Frameworks/UIKit.framework',
'$SDKROOT/System/Library/Frameworks/ImageIO.framework',
'$SDKROOT/System/Library/Frameworks/MobileCoreServices.framework',
'$SDKROOT/System/Library/Frameworks/QuartzCore.framework',
],
visibility = ['PUBLIC'],
)

View File

@@ -0,0 +1,9 @@
apple_library(
name = 'JGMethodSwizzler',
exported_headers = ['JGMethodSwizzler' + '/JGMethodSwizzler.h'],
srcs = ['JGMethodSwizzler' + '/JGMethodSwizzler.m'],
frameworks = [
'$SDKROOT/System/Library/Frameworks/Foundation.framework',
],
visibility = ['PUBLIC'],
)

9
buck-files/BUCK_OCMock Executable file
View File

@@ -0,0 +1,9 @@
apple_library(
name = 'OCMock',
exported_headers = glob(['Source/OCMock' + '/*.h']),
srcs = glob(['Source/OCMock' + '/*.m']),
frameworks = [
'$SDKROOT/System/Library/Frameworks/Foundation.framework',
],
visibility = ['PUBLIC'],
)

23
buck-files/BUCK_PINCache Executable file
View File

@@ -0,0 +1,23 @@
apple_library(
name = 'PINCache',
exported_headers = glob(['PINCache/*.h']),
# PINDiskCache.m should be compiled with '-fobjc-arc-exceptions' (#105)
srcs =
glob(['PINCache/*.m'], excludes = ['PINCache/PINDiskCache.m']) +
[('PINCache/PINDiskCache.m', ['-fobjc-arc-exceptions'])],
preprocessor_flags = ['-fobjc-arc'],
lang_preprocessor_flags = {
'C': ['-std=gnu99'],
'CXX': ['-std=gnu++11', '-stdlib=libc++'],
},
linker_flags = [
'-weak_framework',
'UIKit',
'-weak_framework',
'AppKit',
],
frameworks = [
'$SDKROOT/System/Library/Frameworks/Foundation.framework',
],
visibility = ['PUBLIC'],
)

93
buck-files/BUCK_PINRemoteImage Executable file
View File

@@ -0,0 +1,93 @@
#####################################
# Defines
#####################################
COMMON_PREPROCESSOR_FLAGS = ['-fobjc-arc']
COMMON_LANG_PREPROCESSOR_FLAGS = {
'C': ['-std=gnu99'],
'CXX': ['-std=gnu++11', '-stdlib=libc++'],
}
FLANIMATEDIMAGE_HEADER_FILES = ['Pod/Classes/Image Categories/FLAnimatedImageView+PINRemoteImage.h']
FLANIMATEDIMAGE_SOURCE_FILES = ['Pod/Classes/Image Categories/FLAnimatedImageView+PINRemoteImage.m']
PINCACHE_HEADER_FILES = glob(['Pod/Classes/PINCache/**/*.h'])
PINCACHE_SOURCE_FILES = glob(['Pod/Classes/PINCache/**/*.m'])
#####################################
# PINRemoteImage core targets
#####################################
apple_library(
name = 'PINRemoteImage-Core',
header_path_prefix = 'PINRemoteImage',
exported_headers = glob([
'Pod/Classes/**/*.h',
],
excludes = FLANIMATEDIMAGE_HEADER_FILES + PINCACHE_HEADER_FILES
),
srcs = glob([
'Pod/Classes/**/*.m',
],
excludes = FLANIMATEDIMAGE_SOURCE_FILES + PINCACHE_SOURCE_FILES
),
preprocessor_flags = COMMON_PREPROCESSOR_FLAGS + [
'-DPIN_TARGET_IOS=(TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR || TARGET_OS_TV)',
'-DPIN_TARGET_MAC=(TARGET_OS_MAC)',
],
lang_preprocessor_flags = COMMON_LANG_PREPROCESSOR_FLAGS,
linker_flags = [
'-weak_framework',
'UIKit',
'-weak_framework',
'MobileCoreServices',
'-weak_framework',
'Cocoa',
'-weak_framework',
'CoreServices',
],
frameworks = [
'$SDKROOT/System/Library/Frameworks/ImageIO.framework',
'$SDKROOT/System/Library/Frameworks/Accelerate.framework',
],
visibility = ['PUBLIC'],
)
apple_library(
name = 'PINRemoteImage',
deps = [
':PINRemoteImage-FLAnimatedImage',
':PINRemoteImage-PINCache'
],
visibility = ['PUBLIC'],
)
#####################################
# Other PINRemoteImage targets
#####################################
apple_library(
name = 'PINRemoteImage-FLAnimatedImage',
header_path_prefix = 'PINRemoteImage',
exported_headers = FLANIMATEDIMAGE_HEADER_FILES,
srcs = FLANIMATEDIMAGE_SOURCE_FILES,
preprocessor_flags = COMMON_PREPROCESSOR_FLAGS,
deps = [
':PINRemoteImage-Core',
'//Pods/FLAnimatedImage:FLAnimatedImage'
],
visibility = ['PUBLIC'],
)
apple_library(
name = 'PINRemoteImage-PINCache',
header_path_prefix = 'PINRemoteImage',
exported_headers = PINCACHE_HEADER_FILES,
srcs = PINCACHE_SOURCE_FILES,
preprocessor_flags = COMMON_PREPROCESSOR_FLAGS,
deps = [
':PINRemoteImage-Core',
'//Pods/PINCache:PINCache'
],
visibility = ['PUBLIC'],
)
#TODO WebP variants

View File

@@ -295,7 +295,7 @@ fi
if [ "$MODE" = "cocoapods-lint" ]; then if [ "$MODE" = "cocoapods-lint" ]; then
echo "Verifying that podspec lints." echo "Verifying that podspec lints."
set -o pipefail && pod lib lint set -o pipefail && pod env && pod lib lint
trap - EXIT trap - EXIT
exit 0 exit 0
fi fi

View File

@@ -88,10 +88,11 @@ static const CGFloat kInnerPadding = 10.0f;
// kitten image, with a solid background colour serving as placeholder // kitten image, with a solid background colour serving as placeholder
_imageNode = [[ASNetworkImageNode alloc] init]; _imageNode = [[ASNetworkImageNode alloc] init];
_imageNode.backgroundColor = ASDisplayNodeDefaultPlaceholderColor();
_imageNode.URL = [NSURL URLWithString:[NSString stringWithFormat:@"https://placekitten.com/%zd/%zd", _imageNode.URL = [NSURL URLWithString:[NSString stringWithFormat:@"https://placekitten.com/%zd/%zd",
(NSInteger)roundl(_kittenSize.width), (NSInteger)roundl(_kittenSize.width),
(NSInteger)roundl(_kittenSize.height)]]; (NSInteger)roundl(_kittenSize.height)]];
_imageNode.placeholderFadeDuration = .5;
_imageNode.placeholderColor = ASDisplayNodeDefaultPlaceholderColor();
// _imageNode.contentMode = UIViewContentModeCenter; // _imageNode.contentMode = UIViewContentModeCenter;
[_imageNode addTarget:self action:@selector(toggleNodesSwap) forControlEvents:ASControlNodeEventTouchUpInside]; [_imageNode addTarget:self action:@selector(toggleNodesSwap) forControlEvents:ASControlNodeEventTouchUpInside];
[self addSubnode:_imageNode]; [self addSubnode:_imageNode];