Merge branch 'master' into pr/664

This commit is contained in:
Scott Goodson
2015-12-25 14:32:41 -08:00
48 changed files with 1223 additions and 647 deletions

Binary file not shown.

View File

@@ -128,7 +128,7 @@
242995D31B29743C00090100 /* ASBasicImageDownloaderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 242995D21B29743C00090100 /* ASBasicImageDownloaderTests.m */; }; 242995D31B29743C00090100 /* ASBasicImageDownloaderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 242995D21B29743C00090100 /* ASBasicImageDownloaderTests.m */; };
251B8EF71BBB3D690087C538 /* ASCollectionDataController.h in Headers */ = {isa = PBXBuildFile; fileRef = 251B8EF21BBB3D690087C538 /* ASCollectionDataController.h */; }; 251B8EF71BBB3D690087C538 /* ASCollectionDataController.h in Headers */ = {isa = PBXBuildFile; fileRef = 251B8EF21BBB3D690087C538 /* ASCollectionDataController.h */; };
251B8EF81BBB3D690087C538 /* ASCollectionDataController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 251B8EF31BBB3D690087C538 /* ASCollectionDataController.mm */; }; 251B8EF81BBB3D690087C538 /* ASCollectionDataController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 251B8EF31BBB3D690087C538 /* ASCollectionDataController.mm */; };
251B8EF91BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.h in Headers */ = {isa = PBXBuildFile; fileRef = 251B8EF41BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.h */; }; 251B8EF91BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.h in Headers */ = {isa = PBXBuildFile; fileRef = 251B8EF41BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.h */; settings = {ATTRIBUTES = (Public, ); }; };
251B8EFA1BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.m in Sources */ = {isa = PBXBuildFile; fileRef = 251B8EF51BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.m */; }; 251B8EFA1BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.m in Sources */ = {isa = PBXBuildFile; fileRef = 251B8EF51BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.m */; };
251B8EFB1BBB3D690087C538 /* ASDataController+Subclasses.h in Headers */ = {isa = PBXBuildFile; fileRef = 251B8EF61BBB3D690087C538 /* ASDataController+Subclasses.h */; }; 251B8EFB1BBB3D690087C538 /* ASDataController+Subclasses.h in Headers */ = {isa = PBXBuildFile; fileRef = 251B8EF61BBB3D690087C538 /* ASDataController+Subclasses.h */; };
2538B6F31BC5D2A2003CA0B4 /* ASCollectionViewFlowLayoutInspectorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 2538B6F21BC5D2A2003CA0B4 /* ASCollectionViewFlowLayoutInspectorTests.m */; }; 2538B6F31BC5D2A2003CA0B4 /* ASCollectionViewFlowLayoutInspectorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 2538B6F21BC5D2A2003CA0B4 /* ASCollectionViewFlowLayoutInspectorTests.m */; };
@@ -242,7 +242,7 @@
34EFC7771B701D2D00AD841F /* ASStackUnpositionedLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = ACF6ED491B17847A00DA7C62 /* ASStackUnpositionedLayout.h */; }; 34EFC7771B701D2D00AD841F /* ASStackUnpositionedLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = ACF6ED491B17847A00DA7C62 /* ASStackUnpositionedLayout.h */; };
34EFC7781B701D3100AD841F /* ASStackUnpositionedLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = ACF6ED4A1B17847A00DA7C62 /* ASStackUnpositionedLayout.mm */; }; 34EFC7781B701D3100AD841F /* ASStackUnpositionedLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = ACF6ED4A1B17847A00DA7C62 /* ASStackUnpositionedLayout.mm */; };
34EFC7791B701D3600AD841F /* ASLayoutSpecUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = ACF6ED451B17847A00DA7C62 /* ASLayoutSpecUtilities.h */; }; 34EFC7791B701D3600AD841F /* ASLayoutSpecUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = ACF6ED451B17847A00DA7C62 /* ASLayoutSpecUtilities.h */; };
3C9C128519E616EF00E942A0 /* ASTableViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3C9C128419E616EF00E942A0 /* ASTableViewTests.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; 3C9C128519E616EF00E942A0 /* ASTableViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3C9C128419E616EF00E942A0 /* ASTableViewTests.m */; };
430E7C8F1B4C23F100697A4C /* ASIndexPath.h in Headers */ = {isa = PBXBuildFile; fileRef = 430E7C8D1B4C23F100697A4C /* ASIndexPath.h */; settings = {ATTRIBUTES = (Public, ); }; }; 430E7C8F1B4C23F100697A4C /* ASIndexPath.h in Headers */ = {isa = PBXBuildFile; fileRef = 430E7C8D1B4C23F100697A4C /* ASIndexPath.h */; settings = {ATTRIBUTES = (Public, ); }; };
430E7C901B4C23F100697A4C /* ASIndexPath.h in Headers */ = {isa = PBXBuildFile; fileRef = 430E7C8D1B4C23F100697A4C /* ASIndexPath.h */; settings = {ATTRIBUTES = (Public, ); }; }; 430E7C901B4C23F100697A4C /* ASIndexPath.h in Headers */ = {isa = PBXBuildFile; fileRef = 430E7C8D1B4C23F100697A4C /* ASIndexPath.h */; settings = {ATTRIBUTES = (Public, ); }; };
430E7C911B4C23F100697A4C /* ASIndexPath.m in Sources */ = {isa = PBXBuildFile; fileRef = 430E7C8E1B4C23F100697A4C /* ASIndexPath.m */; }; 430E7C911B4C23F100697A4C /* ASIndexPath.m in Sources */ = {isa = PBXBuildFile; fileRef = 430E7C8E1B4C23F100697A4C /* ASIndexPath.m */; };
@@ -458,8 +458,13 @@
D785F6621A74327E00291744 /* ASScrollNode.h in Headers */ = {isa = PBXBuildFile; fileRef = D785F6601A74327E00291744 /* ASScrollNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; D785F6621A74327E00291744 /* ASScrollNode.h in Headers */ = {isa = PBXBuildFile; fileRef = D785F6601A74327E00291744 /* ASScrollNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
D785F6631A74327E00291744 /* ASScrollNode.m in Sources */ = {isa = PBXBuildFile; fileRef = D785F6611A74327E00291744 /* ASScrollNode.m */; }; D785F6631A74327E00291744 /* ASScrollNode.m in Sources */ = {isa = PBXBuildFile; fileRef = D785F6611A74327E00291744 /* ASScrollNode.m */; };
DB7121BCD50849C498C886FB /* libPods-AsyncDisplayKitTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = EFA731F0396842FF8AB635EE /* libPods-AsyncDisplayKitTests.a */; }; DB7121BCD50849C498C886FB /* libPods-AsyncDisplayKitTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = EFA731F0396842FF8AB635EE /* libPods-AsyncDisplayKitTests.a */; };
DE040EF91C2B40AC004692FF /* ASCollectionViewFlowLayoutInspector.h in Headers */ = {isa = PBXBuildFile; fileRef = 251B8EF41BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.h */; settings = {ATTRIBUTES = (Public, ); }; };
DE6EA3221C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = DE6EA3211C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h */; }; DE6EA3221C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = DE6EA3211C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h */; };
DE6EA3231C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = DE6EA3211C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h */; }; DE6EA3231C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = DE6EA3211C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h */; };
DE8BEAC11C2DF3FC00D57C12 /* ASDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = DE8BEABF1C2DF3FC00D57C12 /* ASDelegateProxy.h */; };
DE8BEAC21C2DF3FC00D57C12 /* ASDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = DE8BEABF1C2DF3FC00D57C12 /* ASDelegateProxy.h */; };
DE8BEAC31C2DF3FC00D57C12 /* ASDelegateProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = DE8BEAC01C2DF3FC00D57C12 /* ASDelegateProxy.m */; };
DE8BEAC41C2DF3FC00D57C12 /* ASDelegateProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = DE8BEAC01C2DF3FC00D57C12 /* ASDelegateProxy.m */; };
DECBD6E71BE56E1900CF4905 /* ASButtonNode.h in Headers */ = {isa = PBXBuildFile; fileRef = DECBD6E51BE56E1900CF4905 /* ASButtonNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; DECBD6E71BE56E1900CF4905 /* ASButtonNode.h in Headers */ = {isa = PBXBuildFile; fileRef = DECBD6E51BE56E1900CF4905 /* ASButtonNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
DECBD6E81BE56E1900CF4905 /* ASButtonNode.h in Headers */ = {isa = PBXBuildFile; fileRef = DECBD6E51BE56E1900CF4905 /* ASButtonNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; DECBD6E81BE56E1900CF4905 /* ASButtonNode.h in Headers */ = {isa = PBXBuildFile; fileRef = DECBD6E51BE56E1900CF4905 /* ASButtonNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
DECBD6E91BE56E1900CF4905 /* ASButtonNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = DECBD6E61BE56E1900CF4905 /* ASButtonNode.mm */; }; DECBD6E91BE56E1900CF4905 /* ASButtonNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = DECBD6E61BE56E1900CF4905 /* ASButtonNode.mm */; };
@@ -753,6 +758,8 @@
D785F6601A74327E00291744 /* ASScrollNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASScrollNode.h; sourceTree = "<group>"; }; D785F6601A74327E00291744 /* ASScrollNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASScrollNode.h; sourceTree = "<group>"; };
D785F6611A74327E00291744 /* ASScrollNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASScrollNode.m; sourceTree = "<group>"; }; D785F6611A74327E00291744 /* ASScrollNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASScrollNode.m; sourceTree = "<group>"; };
DE6EA3211C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASDisplayNode+FrameworkPrivate.h"; sourceTree = "<group>"; }; DE6EA3211C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASDisplayNode+FrameworkPrivate.h"; sourceTree = "<group>"; };
DE8BEABF1C2DF3FC00D57C12 /* ASDelegateProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASDelegateProxy.h; sourceTree = "<group>"; };
DE8BEAC01C2DF3FC00D57C12 /* ASDelegateProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASDelegateProxy.m; sourceTree = "<group>"; };
DECBD6E51BE56E1900CF4905 /* ASButtonNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASButtonNode.h; sourceTree = "<group>"; }; DECBD6E51BE56E1900CF4905 /* ASButtonNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASButtonNode.h; sourceTree = "<group>"; };
DECBD6E61BE56E1900CF4905 /* ASButtonNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASButtonNode.mm; sourceTree = "<group>"; }; DECBD6E61BE56E1900CF4905 /* ASButtonNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASButtonNode.mm; sourceTree = "<group>"; };
EFA731F0396842FF8AB635EE /* libPods-AsyncDisplayKitTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-AsyncDisplayKitTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; EFA731F0396842FF8AB635EE /* libPods-AsyncDisplayKitTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-AsyncDisplayKitTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -1148,6 +1155,8 @@
25B171EA1C12242700508A7A /* Data Controller */ = { 25B171EA1C12242700508A7A /* Data Controller */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
DE8BEABF1C2DF3FC00D57C12 /* ASDelegateProxy.h */,
DE8BEAC01C2DF3FC00D57C12 /* ASDelegateProxy.m */,
251B8EF21BBB3D690087C538 /* ASCollectionDataController.h */, 251B8EF21BBB3D690087C538 /* ASCollectionDataController.h */,
251B8EF31BBB3D690087C538 /* ASCollectionDataController.mm */, 251B8EF31BBB3D690087C538 /* ASCollectionDataController.mm */,
464052191A3F83C40061C0BA /* ASDataController.h */, 464052191A3F83C40061C0BA /* ASDataController.h */,
@@ -1263,6 +1272,7 @@
18C2ED7E1B9B7DE800F627B3 /* ASCollectionNode.h in Headers */, 18C2ED7E1B9B7DE800F627B3 /* ASCollectionNode.h in Headers */,
257754C01BEE458E00737CA5 /* ASTextNodeWordKerner.h in Headers */, 257754C01BEE458E00737CA5 /* ASTextNodeWordKerner.h in Headers */,
AC3C4A511A1139C100143C57 /* ASCollectionView.h in Headers */, AC3C4A511A1139C100143C57 /* ASCollectionView.h in Headers */,
DE8BEAC11C2DF3FC00D57C12 /* ASDelegateProxy.h in Headers */,
205F0E1D1B373A2C007741D0 /* ASCollectionViewLayoutController.h in Headers */, 205F0E1D1B373A2C007741D0 /* ASCollectionViewLayoutController.h in Headers */,
AC3C4A541A113EEC00143C57 /* ASCollectionViewProtocols.h in Headers */, AC3C4A541A113EEC00143C57 /* ASCollectionViewProtocols.h in Headers */,
058D0A49195D05CB00B7D73C /* ASControlNode+Subclasses.h in Headers */, 058D0A49195D05CB00B7D73C /* ASControlNode+Subclasses.h in Headers */,
@@ -1431,6 +1441,7 @@
34EFC7791B701D3600AD841F /* ASLayoutSpecUtilities.h in Headers */, 34EFC7791B701D3600AD841F /* ASLayoutSpecUtilities.h in Headers */,
B350625C1B010F070018CF92 /* ASLog.h in Headers */, B350625C1B010F070018CF92 /* ASLog.h in Headers */,
0442850E1BAA64EC00D16268 /* ASMultidimensionalArrayUtils.h in Headers */, 0442850E1BAA64EC00D16268 /* ASMultidimensionalArrayUtils.h in Headers */,
DE8BEAC21C2DF3FC00D57C12 /* ASDelegateProxy.h in Headers */,
B35062041B010EFD0018CF92 /* ASMultiplexImageNode.h in Headers */, B35062041B010EFD0018CF92 /* ASMultiplexImageNode.h in Headers */,
DECBD6E81BE56E1900CF4905 /* ASButtonNode.h in Headers */, DECBD6E81BE56E1900CF4905 /* ASButtonNode.h in Headers */,
B35062241B010EFD0018CF92 /* ASMutableAttributedStringBuilder.h in Headers */, B35062241B010EFD0018CF92 /* ASMutableAttributedStringBuilder.h in Headers */,
@@ -1451,6 +1462,7 @@
B35062551B010EFD0018CF92 /* ASSentinel.h in Headers */, B35062551B010EFD0018CF92 /* ASSentinel.h in Headers */,
9C8221961BA237B80037F19A /* ASStackBaselinePositionedLayout.h in Headers */, 9C8221961BA237B80037F19A /* ASStackBaselinePositionedLayout.h in Headers */,
9C49C3701B853961000B0DD5 /* ASStackLayoutable.h in Headers */, 9C49C3701B853961000B0DD5 /* ASStackLayoutable.h in Headers */,
DE040EF91C2B40AC004692FF /* ASCollectionViewFlowLayoutInspector.h in Headers */,
34EFC7701B701CFA00AD841F /* ASStackLayoutDefines.h in Headers */, 34EFC7701B701CFA00AD841F /* ASStackLayoutDefines.h in Headers */,
254C6B7B1BF94DF4003EC431 /* ASTextKitRenderer+Positioning.h in Headers */, 254C6B7B1BF94DF4003EC431 /* ASTextKitRenderer+Positioning.h in Headers */,
CC7FD9E21BB603FF005CCB2B /* ASPhotosFrameworkImageRequest.h in Headers */, CC7FD9E21BB603FF005CCB2B /* ASPhotosFrameworkImageRequest.h in Headers */,
@@ -1523,7 +1535,6 @@
058D09B9195D04C000B7D73C /* Frameworks */, 058D09B9195D04C000B7D73C /* Frameworks */,
058D09BA195D04C000B7D73C /* Resources */, 058D09BA195D04C000B7D73C /* Resources */,
3B9D88CDF51B429C8409E4B6 /* Copy Pods Resources */, 3B9D88CDF51B429C8409E4B6 /* Copy Pods Resources */,
1B86F48711505F91D5FEF571 /* Embed Pods Frameworks */,
); );
buildRules = ( buildRules = (
); );
@@ -1623,21 +1634,6 @@
/* End PBXResourcesBuildPhase section */ /* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */
1B86F48711505F91D5FEF571 /* Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Embed Pods Frameworks";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-AsyncDisplayKitTests/Pods-AsyncDisplayKitTests-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
2E61B6A0DB0F436A9DDBE86F /* Check Pods Manifest.lock */ = { 2E61B6A0DB0F436A9DDBE86F /* Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@@ -1701,6 +1697,7 @@
0549634A1A1EA066000F8E56 /* ASBasicImageDownloader.mm in Sources */, 0549634A1A1EA066000F8E56 /* ASBasicImageDownloader.mm in Sources */,
299DA1AA1A828D2900162D41 /* ASBatchContext.mm in Sources */, 299DA1AA1A828D2900162D41 /* ASBatchContext.mm in Sources */,
AC6456091B0A335000CF11B8 /* ASCellNode.m in Sources */, AC6456091B0A335000CF11B8 /* ASCellNode.m in Sources */,
DE8BEAC31C2DF3FC00D57C12 /* ASDelegateProxy.m in Sources */,
ACF6ED1D1B17843500DA7C62 /* ASCenterLayoutSpec.mm in Sources */, ACF6ED1D1B17843500DA7C62 /* ASCenterLayoutSpec.mm in Sources */,
18C2ED801B9B7DE800F627B3 /* ASCollectionNode.m in Sources */, 18C2ED801B9B7DE800F627B3 /* ASCollectionNode.m in Sources */,
92DD2FE41BF4B97E0074C9DD /* ASMapNode.mm in Sources */, 92DD2FE41BF4B97E0074C9DD /* ASMapNode.mm in Sources */,
@@ -1830,6 +1827,7 @@
509E68621B3AEDA5009B9150 /* ASAbstractLayoutController.mm in Sources */, 509E68621B3AEDA5009B9150 /* ASAbstractLayoutController.mm in Sources */,
254C6B861BF94F8A003EC431 /* ASTextKitContext.mm in Sources */, 254C6B861BF94F8A003EC431 /* ASTextKitContext.mm in Sources */,
34EFC7621B701CA400AD841F /* ASBackgroundLayoutSpec.mm in Sources */, 34EFC7621B701CA400AD841F /* ASBackgroundLayoutSpec.mm in Sources */,
DE8BEAC41C2DF3FC00D57C12 /* ASDelegateProxy.m in Sources */,
B35062141B010EFD0018CF92 /* ASBasicImageDownloader.mm in Sources */, B35062141B010EFD0018CF92 /* ASBasicImageDownloader.mm in Sources */,
B35062161B010EFD0018CF92 /* ASBatchContext.mm in Sources */, B35062161B010EFD0018CF92 /* ASBatchContext.mm in Sources */,
AC47D9421B3B891B00AAEE9D /* ASCellNode.m in Sources */, AC47D9421B3B891B00AAEE9D /* ASCellNode.m in Sources */,
@@ -1936,6 +1934,7 @@
CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN_UNREACHABLE_CODE = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO;
GCC_PREPROCESSOR_DEFINITIONS = ( GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1", "DEBUG=1",
"$(inherited)", "$(inherited)",
@@ -1956,6 +1955,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO;
INFOPLIST_FILE = AsyncDisplayKitTestHost/Info.plist; INFOPLIST_FILE = AsyncDisplayKitTestHost/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 7.0; IPHONEOS_DEPLOYMENT_TARGET = 7.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
@@ -2043,6 +2043,7 @@
APPLICATION_EXTENSION_API_ONLY = YES; APPLICATION_EXTENSION_API_ONLY = YES;
DSTROOT = /tmp/AsyncDisplayKit.dst; DSTROOT = /tmp/AsyncDisplayKit.dst;
GCC_INPUT_FILETYPE = automatic; GCC_INPUT_FILETYPE = automatic;
GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO;
GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "AsyncDisplayKit/AsyncDisplayKit-Prefix.pch"; GCC_PREFIX_HEADER = "AsyncDisplayKit/AsyncDisplayKit-Prefix.pch";
GCC_TREAT_WARNINGS_AS_ERRORS = YES; GCC_TREAT_WARNINGS_AS_ERRORS = YES;
@@ -2061,6 +2062,7 @@
APPLICATION_EXTENSION_API_ONLY = YES; APPLICATION_EXTENSION_API_ONLY = YES;
DSTROOT = /tmp/AsyncDisplayKit.dst; DSTROOT = /tmp/AsyncDisplayKit.dst;
GCC_INPUT_FILETYPE = automatic; GCC_INPUT_FILETYPE = automatic;
GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO;
GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "AsyncDisplayKit/AsyncDisplayKit-Prefix.pch"; GCC_PREFIX_HEADER = "AsyncDisplayKit/AsyncDisplayKit-Prefix.pch";
GCC_TREAT_WARNINGS_AS_ERRORS = YES; GCC_TREAT_WARNINGS_AS_ERRORS = YES;
@@ -2083,6 +2085,7 @@
"$(inherited)", "$(inherited)",
"$(DEVELOPER_FRAMEWORKS_DIR)", "$(DEVELOPER_FRAMEWORKS_DIR)",
); );
GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO;
GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "AsyncDisplayKit/AsyncDisplayKit-Prefix.pch"; GCC_PREFIX_HEADER = "AsyncDisplayKit/AsyncDisplayKit-Prefix.pch";
GCC_PREPROCESSOR_DEFINITIONS = ( GCC_PREPROCESSOR_DEFINITIONS = (
@@ -2110,6 +2113,7 @@
"$(inherited)", "$(inherited)",
"$(DEVELOPER_FRAMEWORKS_DIR)", "$(DEVELOPER_FRAMEWORKS_DIR)",
); );
GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO;
GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "AsyncDisplayKit/AsyncDisplayKit-Prefix.pch"; GCC_PREFIX_HEADER = "AsyncDisplayKit/AsyncDisplayKit-Prefix.pch";
GCC_PREPROCESSOR_DEFINITIONS = ( GCC_PREPROCESSOR_DEFINITIONS = (
@@ -2138,6 +2142,7 @@
DYLIB_CURRENT_VERSION = 1; DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath"; DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO;
GCC_NO_COMMON_BLOCKS = YES; GCC_NO_COMMON_BLOCKS = YES;
GCC_PREPROCESSOR_DEFINITIONS = ( GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1", "DEBUG=1",
@@ -2170,6 +2175,7 @@
DYLIB_CURRENT_VERSION = 1; DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath"; DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO;
GCC_NO_COMMON_BLOCKS = YES; GCC_NO_COMMON_BLOCKS = YES;
INFOPLIST_FILE = "$(SRCROOT)/AsyncDisplayKit-iOS/Info.plist"; INFOPLIST_FILE = "$(SRCROOT)/AsyncDisplayKit-iOS/Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";

View File

@@ -31,6 +31,17 @@ typedef enum : NSUInteger {
*/ */
@property (nonatomic, assign) BOOL laysOutHorizontally; @property (nonatomic, assign) BOOL laysOutHorizontally;
/** Horizontally align content (text or image).
Defaults to ASAlignmentMiddle.
*/
@property (nonatomic, assign) ASHorizontalAlignment contentHorizontalAlignment;
/** Vertically align content (text or image).
Defaults to ASAlignmentCenter.
*/
@property (nonatomic, assign) ASVerticalAlignment contentVerticalAlignment;
- (NSAttributedString *)attributedTitleForState:(ASButtonState)state; - (NSAttributedString *)attributedTitleForState:(ASButtonState)state;
- (void)setAttributedTitle:(NSAttributedString *)title forState:(ASButtonState)state; - (void)setAttributedTitle:(NSAttributedString *)title forState:(ASButtonState)state;

View File

@@ -39,6 +39,9 @@
_titleNode = [[ASTextNode alloc] init]; _titleNode = [[ASTextNode alloc] init];
_imageNode = [[ASImageNode alloc] init]; _imageNode = [[ASImageNode alloc] init];
_contentHorizontalAlignment = ASAlignmentMiddle;
_contentVerticalAlignment = ASAlignmentCenter;
[self addSubnode:_titleNode]; [self addSubnode:_titleNode];
[self addSubnode:_imageNode]; [self addSubnode:_imageNode];
@@ -195,8 +198,8 @@
ASStackLayoutSpec *stack = [[ASStackLayoutSpec alloc] init]; ASStackLayoutSpec *stack = [[ASStackLayoutSpec alloc] init];
stack.direction = self.laysOutHorizontally ? ASStackLayoutDirectionHorizontal : ASStackLayoutDirectionVertical; stack.direction = self.laysOutHorizontally ? ASStackLayoutDirectionHorizontal : ASStackLayoutDirectionVertical;
stack.spacing = self.contentSpacing; stack.spacing = self.contentSpacing;
stack.justifyContent = ASStackLayoutJustifyContentCenter; stack.horizontalAlignment = _contentHorizontalAlignment;
stack.alignItems = ASStackLayoutAlignItemsCenter; stack.verticalAlignment = _contentVerticalAlignment;
NSMutableArray *children = [[NSMutableArray alloc] initWithCapacity:2]; NSMutableArray *children = [[NSMutableArray alloc] initWithCapacity:2];
if (self.imageNode.image) { if (self.imageNode.image) {

View File

@@ -16,7 +16,11 @@ NS_ASSUME_NONNULL_BEGIN
*/ */
@interface ASCollectionNode : ASDisplayNode @interface ASCollectionNode : ASDisplayNode
- (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout NS_DESIGNATED_INITIALIZER; - (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout;
- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout;
@property (weak, nonatomic) id <ASCollectionDelegate> delegate;
@property (weak, nonatomic) id <ASCollectionDataSource> dataSource;
@property (nonatomic, readonly) ASCollectionView *view; @property (nonatomic, readonly) ASCollectionView *view;

View File

@@ -9,6 +9,22 @@
#import "ASCollectionNode.h" #import "ASCollectionNode.h"
#import "ASDisplayNode+Subclasses.h" #import "ASDisplayNode+Subclasses.h"
@interface _ASCollectionPendingState : NSObject
@property (weak, nonatomic) id <ASCollectionDelegate> delegate;
@property (weak, nonatomic) id <ASCollectionDataSource> dataSource;
@end
@implementation _ASCollectionPendingState
@end
@interface ASCollectionNode ()
@property (nonatomic) _ASCollectionPendingState *pendingState;
@end
@interface ASCollectionView ()
- (instancetype)_initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout;
@end
@implementation ASCollectionNode @implementation ASCollectionNode
- (instancetype)init - (instancetype)init
@@ -21,17 +37,92 @@
- (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout - (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout
{ {
if (self = [super initWithViewBlock:^UIView *{ return [[ASCollectionView alloc] initWithCollectionViewLayout:layout]; }]) { return [self initWithFrame:CGRectZero collectionViewLayout:layout];
}
- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout
{
ASDisplayNodeViewBlock collectionViewBlock = ^UIView *{
return [[ASCollectionView alloc] _initWithFrame:frame collectionViewLayout:layout];
};
if (self = [super initWithViewBlock:collectionViewBlock]) {
return self; return self;
} }
return nil; return nil;
} }
- (void)didLoad
{
[super didLoad];
if (_pendingState) {
_ASCollectionPendingState *pendingState = _pendingState;
self.pendingState = nil;
ASCollectionView *view = self.view;
view.asyncDelegate = pendingState.delegate;
view.asyncDataSource = pendingState.dataSource;
}
}
- (_ASCollectionPendingState *)pendingState
{
if (!_pendingState && ![self isNodeLoaded]) {
self.pendingState = [[_ASCollectionPendingState alloc] init];
}
ASDisplayNodeAssert(![self isNodeLoaded] || !_pendingState, @"ASCollectionNode should not have a pendingState once it is loaded");
return _pendingState;
}
- (void)setDelegate:(id <ASCollectionDelegate>)delegate
{
if ([self pendingState]) {
_pendingState.delegate = delegate;
} else {
ASDisplayNodeAssert([self isNodeLoaded], @"ASCollectionNode should be loaded if pendingState doesn't exist");
self.view.asyncDelegate = delegate;
}
}
- (id <ASCollectionDelegate>)delegate
{
if ([self pendingState]) {
return _pendingState.delegate;
} else {
return self.view.asyncDelegate;
}
}
- (void)setDataSource:(id <ASCollectionDataSource>)dataSource
{
if ([self pendingState]) {
_pendingState.dataSource = dataSource;
} else {
ASDisplayNodeAssert([self isNodeLoaded], @"ASCollectionNode should be loaded if pendingState doesn't exist");
self.view.asyncDataSource = dataSource;
}
}
- (id <ASCollectionDataSource>)dataSource
{
if ([self pendingState]) {
return _pendingState.dataSource;
} else {
return self.view.asyncDataSource;
}
}
- (ASCollectionView *)view - (ASCollectionView *)view
{ {
return (ASCollectionView *)[super view]; return (ASCollectionView *)[super view];
} }
- (void)visibilityDidChange:(BOOL)isVisible
{
}
- (void)clearContents - (void)clearContents
{ {
[super clearContents]; [super clearContents];

View File

@@ -12,11 +12,11 @@
#import <AsyncDisplayKit/ASCollectionViewProtocols.h> #import <AsyncDisplayKit/ASCollectionViewProtocols.h>
#import <AsyncDisplayKit/ASBaseDefines.h> #import <AsyncDisplayKit/ASBaseDefines.h>
#import <AsyncDisplayKit/ASBatchContext.h> #import <AsyncDisplayKit/ASBatchContext.h>
#import <AsyncDisplayKit/ASCollectionViewFlowLayoutInspector.h>
@class ASCellNode; @class ASCellNode;
@protocol ASCollectionViewDataSource; @protocol ASCollectionDataSource;
@protocol ASCollectionViewDelegate; @protocol ASCollectionDelegate;
@protocol ASCollectionViewLayoutInspecting; @protocol ASCollectionViewLayoutInspecting;
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
@@ -29,10 +29,16 @@ NS_ASSUME_NONNULL_BEGIN
*/ */
@interface ASCollectionView : UICollectionView @interface ASCollectionView : UICollectionView
/**
* Initializer.
*
* @param layout The layout object to use for organizing items. The collection view stores a strong reference to the specified object. Must not be nil.
*/
- (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout; - (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout;
- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout;
@property (nonatomic, weak) id<ASCollectionViewDataSource> asyncDataSource; @property (nonatomic, weak) id<ASCollectionDelegate> asyncDelegate;
@property (nonatomic, weak) id<ASCollectionViewDelegate> asyncDelegate; // must not be nil @property (nonatomic, weak) id<ASCollectionDataSource> asyncDataSource;
/** /**
* Tuning parameters for a range type. * Tuning parameters for a range type.
@@ -54,27 +60,6 @@ NS_ASSUME_NONNULL_BEGIN
*/ */
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType; - (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType;
/**
* Initializer.
*
* @param frame The frame rectangle for the collection view, measured in points. The origin of the frame is relative to the superview
* in which you plan to add it. This frame is passed to the superclass during initialization.
*
* @param layout The layout object to use for organizing items. The collection view stores a strong reference to the specified object.
* Must not be nil.
*
* @param asyncDataFetchingEnabled Enable the data fetching in async mode.
*
* @discussion If asyncDataFetching is enabled, the `ASCollectionView` will fetch data through `collectionView:numberOfRowsInSection:` and
* `collectionView:nodeForRowAtIndexPath:` in async mode from background thread. Otherwise, the methods will be invoked synchronically
* from calling thread.
* Enabling asyncDataFetching could avoid blocking main thread for `ASCellNode` allocation, which is frequently reported issue for
* large scale data. On another hand, the application code need take the responsibility to avoid data inconsistence. Specifically,
* we will lock the data source through `collectionViewLockDataSource`, and unlock it by `collectionViewUnlockDataSource` after the data fetching.
* The application should not update the data source while the data source is locked, to keep data consistence.
*/
- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout asyncDataFetching:(BOOL)asyncDataFetchingEnabled;
/** /**
* The number of screens left to scroll before the delegate -collectionView:beginBatchFetchingWithContext: is called. * The number of screens left to scroll before the delegate -collectionView:beginBatchFetchingWithContext: is called.
* *
@@ -303,7 +288,8 @@ NS_ASSUME_NONNULL_BEGIN
/** /**
* This is a node-based UICollectionViewDataSource. * This is a node-based UICollectionViewDataSource.
*/ */
@protocol ASCollectionViewDataSource <ASCommonCollectionViewDataSource, NSObject> #define ASCollectionViewDataSource ASCollectionDataSource
@protocol ASCollectionDataSource <ASCommonCollectionViewDataSource, NSObject>
/** /**
* Similar to -collectionView:cellForItemAtIndexPath:. * Similar to -collectionView:cellForItemAtIndexPath:.
@@ -364,7 +350,8 @@ NS_ASSUME_NONNULL_BEGIN
/** /**
* This is a node-based UICollectionViewDelegate. * This is a node-based UICollectionViewDelegate.
*/ */
@protocol ASCollectionViewDelegate <ASCommonCollectionViewDelegate, NSObject> #define ASCollectionViewDelegate ASCollectionDelegate
@protocol ASCollectionDelegate <ASCommonCollectionViewDelegate, NSObject>
@optional @optional
@@ -433,4 +420,10 @@ NS_ASSUME_NONNULL_BEGIN
@end @end
@interface ASCollectionView (Deprecated)
- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout asyncDataFetching:(BOOL)asyncDataFetchingEnabled ASDISPLAYNODE_DEPRECATED;
@end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END

View File

@@ -8,7 +8,9 @@
#import "ASAssert.h" #import "ASAssert.h"
#import "ASBatchFetching.h" #import "ASBatchFetching.h"
#import "ASDelegateProxy.h"
#import "ASCollectionView.h" #import "ASCollectionView.h"
#import "ASCollectionNode.h"
#import "ASCollectionDataController.h" #import "ASCollectionDataController.h"
#import "ASCollectionViewLayoutController.h" #import "ASCollectionViewLayoutController.h"
#import "ASCollectionViewFlowLayoutInspector.h" #import "ASCollectionViewFlowLayoutInspector.h"
@@ -21,87 +23,6 @@ static const NSUInteger kASCollectionViewAnimationNone = UITableViewRowAnimation
static const ASSizeRange kInvalidSizeRange = {CGSizeZero, CGSizeZero}; static const ASSizeRange kInvalidSizeRange = {CGSizeZero, CGSizeZero};
static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
#pragma mark -
#pragma mark Proxying.
/**
* ASCollectionView intercepts and/or overrides a few of UICollectionView's critical data source and delegate methods.
*
* Any selector included in this function *MUST* be implemented by ASCollectionView.
*/
static BOOL _isInterceptedSelector(SEL sel)
{
return (
// handled by ASCollectionView node<->cell machinery
sel == @selector(collectionView:cellForItemAtIndexPath:) ||
sel == @selector(collectionView:layout:sizeForItemAtIndexPath:) ||
sel == @selector(collectionView:viewForSupplementaryElementOfKind:atIndexPath:) ||
// handled by ASRangeController
sel == @selector(numberOfSectionsInCollectionView:) ||
sel == @selector(collectionView:numberOfItemsInSection:) ||
// used for ASRangeController visibility updates
sel == @selector(collectionView:willDisplayCell:forItemAtIndexPath:) ||
sel == @selector(collectionView:didEndDisplayingCell:forItemAtIndexPath:) ||
// used for batch fetching API
sel == @selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:)
);
}
/**
* Stand-in for UICollectionViewDataSource and UICollectionViewDelegate. Any method calls we intercept are routed to ASCollectionView;
* everything else leaves AsyncDisplayKit safely and arrives at the original intended data source and delegate.
*/
@interface _ASCollectionViewProxy : NSProxy
- (instancetype)initWithTarget:(id<NSObject>)target interceptor:(ASCollectionView *)interceptor;
@end
@implementation _ASCollectionViewProxy {
id<NSObject> __weak _target;
ASCollectionView * __weak _interceptor;
}
- (instancetype)initWithTarget:(id<NSObject>)target interceptor:(ASCollectionView *)interceptor
{
// -[NSProxy init] is undefined
if (!self) {
return nil;
}
ASDisplayNodeAssert(target, @"target must not be nil");
ASDisplayNodeAssert(interceptor, @"interceptor must not be nil");
_target = target;
_interceptor = interceptor;
return self;
}
- (BOOL)respondsToSelector:(SEL)aSelector
{
ASDisplayNodeAssert(_target, @"target must not be nil"); // catch weak ref's being nilled early
ASDisplayNodeAssert(_interceptor, @"interceptor must not be nil");
return (_isInterceptedSelector(aSelector) || [_target respondsToSelector:aSelector]);
}
- (id)forwardingTargetForSelector:(SEL)aSelector
{
ASDisplayNodeAssert(_target, @"target must not be nil"); // catch weak ref's being nilled early
ASDisplayNodeAssert(_interceptor, @"interceptor must not be nil");
if (_isInterceptedSelector(aSelector)) {
return _interceptor;
}
return [_target respondsToSelector:aSelector] ? _target : nil;
}
@end
#pragma mark - #pragma mark -
#pragma mark ASCellNode<->UICollectionViewCell bridging. #pragma mark ASCellNode<->UICollectionViewCell bridging.
@@ -137,24 +58,25 @@ static BOOL _isInterceptedSelector(SEL sel)
#pragma mark - #pragma mark -
#pragma mark ASCollectionView. #pragma mark ASCollectionView.
@interface ASCollectionView () <ASRangeControllerDataSource, ASRangeControllerDelegate, ASDataControllerSource, ASCellNodeLayoutDelegate> { @interface ASCollectionView () <ASRangeControllerDataSource, ASRangeControllerDelegate, ASDataControllerSource, ASCellNodeLayoutDelegate, ASDelegateProxyInterceptor> {
_ASCollectionViewProxy *_proxyDataSource; ASCollectionViewProxy *_proxyDataSource;
_ASCollectionViewProxy *_proxyDelegate; ASCollectionViewProxy *_proxyDelegate;
ASCollectionDataController *_dataController; ASCollectionDataController *_dataController;
ASRangeController *_rangeController; ASRangeController *_rangeController;
ASCollectionViewLayoutController *_layoutController; ASCollectionViewLayoutController *_layoutController;
ASCollectionViewFlowLayoutInspector *_flowLayoutInspector; ASCollectionViewFlowLayoutInspector *_flowLayoutInspector;
BOOL _performingBatchUpdates; BOOL _performingBatchUpdates;
NSMutableArray *_batchUpdateBlocks; NSMutableArray *_batchUpdateBlocks;
BOOL _asyncDataFetchingEnabled; BOOL _asyncDataFetchingEnabled;
BOOL _asyncDelegateImplementsInsetSection; BOOL _asyncDelegateImplementsInsetSection;
BOOL _collectionViewLayoutImplementsInsetSection; BOOL _collectionViewLayoutImplementsInsetSection;
BOOL _asyncDataSourceImplementsConstrainedSizeForNode; BOOL _asyncDataSourceImplementsConstrainedSizeForNode;
BOOL _queuedNodeSizeUpdate; BOOL _queuedNodeSizeUpdate;
BOOL _isDeallocating;
ASBatchContext *_batchContext; ASBatchContext *_batchContext;
CGSize _maxSizeForNodesConstrainedSize; CGSize _maxSizeForNodesConstrainedSize;
@@ -172,7 +94,7 @@ static BOOL _isInterceptedSelector(SEL sel)
* You will get an assertion failure saying `Invalid number of items in section 0. * You will get an assertion failure saying `Invalid number of items in section 0.
* The number of items after the update (1) must be equal to the number of items before the update (1) plus or minus the items added and removed (1 added, 0 removed).` * The number of items after the update (1) must be equal to the number of items before the update (1) plus or minus the items added and removed (1 added, 0 removed).`
* The collection view never queried your data source before the update to see that it actually had 0 items. * The collection view never queried your data source before the update to see that it actually had 0 items.
*/ */
BOOL _superIsPendingDataLoad; BOOL _superIsPendingDataLoad;
} }
@@ -187,48 +109,51 @@ static BOOL _isInterceptedSelector(SEL sel)
- (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout - (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout
{ {
return [self initWithFrame:CGRectZero collectionViewLayout:layout asyncDataFetching:NO]; return [self initWithFrame:CGRectZero collectionViewLayout:layout];
} }
- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout - (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout
{ {
return [self initWithFrame:frame collectionViewLayout:layout asyncDataFetching:NO]; ASCollectionNode *collectionNode = [[ASCollectionNode alloc] initWithFrame:frame collectionViewLayout:layout];
return collectionNode.view;
} }
// FIXME: This method is deprecated and will probably be removed in or shortly after 2.0.
- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout asyncDataFetching:(BOOL)asyncDataFetchingEnabled - (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout asyncDataFetching:(BOOL)asyncDataFetchingEnabled
{
return [self initWithFrame:frame collectionViewLayout:layout];
}
- (instancetype)_initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout
{ {
if (!(self = [super initWithFrame:frame collectionViewLayout:layout])) if (!(self = [super initWithFrame:frame collectionViewLayout:layout]))
return nil; return nil;
// FIXME: asyncDataFetching is currently unreliable for some use cases.
// https://github.com/facebook/AsyncDisplayKit/issues/385
asyncDataFetchingEnabled = NO;
_layoutController = [[ASCollectionViewLayoutController alloc] initWithCollectionView:self]; _layoutController = [[ASCollectionViewLayoutController alloc] initWithCollectionView:self];
_rangeController = [[ASRangeController alloc] init]; _rangeController = [[ASRangeController alloc] init];
_rangeController.dataSource = self; _rangeController.dataSource = self;
_rangeController.delegate = self; _rangeController.delegate = self;
_rangeController.layoutController = _layoutController; _rangeController.layoutController = _layoutController;
_dataController = [[ASCollectionDataController alloc] initWithAsyncDataFetching:asyncDataFetchingEnabled]; _dataController = [[ASCollectionDataController alloc] initWithAsyncDataFetching:NO];
_dataController.delegate = _rangeController; _dataController.delegate = _rangeController;
_dataController.dataSource = self; _dataController.dataSource = self;
_batchContext = [[ASBatchContext alloc] init]; _batchContext = [[ASBatchContext alloc] init];
_leadingScreensForBatching = 1.0; _leadingScreensForBatching = 1.0;
_asyncDataFetchingEnabled = asyncDataFetchingEnabled; _asyncDataFetchingEnabled = NO;
_asyncDataSourceLocked = NO; _asyncDataSourceLocked = NO;
_performingBatchUpdates = NO; _performingBatchUpdates = NO;
_batchUpdateBlocks = [NSMutableArray array]; _batchUpdateBlocks = [NSMutableArray array];
_superIsPendingDataLoad = YES; _superIsPendingDataLoad = YES;
_collectionViewLayoutImplementsInsetSection = [layout respondsToSelector:@selector(sectionInset)]; _collectionViewLayoutImplementsInsetSection = [layout respondsToSelector:@selector(sectionInset)];
_maxSizeForNodesConstrainedSize = self.bounds.size; _maxSizeForNodesConstrainedSize = self.bounds.size;
// If the initial size is 0, expect a size change very soon which is part of the initial configuration // If the initial size is 0, expect a size change very soon which is part of the initial configuration
// and should not trigger a relayout. // and should not trigger a relayout.
@@ -240,6 +165,12 @@ static BOOL _isInterceptedSelector(SEL sel)
_layoutInspector = [self flowLayoutInspector]; _layoutInspector = [self flowLayoutInspector];
} }
_proxyDelegate = [[ASCollectionViewProxy alloc] initWithTarget:nil interceptor:self];
super.delegate = (id<UICollectionViewDelegate>)_proxyDelegate;
_proxyDataSource = [[ASCollectionViewProxy alloc] initWithTarget:nil interceptor:self];
super.dataSource = (id<UICollectionViewDataSource>)_proxyDataSource;
_registeredSupplementaryKinds = [NSMutableSet set]; _registeredSupplementaryKinds = [NSMutableSet set];
self.backgroundColor = [UIColor whiteColor]; self.backgroundColor = [UIColor whiteColor];
@@ -251,10 +182,10 @@ static BOOL _isInterceptedSelector(SEL sel)
- (void)dealloc - (void)dealloc
{ {
// Sometimes the UIKit classes can call back to their delegate even during deallocation. // Sometimes the UIKit classes can call back to their delegate even during deallocation, due to animation completion blocks etc.
// This bug might be iOS 7-specific. _isDeallocating = YES;
super.delegate = nil; [self setAsyncDelegate:nil];
super.dataSource = nil; [self setAsyncDataSource:nil];
} }
/** /**
@@ -262,13 +193,13 @@ static BOOL _isInterceptedSelector(SEL sel)
*/ */
- (ASCollectionViewFlowLayoutInspector *)flowLayoutInspector - (ASCollectionViewFlowLayoutInspector *)flowLayoutInspector
{ {
if (_flowLayoutInspector == nil) { if (_flowLayoutInspector == nil) {
UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)self.collectionViewLayout; UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)self.collectionViewLayout;
ASDisplayNodeAssertNotNil(layout, @"Collection view layout must be a flow layout to use the built-in inspector"); ASDisplayNodeAssertNotNil(layout, @"Collection view layout must be a flow layout to use the built-in inspector");
_flowLayoutInspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:self _flowLayoutInspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:self
flowLayout:layout]; flowLayout:layout];
} }
return _flowLayoutInspector; return _flowLayoutInspector;
} }
#pragma mark - #pragma mark -
@@ -308,24 +239,35 @@ static BOOL _isInterceptedSelector(SEL sel)
ASDisplayNodeAssert(delegate == nil, @"ASCollectionView uses asyncDelegate, not UICollectionView's delegate property."); ASDisplayNodeAssert(delegate == nil, @"ASCollectionView uses asyncDelegate, not UICollectionView's delegate property.");
} }
- (void)proxyTargetHasDeallocated:(ASDelegateProxy *)proxy
{
if (proxy == _proxyDelegate) {
[self setAsyncDelegate:nil];
} else if (proxy == _proxyDataSource) {
[self setAsyncDataSource:nil];
}
}
- (void)setAsyncDataSource:(id<ASCollectionViewDataSource>)asyncDataSource - (void)setAsyncDataSource:(id<ASCollectionViewDataSource>)asyncDataSource
{ {
// Note: It's common to check if the value hasn't changed and short-circuit but we aren't doing that here to handle // Note: It's common to check if the value hasn't changed and short-circuit but we aren't doing that here to handle
// the (common) case of nilling the asyncDataSource in the ViewController's dealloc. In this case our _asyncDataSource // the (common) case of nilling the asyncDataSource in the ViewController's dealloc. In this case our _asyncDataSource
// will return as nil (ARC magic) even though the _proxyDataSource still exists. It's really important to nil out // will return as nil (ARC magic) even though the _proxyDataSource still exists. It's really important to nil out
// super.dataSource in this case because calls to _ASTableViewProxy will start failing and cause crashes. // super.dataSource in this case because calls to ASCollectionViewProxy will start failing and cause crashes.
super.dataSource = nil;
if (asyncDataSource == nil) { if (asyncDataSource == nil) {
super.dataSource = nil;
_asyncDataSource = nil; _asyncDataSource = nil;
_proxyDataSource = nil; _proxyDataSource = _isDeallocating ? nil : [[ASCollectionViewProxy alloc] initWithTarget:nil interceptor:self];
_asyncDataSourceImplementsConstrainedSizeForNode = NO; _asyncDataSourceImplementsConstrainedSizeForNode = NO;
} else { } else {
_asyncDataSource = asyncDataSource; _asyncDataSource = asyncDataSource;
_proxyDataSource = [[_ASCollectionViewProxy alloc] initWithTarget:_asyncDataSource interceptor:self]; _proxyDataSource = [[ASCollectionViewProxy alloc] initWithTarget:_asyncDataSource interceptor:self];
super.dataSource = (id<UICollectionViewDataSource>)_proxyDataSource;
_asyncDataSourceImplementsConstrainedSizeForNode = ([_asyncDataSource respondsToSelector:@selector(collectionView:constrainedSizeForNodeAtIndexPath:)] ? 1 : 0); _asyncDataSourceImplementsConstrainedSizeForNode = ([_asyncDataSource respondsToSelector:@selector(collectionView:constrainedSizeForNodeAtIndexPath:)] ? 1 : 0);
} }
super.dataSource = (id<UICollectionViewDataSource>)_proxyDataSource;
} }
- (void)setAsyncDelegate:(id<ASCollectionViewDelegate>)asyncDelegate - (void)setAsyncDelegate:(id<ASCollectionViewDelegate>)asyncDelegate
@@ -333,22 +275,25 @@ static BOOL _isInterceptedSelector(SEL sel)
// Note: It's common to check if the value hasn't changed and short-circuit but we aren't doing that here to handle // Note: It's common to check if the value hasn't changed and short-circuit but we aren't doing that here to handle
// the (common) case of nilling the asyncDelegate in the ViewController's dealloc. In this case our _asyncDelegate // the (common) case of nilling the asyncDelegate in the ViewController's dealloc. In this case our _asyncDelegate
// will return as nil (ARC magic) even though the _proxyDelegate still exists. It's really important to nil out // will return as nil (ARC magic) even though the _proxyDelegate still exists. It's really important to nil out
// super.delegate in this case because calls to _ASTableViewProxy will start failing and cause crashes. // super.delegate in this case because calls to ASCollectionViewProxy will start failing and cause crashes.
// Order is important here, the asyncDelegate must be callable while nilling super.delegate to avoid random crashes
// in UIScrollViewAccessibility.
super.delegate = nil;
if (asyncDelegate == nil) { if (asyncDelegate == nil) {
// order is important here, the delegate must be callable while nilling super.delegate to avoid random crashes
// in UIScrollViewAccessibility.
super.delegate = nil;
_asyncDelegate = nil; _asyncDelegate = nil;
_proxyDelegate = nil; _proxyDelegate = _isDeallocating ? nil : [[ASCollectionViewProxy alloc] initWithTarget:nil interceptor:self];
_asyncDelegateImplementsInsetSection = NO; _asyncDelegateImplementsInsetSection = NO;
} else { } else {
_asyncDelegate = asyncDelegate; _asyncDelegate = asyncDelegate;
_proxyDelegate = [[_ASCollectionViewProxy alloc] initWithTarget:_asyncDelegate interceptor:self]; _proxyDelegate = [[ASCollectionViewProxy alloc] initWithTarget:_asyncDelegate interceptor:self];
super.delegate = (id<UICollectionViewDelegate>)_proxyDelegate;
_asyncDelegateImplementsInsetSection = ([_asyncDelegate respondsToSelector:@selector(collectionView:layout:insetForSectionAtIndex:)] ? 1 : 0); _asyncDelegateImplementsInsetSection = ([_asyncDelegate respondsToSelector:@selector(collectionView:layout:insetForSectionAtIndex:)] ? 1 : 0);
} }
super.delegate = (id<UICollectionViewDelegate>)_proxyDelegate;
[_layoutInspector didChangeCollectionViewDelegate:asyncDelegate]; [_layoutInspector didChangeCollectionViewDelegate:asyncDelegate];
} }
@@ -409,7 +354,7 @@ static BOOL _isInterceptedSelector(SEL sel)
- (void)performBatchAnimated:(BOOL)animated updates:(void (^)())updates completion:(void (^)(BOOL))completion - (void)performBatchAnimated:(BOOL)animated updates:(void (^)())updates completion:(void (^)(BOOL))completion
{ {
ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertMainThread();
[_dataController beginUpdates]; [_dataController beginUpdates];
updates(); updates();
[_dataController endUpdatesAnimated:animated completion:completion]; [_dataController endUpdatesAnimated:animated completion:completion];
@@ -487,7 +432,7 @@ static BOOL _isInterceptedSelector(SEL sel)
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{ {
_ASCollectionViewCell *cell = [self dequeueReusableCellWithReuseIdentifier:kCellReuseIdentifier forIndexPath:indexPath]; _ASCollectionViewCell *cell = [self dequeueReusableCellWithReuseIdentifier:kCellReuseIdentifier forIndexPath:indexPath];
ASCellNode *node = [_dataController nodeAtIndexPath:indexPath]; ASCellNode *node = [_dataController nodeAtIndexPath:indexPath];
cell.node = node; cell.node = node;
[_rangeController configureContentView:cell.contentView forCellNode:node]; [_rangeController configureContentView:cell.contentView forCellNode:node];
@@ -524,7 +469,7 @@ static BOOL _isInterceptedSelector(SEL sel)
CGPoint scrollVelocity = [self.panGestureRecognizer velocityInView:self.superview]; CGPoint scrollVelocity = [self.panGestureRecognizer velocityInView:self.superview];
return [self scrollDirectionForVelocity:scrollVelocity]; return [self scrollDirectionForVelocity:scrollVelocity];
} }
- (ASScrollDirection)scrollDirectionForVelocity:(CGPoint)scrollVelocity - (ASScrollDirection)scrollDirectionForVelocity:(CGPoint)scrollVelocity
{ {
ASScrollDirection direction = ASScrollDirectionNone; ASScrollDirection direction = ASScrollDirectionNone;
@@ -544,7 +489,7 @@ static BOOL _isInterceptedSelector(SEL sel)
direction |= ASScrollDirectionUp; direction |= ASScrollDirectionUp;
} }
} }
return direction; return direction;
} }
@@ -623,7 +568,7 @@ static BOOL _isInterceptedSelector(SEL sel)
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
{ {
[self handleBatchFetchScrollingToOffset:*targetContentOffset]; [self handleBatchFetchScrollingToOffset:*targetContentOffset];
if ([_asyncDelegate respondsToSelector:@selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:)]) { if ([_asyncDelegate respondsToSelector:@selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:)]) {
[_asyncDelegate scrollViewWillEndDragging:scrollView withVelocity:velocity targetContentOffset:targetContentOffset]; [_asyncDelegate scrollViewWillEndDragging:scrollView withVelocity:velocity targetContentOffset:targetContentOffset];
} }
@@ -643,11 +588,11 @@ static BOOL _isInterceptedSelector(SEL sel)
- (void)handleBatchFetchScrollingToOffset:(CGPoint)targetOffset - (void)handleBatchFetchScrollingToOffset:(CGPoint)targetOffset
{ {
ASDisplayNodeAssert(_batchContext != nil, @"Batch context should exist"); ASDisplayNodeAssert(_batchContext != nil, @"Batch context should exist");
if (![self shouldBatchFetch]) { if (![self shouldBatchFetch]) {
return; return;
} }
if (ASDisplayShouldFetchBatchForContext(_batchContext, [self scrollDirection], self.bounds, self.contentSize, targetOffset, _leadingScreensForBatching)) { if (ASDisplayShouldFetchBatchForContext(_batchContext, [self scrollDirection], self.bounds, self.contentSize, targetOffset, _leadingScreensForBatching)) {
[_batchContext beginBatchFetching]; [_batchContext beginBatchFetching];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
@@ -695,7 +640,7 @@ static BOOL _isInterceptedSelector(SEL sel)
} }
constrainedSize = ASSizeRangeMake(CGSizeZero, maxSize); constrainedSize = ASSizeRangeMake(CGSizeZero, maxSize);
} }
UIEdgeInsets sectionInset = UIEdgeInsetsZero; UIEdgeInsets sectionInset = UIEdgeInsetsZero;
if (_collectionViewLayoutImplementsInsetSection) { if (_collectionViewLayoutImplementsInsetSection) {
sectionInset = [(UICollectionViewFlowLayout *)self.collectionViewLayout sectionInset]; sectionInset = [(UICollectionViewFlowLayout *)self.collectionViewLayout sectionInset];
@@ -704,7 +649,7 @@ static BOOL _isInterceptedSelector(SEL sel)
if (_asyncDelegateImplementsInsetSection) { if (_asyncDelegateImplementsInsetSection) {
sectionInset = [(id<ASCollectionViewDelegateFlowLayout>)_asyncDelegate collectionView:self layout:self.collectionViewLayout insetForSectionAtIndex:indexPath.section]; sectionInset = [(id<ASCollectionViewDelegateFlowLayout>)_asyncDelegate collectionView:self layout:self.collectionViewLayout insetForSectionAtIndex:indexPath.section];
} }
if (ASScrollDirectionContainsHorizontalDirection([self scrollableDirections])) { if (ASScrollDirectionContainsHorizontalDirection([self scrollableDirections])) {
constrainedSize.min.width = MAX(0, constrainedSize.min.width - sectionInset.left - sectionInset.right); constrainedSize.min.width = MAX(0, constrainedSize.min.width - sectionInset.left - sectionInset.right);
//ignore insets for FLT_MAX so FLT_MAX can be compared against //ignore insets for FLT_MAX so FLT_MAX can be compared against
@@ -718,7 +663,7 @@ static BOOL _isInterceptedSelector(SEL sel)
constrainedSize.max.height = MAX(0, constrainedSize.max.height - sectionInset.top - sectionInset.bottom); constrainedSize.max.height = MAX(0, constrainedSize.max.height - sectionInset.top - sectionInset.bottom);
} }
} }
return constrainedSize; return constrainedSize;
} }
@@ -738,7 +683,7 @@ static BOOL _isInterceptedSelector(SEL sel)
- (void)dataControllerLockDataSource - (void)dataControllerLockDataSource
{ {
ASDisplayNodeAssert(!self.asyncDataSourceLocked, @"The data source has already been locked"); ASDisplayNodeAssert(!self.asyncDataSourceLocked, @"The data source has already been locked");
self.asyncDataSourceLocked = YES; self.asyncDataSourceLocked = YES;
if ([_asyncDataSource respondsToSelector:@selector(collectionViewLockDataSource:)]) { if ([_asyncDataSource respondsToSelector:@selector(collectionViewLockDataSource:)]) {
[_asyncDataSource collectionViewLockDataSource:self]; [_asyncDataSource collectionViewLockDataSource:self];
@@ -748,7 +693,7 @@ static BOOL _isInterceptedSelector(SEL sel)
- (void)dataControllerUnlockDataSource - (void)dataControllerUnlockDataSource
{ {
ASDisplayNodeAssert(self.asyncDataSourceLocked, @"The data source has already been unlocked"); ASDisplayNodeAssert(self.asyncDataSourceLocked, @"The data source has already been unlocked");
self.asyncDataSourceLocked = NO; self.asyncDataSourceLocked = NO;
if ([_asyncDataSource respondsToSelector:@selector(collectionViewUnlockDataSource:)]) { if ([_asyncDataSource respondsToSelector:@selector(collectionViewUnlockDataSource:)]) {
[_asyncDataSource collectionViewUnlockDataSource:self]; [_asyncDataSource collectionViewUnlockDataSource:self];
@@ -817,7 +762,7 @@ static BOOL _isInterceptedSelector(SEL sel)
- (void)rangeController:(ASRangeController *)rangeController didEndUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL))completion - (void)rangeController:(ASRangeController *)rangeController didEndUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL))completion
{ {
ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertMainThread();
if (!self.asyncDataSource || _superIsPendingDataLoad) { if (!self.asyncDataSource || _superIsPendingDataLoad) {
if (completion) { if (completion) {
completion(NO); completion(NO);
@@ -832,7 +777,7 @@ static BOOL _isInterceptedSelector(SEL sel)
} }
} completion:completion]; } completion:completion];
}); });
[_batchUpdateBlocks removeAllObjects]; [_batchUpdateBlocks removeAllObjects];
_performingBatchUpdates = NO; _performingBatchUpdates = NO;
} }
@@ -840,11 +785,11 @@ static BOOL _isInterceptedSelector(SEL sel)
- (void)rangeController:(ASRangeController *)rangeController didInsertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions - (void)rangeController:(ASRangeController *)rangeController didInsertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
{ {
ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertMainThread();
if (!self.asyncDataSource || _superIsPendingDataLoad) { if (!self.asyncDataSource || _superIsPendingDataLoad) {
return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes
} }
if (_performingBatchUpdates) { if (_performingBatchUpdates) {
[_batchUpdateBlocks addObject:^{ [_batchUpdateBlocks addObject:^{
[super insertItemsAtIndexPaths:indexPaths]; [super insertItemsAtIndexPaths:indexPaths];
@@ -859,11 +804,11 @@ static BOOL _isInterceptedSelector(SEL sel)
- (void)rangeController:(ASRangeController *)rangeController didDeleteNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions - (void)rangeController:(ASRangeController *)rangeController didDeleteNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
{ {
ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertMainThread();
if (!self.asyncDataSource || _superIsPendingDataLoad) { if (!self.asyncDataSource || _superIsPendingDataLoad) {
return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes
} }
if (_performingBatchUpdates) { if (_performingBatchUpdates) {
[_batchUpdateBlocks addObject:^{ [_batchUpdateBlocks addObject:^{
[super deleteItemsAtIndexPaths:indexPaths]; [super deleteItemsAtIndexPaths:indexPaths];
@@ -878,11 +823,11 @@ static BOOL _isInterceptedSelector(SEL sel)
- (void)rangeController:(ASRangeController *)rangeController didInsertSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions - (void)rangeController:(ASRangeController *)rangeController didInsertSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
{ {
ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertMainThread();
if (!self.asyncDataSource || _superIsPendingDataLoad) { if (!self.asyncDataSource || _superIsPendingDataLoad) {
return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes
} }
if (_performingBatchUpdates) { if (_performingBatchUpdates) {
[_batchUpdateBlocks addObject:^{ [_batchUpdateBlocks addObject:^{
[super insertSections:indexSet]; [super insertSections:indexSet];
@@ -897,11 +842,11 @@ static BOOL _isInterceptedSelector(SEL sel)
- (void)rangeController:(ASRangeController *)rangeController didDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions - (void)rangeController:(ASRangeController *)rangeController didDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
{ {
ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertMainThread();
if (!self.asyncDataSource || _superIsPendingDataLoad) { if (!self.asyncDataSource || _superIsPendingDataLoad) {
return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes
} }
if (_performingBatchUpdates) { if (_performingBatchUpdates) {
[_batchUpdateBlocks addObject:^{ [_batchUpdateBlocks addObject:^{
[super deleteSections:indexSet]; [super deleteSections:indexSet];
@@ -918,11 +863,11 @@ static BOOL _isInterceptedSelector(SEL sel)
- (void)nodeDidRelayout:(ASCellNode *)node sizeChanged:(BOOL)sizeChanged - (void)nodeDidRelayout:(ASCellNode *)node sizeChanged:(BOOL)sizeChanged
{ {
ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertMainThread();
if (!sizeChanged || _queuedNodeSizeUpdate) { if (!sizeChanged || _queuedNodeSizeUpdate) {
return; return;
} }
_queuedNodeSizeUpdate = YES; _queuedNodeSizeUpdate = YES;
[self performSelector:@selector(requeryNodeSizes) [self performSelector:@selector(requeryNodeSizes)
withObject:nil withObject:nil
@@ -934,7 +879,7 @@ static BOOL _isInterceptedSelector(SEL sel)
- (void)requeryNodeSizes - (void)requeryNodeSizes
{ {
_queuedNodeSizeUpdate = NO; _queuedNodeSizeUpdate = NO;
[super performBatchUpdates:^{} completion:nil]; [super performBatchUpdates:^{} completion:nil];
} }
@@ -944,7 +889,7 @@ static BOOL _isInterceptedSelector(SEL sel)
{ {
for (NSArray *section in [_dataController completedNodes]) { for (NSArray *section in [_dataController completedNodes]) {
for (ASDisplayNode *node in section) { for (ASDisplayNode *node in section) {
[node recursivelyClearContents]; [node exitInterfaceState:ASInterfaceStateDisplay];
} }
} }
} }
@@ -953,7 +898,7 @@ static BOOL _isInterceptedSelector(SEL sel)
{ {
for (NSArray *section in [_dataController completedNodes]) { for (NSArray *section in [_dataController completedNodes]) {
for (ASDisplayNode *node in section) { for (ASDisplayNode *node in section) {
[node recursivelyClearFetchedData]; [node exitInterfaceState:ASInterfaceStateFetchData];
} }
} }
} }

View File

@@ -76,7 +76,6 @@ void _ASEnumerateControlEventsIncludedInMaskWithBlock(ASControlNodeEvent mask, v
if (!(self = [super init])) if (!(self = [super init]))
return nil; return nil;
_controlEventDispatchTable = [[NSMutableDictionary alloc] initWithCapacity:kASControlNodeEventDispatchTableInitialCapacity]; // enough to handle common types without re-hashing the dictionary when adding entries.
_enabled = YES; _enabled = YES;
// As we have no targets yet, we start off with user interaction off. When a target is added, it'll get turned back on. // As we have no targets yet, we start off with user interaction off. When a target is added, it'll get turned back on.
@@ -214,6 +213,10 @@ void _ASEnumerateControlEventsIncludedInMaskWithBlock(ASControlNodeEvent mask, v
// Convert nil to [NSNull null] so that it can be used as a key for NSMapTable. // Convert nil to [NSNull null] so that it can be used as a key for NSMapTable.
if (!target) if (!target)
target = [NSNull null]; target = [NSNull null];
if (!_controlEventDispatchTable) {
_controlEventDispatchTable = [[NSMutableDictionary alloc] initWithCapacity:kASControlNodeEventDispatchTableInitialCapacity]; // enough to handle common types without re-hashing the dictionary when adding entries.
}
// Enumerate the events in the mask, adding the target-action pair for each control event included in controlEventMask // Enumerate the events in the mask, adding the target-action pair for each control event included in controlEventMask
_ASEnumerateControlEventsIncludedInMaskWithBlock(controlEventMask, ^ _ASEnumerateControlEventsIncludedInMaskWithBlock(controlEventMask, ^

View File

@@ -39,35 +39,8 @@ NS_ASSUME_NONNULL_BEGIN
@interface ASDisplayNode (Subclassing) @interface ASDisplayNode (Subclassing)
/** @name View Configuration */
/**
* @return The view class to use when creating a new display node instance. Defaults to _ASDisplayView.
*/
+ (Class)viewClass;
/** @name Properties */ /** @name Properties */
/**
* @abstract The scale factor to apply to the rendering.
*
* @discussion Use setNeedsDisplayAtScale: to set a value and then after display, the display node will set the layer's
* contentsScale. This is to prevent jumps when re-rasterizing at a different contentsScale.
* Read this property if you need to know the future contentsScale of your layer, eg in drawParameters.
*
* @see setNeedsDisplayAtScale:
*/
@property (nonatomic, assign, readonly) CGFloat contentsScaleForDisplay;
/**
* @abstract Whether the view or layer of this display node is currently in a window
*/
@property (nonatomic, readonly, assign, getter=isInHierarchy) BOOL inHierarchy;
/** /**
* @abstract Return the calculated layout. * @abstract Return the calculated layout.
* *
@@ -192,10 +165,9 @@ 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 + (void)drawRect:(CGRect)bounds withParameters:(nullable id <NSObject>)parameters
withParameters:(nullable id<NSObject>)parameters isCancelled:(asdisplaynode_iscancelled_block_t)isCancelledBlock
isCancelled:(asdisplaynode_iscancelled_block_t)isCancelledBlock isRasterizing:(BOOL)isRasterizing;
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.
@@ -238,6 +210,33 @@ NS_ASSUME_NONNULL_BEGIN
*/ */
- (void)displayDidFinish ASDISPLAYNODE_REQUIRES_SUPER; - (void)displayDidFinish ASDISPLAYNODE_REQUIRES_SUPER;
/** @name Observing node-related changes */
/**
* @abstract Called whenever any bit in the ASInterfaceState bitfield is changed.
*
* @discussion Subclasses may use this to monitor when they become visible, should free cached data, and much more.
* @see ASInterfaceState
*/
- (void)interfaceStateDidChange:(ASInterfaceState)newState fromState:(ASInterfaceState)oldState;
- (void)visibilityDidChange:(BOOL)isVisible;
/**
* Called just before the view is added to a window.
*/
- (void)willEnterHierarchy ASDISPLAYNODE_REQUIRES_SUPER;
/**
* Called after the view is removed from the window.
*/
- (void)didExitHierarchy ASDISPLAYNODE_REQUIRES_SUPER;
/**
* @abstract Whether the view or layer of this display node is currently in a window
*/
@property (nonatomic, readonly, assign, getter=isInHierarchy) BOOL inHierarchy;
/** /**
* @abstract Indicates that the node should fetch any external data, such as images. * @abstract Indicates that the node should fetch any external data, such as images.
* *
@@ -247,6 +246,23 @@ NS_ASSUME_NONNULL_BEGIN
*/ */
- (void)fetchData ASDISPLAYNODE_REQUIRES_SUPER; - (void)fetchData ASDISPLAYNODE_REQUIRES_SUPER;
/**
* Provides an opportunity to clear any fetched data (e.g. remote / network or database-queried) on the current node.
*
* @discussion This will not clear data recursively for all subnodes. Either call -recursivelyClearFetchedData or
* selectively clear fetched data.
*/
- (void)clearFetchedData ASDISPLAYNODE_REQUIRES_SUPER;
/**
* Provides an opportunity to clear backing store and other memory-intensive intermediates, such as text layout managers
* on the current node.
*
* @discussion Called by -recursivelyClearContents. Base class implements self.contents = nil, clearing any backing
* store, for asynchronous regeneration when needed.
*/
- (void)clearContents ASDISPLAYNODE_REQUIRES_SUPER;
/** /**
* @abstract Indicates that the receiver is about to display its subnodes. This method is not called if there are no * @abstract Indicates that the receiver is about to display its subnodes. This method is not called if there are no
* subnodes present. * subnodes present.
@@ -269,7 +285,6 @@ NS_ASSUME_NONNULL_BEGIN
*/ */
- (void)subnodeDisplayDidFinish:(ASDisplayNode *)subnode ASDISPLAYNODE_REQUIRES_SUPER; - (void)subnodeDisplayDidFinish:(ASDisplayNode *)subnode ASDISPLAYNODE_REQUIRES_SUPER;
/** /**
* @abstract Marks the receiver's bounds as needing to be redrawn, with a scale value. * @abstract Marks the receiver's bounds as needing to be redrawn, with a scale value.
* *
@@ -297,6 +312,17 @@ NS_ASSUME_NONNULL_BEGIN
*/ */
- (void)recursivelySetNeedsDisplayAtScale:(CGFloat)contentsScale; - (void)recursivelySetNeedsDisplayAtScale:(CGFloat)contentsScale;
/**
* @abstract The scale factor to apply to the rendering.
*
* @discussion Use setNeedsDisplayAtScale: to set a value and then after display, the display node will set the layer's
* contentsScale. This is to prevent jumps when re-rasterizing at a different contentsScale.
* Read this property if you need to know the future contentsScale of your layer, eg in drawParameters.
*
* @see setNeedsDisplayAtScale:
*/
@property (nonatomic, assign, readonly) CGFloat contentsScaleForDisplay;
/** @name Touch handling */ /** @name Touch handling */
@@ -363,38 +389,6 @@ NS_ASSUME_NONNULL_BEGIN
*/ */
- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event; - (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event;
/** @name Observing node-related changes */
/**
* Called just before the view is added to a window.
*/
- (void)willEnterHierarchy ASDISPLAYNODE_REQUIRES_SUPER;
/**
* Called after the view is removed from the window.
*/
- (void)didExitHierarchy ASDISPLAYNODE_REQUIRES_SUPER;
/**
* Provides an opportunity to clear backing store and other memory-intensive intermediates, such as text layout managers
* on the current node.
*
* @discussion Called by -recursivelyClearContents. Base class implements self.contents = nil, clearing any backing
* store, for asynchronous regeneration when needed.
*/
- (void)clearContents ASDISPLAYNODE_REQUIRES_SUPER;
/**
* Provides an opportunity to clear any fetched data (e.g. remote / network or database-queried) on the current node.
*
* @discussion This will not clear data recursively for all subnodes. Either call -recursivelyClearFetchedData or
* selectively clear fetched data.
*/
- (void)clearFetchedData ASDISPLAYNODE_REQUIRES_SUPER;
/** @name Placeholders */ /** @name Placeholders */
/** /**
@@ -414,6 +408,7 @@ NS_ASSUME_NONNULL_BEGIN
*/ */
- (nullable UIImage *)placeholderImage; - (nullable UIImage *)placeholderImage;
/** @name Description */ /** @name Description */
/** /**

View File

@@ -1470,9 +1470,9 @@ static NSInteger incrementIfFound(NSInteger i) {
} }
// Helper method to summarize whether or not the node run through the display process // Helper method to summarize whether or not the node run through the display process
- (BOOL)_implementsDisplay - (BOOL)__implementsDisplay
{ {
return _flags.implementsDrawRect == YES || _flags.implementsImageDisplay == YES; return _flags.implementsDrawRect == YES || _flags.implementsImageDisplay == YES || self.shouldRasterizeDescendants;
} }
- (void)_setupPlaceholderLayer - (void)_setupPlaceholderLayer
@@ -1502,7 +1502,7 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
// (even a runloop observer at a late call order will not stop the next frame from compositing, showing placeholders). // (even a runloop observer at a late call order will not stop the next frame from compositing, showing placeholders).
ASDisplayNode *node = [layer asyncdisplaykit_node]; ASDisplayNode *node = [layer asyncdisplaykit_node];
if (!layer.contents && [node _implementsDisplay]) { if (!layer.contents && [node __implementsDisplay]) {
// For layers that do get displayed here, this immediately kicks off the work on the concurrent -[_ASDisplayLayer displayQueue]. // For layers that do get displayed here, this immediately kicks off the work on the concurrent -[_ASDisplayLayer displayQueue].
// At the same time, it creates an associated _ASAsyncTransaction, which we can use to block on display completion. See ASDisplayNode+AsyncDisplay.mm. // At the same time, it creates an associated _ASAsyncTransaction, which we can use to block on display completion. See ASDisplayNode+AsyncDisplay.mm.
[layer displayIfNeeded]; [layer displayIfNeeded];
@@ -1722,6 +1722,10 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
[self clearFetchedData]; [self clearFetchedData];
} }
- (void)visibilityDidChange:(BOOL)isVisible
{
}
/** /**
* We currently only set interface state on nodes in table/collection views. For other nodes, if they are * We currently only set interface state on nodes in table/collection views. For other nodes, if they are
* in the hierarchy we enable all ASInterfaceState types with `ASInterfaceStateInHierarchy`, otherwise `None`. * in the hierarchy we enable all ASInterfaceState types with `ASInterfaceStateInHierarchy`, otherwise `None`.
@@ -1776,11 +1780,17 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
// Entered or exited data loading state. // Entered or exited data loading state.
if ((newState & ASInterfaceStateVisible) != (oldState & ASInterfaceStateVisible)) { if ((newState & ASInterfaceStateVisible) != (oldState & ASInterfaceStateVisible)) {
if (newState & ASInterfaceStateVisible) { if (newState & ASInterfaceStateVisible) {
// Consider providing a -didBecomeVisible. [self visibilityDidChange:YES];
} else { } else {
// Consider providing a -didBecomeInvisible. [self visibilityDidChange:NO];
} }
} }
[self interfaceStateDidChange:newState fromState:oldState];
}
- (void)interfaceStateDidChange:(ASInterfaceState)newState fromState:(ASInterfaceState)oldState
{
} }
- (void)enterInterfaceState:(ASInterfaceState)interfaceState - (void)enterInterfaceState:(ASInterfaceState)interfaceState
@@ -2138,7 +2148,7 @@ static void _recursivelySetDisplaySuspended(ASDisplayNode *node, CALayer *layer,
self.asyncLayer.displaySuspended = flag; self.asyncLayer.displaySuspended = flag;
if ([self _implementsDisplay]) { if ([self __implementsDisplay]) {
if (flag) { if (flag) {
[_supernode subnodeDisplayDidFinish:self]; [_supernode subnodeDisplayDidFinish:self];
} else { } else {
@@ -2307,7 +2317,6 @@ static void _recursivelySetDisplaySuspended(ASDisplayNode *node, CALayer *layer,
static const char *ASDisplayNodeAssociatedNodeKey = "ASAssociatedNode"; static const char *ASDisplayNodeAssociatedNodeKey = "ASAssociatedNode";
@implementation UIView (ASDisplayNodeInternal) @implementation UIView (ASDisplayNodeInternal)
@dynamic asyncdisplaykit_node;
- (void)setAsyncdisplaykit_node:(ASDisplayNode *)node - (void)setAsyncdisplaykit_node:(ASDisplayNode *)node
{ {
@@ -2316,16 +2325,24 @@ static const char *ASDisplayNodeAssociatedNodeKey = "ASAssociatedNode";
- (ASDisplayNode *)asyncdisplaykit_node - (ASDisplayNode *)asyncdisplaykit_node
{ {
ASDisplayNode *node = objc_getAssociatedObject(self, ASDisplayNodeAssociatedNodeKey); return objc_getAssociatedObject(self, ASDisplayNodeAssociatedNodeKey);
return node;
} }
@end @end
@implementation CALayer (ASDisplayNodeInternal) @implementation CALayer (ASDisplayNodeInternal)
@dynamic asyncdisplaykit_node;
@end
- (void)setAsyncdisplaykit_node:(ASDisplayNode *)node
{
objc_setAssociatedObject(self, ASDisplayNodeAssociatedNodeKey, node, OBJC_ASSOCIATION_ASSIGN); // Weak reference to avoid cycle, since the node retains the layer.
}
- (ASDisplayNode *)asyncdisplaykit_node
{
return objc_getAssociatedObject(self, ASDisplayNodeAssociatedNodeKey);
}
@end
@implementation UIView (AsyncDisplayKit) @implementation UIView (AsyncDisplayKit)

View File

@@ -14,7 +14,7 @@ NS_ASSUME_NONNULL_BEGIN
@interface ASMapNode : ASImageNode @interface ASMapNode : ASImageNode
/** /**
The current region of ASMapNode. This can be set at any time and ASMapNode will animate the change. The current region of ASMapNode. This can be set at any time and ASMapNode will animate the change. This property may be set from a background thread before the node is loaded, and will automatically be applied to define the region of the static snapshot (if .liveMap = NO) or the internal MKMapView (otherwise).
*/ */
@property (nonatomic, assign) MKCoordinateRegion region; @property (nonatomic, assign) MKCoordinateRegion region;
@@ -24,7 +24,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (nullable, nonatomic, readonly) MKMapView *mapView; @property (nullable, nonatomic, readonly) MKMapView *mapView;
/** /**
Set this to YES to turn the snapshot into an interactive MKMapView and vice versa. Defaults to NO. Set this to YES to turn the snapshot into an interactive MKMapView and vice versa. Defaults to NO. This property may be set on a background thread before the node is loaded, and will automatically be actioned, once the node is loaded.
*/ */
@property (nonatomic, assign, getter=isLiveMap) BOOL liveMap; @property (nonatomic, assign, getter=isLiveMap) BOOL liveMap;

View File

@@ -26,6 +26,7 @@
@synthesize needsMapReloadOnBoundsChange = _needsMapReloadOnBoundsChange; @synthesize needsMapReloadOnBoundsChange = _needsMapReloadOnBoundsChange;
@synthesize mapDelegate = _mapDelegate; @synthesize mapDelegate = _mapDelegate;
@synthesize region = _region; @synthesize region = _region;
@synthesize liveMap = _liveMap;
#pragma mark - Lifecycle #pragma mark - Lifecycle
- (instancetype)init - (instancetype)init
@@ -37,9 +38,11 @@
self.clipsToBounds = YES; self.clipsToBounds = YES;
_needsMapReloadOnBoundsChange = YES; _needsMapReloadOnBoundsChange = YES;
_liveMap = NO;
_centerCoordinateOfMap = kCLLocationCoordinate2DInvalid; _centerCoordinateOfMap = kCLLocationCoordinate2DInvalid;
_region = MKCoordinateRegionMake(CLLocationCoordinate2DMake(43.432858, 13.183671), MKCoordinateSpanMake(0.2, 0.2));
//Default world-scale view
_region = MKCoordinateRegionForMapRect(MKMapRectWorld);
_options = [[MKMapSnapshotOptions alloc] init]; _options = [[MKMapSnapshotOptions alloc] init];
_options.region = _region; _options.region = _region;
@@ -50,26 +53,31 @@
- (void)didLoad - (void)didLoad
{ {
[super didLoad]; [super didLoad];
if ([self wasLiveMapPreviously]) { if (self.isLiveMap) {
self.userInteractionEnabled = YES; self.userInteractionEnabled = YES;
[self addLiveMap]; [self addLiveMap];
} }
} }
- (void)setLayerBacked:(BOOL)layerBacked
{
ASDisplayNodeAssert(!self.isLiveMap, @"ASMapNode can not be layer backed whilst .liveMap = YES, set .liveMap = NO to use layer backing.");
[super setLayerBacked:layerBacked];
}
- (void)fetchData - (void)fetchData
{ {
[super fetchData]; [super fetchData];
if ([self wasLiveMapPreviously]) { if (self.isLiveMap) {
[self addLiveMap]; [self addLiveMap];
} else { } else {
[self setUpSnapshotter];
[self takeSnapshot]; [self takeSnapshot];
} }
} }
- (void)clearFetchedData - (void)clearContents
{ {
[super clearFetchedData]; [super clearContents];
if (self.isLiveMap) { if (self.isLiveMap) {
[self removeLiveMap]; [self removeLiveMap];
} }
@@ -79,17 +87,21 @@
- (BOOL)isLiveMap - (BOOL)isLiveMap
{ {
return (_mapView != nil); ASDN::MutexLocker l(_propertyLock);
return _liveMap;
} }
- (void)setLiveMap:(BOOL)liveMap - (void)setLiveMap:(BOOL)liveMap
{ {
liveMap ? [self addLiveMap] : [self removeLiveMap]; ASDisplayNodeAssert(!self.isLayerBacked, @"ASMapNode can not use the interactive map feature whilst .isLayerBacked = YES, set .layerBacked = NO to use the interactive map feature.");
} ASDN::MutexLocker l(_propertyLock);
if (liveMap == _liveMap) {
- (BOOL)wasLiveMapPreviously return;
{ }
return CLLocationCoordinate2DIsValid(_centerCoordinateOfMap); _liveMap = liveMap;
if (self.nodeLoaded) {
liveMap ? [self addLiveMap] : [self removeLiveMap];
}
} }
- (BOOL)needsMapReloadOnBoundsChange - (BOOL)needsMapReloadOnBoundsChange
@@ -127,63 +139,62 @@
- (void)takeSnapshot - (void)takeSnapshot
{ {
if (!_snapshotter.isLoading) { if (!_snapshotter) {
[_snapshotter startWithCompletionHandler:^(MKMapSnapshot *snapshot, NSError *error) { [self setUpSnapshotter];
if (!error) { }
UIImage *image = snapshot.image; [_snapshotter cancel];
CGRect finalImageRect = CGRectMake(0, 0, image.size.width, image.size.height); [_snapshotter startWithCompletionHandler:^(MKMapSnapshot *snapshot, NSError *error) {
if (!error) {
UIGraphicsBeginImageContextWithOptions(image.size, YES, image.scale); UIImage *image = snapshot.image;
[image drawAtPoint:CGPointMake(0, 0)]; CGRect finalImageRect = CGRectMake(0, 0, image.size.width, image.size.height);
if (_annotations.count > 0 ) { UIGraphicsBeginImageContextWithOptions(image.size, YES, image.scale);
// Get a standard annotation view pin. Future implementations should use a custom annotation image property. [image drawAtPoint:CGPointMake(0, 0)];
MKAnnotationView *pin = [[MKPinAnnotationView alloc] initWithAnnotation:nil reuseIdentifier:@""];
UIImage *pinImage = pin.image; if (_annotations.count > 0 ) {
for (id<MKAnnotation>annotation in _annotations) // Get a standard annotation view pin. Future implementations should use a custom annotation image property.
MKAnnotationView *pin = [[MKPinAnnotationView alloc] initWithAnnotation:nil reuseIdentifier:@""];
UIImage *pinImage = pin.image;
for (id<MKAnnotation>annotation in _annotations)
{
CGPoint point = [snapshot pointForCoordinate:annotation.coordinate];
if (CGRectContainsPoint(finalImageRect, point))
{ {
CGPoint point = [snapshot pointForCoordinate:annotation.coordinate]; CGPoint pinCenterOffset = pin.centerOffset;
if (CGRectContainsPoint(finalImageRect, point)) point.x -= pin.bounds.size.width / 2.0;
{ point.y -= pin.bounds.size.height / 2.0;
CGPoint pinCenterOffset = pin.centerOffset; point.x += pinCenterOffset.x;
point.x -= pin.bounds.size.width / 2.0; point.y += pinCenterOffset.y;
point.y -= pin.bounds.size.height / 2.0; [pinImage drawAtPoint:point];
point.x += pinCenterOffset.x;
point.y += pinCenterOffset.y;
[pinImage drawAtPoint:point];
}
} }
} }
UIImage *finalImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
self.image = finalImage;
} }
}];
} UIImage *finalImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
self.image = finalImage;
}
}];
} }
- (void)setUpSnapshotter - (void)setUpSnapshotter
{ {
if (!_snapshotter) { ASDisplayNodeAssert(!CGSizeEqualToSize(CGSizeZero, self.calculatedSize), @"self.calculatedSize can not be zero. Make sure that you are setting a preferredFrameSize or wrapping ASMapNode in a ASRatioLayoutSpec or similar.");
ASDisplayNodeAssert(!CGSizeEqualToSize(CGSizeZero, self.calculatedSize), @"self.calculatedSize can not be zero. Make sure that you are setting a preferredFrameSize or wrapping ASMapNode in a ASRatioLayoutSpec or similar."); _options.size = self.calculatedSize;
_options.size = self.calculatedSize; _snapshotter = [[MKMapSnapshotter alloc] initWithOptions:_options];
_snapshotter = [[MKMapSnapshotter alloc] initWithOptions:_options];
}
} }
- (void)resetSnapshotter - (void)resetSnapshotter
{ {
if (!_snapshotter.isLoading) { [_snapshotter cancel];
_snapshotter = [[MKMapSnapshotter alloc] initWithOptions:_options]; _snapshotter = [[MKMapSnapshotter alloc] initWithOptions:_options];
}
} }
#pragma mark - Actions #pragma mark - Actions
- (void)addLiveMap - (void)addLiveMap
{ {
ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertMainThread();
if (!self.isLiveMap) { if (!_mapView) {
__weak ASMapNode *weakSelf = self; __weak ASMapNode *weakSelf = self;
_mapView = [[MKMapView alloc] initWithFrame:CGRectZero]; _mapView = [[MKMapView alloc] initWithFrame:CGRectZero];
_mapView.delegate = weakSelf.mapDelegate; _mapView.delegate = weakSelf.mapDelegate;
@@ -194,8 +205,6 @@
if (CLLocationCoordinate2DIsValid(_centerCoordinateOfMap)) { if (CLLocationCoordinate2DIsValid(_centerCoordinateOfMap)) {
[_mapView setCenterCoordinate:_centerCoordinateOfMap]; [_mapView setCenterCoordinate:_centerCoordinateOfMap];
} else {
_centerCoordinateOfMap = _options.region.center;
} }
} }
} }

View File

@@ -8,20 +8,35 @@
#import <AsyncDisplayKit/ASCollectionNode.h> #import <AsyncDisplayKit/ASCollectionNode.h>
@protocol ASPagerNodeDataSource; @class ASPagerNode;
@protocol ASPagerNodeDataSource <NSObject>
// This method replaces -collectionView:numberOfItemsInSection:
- (NSInteger)numberOfPagesInPagerNode:(ASPagerNode *)pagerNode;
// This method replaces -collectionView:nodeForItemAtIndexPath:
- (ASCellNode *)pagerNode:(ASPagerNode *)pagerNode nodeAtIndex:(NSInteger)index;
@end
@interface ASPagerNode : ASCollectionNode @interface ASPagerNode : ASCollectionNode
@property (weak, nonatomic) id<ASPagerNodeDataSource> dataSource; // Configures a default horizontal, paging flow layout with 0 inter-item spacing.
- (instancetype)init;
// Initializer with custom-configured flow layout properties.
- (instancetype)initWithFlowLayout:(UICollectionViewFlowLayout *)flowLayout;
// The underlying ASCollectionView object.
- (ASCollectionView *)collectionView;
// Delegate is optional, and uses the same protocol as ASCollectionNode.
// This includes UIScrollViewDelegate as well as most methods from UICollectionViewDelegate, like willDisplay...
@property (weak, nonatomic) id <ASCollectionDelegate> delegate;
// Data Source is required, and uses a different protocol from ASCollectionNode.
- (void)setDataSource:(id <ASPagerNodeDataSource>)dataSource;
- (id <ASPagerNodeDataSource>)dataSource;
- (void)scrollToPageAtIndex:(NSInteger)index animated:(BOOL)animated; - (void)scrollToPageAtIndex:(NSInteger)index animated:(BOOL)animated;
@end @end
@protocol ASPagerNodeDataSource <NSObject>
- (NSInteger)numberOfPagesInPagerNode:(ASPagerNode *)pagerNode;
- (ASCellNode *)pagerNode:(ASPagerNode *)pagerNode nodeAtIndex:(NSInteger)index;
@end

View File

@@ -7,41 +7,77 @@
// //
#import "ASPagerNode.h" #import "ASPagerNode.h"
#import "ASDelegateProxy.h"
#import <AsyncDisplayKit/AsyncDisplayKit.h> #import <AsyncDisplayKit/AsyncDisplayKit.h>
@interface ASPagerNode () <ASCollectionViewDataSource, ASCollectionViewDelegateFlowLayout> { @interface ASPagerNode () <ASCollectionDataSource, ASCollectionViewDelegateFlowLayout, ASDelegateProxyInterceptor> {
UICollectionViewFlowLayout *_flowLayout; UICollectionViewFlowLayout *_flowLayout;
ASPagerNodeProxy *_proxy;
id <ASPagerNodeDataSource> _pagerDataSource;
} }
@end @end
@implementation ASPagerNode @implementation ASPagerNode
@dynamic delegate;
- (instancetype)init - (instancetype)init
{ {
_flowLayout = [[UICollectionViewFlowLayout alloc] init]; UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
_flowLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal; flowLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
_flowLayout.minimumInteritemSpacing = 0; flowLayout.minimumInteritemSpacing = 0;
_flowLayout.minimumLineSpacing = 0; flowLayout.minimumLineSpacing = 0;
self = [super initWithCollectionViewLayout:_flowLayout]; return [self initWithFlowLayout:flowLayout];
}
- (instancetype)initWithFlowLayout:(UICollectionViewFlowLayout *)flowLayout
{
self = [super initWithCollectionViewLayout:flowLayout];
if (self != nil) { if (self != nil) {
_flowLayout = flowLayout;
} }
return self; return self;
} }
- (ASCollectionView *)collectionView
{
return self.view;
}
- (void)setDataSource:(id <ASPagerNodeDataSource>)pagerDataSource
{
if (pagerDataSource != _pagerDataSource) {
_pagerDataSource = pagerDataSource;
_proxy = pagerDataSource ? [[ASPagerNodeProxy alloc] initWithTarget:pagerDataSource interceptor:self] : nil;
super.dataSource = (id <ASCollectionDataSource>)_proxy;
}
}
- (void)proxyTargetHasDeallocated:(ASDelegateProxy *)proxy
{
[self setDataSource:nil];
}
- (id <ASPagerNodeDataSource>)dataSource
{
return _pagerDataSource;
}
- (void)didLoad - (void)didLoad
{ {
[super didLoad]; [super didLoad];
self.view.asyncDataSource = self; ASCollectionView *cv = self.view;
self.view.asyncDelegate = self; cv.asyncDataSource = self;
cv.asyncDelegate = self;
self.view.pagingEnabled = YES; cv.pagingEnabled = YES;
self.view.allowsSelection = NO; cv.allowsSelection = NO;
self.view.showsVerticalScrollIndicator = NO; cv.showsVerticalScrollIndicator = NO;
self.view.showsHorizontalScrollIndicator = NO; cv.showsHorizontalScrollIndicator = NO;
cv.scrollsToTop = NO;
ASRangeTuningParameters preloadParams = { .leadingBufferScreenfuls = 2.0, .trailingBufferScreenfuls = 2.0 }; ASRangeTuningParameters preloadParams = { .leadingBufferScreenfuls = 2.0, .trailingBufferScreenfuls = 2.0 };
ASRangeTuningParameters renderParams = { .leadingBufferScreenfuls = 1.0, .trailingBufferScreenfuls = 1.0 }; ASRangeTuningParameters renderParams = { .leadingBufferScreenfuls = 1.0, .trailingBufferScreenfuls = 1.0 };
@@ -61,14 +97,14 @@
- (ASCellNode *)collectionView:(ASCollectionView *)collectionView nodeForItemAtIndexPath:(NSIndexPath *)indexPath - (ASCellNode *)collectionView:(ASCollectionView *)collectionView nodeForItemAtIndexPath:(NSIndexPath *)indexPath
{ {
ASDisplayNodeAssert(self.dataSource != nil, @"ASPagerNode must have a data source to load paging nodes"); ASDisplayNodeAssert(_pagerDataSource != nil, @"ASPagerNode must have a data source to load paging nodes");
return [self.dataSource pagerNode:self nodeAtIndex:indexPath.item]; return [_pagerDataSource pagerNode:self nodeAtIndex:indexPath.item];
} }
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{ {
ASDisplayNodeAssert(self.dataSource != nil, @"ASPagerNode must have a data source to load paging nodes"); ASDisplayNodeAssert(_pagerDataSource != nil, @"ASPagerNode must have a data source to load paging nodes");
return [self.dataSource numberOfPagesInPagerNode:self]; return [_pagerDataSource numberOfPagesInPagerNode:self];
} }
- (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath - (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath

View File

@@ -14,8 +14,13 @@
*/ */
@interface ASTableNode : ASDisplayNode @interface ASTableNode : ASDisplayNode
- (instancetype)initWithStyle:(UITableViewStyle)style NS_DESIGNATED_INITIALIZER; - (instancetype)init; // UITableViewStylePlain
- (instancetype)initWithStyle:(UITableViewStyle)style;
@property (nonatomic, readonly) ASTableView *view; @property (nonatomic, readonly) ASTableView *view;
// These properties can be set without triggering the view to be created, so it's fine to set them in -init.
@property (weak, nonatomic) id <ASTableDelegate> delegate;
@property (weak, nonatomic) id <ASTableDataSource> dataSource;
@end @end

View File

@@ -8,18 +8,105 @@
#import "ASTableNode.h" #import "ASTableNode.h"
@interface _ASTablePendingState : NSObject
@property (weak, nonatomic) id <ASTableDelegate> delegate;
@property (weak, nonatomic) id <ASTableDataSource> dataSource;
@end
@implementation _ASTablePendingState
@end
@interface ASTableNode ()
@property (nonatomic) _ASTablePendingState *pendingState;
@end
@interface ASTableView ()
- (instancetype)_initWithFrame:(CGRect)frame style:(UITableViewStyle)style dataControllerClass:(Class)dataControllerClass;
@end
@implementation ASTableNode @implementation ASTableNode
- (instancetype)initWithStyle:(UITableViewStyle)style - (instancetype)_initWithStyle:(UITableViewStyle)style dataControllerClass:(Class)dataControllerClass
{ {
if (self = [super initWithViewBlock:^UIView *{ if (self = [super initWithViewBlock:^UIView *{ return [[ASTableView alloc] _initWithFrame:CGRectZero
return [[ASTableView alloc] initWithFrame:CGRectZero style:style]; style:style
}]) { dataControllerClass:dataControllerClass]; }]) {
return self; return self;
} }
return nil; return nil;
} }
- (instancetype)initWithStyle:(UITableViewStyle)style
{
return [self _initWithStyle:style dataControllerClass:nil];
}
- (instancetype)init
{
return [self _initWithStyle:UITableViewStylePlain dataControllerClass:nil];
}
- (void)didLoad
{
[super didLoad];
if (_pendingState) {
_ASTablePendingState *pendingState = _pendingState;
self.pendingState = nil;
ASTableView *view = self.view;
view.asyncDelegate = pendingState.delegate;
view.asyncDataSource = pendingState.dataSource;
}
}
- (_ASTablePendingState *)pendingState
{
if (!_pendingState && ![self isNodeLoaded]) {
self.pendingState = [[_ASTablePendingState alloc] init];
}
ASDisplayNodeAssert(![self isNodeLoaded] || !_pendingState, @"ASTableNode should not have a pendingState once it is loaded");
return _pendingState;
}
- (void)setDelegate:(id <ASTableDelegate>)delegate
{
if ([self pendingState]) {
_pendingState.delegate = delegate;
} else {
ASDisplayNodeAssert([self isNodeLoaded], @"ASTableNode should be loaded if pendingState doesn't exist");
self.view.asyncDelegate = delegate;
}
}
- (id <ASTableDelegate>)delegate
{
if ([self pendingState]) {
return _pendingState.delegate;
} else {
return self.view.asyncDelegate;
}
}
- (void)setDataSource:(id <ASTableDataSource>)dataSource
{
if ([self pendingState]) {
_pendingState.dataSource = dataSource;
} else {
ASDisplayNodeAssert([self isNodeLoaded], @"ASTableNode should be loaded if pendingState doesn't exist");
self.view.asyncDataSource = dataSource;
}
}
- (id <ASTableDataSource>)dataSource
{
if ([self pendingState]) {
return _pendingState.dataSource;
} else {
return self.view.asyncDataSource;
}
}
- (ASTableView *)view - (ASTableView *)view
{ {
return (ASTableView *)[super view]; return (ASTableView *)[super view];

View File

@@ -7,18 +7,16 @@
*/ */
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASRangeController.h> #import <AsyncDisplayKit/ASRangeController.h>
#import <AsyncDisplayKit/ASTableViewProtocols.h> #import <AsyncDisplayKit/ASTableViewProtocols.h>
#import <AsyncDisplayKit/ASBaseDefines.h> #import <AsyncDisplayKit/ASBaseDefines.h>
#import <AsyncDisplayKit/ASBatchContext.h> #import <AsyncDisplayKit/ASBatchContext.h>
NS_ASSUME_NONNULL_BEGIN
@class ASCellNode; @class ASCellNode;
@protocol ASTableViewDataSource; @protocol ASTableDataSource;
@protocol ASTableViewDelegate; @protocol ASTableDelegate;
NS_ASSUME_NONNULL_BEGIN
/** /**
* Node-based table view. * Node-based table view.
@@ -28,8 +26,8 @@ NS_ASSUME_NONNULL_BEGIN
*/ */
@interface ASTableView : UITableView @interface ASTableView : UITableView
@property (nonatomic, weak) id<ASTableViewDelegate> asyncDelegate; // must not be nil @property (nonatomic, weak) id<ASTableDelegate> asyncDelegate;
@property (nonatomic, weak) id<ASTableViewDataSource> asyncDataSource; @property (nonatomic, weak) id<ASTableDataSource> asyncDataSource;
/** /**
* Initializer. * Initializer.
@@ -281,7 +279,7 @@ NS_ASSUME_NONNULL_BEGIN
/** /**
* This is a node-based UITableViewDataSource. * This is a node-based UITableViewDataSource.
*/ */
@protocol ASTableViewDataSource <ASCommonTableViewDataSource, NSObject> @protocol ASTableDataSource <ASCommonTableViewDataSource, NSObject>
/** /**
* Similar to -tableView:cellForRowAtIndexPath:. * Similar to -tableView:cellForRowAtIndexPath:.
@@ -318,6 +316,8 @@ NS_ASSUME_NONNULL_BEGIN
@end @end
@protocol ASTableViewDataSource <ASTableDataSource>
@end
/** /**
* This is a node-based UITableViewDelegate. * This is a node-based UITableViewDelegate.
@@ -325,7 +325,7 @@ NS_ASSUME_NONNULL_BEGIN
* Note that -tableView:heightForRowAtIndexPath: has been removed; instead, your custom ASCellNode subclasses are * Note that -tableView:heightForRowAtIndexPath: has been removed; instead, your custom ASCellNode subclasses are
* responsible for deciding their preferred onscreen height in -calculateSizeThatFits:. * responsible for deciding their preferred onscreen height in -calculateSizeThatFits:.
*/ */
@protocol ASTableViewDelegate <ASCommonTableViewDelegate, NSObject> @protocol ASTableDelegate <ASCommonTableViewDelegate, NSObject>
@optional @optional
@@ -361,4 +361,9 @@ NS_ASSUME_NONNULL_BEGIN
@end @end
<<<<<<< HEAD
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END
=======
@protocol ASTableViewDelegate <ASTableDelegate>;
@end
>>>>>>> master

View File

@@ -8,103 +8,26 @@
#import "ASTableView.h" #import "ASTableView.h"
#import "ASTableViewInternal.h" #import "ASTableViewInternal.h"
#import "ASTableNode.h"
#import "ASAssert.h" #import "ASAssert.h"
#import "ASBatchFetching.h" #import "ASBatchFetching.h"
#import "ASChangeSetDataController.h" #import "ASChangeSetDataController.h"
#import "ASCollectionViewLayoutController.h" #import "ASCollectionViewLayoutController.h"
#import "ASDelegateProxy.h"
#import "ASDisplayNode+FrameworkPrivate.h" #import "ASDisplayNode+FrameworkPrivate.h"
#import "ASInternalHelpers.h" #import "ASInternalHelpers.h"
#import "ASLayout.h" #import "ASLayout.h"
#import "ASLayoutController.h" #import "ASLayoutController.h"
#import "ASRangeController.h" #import "ASRangeController.h"
#import <CoreFoundation/CoreFoundation.h>
static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
//#define LOG(...) NSLog(__VA_ARGS__) //#define LOG(...) NSLog(__VA_ARGS__)
#define LOG(...) #define LOG(...)
#pragma mark -
#pragma mark Proxying.
/**
* ASTableView intercepts and/or overrides a few of UITableView's critical data source and delegate methods.
*
* Any selector included in this function *MUST* be implemented by ASTableView.
*/
static BOOL _isInterceptedSelector(SEL sel)
{
return (
// handled by ASTableView node<->cell machinery
sel == @selector(tableView:cellForRowAtIndexPath:) ||
sel == @selector(tableView:heightForRowAtIndexPath:) ||
// handled by ASRangeController
sel == @selector(numberOfSectionsInTableView:) ||
sel == @selector(tableView:numberOfRowsInSection:) ||
// used for ASRangeController visibility updates
sel == @selector(tableView:willDisplayCell:forRowAtIndexPath:) ||
sel == @selector(tableView:didEndDisplayingCell:forRowAtIndexPath:) ||
// used for batch fetching API
sel == @selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:)
);
}
/**
* Stand-in for UITableViewDataSource and UITableViewDelegate. Any method calls we intercept are routed to ASTableView;
* everything else leaves AsyncDisplayKit safely and arrives at the original intended data source and delegate.
*/
@interface _ASTableViewProxy : NSProxy
- (instancetype)initWithTarget:(id<NSObject>)target interceptor:(ASTableView *)interceptor;
@end
@implementation _ASTableViewProxy {
id<NSObject> __weak _target;
ASTableView * __weak _interceptor;
}
- (instancetype)initWithTarget:(id<NSObject>)target interceptor:(ASTableView *)interceptor
{
// -[NSProxy init] is undefined
if (!self) {
return nil;
}
ASDisplayNodeAssert(target, @"target must not be nil");
ASDisplayNodeAssert(interceptor, @"interceptor must not be nil");
_target = target;
_interceptor = interceptor;
return self;
}
- (BOOL)respondsToSelector:(SEL)aSelector
{
ASDisplayNodeAssert(_target, @"target must not be nil"); // catch weak ref's being nilled early
ASDisplayNodeAssert(_interceptor, @"interceptor must not be nil");
return (_isInterceptedSelector(aSelector) || [_target respondsToSelector:aSelector]);
}
- (id)forwardingTargetForSelector:(SEL)aSelector
{
ASDisplayNodeAssert(_target, @"target must not be nil"); // catch weak ref's being nilled early
ASDisplayNodeAssert(_interceptor, @"interceptor must not be nil");
if (_isInterceptedSelector(aSelector)) {
return _interceptor;
}
return [_target respondsToSelector:aSelector] ? _target : nil;
}
@end
#pragma mark - #pragma mark -
#pragma mark ASCellNode<->UITableViewCell bridging. #pragma mark ASCellNode<->UITableViewCell bridging.
@@ -156,13 +79,16 @@ static BOOL _isInterceptedSelector(SEL sel)
@end @end
#pragma mark - #pragma mark -
#pragma mark ASTableView #pragma mark ASTableView
@interface ASTableView () <ASRangeControllerDataSource, ASRangeControllerDelegate, ASDataControllerSource, _ASTableViewCellDelegate, ASCellNodeLayoutDelegate> { @interface ASTableNode ()
_ASTableViewProxy *_proxyDataSource; - (instancetype)_initWithStyle:(UITableViewStyle)style dataControllerClass:(Class)dataControllerClass;
_ASTableViewProxy *_proxyDelegate; @end
@interface ASTableView () <ASRangeControllerDataSource, ASRangeControllerDelegate, ASDataControllerSource, _ASTableViewCellDelegate, ASCellNodeLayoutDelegate, ASDelegateProxyInterceptor> {
ASTableViewProxy *_proxyDataSource;
ASTableViewProxy *_proxyDelegate;
ASFlowLayoutController *_layoutController; ASFlowLayoutController *_layoutController;
@@ -180,6 +106,7 @@ static BOOL _isInterceptedSelector(SEL sel)
CGFloat _nodesConstrainedWidth; CGFloat _nodesConstrainedWidth;
BOOL _ignoreNodesConstrainedWidthChange; BOOL _ignoreNodesConstrainedWidthChange;
BOOL _queuedNodeHeightUpdate; BOOL _queuedNodeHeightUpdate;
BOOL _isDeallocating;
} }
@property (atomic, assign) BOOL asyncDataSourceLocked; @property (atomic, assign) BOOL asyncDataSourceLocked;
@@ -224,6 +151,12 @@ static BOOL _isInterceptedSelector(SEL sel)
// If the initial size is 0, expect a size change very soon which is part of the initial configuration // If the initial size is 0, expect a size change very soon which is part of the initial configuration
// and should not trigger a relayout. // and should not trigger a relayout.
_ignoreNodesConstrainedWidthChange = (_nodesConstrainedWidth == 0); _ignoreNodesConstrainedWidthChange = (_nodesConstrainedWidth == 0);
_proxyDelegate = [[ASTableViewProxy alloc] initWithTarget:nil interceptor:self];
super.delegate = (id<UITableViewDelegate>)_proxyDelegate;
_proxyDataSource = [[ASTableViewProxy alloc] initWithTarget:nil interceptor:self];
super.dataSource = (id<UITableViewDataSource>)_proxyDataSource;
[self registerClass:_ASTableViewCell.class forCellReuseIdentifier:kCellReuseIdentifier]; [self registerClass:_ASTableViewCell.class forCellReuseIdentifier:kCellReuseIdentifier];
} }
@@ -233,41 +166,45 @@ static BOOL _isInterceptedSelector(SEL sel)
return [self initWithFrame:frame style:style asyncDataFetching:NO]; return [self initWithFrame:frame style:style asyncDataFetching:NO];
} }
// FIXME: This method is deprecated and will probably be removed in or shortly after 2.0.
- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style asyncDataFetching:(BOOL)asyncDataFetchingEnabled - (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style asyncDataFetching:(BOOL)asyncDataFetchingEnabled
{ {
return [self initWithFrame:frame style:style dataControllerClass:[self.class dataControllerClass] asyncDataFetching:asyncDataFetchingEnabled]; return [self initWithFrame:frame style:style dataControllerClass:[self.class dataControllerClass] asyncDataFetching:asyncDataFetchingEnabled];
} }
- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style dataControllerClass:(Class)dataControllerClass asyncDataFetching:(BOOL)asyncDataFetchingEnabled - (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style dataControllerClass:(Class)dataControllerClass asyncDataFetching:(BOOL)asyncDataFetchingEnabled
{
// ASTableNode *tableNode = [[ASTableNode alloc] _initWithStyle:style dataControllerClass:dataControllerClass];
// tableNode.frame = frame;
// return tableNode.view;
return [self _initWithFrame:frame style:style dataControllerClass:dataControllerClass];
}
- (instancetype)_initWithFrame:(CGRect)frame style:(UITableViewStyle)style dataControllerClass:(Class)dataControllerClass
{ {
if (!(self = [super initWithFrame:frame style:style])) if (!(self = [super initWithFrame:frame style:style]))
return nil; return nil;
// FIXME: asyncDataFetching is currently unreliable for some use cases.
// https://github.com/facebook/AsyncDisplayKit/issues/385
asyncDataFetchingEnabled = NO;
[self configureWithDataControllerClass:dataControllerClass asyncDataFetching:asyncDataFetchingEnabled]; if (!dataControllerClass) {
dataControllerClass = [self.class dataControllerClass];
}
[self configureWithDataControllerClass:dataControllerClass asyncDataFetching:NO];
return self; return self;
} }
- (instancetype)initWithCoder:(NSCoder *)aDecoder - (instancetype)initWithCoder:(NSCoder *)aDecoder
{ {
if (!(self = [super initWithCoder:aDecoder])) NSLog(@"Warning: AsyncDisplayKit is not designed to be used with Interface Builder. Table properties set in IB will be lost.");
return nil; return [self initWithFrame:CGRectZero style:UITableViewStylePlain];
[self configureWithDataControllerClass:[self.class dataControllerClass] asyncDataFetching:NO];
return self;
} }
- (void)dealloc - (void)dealloc
{ {
// Sometimes the UIKit classes can call back to their delegate even during deallocation. // Sometimes the UIKit classes can call back to their delegate even during deallocation.
// This bug might be iOS 7-specific. _isDeallocating = YES;
super.delegate = nil; [self setAsyncDelegate:nil];
super.dataSource = nil; [self setAsyncDataSource:nil];
} }
#pragma mark - #pragma mark -
@@ -290,17 +227,19 @@ static BOOL _isInterceptedSelector(SEL sel)
// Note: It's common to check if the value hasn't changed and short-circuit but we aren't doing that here to handle // Note: It's common to check if the value hasn't changed and short-circuit but we aren't doing that here to handle
// the (common) case of nilling the asyncDataSource in the ViewController's dealloc. In this case our _asyncDataSource // the (common) case of nilling the asyncDataSource in the ViewController's dealloc. In this case our _asyncDataSource
// will return as nil (ARC magic) even though the _proxyDataSource still exists. It's really important to nil out // will return as nil (ARC magic) even though the _proxyDataSource still exists. It's really important to nil out
// super.dataSource in this case because calls to _ASTableViewProxy will start failing and cause crashes. // super.dataSource in this case because calls to ASTableViewProxy will start failing and cause crashes.
super.dataSource = nil;
if (asyncDataSource == nil) { if (asyncDataSource == nil) {
super.dataSource = nil;
_asyncDataSource = nil; _asyncDataSource = nil;
_proxyDataSource = nil; _proxyDataSource = _isDeallocating ? nil : [[ASTableViewProxy alloc] initWithTarget:nil interceptor:self];
} else { } else {
_asyncDataSource = asyncDataSource; _asyncDataSource = asyncDataSource;
_proxyDataSource = [[_ASTableViewProxy alloc] initWithTarget:_asyncDataSource interceptor:self]; _proxyDataSource = [[ASTableViewProxy alloc] initWithTarget:_asyncDataSource interceptor:self];
super.dataSource = (id<UITableViewDataSource>)_proxyDataSource;
} }
super.dataSource = (id<UITableViewDataSource>)_proxyDataSource;
} }
- (void)setAsyncDelegate:(id<ASTableViewDelegate>)asyncDelegate - (void)setAsyncDelegate:(id<ASTableViewDelegate>)asyncDelegate
@@ -308,18 +247,30 @@ static BOOL _isInterceptedSelector(SEL sel)
// Note: It's common to check if the value hasn't changed and short-circuit but we aren't doing that here to handle // Note: It's common to check if the value hasn't changed and short-circuit but we aren't doing that here to handle
// the (common) case of nilling the asyncDelegate in the ViewController's dealloc. In this case our _asyncDelegate // the (common) case of nilling the asyncDelegate in the ViewController's dealloc. In this case our _asyncDelegate
// will return as nil (ARC magic) even though the _proxyDelegate still exists. It's really important to nil out // will return as nil (ARC magic) even though the _proxyDelegate still exists. It's really important to nil out
// super.delegate in this case because calls to _ASTableViewProxy will start failing and cause crashes. // super.delegate in this case because calls to ASTableViewProxy will start failing and cause crashes.
// Order is important here, the asyncDelegate must be callable while nilling super.delegate to avoid random crashes
// in UIScrollViewAccessibility.
super.delegate = nil;
if (asyncDelegate == nil) { if (asyncDelegate == nil) {
// order is important here, the delegate must be callable while nilling super.delegate to avoid random crashes
// in UIScrollViewAccessibility.
super.delegate = nil;
_asyncDelegate = nil; _asyncDelegate = nil;
_proxyDelegate = nil; _proxyDelegate = _isDeallocating ? nil : [[ASTableViewProxy alloc] initWithTarget:nil interceptor:self];
} else { } else {
_asyncDelegate = asyncDelegate; _asyncDelegate = asyncDelegate;
_proxyDelegate = [[_ASTableViewProxy alloc] initWithTarget:asyncDelegate interceptor:self]; _proxyDelegate = [[ASTableViewProxy alloc] initWithTarget:_asyncDelegate interceptor:self];
super.delegate = (id<UITableViewDelegate>)_proxyDelegate; }
super.delegate = (id<UITableViewDelegate>)_proxyDelegate;
}
- (void)proxyTargetHasDeallocated:(ASDelegateProxy *)proxy
{
if (proxy == _proxyDelegate) {
[self setAsyncDelegate:nil];
} else if (proxy == _proxyDataSource) {
[self setAsyncDataSource:nil];
} }
} }

View File

@@ -62,6 +62,15 @@ static NSString *ASTextNodeTruncationTokenAttributeName = @"ASTextNodeTruncation
- (void)dealloc - (void)dealloc
{ {
CGColorRelease(_backgroundColor); CGColorRelease(_backgroundColor);
// Destruction of the layout managers/containers/text storage is quite
// expensive, and can take some time, so we dispatch onto a bg queue to
// actually dealloc.
__block ASTextKitRenderer *renderer = _renderer;
ASPerformBlockOnBackgroundThread(^{
renderer = nil;
});
_renderer = nil;
} }
@end @end
@@ -97,6 +106,8 @@ static NSString *ASTextNodeTruncationTokenAttributeName = @"ASTextNodeTruncation
#pragma mark - NSObject #pragma mark - NSObject
static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
- (instancetype)init - (instancetype)init
{ {
if (self = [super init]) { if (self = [super init]) {
@@ -120,7 +131,7 @@ static NSString *ASTextNodeTruncationTokenAttributeName = @"ASTextNodeTruncation
self.opaque = NO; self.opaque = NO;
self.backgroundColor = [UIColor clearColor]; self.backgroundColor = [UIColor clearColor];
self.linkAttributeNames = @[ NSLinkAttributeName ]; self.linkAttributeNames = DefaultLinkAttributeNames;
// Accessibility // Accessibility
self.isAccessibilityElement = YES; self.isAccessibilityElement = YES;
@@ -155,6 +166,8 @@ static NSString *ASTextNodeTruncationTokenAttributeName = @"ASTextNodeTruncation
if (_shadowColor != NULL) { if (_shadowColor != NULL) {
CGColorRelease(_shadowColor); CGColorRelease(_shadowColor);
} }
[self _invalidateRenderer];
if (_longPressGestureRecognizer) { if (_longPressGestureRecognizer) {
_longPressGestureRecognizer.delegate = nil; _longPressGestureRecognizer.delegate = nil;
@@ -187,6 +200,8 @@ static NSString *ASTextNodeTruncationTokenAttributeName = @"ASTextNodeTruncation
return [[self _renderer] size]; return [[self _renderer] size];
} }
// FIXME: Re-evaluate if it is still the right decision to clear the renderer at this stage.
// This code was written before TextKit and when 512MB devices were still the overwhelming majority.
- (void)displayDidFinish - (void)displayDidFinish
{ {
[super displayDidFinish]; [super displayDidFinish];
@@ -261,16 +276,17 @@ static NSString *ASTextNodeTruncationTokenAttributeName = @"ASTextNodeTruncation
- (void)_invalidateRenderer - (void)_invalidateRenderer
{ {
ASDN::MutexLocker l(_rendererLock); ASDN::MutexLocker l(_rendererLock);
if (_renderer) { if (_renderer) {
// Destruction of the layout managers/containers/text storage is quite // Destruction of the layout managers/containers/text storage is quite
// expensive, and can take some time, so we dispatch onto a bg queue to // expensive, and can take some time, so we dispatch onto a bg queue to
// actually dealloc. // actually dealloc.
__block ASTextKitRenderer *renderer = _renderer; __block ASTextKitRenderer *renderer = _renderer;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ ASPerformBlockOnBackgroundThread(^{
renderer = nil; renderer = nil;
}); });
_renderer = nil;
} }
_renderer = nil;
} }
- (void)_invalidateRendererIfNeeded - (void)_invalidateRendererIfNeeded
@@ -318,7 +334,8 @@ static NSString *ASTextNodeTruncationTokenAttributeName = @"ASTextNodeTruncation
#pragma mark - Modifying User Text #pragma mark - Modifying User Text
- (void)setAttributedString:(NSAttributedString *)attributedString { - (void)setAttributedString:(NSAttributedString *)attributedString
{
if (ASObjectIsEqual(attributedString, _attributedString)) { if (ASObjectIsEqual(attributedString, _attributedString)) {
return; return;
} }

View File

@@ -13,14 +13,23 @@ NS_ASSUME_NONNULL_BEGIN
@interface ASViewController : UIViewController @interface ASViewController : UIViewController
- (instancetype)initWithNode:(ASDisplayNode *)node NS_DESIGNATED_INITIALIZER;
@property (nonatomic, strong, readonly) ASDisplayNode *node; @property (nonatomic, strong, readonly) ASDisplayNode *node;
/**
* @abstract Passthrough property to the the .interfaceState of the node.
* @return The current ASInterfaceState of the node, indicating whether it is visible and other situational properties.
* @see ASInterfaceState
*/
@property (nonatomic, readonly) ASInterfaceState interfaceState;
// AsyncDisplayKit 2.0 BETA: This property is still being tested, but it allows // AsyncDisplayKit 2.0 BETA: This property is still being tested, but it allows
// blocking as a view controller becomes visible to ensure no placeholders flash onscreen. // blocking as a view controller becomes visible to ensure no placeholders flash onscreen.
// Refer to examples/SynchronousConcurrency, AsyncViewController.m // Refer to examples/SynchronousConcurrency, AsyncViewController.m
@property (nonatomic, assign) BOOL neverShowPlaceholders; @property (nonatomic, assign) BOOL neverShowPlaceholders;
- (instancetype)initWithNode:(ASDisplayNode *)node;
/** /**
* The constrained size used to measure the backing node. * The constrained size used to measure the backing node.

View File

@@ -16,6 +16,16 @@
BOOL _ensureDisplayed; BOOL _ensureDisplayed;
} }
- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
return [self initWithNode:nil];
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
return [self initWithNode:nil];
}
- (instancetype)initWithNode:(ASDisplayNode *)node - (instancetype)initWithNode:(ASDisplayNode *)node
{ {
if (!(self = [super initWithNibName:nil bundle:nil])) { if (!(self = [super initWithNibName:nil bundle:nil])) {
@@ -65,4 +75,9 @@
return ASSizeRangeMake(viewSize, viewSize); return ASSizeRangeMake(viewSize, viewSize);
} }
- (ASInterfaceState)interfaceState
{
return _node.interfaceState;
}
@end @end

View File

@@ -11,7 +11,7 @@
#import <AsyncDisplayKit/ASDimension.h> #import <AsyncDisplayKit/ASDimension.h>
@class ASCollectionView; @class ASCollectionView;
@protocol ASCollectionViewDelegate; @protocol ASCollectionDelegate;
@protocol ASCollectionViewLayoutInspecting <NSObject> @protocol ASCollectionViewLayoutInspecting <NSObject>
@@ -42,7 +42,7 @@
* *
* @discussion A great time to update perform selector caches! * @discussion A great time to update perform selector caches!
*/ */
- (void)didChangeCollectionViewDelegate:(id<ASCollectionViewDelegate>)delegate; - (void)didChangeCollectionViewDelegate:(id<ASCollectionDelegate>)delegate;
@end @end

View File

@@ -36,7 +36,7 @@
return self; return self;
} }
- (void)didChangeCollectionViewDelegate:(id<ASCollectionViewDelegate>)delegate; - (void)didChangeCollectionViewDelegate:(id<ASCollectionDelegate>)delegate;
{ {
if (delegate == nil) { if (delegate == nil) {
_delegateImplementsReferenceSizeForHeader = NO; _delegateImplementsReferenceSizeForHeader = NO;

View File

@@ -0,0 +1,55 @@
/* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in 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 <Foundation/Foundation.h>
@class ASDelegateProxy;
@protocol ASDelegateProxyInterceptor
@required
// Called if the target object is discovered to be nil if it had been non-nil at init time.
// This happens if the object is deallocated, because the proxy must maintain a weak reference to avoid cycles.
// Though the target object may become nil, the interceptor must not; it is assumed the interceptor owns the proxy.
- (void)proxyTargetHasDeallocated:(ASDelegateProxy *)proxy;
@end
/**
* Stand-in for delegates like UITableView or UICollectionView's delegate / dataSource.
* Any selectors flagged by "interceptsSelector" are routed to the interceptor object and are not delivered to the target.
* Everything else leaves AsyncDisplayKit safely and arrives at the original target object.
*/
@interface ASDelegateProxy : NSProxy
- (instancetype)initWithTarget:(id<NSObject>)target interceptor:(id <ASDelegateProxyInterceptor>)interceptor;
// This method must be overridden by a subclass.
- (BOOL)interceptsSelector:(SEL)selector;
@end
/**
* ASTableView intercepts and/or overrides a few of UITableView's critical data source and delegate methods.
*
* Any selector included in this function *MUST* be implemented by ASTableView.
*/
@interface ASTableViewProxy : ASDelegateProxy
@end
/**
* ASCollectionView intercepts and/or overrides a few of UICollectionView's critical data source and delegate methods.
*
* Any selector included in this function *MUST* be implemented by ASCollectionView.
*/
@interface ASCollectionViewProxy : ASDelegateProxy
@end
@interface ASPagerNodeProxy : ASDelegateProxy
@end

View File

@@ -0,0 +1,127 @@
/* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in 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 "ASDelegateProxy.h"
#import "ASTableView.h"
#import "ASCollectionView.h"
#import "ASAssert.h"
@implementation ASTableViewProxy
- (BOOL)interceptsSelector:(SEL)selector
{
return (
// handled by ASTableView node<->cell machinery
selector == @selector(tableView:cellForRowAtIndexPath:) ||
selector == @selector(tableView:heightForRowAtIndexPath:) ||
// handled by ASRangeController
selector == @selector(numberOfSectionsInTableView:) ||
selector == @selector(tableView:numberOfRowsInSection:) ||
// used for ASRangeController visibility updates
selector == @selector(tableView:willDisplayCell:forRowAtIndexPath:) ||
selector == @selector(tableView:didEndDisplayingCell:forRowAtIndexPath:) ||
// used for batch fetching API
selector == @selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:)
);
}
@end
@implementation ASCollectionViewProxy
- (BOOL)interceptsSelector:(SEL)selector
{
return (
// handled by ASCollectionView node<->cell machinery
selector == @selector(collectionView:cellForItemAtIndexPath:) ||
selector == @selector(collectionView:layout:sizeForItemAtIndexPath:) ||
selector == @selector(collectionView:viewForSupplementaryElementOfKind:atIndexPath:) ||
// handled by ASRangeController
selector == @selector(numberOfSectionsInCollectionView:) ||
selector == @selector(collectionView:numberOfItemsInSection:) ||
// used for ASRangeController visibility updates
selector == @selector(collectionView:willDisplayCell:forItemAtIndexPath:) ||
selector == @selector(collectionView:didEndDisplayingCell:forItemAtIndexPath:) ||
// used for batch fetching API
selector == @selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:)
);
}
@end
@implementation ASPagerNodeProxy
- (BOOL)interceptsSelector:(SEL)selector
{
return (
// handled by ASPagerNodeDataSource node<->cell machinery
selector == @selector(collectionView:nodeForItemAtIndexPath:) ||
selector == @selector(collectionView:numberOfItemsInSection:) ||
selector == @selector(collectionView:constrainedSizeForNodeAtIndexPath:)
);
}
@end
@implementation ASDelegateProxy {
id <NSObject> __weak _target;
id <ASDelegateProxyInterceptor> __weak _interceptor;
}
- (instancetype)initWithTarget:(id <NSObject>)target interceptor:(id <ASDelegateProxyInterceptor>)interceptor
{
// -[NSProxy init] is undefined
if (!self) {
return nil;
}
ASDisplayNodeAssert(interceptor, @"interceptor must not be nil");
_target = target ? : [NSNull null];
_interceptor = interceptor;
return self;
}
- (BOOL)respondsToSelector:(SEL)aSelector
{
if ([self interceptsSelector:aSelector]) {
return (_interceptor != nil);
} else {
// Also return NO if _target has become nil due to zeroing weak reference (or placeholder initialization).
return [_target respondsToSelector:aSelector];
}
}
- (id)forwardingTargetForSelector:(SEL)aSelector
{
if ([self interceptsSelector:aSelector]) {
return _interceptor;
} else {
if (_target) {
return [_target respondsToSelector:aSelector] ? _target : nil;
} else {
[_interceptor proxyTargetHasDeallocated:self];
return nil;
}
}
}
- (BOOL)interceptsSelector:(SEL)selector
{
ASDisplayNodeAssert(NO, @"This method must be overridden by subclasses.");
return NO;
}
@end

View File

@@ -10,19 +10,53 @@
#import "_ASAsyncTransaction.h" #import "_ASAsyncTransaction.h"
#import "_ASAsyncTransactionGroup.h" #import "_ASAsyncTransactionGroup.h"
#import <objc/runtime.h>
static const char *ASDisplayNodeAssociatedTransactionsKey = "ASAssociatedTransactions";
static const char *ASDisplayNodeAssociatedCurrentTransactionKey = "ASAssociatedCurrentTransaction";
@implementation CALayer (ASAsyncTransactionContainerTransactions) @implementation CALayer (ASAsyncTransactionContainerTransactions)
@dynamic asyncdisplaykit_asyncLayerTransactions;
@dynamic asyncdisplaykit_currentAsyncLayerTransaction; - (_ASAsyncTransaction *)asyncdisplaykit_currentAsyncLayerTransaction
{
return objc_getAssociatedObject(self, ASDisplayNodeAssociatedCurrentTransactionKey);
}
- (void)asyncdisplaykit_setCurrentAsyncLayerTransaction:(_ASAsyncTransaction *)transaction
{
objc_setAssociatedObject(self, ASDisplayNodeAssociatedCurrentTransactionKey, transaction, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSHashTable *)asyncdisplaykit_asyncLayerTransactions
{
return objc_getAssociatedObject(self, ASDisplayNodeAssociatedTransactionsKey);
}
- (void)asyncdisplaykit_setAsyncLayerTransactions:(NSHashTable *)transactions
{
objc_setAssociatedObject(self, ASDisplayNodeAssociatedTransactionsKey, transactions, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
// No-ops in the base class. Mostly exposed for testing. // No-ops in the base class. Mostly exposed for testing.
- (void)asyncdisplaykit_asyncTransactionContainerWillBeginTransaction:(_ASAsyncTransaction *)transaction {} - (void)asyncdisplaykit_asyncTransactionContainerWillBeginTransaction:(_ASAsyncTransaction *)transaction {}
- (void)asyncdisplaykit_asyncTransactionContainerDidCompleteTransaction:(_ASAsyncTransaction *)transaction {} - (void)asyncdisplaykit_asyncTransactionContainerDidCompleteTransaction:(_ASAsyncTransaction *)transaction {}
@end @end
static const char *ASAsyncTransactionIsContainerKey = "ASTransactionIsContainer";
@implementation CALayer (ASDisplayNodeAsyncTransactionContainer) @implementation CALayer (ASDisplayNodeAsyncTransactionContainer)
@dynamic asyncdisplaykit_asyncTransactionContainer; - (BOOL)asyncdisplaykit_isAsyncTransactionContainer
{
CFBooleanRef isContainerBool = (__bridge CFBooleanRef)objc_getAssociatedObject(self, ASAsyncTransactionIsContainerKey);
BOOL isContainer = (isContainerBool == kCFBooleanTrue);
return isContainer;
}
- (void)asyncdisplaykit_setAsyncTransactionContainer:(BOOL)isContainer
{
objc_setAssociatedObject(self, ASAsyncTransactionIsContainerKey, (id)(isContainer ? kCFBooleanTrue : kCFBooleanFalse), OBJC_ASSOCIATION_ASSIGN);
}
- (ASAsyncTransactionContainerState)asyncdisplaykit_asyncTransactionContainerState - (ASAsyncTransactionContainerState)asyncdisplaykit_asyncTransactionContainerState
{ {

View File

@@ -39,33 +39,34 @@
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
{ {
CGSize size = { CGSize maxConstrainedSize = CGSizeMake(constrainedSize.max.width, constrainedSize.max.height);
constrainedSize.max.width,
constrainedSize.max.height NSArray *children = self.children;
}; NSMutableArray *sublayouts = [NSMutableArray arrayWithCapacity:children.count];
NSMutableArray *sublayouts = [NSMutableArray arrayWithCapacity:self.children.count]; for (id<ASLayoutable> child in children) {
for (id<ASLayoutable> child in self.children) { CGPoint layoutPosition = child.layoutPosition;
CGSize autoMaxSize = { CGSize autoMaxSize = CGSizeMake(maxConstrainedSize.width - layoutPosition.x,
constrainedSize.max.width - child.layoutPosition.x, maxConstrainedSize.height - layoutPosition.y);
constrainedSize.max.height - child.layoutPosition.y
}; ASRelativeSizeRange childSizeRange = child.sizeRange;
ASSizeRange childConstraint = ASRelativeSizeRangeEqualToRelativeSizeRange(ASRelativeSizeRangeUnconstrained, child.sizeRange) BOOL childIsUnconstrained = ASRelativeSizeRangeEqualToRelativeSizeRange(ASRelativeSizeRangeUnconstrained, childSizeRange);
? ASSizeRangeMake({0, 0}, autoMaxSize) ASSizeRange childConstraint = childIsUnconstrained ? ASSizeRangeMake({0, 0}, autoMaxSize)
: ASRelativeSizeRangeResolve(child.sizeRange, size); : ASRelativeSizeRangeResolve(childSizeRange, maxConstrainedSize);
ASLayout *sublayout = [child measureWithSizeRange:childConstraint]; ASLayout *sublayout = [child measureWithSizeRange:childConstraint];
sublayout.position = child.layoutPosition; sublayout.position = layoutPosition;
[sublayouts addObject:sublayout]; [sublayouts addObject:sublayout];
} }
size.width = constrainedSize.min.width; CGSize size = CGSizeMake(constrainedSize.min.width, constrainedSize.min.height);
for (ASLayout *sublayout in sublayouts) {
size.width = MAX(size.width, sublayout.position.x + sublayout.size.width);
}
size.height = constrainedSize.min.height;
for (ASLayout *sublayout in sublayouts) { for (ASLayout *sublayout in sublayouts) {
size.height = MAX(size.height, sublayout.position.y + sublayout.size.height); CGPoint sublayoutPosition = sublayout.position;
CGSize sublayoutSize = sublayout.size;
size.width = MAX(size.width, sublayoutPosition.x + sublayoutSize.width);
size.height = MAX(size.height, sublayoutPosition.y + sublayoutSize.height);
} }
return [ASLayout layoutWithLayoutableObject:self return [ASLayout layoutWithLayoutableObject:self
@@ -75,12 +76,12 @@
- (void)setChild:(id<ASLayoutable>)child forIdentifier:(NSString *)identifier - (void)setChild:(id<ASLayoutable>)child forIdentifier:(NSString *)identifier
{ {
ASDisplayNodeAssert(NO, @"ASStackLayoutSpec only supports setChildren"); ASDisplayNodeAssert(NO, @"ASStaticLayoutSpec only supports setChildren");
} }
- (id<ASLayoutable>)childForIdentifier:(NSString *)identifier - (id<ASLayoutable>)childForIdentifier:(NSString *)identifier
{ {
ASDisplayNodeAssert(NO, @"ASStackLayoutSpec only supports children"); ASDisplayNodeAssert(NO, @"ASStaticLayoutSpec only supports children");
return nil; return nil;
} }

View File

@@ -175,8 +175,6 @@ static void __ASDisplayLayerDecrementConcurrentDisplayCount(BOOL displayIsAsync,
- (asyncdisplaykit_async_transaction_operation_block_t)_displayBlockWithAsynchronous:(BOOL)asynchronous isCancelledBlock:(asdisplaynode_iscancelled_block_t)isCancelledBlock rasterizing:(BOOL)rasterizing - (asyncdisplaykit_async_transaction_operation_block_t)_displayBlockWithAsynchronous:(BOOL)asynchronous isCancelledBlock:(asdisplaynode_iscancelled_block_t)isCancelledBlock rasterizing:(BOOL)rasterizing
{ {
id nodeClass = [self class];
asyncdisplaykit_async_transaction_operation_block_t displayBlock = nil; asyncdisplaykit_async_transaction_operation_block_t displayBlock = nil;
ASDisplayNodeAssert(rasterizing || !(_hierarchyState & ASHierarchyStateRasterized), @"Rasterized descendants should never display unless being drawn into the rasterized container."); ASDisplayNodeAssert(rasterizing || !(_hierarchyState & ASHierarchyStateRasterized), @"Rasterized descendants should never display unless being drawn into the rasterized container.");
@@ -235,7 +233,7 @@ static void __ASDisplayLayerDecrementConcurrentDisplayCount(BOOL displayIsAsync,
ASDN_DELAY_FOR_DISPLAY(); ASDN_DELAY_FOR_DISPLAY();
UIImage *result = [nodeClass displayWithParameters:drawParameters isCancelled:isCancelledBlock]; UIImage *result = [[self class] displayWithParameters:drawParameters isCancelled:isCancelledBlock];
__ASDisplayLayerDecrementConcurrentDisplayCount(asynchronous, rasterizing); __ASDisplayLayerDecrementConcurrentDisplayCount(asynchronous, rasterizing);
return result; return result;
}; };
@@ -265,7 +263,7 @@ static void __ASDisplayLayerDecrementConcurrentDisplayCount(BOOL displayIsAsync,
UIGraphicsBeginImageContextWithOptions(bounds.size, opaque, contentsScaleForDisplay); UIGraphicsBeginImageContextWithOptions(bounds.size, opaque, contentsScaleForDisplay);
} }
[nodeClass drawRect:bounds withParameters:drawParameters isCancelled:isCancelledBlock isRasterizing:rasterizing]; [[self class] drawRect:bounds withParameters:drawParameters isCancelled:isCancelledBlock isRasterizing:rasterizing];
if (isCancelledBlock()) { if (isCancelledBlock()) {
if (!rasterizing) { if (!rasterizing) {

View File

@@ -59,6 +59,9 @@ typedef NS_OPTIONS(NSUInteger, ASHierarchyState)
ASHierarchyState _hierarchyState; ASHierarchyState _hierarchyState;
} }
// The view class to use when creating a new display node instance. Defaults to _ASDisplayView.
+ (Class)viewClass;
// These methods are recursive, and either union or remove the provided interfaceState to all sub-elements. // These methods are recursive, and either union or remove the provided interfaceState to all sub-elements.
- (void)enterInterfaceState:(ASInterfaceState)interfaceState; - (void)enterInterfaceState:(ASInterfaceState)interfaceState;
- (void)exitInterfaceState:(ASInterfaceState)interfaceState; - (void)exitInterfaceState:(ASInterfaceState)interfaceState;

View File

@@ -446,19 +446,27 @@
{ {
_bridge_prologue; _bridge_prologue;
if (__loaded) { if (__loaded) {
return ASDisplayNodeUIContentModeFromCAContentsGravity(_layer.contentsGravity); if (_flags.layerBacked) {
return ASDisplayNodeUIContentModeFromCAContentsGravity(_layer.contentsGravity);
} else {
return _view.contentMode;
}
} else { } else {
return self.pendingViewState.contentMode; return self.pendingViewState.contentMode;
} }
} }
- (void)setContentMode:(UIViewContentMode)mode - (void)setContentMode:(UIViewContentMode)contentMode
{ {
_bridge_prologue; _bridge_prologue;
if (__loaded) { if (__loaded) {
_layer.contentsGravity = ASDisplayNodeCAContentsGravityFromUIContentMode(mode); if (_flags.layerBacked) {
_layer.contentsGravity = ASDisplayNodeCAContentsGravityFromUIContentMode(contentMode);
} else {
_view.contentMode = contentMode;
}
} else { } else {
self.pendingViewState.contentMode = mode; self.pendingViewState.contentMode = contentMode;
} }
} }

View File

@@ -144,6 +144,9 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides)
// Call didExitHierarchy if necessary and set inHierarchy = NO if visibility notifications are enabled on all of its parents // Call didExitHierarchy if necessary and set inHierarchy = NO if visibility notifications are enabled on all of its parents
- (void)__exitHierarchy; - (void)__exitHierarchy;
// Helper method to summarize whether or not the node run through the display process
- (BOOL)__implementsDisplay;
// Display the node's view/layer immediately on the current thread, bypassing the background thread rendering. Will be deprecated. // Display the node's view/layer immediately on the current thread, bypassing the background thread rendering. Will be deprecated.
- (void)displayImmediately; - (void)displayImmediately;

View File

@@ -18,6 +18,7 @@ ASDISPLAYNODE_EXTERN_C_BEGIN
BOOL ASSubclassOverridesSelector(Class superclass, Class subclass, SEL selector); BOOL ASSubclassOverridesSelector(Class superclass, Class subclass, SEL selector);
BOOL ASSubclassOverridesClassSelector(Class superclass, Class subclass, SEL selector); BOOL ASSubclassOverridesClassSelector(Class superclass, Class subclass, SEL selector);
void ASPerformBlockOnMainThread(void (^block)()); void ASPerformBlockOnMainThread(void (^block)());
void ASPerformBlockOnBackgroundThread(void (^block)()); // DISPATCH_QUEUE_PRIORITY_DEFAULT
CGFloat ASScreenScale(); CGFloat ASScreenScale();

View File

@@ -57,6 +57,18 @@ void ASPerformBlockOnMainThread(void (^block)())
} }
} }
void ASPerformBlockOnBackgroundThread(void (^block)())
{
if ([NSThread isMainThread]) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
block();
});
} else {
block();
}
}
CGFloat ASScreenScale() CGFloat ASScreenScale()
{ {
static CGFloat _scale; static CGFloat _scale;

View File

@@ -7,7 +7,7 @@
*/ */
#import "_ASCoreAnimationExtras.h" #import "_ASCoreAnimationExtras.h"
#import "ASEqualityHelpers.h"
#import "ASAssert.h" #import "ASAssert.h"
extern void ASDisplayNodeSetupLayerContentsWithResizableImage(CALayer *layer, UIImage *image) extern void ASDisplayNodeSetupLayerContentsWithResizableImage(CALayer *layer, UIImage *image)
@@ -87,7 +87,8 @@ static const struct _UIContentModeStringLUTEntry UIContentModeDescriptionLUT[] =
{UIViewContentModeBottomRight, @"bottomRight"}, {UIViewContentModeBottomRight, @"bottomRight"},
}; };
NSString *ASDisplayNodeNSStringFromUIContentMode(UIViewContentMode contentMode) { NSString *ASDisplayNodeNSStringFromUIContentMode(UIViewContentMode contentMode)
{
for (int i=0; i< ARRAY_COUNT(UIContentModeDescriptionLUT); i++) { for (int i=0; i< ARRAY_COUNT(UIContentModeDescriptionLUT); i++) {
if (UIContentModeDescriptionLUT[i].contentMode == contentMode) { if (UIContentModeDescriptionLUT[i].contentMode == contentMode) {
return UIContentModeDescriptionLUT[i].string; return UIContentModeDescriptionLUT[i].string;
@@ -96,16 +97,10 @@ NSString *ASDisplayNodeNSStringFromUIContentMode(UIViewContentMode contentMode)
return [NSString stringWithFormat:@"%d", (int)contentMode]; return [NSString stringWithFormat:@"%d", (int)contentMode];
} }
UIViewContentMode ASDisplayNodeUIContentModeFromNSString(NSString *string) { UIViewContentMode ASDisplayNodeUIContentModeFromNSString(NSString *string)
// If you passed one of the constants (this is just an optimization to avoid string comparison) {
for (int i=0; i < ARRAY_COUNT(UIContentModeDescriptionLUT); i++) { for (int i=0; i < ARRAY_COUNT(UIContentModeDescriptionLUT); i++) {
if (UIContentModeDescriptionLUT[i].string == string) { if (ASObjectIsEqual(UIContentModeDescriptionLUT[i].string, string)) {
return UIContentModeDescriptionLUT[i].contentMode;
}
}
// If you passed something isEqualToString: to one of the constants
for (int i=0; i < ARRAY_COUNT(UIContentModeDescriptionLUT); i++) {
if ([UIContentModeDescriptionLUT[i].string isEqualToString:string]) {
return UIContentModeDescriptionLUT[i].contentMode; return UIContentModeDescriptionLUT[i].contentMode;
} }
} }
@@ -124,20 +119,34 @@ NSString *const ASDisplayNodeCAContentsGravityFromUIContentMode(UIViewContentMod
return nil; return nil;
} }
#define ContentModeCacheSize 10
UIViewContentMode ASDisplayNodeUIContentModeFromCAContentsGravity(NSString *const contentsGravity) UIViewContentMode ASDisplayNodeUIContentModeFromCAContentsGravity(NSString *const contentsGravity)
{ {
// If you passed one of the constants (this is just an optimization to avoid string comparison) static int currentCacheIndex = 0;
for (int i=0; i < ARRAY_COUNT(UIContentModeCAGravityLUT); i++) { static NSMutableArray *cachedStrings = [NSMutableArray arrayWithCapacity:ContentModeCacheSize];
if (UIContentModeCAGravityLUT[i].string == contentsGravity) { static UIViewContentMode cachedModes[ContentModeCacheSize] = {};
return UIContentModeCAGravityLUT[i].contentMode;
} NSInteger foundCacheIndex = [cachedStrings indexOfObjectIdenticalTo:contentsGravity];
} if (foundCacheIndex != NSNotFound && foundCacheIndex < ContentModeCacheSize) {
// If you passed something isEqualToString: to one of the constants return cachedModes[foundCacheIndex];
for (int i=0; i < ARRAY_COUNT(UIContentModeCAGravityLUT); i++) { }
if ([UIContentModeCAGravityLUT[i].string isEqualToString:contentsGravity]) {
return UIContentModeCAGravityLUT[i].contentMode; for (int i = 0; i < ARRAY_COUNT(UIContentModeCAGravityLUT); i++) {
if (ASObjectIsEqual(UIContentModeCAGravityLUT[i].string, contentsGravity)) {
UIViewContentMode foundContentMode = UIContentModeCAGravityLUT[i].contentMode;
if (currentCacheIndex < ContentModeCacheSize) {
// Cache the input value. This is almost always a different pointer than in our LUT and will frequently
// be the same value for an overwhelming majority of inputs.
[cachedStrings addObject:contentsGravity];
cachedModes[currentCacheIndex] = foundContentMode;
currentCacheIndex++;
}
return foundContentMode;
} }
} }
ASDisplayNodeCAssert(contentsGravity, @"Encountered an unknown contentsGravity \"%@\". Is this a new version of iOS?", contentsGravity); ASDisplayNodeCAssert(contentsGravity, @"Encountered an unknown contentsGravity \"%@\". Is this a new version of iOS?", contentsGravity);
ASDisplayNodeCAssert(!contentsGravity, @"You passed nil to ASDisplayNodeUIContentModeFromCAContentsGravity. We're falling back to resize, but this is probably a bug."); ASDisplayNodeCAssert(!contentsGravity, @"You passed nil to ASDisplayNodeUIContentModeFromCAContentsGravity. We're falling back to resize, but this is probably a bug.");
// If asserts disabled, fall back to this // If asserts disabled, fall back to this

View File

@@ -135,19 +135,24 @@
@synthesize borderColor=borderColor; @synthesize borderColor=borderColor;
@synthesize asyncdisplaykit_asyncTransactionContainer=asyncTransactionContainer; @synthesize asyncdisplaykit_asyncTransactionContainer=asyncTransactionContainer;
static CGColorRef blackColorRef = NULL;
static UIColor *defaultTintColor = nil;
- (id)init - (id)init
{ {
if (!(self = [super init])) if (!(self = [super init]))
return nil; return nil;
// Default UIKit color is an RGB color
static CGColorRef black;
static dispatch_once_t onceToken; static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{ dispatch_once(&onceToken, ^{
// Default UIKit color is an RGB color
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
black = CGColorCreate(colorSpace, (CGFloat[]){0,0,0,1} ); blackColorRef = CGColorCreate(colorSpace, (CGFloat[]){0,0,0,1} );
CFRetain(black); CFRetain(blackColorRef);
CGColorSpaceRelease(colorSpace); CGColorSpaceRelease(colorSpace);
defaultTintColor = [UIColor colorWithRed:0.0 green:0.478 blue:1.0 alpha:1.0];
}); });
// Set defaults, these come from the defaults specified in CALayer and UIView // Set defaults, these come from the defaults specified in CALayer and UIView
@@ -156,7 +161,7 @@
frame = CGRectZero; frame = CGRectZero;
bounds = CGRectZero; bounds = CGRectZero;
backgroundColor = nil; backgroundColor = nil;
tintColor = [UIColor colorWithRed:0.0 green:0.478 blue:1.0 alpha:1.0]; tintColor = defaultTintColor;
contents = nil; contents = nil;
isHidden = NO; isHidden = NO;
needsDisplayOnBoundsChange = NO; needsDisplayOnBoundsChange = NO;
@@ -172,14 +177,12 @@
transform = CATransform3DIdentity; transform = CATransform3DIdentity;
sublayerTransform = CATransform3DIdentity; sublayerTransform = CATransform3DIdentity;
userInteractionEnabled = YES; userInteractionEnabled = YES;
CFRetain(black); shadowColor = blackColorRef;
shadowColor = black;
shadowOpacity = 0.0; shadowOpacity = 0.0;
shadowOffset = CGSizeMake(0, -3); shadowOffset = CGSizeMake(0, -3);
shadowRadius = 3; shadowRadius = 3;
borderWidth = 0; borderWidth = 0;
CFRetain(black); borderColor = blackColorRef;
borderColor = black;
isAccessibilityElement = NO; isAccessibilityElement = NO;
accessibilityLabel = nil; accessibilityLabel = nil;
accessibilityHint = nil; accessibilityHint = nil;
@@ -376,7 +379,9 @@
return; return;
} }
CGColorRelease(shadowColor); if (shadowColor != blackColorRef) {
CGColorRelease(shadowColor);
}
shadowColor = color; shadowColor = color;
CGColorRetain(shadowColor); CGColorRetain(shadowColor);
@@ -413,7 +418,9 @@
return; return;
} }
CGColorRelease(borderColor); if (borderColor != blackColorRef) {
CGColorRelease(borderColor);
}
borderColor = color; borderColor = color;
CGColorRetain(borderColor); CGColorRetain(borderColor);
@@ -1002,8 +1009,14 @@
- (void)dealloc - (void)dealloc
{ {
CGColorRelease(backgroundColor); CGColorRelease(backgroundColor);
CGColorRelease(shadowColor);
CGColorRelease(borderColor); if (shadowColor != blackColorRef) {
CGColorRelease(shadowColor);
}
if (borderColor != blackColorRef) {
CGColorRelease(borderColor);
}
} }
@end @end

View File

@@ -32,7 +32,9 @@ static NSCharacterSet *_defaultAvoidTruncationCharacterSet()
@implementation ASTextKitRenderer { @implementation ASTextKitRenderer {
CGSize _calculatedSize; CGSize _calculatedSize;
BOOL _sizeIsCalculated;
} }
@synthesize attributes = _attributes, context = _context, shadower = _shadower, truncater = _truncater;
#pragma mark - Initialization #pragma mark - Initialization
@@ -42,30 +44,50 @@ static NSCharacterSet *_defaultAvoidTruncationCharacterSet()
if (self = [super init]) { if (self = [super init]) {
_constrainedSize = constrainedSize; _constrainedSize = constrainedSize;
_attributes = attributes; _attributes = attributes;
_sizeIsCalculated = NO;
}
return self;
}
- (ASTextKitShadower *)shadower
{
if (!_shadower) {
ASTextKitAttributes attributes = _attributes;
_shadower = [[ASTextKitShadower alloc] initWithShadowOffset:attributes.shadowOffset _shadower = [[ASTextKitShadower alloc] initWithShadowOffset:attributes.shadowOffset
shadowColor:attributes.shadowColor shadowColor:attributes.shadowColor
shadowOpacity:attributes.shadowOpacity shadowOpacity:attributes.shadowOpacity
shadowRadius:attributes.shadowRadius]; shadowRadius:attributes.shadowRadius];
}
return _shadower;
}
- (ASTextKitTailTruncater *)truncater
{
if (!_truncater) {
ASTextKitAttributes attributes = _attributes;
// We must inset the constrained size by the size of the shadower. // We must inset the constrained size by the size of the shadower.
CGSize shadowConstrainedSize = [_shadower insetSizeWithConstrainedSize:_constrainedSize]; CGSize shadowConstrainedSize = [[self shadower] insetSizeWithConstrainedSize:_constrainedSize];
_truncater = [[ASTextKitTailTruncater alloc] initWithContext:[self context]
truncationAttributedString:attributes.truncationAttributedString
avoidTailTruncationSet:attributes.avoidTailTruncationSet ?: _defaultAvoidTruncationCharacterSet()
constrainedSize:shadowConstrainedSize];
}
return _truncater;
}
- (ASTextKitContext *)context
{
if (!_context) {
ASTextKitAttributes attributes = _attributes;
CGSize shadowConstrainedSize = [[self shadower] insetSizeWithConstrainedSize:_constrainedSize];
_context = [[ASTextKitContext alloc] initWithAttributedString:attributes.attributedString _context = [[ASTextKitContext alloc] initWithAttributedString:attributes.attributedString
lineBreakMode:attributes.lineBreakMode lineBreakMode:attributes.lineBreakMode
maximumNumberOfLines:attributes.maximumNumberOfLines maximumNumberOfLines:attributes.maximumNumberOfLines
exclusionPaths:attributes.exclusionPaths exclusionPaths:attributes.exclusionPaths
constrainedSize:shadowConstrainedSize constrainedSize:shadowConstrainedSize
layoutManagerFactory:attributes.layoutManagerFactory]; layoutManagerFactory:attributes.layoutManagerFactory];
_truncater = [[ASTextKitTailTruncater alloc] initWithContext:_context
truncationAttributedString:attributes.truncationAttributedString
avoidTailTruncationSet:attributes.avoidTailTruncationSet ?: _defaultAvoidTruncationCharacterSet()
constrainedSize:shadowConstrainedSize];
[self _calculateSize];
} }
return self; return _context;
} }
#pragma mark - Sizing #pragma mark - Sizing
@@ -74,14 +96,14 @@ static NSCharacterSet *_defaultAvoidTruncationCharacterSet()
{ {
// Force glyph generation and layout, which may not have happened yet (and isn't triggered by // Force glyph generation and layout, which may not have happened yet (and isn't triggered by
// -usedRectForTextContainer:). // -usedRectForTextContainer:).
[_context performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) { [[self context] performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) {
[layoutManager ensureLayoutForTextContainer:textContainer]; [layoutManager ensureLayoutForTextContainer:textContainer];
}]; }];
CGRect constrainedRect = {CGPointZero, _constrainedSize}; CGRect constrainedRect = {CGPointZero, _constrainedSize};
__block CGRect boundingRect; __block CGRect boundingRect;
[_context performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) { [[self context] performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) {
boundingRect = [layoutManager usedRectForTextContainer:textContainer]; boundingRect = [layoutManager usedRectForTextContainer:textContainer];
}]; }];
@@ -94,6 +116,10 @@ static NSCharacterSet *_defaultAvoidTruncationCharacterSet()
- (CGSize)size - (CGSize)size
{ {
if (!_sizeIsCalculated) {
[self _calculateSize];
_sizeIsCalculated = YES;
}
return _calculatedSize; return _calculatedSize;
} }
@@ -104,13 +130,13 @@ static NSCharacterSet *_defaultAvoidTruncationCharacterSet()
// We add an assertion so we can track the rare conditions where a graphics context is not present // We add an assertion so we can track the rare conditions where a graphics context is not present
ASDisplayNodeAssertNotNil(context, @"This is no good without a context."); ASDisplayNodeAssertNotNil(context, @"This is no good without a context.");
CGRect shadowInsetBounds = [_shadower insetRectWithConstrainedRect:bounds]; CGRect shadowInsetBounds = [[self shadower] insetRectWithConstrainedRect:bounds];
CGContextSaveGState(context); CGContextSaveGState(context);
[_shadower setShadowInContext:context]; [[self shadower] setShadowInContext:context];
UIGraphicsPushContext(context); UIGraphicsPushContext(context);
[_context performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) { [[self context] performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) {
NSRange glyphRange = [layoutManager glyphRangeForTextContainer:textContainer]; NSRange glyphRange = [layoutManager glyphRangeForTextContainer:textContainer];
[layoutManager drawBackgroundForGlyphRange:glyphRange atPoint:shadowInsetBounds.origin]; [layoutManager drawBackgroundForGlyphRange:glyphRange atPoint:shadowInsetBounds.origin];
[layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:shadowInsetBounds.origin]; [layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:shadowInsetBounds.origin];
@@ -125,7 +151,7 @@ static NSCharacterSet *_defaultAvoidTruncationCharacterSet()
- (NSUInteger)lineCount - (NSUInteger)lineCount
{ {
__block NSUInteger lineCount = 0; __block NSUInteger lineCount = 0;
[_context performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) { [[self context] performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) {
for (NSRange lineRange = { 0, 0 }; NSMaxRange(lineRange) < [layoutManager numberOfGlyphs]; lineCount++) { for (NSRange lineRange = { 0, 0 }; NSMaxRange(lineRange) < [layoutManager numberOfGlyphs]; lineCount++) {
[layoutManager lineFragmentRectForGlyphAtIndex:NSMaxRange(lineRange) effectiveRange:&lineRange]; [layoutManager lineFragmentRectForGlyphAtIndex:NSMaxRange(lineRange) effectiveRange:&lineRange];
} }
@@ -135,7 +161,7 @@ static NSCharacterSet *_defaultAvoidTruncationCharacterSet()
- (std::vector<NSRange>)visibleRanges - (std::vector<NSRange>)visibleRanges
{ {
return _truncater.visibleRanges; return [self truncater].visibleRanges;
} }
@end @end

View File

@@ -1,5 +1,5 @@
// //
// ASBasicImageDownloaderTests.m // ASZBasicImageDownloaderTests.m
// AsyncDisplayKit // AsyncDisplayKit
// //
// Created by Victor Mayorov on 10/06/15. // Created by Victor Mayorov on 10/06/15.
@@ -10,6 +10,7 @@
#import <AsyncDisplayKit/ASBasicImageDownloader.h> #import <AsyncDisplayKit/ASBasicImageDownloader.h>
// Z in the name to delay running until after the test instance is operating normally.
@interface ASBasicImageDownloaderTests : XCTestCase @interface ASBasicImageDownloaderTests : XCTestCase
@end @end
@@ -21,34 +22,27 @@
ASBasicImageDownloader *downloader = [ASBasicImageDownloader sharedImageDownloader]; ASBasicImageDownloader *downloader = [ASBasicImageDownloader sharedImageDownloader];
NSURL *URL = [NSURL URLWithString:@"http://wrongPath/wrongResource.png"]; NSURL *URL = [NSURL URLWithString:@"http://wrongPath/wrongResource.png"];
dispatch_group_t group = dispatch_group_create();
__block BOOL firstDone = NO; __block BOOL firstDone = NO;
dispatch_group_enter(group);
[downloader downloadImageWithURL:URL [downloader downloadImageWithURL:URL
callbackQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) callbackQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
downloadProgressBlock:nil downloadProgressBlock:nil
completion:^(CGImageRef image, NSError *error) { completion:^(CGImageRef image, NSError *error) {
firstDone = YES; firstDone = YES;
dispatch_group_leave(group);
}]; }];
__block BOOL secondDone = NO; __block BOOL secondDone = NO;
dispatch_group_enter(group);
[downloader downloadImageWithURL:URL [downloader downloadImageWithURL:URL
callbackQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) callbackQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
downloadProgressBlock:nil downloadProgressBlock:nil
completion:^(CGImageRef image, NSError *error) { completion:^(CGImageRef image, NSError *error) {
secondDone = YES; secondDone = YES;
dispatch_group_leave(group);
}]; }];
XCTAssert(0 == dispatch_group_wait(group, dispatch_time(0, 10 * 1000000000)), @"URL loading takes too long"); sleep(3);
XCTAssert(firstDone && secondDone, @"Not all ASBasicImageDownloader completion handlers have been called after 3 seconds");
XCTAssert(firstDone && secondDone, @"Not all handlers has been called");
} }
@end @end

View File

@@ -100,7 +100,7 @@
layout.scrollDirection = UICollectionViewScrollDirectionVertical; layout.scrollDirection = UICollectionViewScrollDirectionVertical;
CGRect rect = CGRectMake(0, 0, 100.0, 100.0); CGRect rect = CGRectMake(0, 0, 100.0, 100.0);
ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:rect collectionViewLayout:layout asyncDataFetching:NO]; ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:rect collectionViewLayout:layout];
collectionView.asyncDataSource = dataSource; collectionView.asyncDataSource = dataSource;
collectionView.asyncDelegate = delegate; collectionView.asyncDelegate = delegate;
@@ -122,7 +122,7 @@
layout.scrollDirection = UICollectionViewScrollDirectionVertical; layout.scrollDirection = UICollectionViewScrollDirectionVertical;
CGRect rect = CGRectMake(0, 0, 100.0, 100.0); CGRect rect = CGRectMake(0, 0, 100.0, 100.0);
ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:rect collectionViewLayout:layout asyncDataFetching:NO]; ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:rect collectionViewLayout:layout];
collectionView.asyncDataSource = dataSource; collectionView.asyncDataSource = dataSource;
collectionView.asyncDelegate = delegate; collectionView.asyncDelegate = delegate;
@@ -146,7 +146,7 @@
layout.headerReferenceSize = CGSizeMake(125.0, 125.0); layout.headerReferenceSize = CGSizeMake(125.0, 125.0);
CGRect rect = CGRectMake(0, 0, 100.0, 100.0); CGRect rect = CGRectMake(0, 0, 100.0, 100.0);
ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:rect collectionViewLayout:layout asyncDataFetching:NO]; ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:rect collectionViewLayout:layout];
collectionView.asyncDataSource = dataSource; collectionView.asyncDataSource = dataSource;
ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout];
@@ -167,7 +167,7 @@
layout.footerReferenceSize = CGSizeMake(125.0, 125.0); layout.footerReferenceSize = CGSizeMake(125.0, 125.0);
CGRect rect = CGRectMake(0, 0, 100.0, 100.0); CGRect rect = CGRectMake(0, 0, 100.0, 100.0);
ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:rect collectionViewLayout:layout asyncDataFetching:NO]; ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:rect collectionViewLayout:layout];
collectionView.asyncDataSource = dataSource; collectionView.asyncDataSource = dataSource;
ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout];
@@ -190,7 +190,7 @@
layout.scrollDirection = UICollectionViewScrollDirectionHorizontal; layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
CGRect rect = CGRectMake(0, 0, 100.0, 100.0); CGRect rect = CGRectMake(0, 0, 100.0, 100.0);
ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:rect collectionViewLayout:layout asyncDataFetching:NO]; ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:rect collectionViewLayout:layout];
collectionView.asyncDataSource = dataSource; collectionView.asyncDataSource = dataSource;
collectionView.asyncDelegate = delegate; collectionView.asyncDelegate = delegate;
@@ -212,7 +212,7 @@
layout.scrollDirection = UICollectionViewScrollDirectionHorizontal; layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
CGRect rect = CGRectMake(0, 0, 100.0, 100.0); CGRect rect = CGRectMake(0, 0, 100.0, 100.0);
ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:rect collectionViewLayout:layout asyncDataFetching:NO]; ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:rect collectionViewLayout:layout];
collectionView.asyncDataSource = dataSource; collectionView.asyncDataSource = dataSource;
collectionView.asyncDelegate = delegate; collectionView.asyncDelegate = delegate;
@@ -236,7 +236,7 @@
layout.headerReferenceSize = CGSizeMake(125.0, 125.0); layout.headerReferenceSize = CGSizeMake(125.0, 125.0);
CGRect rect = CGRectMake(0, 0, 100.0, 100.0); CGRect rect = CGRectMake(0, 0, 100.0, 100.0);
ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:rect collectionViewLayout:layout asyncDataFetching:NO]; ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:rect collectionViewLayout:layout];
collectionView.asyncDataSource = dataSource; collectionView.asyncDataSource = dataSource;
ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout];
@@ -257,7 +257,7 @@
layout.footerReferenceSize = CGSizeMake(125.0, 125.0); layout.footerReferenceSize = CGSizeMake(125.0, 125.0);
CGRect rect = CGRectMake(0, 0, 100.0, 100.0); CGRect rect = CGRectMake(0, 0, 100.0, 100.0);
ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:rect collectionViewLayout:layout asyncDataFetching:NO]; ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:rect collectionViewLayout:layout];
collectionView.asyncDataSource = dataSource; collectionView.asyncDataSource = dataSource;
ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout];
@@ -274,7 +274,7 @@
InspectorTestDataSource *dataSource = [[InspectorTestDataSource alloc] init]; InspectorTestDataSource *dataSource = [[InspectorTestDataSource alloc] init];
HeaderReferenceSizeTestDelegate *delegate = [[HeaderReferenceSizeTestDelegate alloc] init]; HeaderReferenceSizeTestDelegate *delegate = [[HeaderReferenceSizeTestDelegate alloc] init];
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout asyncDataFetching:NO]; ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
collectionView.asyncDataSource = dataSource; collectionView.asyncDataSource = dataSource;
collectionView.asyncDelegate = delegate; collectionView.asyncDelegate = delegate;
ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout];
@@ -291,7 +291,7 @@
- (void)testThatItRespondsWithTheDefaultNumberOfSections - (void)testThatItRespondsWithTheDefaultNumberOfSections
{ {
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout asyncDataFetching:NO]; ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout];
NSUInteger sections = [inspector collectionView:collectionView numberOfSectionsForSupplementaryNodeOfKind:UICollectionElementKindSectionHeader]; NSUInteger sections = [inspector collectionView:collectionView numberOfSectionsForSupplementaryNodeOfKind:UICollectionElementKindSectionHeader];
XCTAssert(sections == 1, @"should return 1 by default"); XCTAssert(sections == 1, @"should return 1 by default");
@@ -304,7 +304,7 @@
{ {
InspectorTestDataSource *dataSource = [[InspectorTestDataSource alloc] init]; InspectorTestDataSource *dataSource = [[InspectorTestDataSource alloc] init];
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout asyncDataFetching:NO]; ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
collectionView.asyncDataSource = dataSource; collectionView.asyncDataSource = dataSource;
ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout];
NSUInteger sections = [inspector collectionView:collectionView numberOfSectionsForSupplementaryNodeOfKind:UICollectionElementKindSectionHeader]; NSUInteger sections = [inspector collectionView:collectionView numberOfSectionsForSupplementaryNodeOfKind:UICollectionElementKindSectionHeader];
@@ -321,7 +321,7 @@
InspectorTestDataSource *dataSource = [[InspectorTestDataSource alloc] init]; InspectorTestDataSource *dataSource = [[InspectorTestDataSource alloc] init];
HeaderReferenceSizeTestDelegate *delegate = [[HeaderReferenceSizeTestDelegate alloc] init]; HeaderReferenceSizeTestDelegate *delegate = [[HeaderReferenceSizeTestDelegate alloc] init];
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout asyncDataFetching:NO]; ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
collectionView.asyncDataSource = dataSource; collectionView.asyncDataSource = dataSource;
collectionView.asyncDelegate = delegate; collectionView.asyncDelegate = delegate;
ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout];
@@ -338,7 +338,7 @@
HeaderReferenceSizeTestDelegate *delegate = [[HeaderReferenceSizeTestDelegate alloc] init]; HeaderReferenceSizeTestDelegate *delegate = [[HeaderReferenceSizeTestDelegate alloc] init];
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
layout.footerReferenceSize = CGSizeMake(125.0, 125.0); layout.footerReferenceSize = CGSizeMake(125.0, 125.0);
ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout asyncDataFetching:NO]; ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
collectionView.asyncDataSource = dataSource; collectionView.asyncDataSource = dataSource;
collectionView.asyncDelegate = delegate; collectionView.asyncDelegate = delegate;
ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout];
@@ -354,7 +354,7 @@
InspectorTestDataSource *dataSource = [[InspectorTestDataSource alloc] init]; InspectorTestDataSource *dataSource = [[InspectorTestDataSource alloc] init];
HeaderReferenceSizeTestDelegate *delegate = [[HeaderReferenceSizeTestDelegate alloc] init]; HeaderReferenceSizeTestDelegate *delegate = [[HeaderReferenceSizeTestDelegate alloc] init];
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout asyncDataFetching:NO]; ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
collectionView.asyncDataSource = dataSource; collectionView.asyncDataSource = dataSource;
collectionView.asyncDelegate = delegate; collectionView.asyncDelegate = delegate;
ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout];

View File

@@ -52,7 +52,6 @@
if (_willDeallocBlock) { if (_willDeallocBlock) {
_willDeallocBlock(self); _willDeallocBlock(self);
} }
[super dealloc];
} }
@end @end
@@ -78,7 +77,6 @@
if (_willDeallocBlock) { if (_willDeallocBlock) {
_willDeallocBlock(self); _willDeallocBlock(self);
} }
[super dealloc];
} }
@end @end
@@ -130,6 +128,7 @@
@implementation ASTableViewTests @implementation ASTableViewTests
// TODO: Convert this to ARC.
- (void)DISABLED_testTableViewDoesNotRetainItselfAndDelegate - (void)DISABLED_testTableViewDoesNotRetainItselfAndDelegate
{ {
ASTestTableView *tableView = [[ASTestTableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain]; ASTestTableView *tableView = [[ASTestTableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
@@ -148,11 +147,11 @@
tableView.asyncDataSource = delegate; tableView.asyncDataSource = delegate;
tableView.asyncDelegate = delegate; tableView.asyncDelegate = delegate;
[delegate release]; // [delegate release];
XCTAssertTrue(delegateDidDealloc, @"unexpected delegate lifetime:%@", delegate); XCTAssertTrue(delegateDidDealloc, @"unexpected delegate lifetime:%@", delegate);
XCTAssertNoThrow([tableView release], @"unexpected exception when deallocating table view:%@", tableView); // XCTAssertNoThrow([tableView release], @"unexpected exception when deallocating table view:%@", tableView);
XCTAssertTrue(tableViewDidDealloc, @"unexpected table view lifetime:%@", tableView); XCTAssertTrue(tableViewDidDealloc, @"unexpected table view lifetime:%@", tableView);
} }
@@ -399,7 +398,10 @@
style:UITableViewStylePlain style:UITableViewStylePlain
asyncDataFetching:YES]; asyncDataFetching:YES];
ASTableViewFilledDataSource *dataSource = [ASTableViewFilledDataSource new]; ASTableViewFilledDataSource *dataSource = [ASTableViewFilledDataSource new];
#if ! __has_feature(objc_arc)
#error This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
#endif
tableView.asyncDelegate = dataSource; tableView.asyncDelegate = dataSource;
tableView.asyncDataSource = dataSource; tableView.asyncDataSource = dataSource;

View File

@@ -15,6 +15,7 @@
AC3C4A6A1A11F47200143C57 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = AC3C4A691A11F47200143C57 /* ViewController.m */; }; AC3C4A6A1A11F47200143C57 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = AC3C4A691A11F47200143C57 /* ViewController.m */; };
AC3C4A8E1A11F80C00143C57 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = AC3C4A8D1A11F80C00143C57 /* Images.xcassets */; }; AC3C4A8E1A11F80C00143C57 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = AC3C4A8D1A11F80C00143C57 /* Images.xcassets */; };
FABD6D156A3EB118497E5CE6 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F02BAF78E68BC56FD8C161B7 /* libPods.a */; }; FABD6D156A3EB118497E5CE6 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F02BAF78E68BC56FD8C161B7 /* libPods.a */; };
FC3FCA801C2B1564009F6D6D /* PresentingViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FC3FCA7F1C2B1564009F6D6D /* PresentingViewController.m */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
@@ -34,6 +35,8 @@
AC3C4A8D1A11F80C00143C57 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; }; AC3C4A8D1A11F80C00143C57 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
CD1ABB23007FEDB31D8C1978 /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = "<group>"; }; CD1ABB23007FEDB31D8C1978 /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = "<group>"; };
F02BAF78E68BC56FD8C161B7 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; }; F02BAF78E68BC56FD8C161B7 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; };
FC3FCA7E1C2B1564009F6D6D /* PresentingViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PresentingViewController.h; sourceTree = "<group>"; };
FC3FCA7F1C2B1564009F6D6D /* PresentingViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PresentingViewController.m; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
@@ -82,6 +85,8 @@
AC3C4A661A11F47200143C57 /* AppDelegate.m */, AC3C4A661A11F47200143C57 /* AppDelegate.m */,
AC3C4A681A11F47200143C57 /* ViewController.h */, AC3C4A681A11F47200143C57 /* ViewController.h */,
AC3C4A691A11F47200143C57 /* ViewController.m */, AC3C4A691A11F47200143C57 /* ViewController.m */,
FC3FCA7E1C2B1564009F6D6D /* PresentingViewController.h */,
FC3FCA7F1C2B1564009F6D6D /* PresentingViewController.m */,
AC3C4A8D1A11F80C00143C57 /* Images.xcassets */, AC3C4A8D1A11F80C00143C57 /* Images.xcassets */,
AC3C4A611A11F47200143C57 /* Supporting Files */, AC3C4A611A11F47200143C57 /* Supporting Files */,
9B92C87F1BC17D3000EE46B2 /* SupplementaryNode.h */, 9B92C87F1BC17D3000EE46B2 /* SupplementaryNode.h */,
@@ -125,7 +130,6 @@
AC3C4A5B1A11F47200143C57 /* Frameworks */, AC3C4A5B1A11F47200143C57 /* Frameworks */,
AC3C4A5C1A11F47200143C57 /* Resources */, AC3C4A5C1A11F47200143C57 /* Resources */,
A6902C454C7661D0D277AC62 /* Copy Pods Resources */, A6902C454C7661D0D277AC62 /* Copy Pods Resources */,
EC37EEC9933F5786936BFE7C /* Embed Pods Frameworks */,
); );
buildRules = ( buildRules = (
); );
@@ -196,21 +200,6 @@
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n"; shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n";
showEnvVarsInLog = 0; showEnvVarsInLog = 0;
}; };
EC37EEC9933F5786936BFE7C /* Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Embed Pods Frameworks";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
F868CFBB21824CC9521B6588 /* Check Pods Manifest.lock */ = { F868CFBB21824CC9521B6588 /* Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@@ -236,6 +225,7 @@
25FDEC921BF31EE700CEB123 /* ItemNode.m in Sources */, 25FDEC921BF31EE700CEB123 /* ItemNode.m in Sources */,
AC3C4A6A1A11F47200143C57 /* ViewController.m in Sources */, AC3C4A6A1A11F47200143C57 /* ViewController.m in Sources */,
9B92C8811BC17D3000EE46B2 /* SupplementaryNode.m in Sources */, 9B92C8811BC17D3000EE46B2 /* SupplementaryNode.m in Sources */,
FC3FCA801C2B1564009F6D6D /* PresentingViewController.m in Sources */,
AC3C4A671A11F47200143C57 /* AppDelegate.m in Sources */, AC3C4A671A11F47200143C57 /* AppDelegate.m in Sources */,
AC3C4A641A11F47200143C57 /* main.m in Sources */, AC3C4A641A11F47200143C57 /* main.m in Sources */,
); );

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Sample.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>

View File

@@ -11,6 +11,8 @@
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
#define SIMULATE_WEB_RESPONSE 0
@interface AppDelegate : UIResponder <UIApplicationDelegate> @interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window; @property (strong, nonatomic) UIWindow *window;

View File

@@ -11,6 +11,7 @@
#import "AppDelegate.h" #import "AppDelegate.h"
#import "PresentingViewController.h"
#import "ViewController.h" #import "ViewController.h"
@implementation AppDelegate @implementation AppDelegate
@@ -31,10 +32,14 @@
- (void)pushNewViewControllerAnimated:(BOOL)animated - (void)pushNewViewControllerAnimated:(BOOL)animated
{ {
UINavigationController *navController = (UINavigationController *)self.window.rootViewController; UINavigationController *navController = (UINavigationController *)self.window.rootViewController;
#if SIMULATE_WEB_RESPONSE
UIViewController *viewController = [[PresentingViewController alloc] init];
#else
UIViewController *viewController = [[ViewController alloc] init]; UIViewController *viewController = [[ViewController alloc] init];
viewController.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Push Another Copy" style:UIBarButtonItemStylePlain target:self action:@selector(pushNewViewController)]; viewController.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Push Another Copy" style:UIBarButtonItemStylePlain target:self action:@selector(pushNewViewController)];
#endif
[navController pushViewController:viewController animated:animated]; [navController pushViewController:viewController animated:animated];
} }

View File

@@ -0,0 +1,13 @@
//
// PresentingViewController.h
// Sample
//
// Created by Tom King on 12/23/15.
// Copyright © 2015 Facebook. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface PresentingViewController : UIViewController
@end

View File

@@ -0,0 +1,30 @@
//
// PresentingViewController.m
// Sample
//
// Created by Tom King on 12/23/15.
// Copyright © 2015 Facebook. All rights reserved.
//
#import "PresentingViewController.h"
#import "ViewController.h"
@interface PresentingViewController ()
@end
@implementation PresentingViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Push Details" style:UIBarButtonItemStylePlain target:self action:@selector(pushNewViewController)];
}
- (void)pushNewViewController
{
ViewController *controller = [[ViewController alloc] init];
[self.navigationController pushViewController:controller animated:true];
}
@end

View File

@@ -18,6 +18,7 @@
@interface ViewController () <ASCollectionViewDataSource, ASCollectionViewDelegateFlowLayout> @interface ViewController () <ASCollectionViewDataSource, ASCollectionViewDelegateFlowLayout>
{ {
ASCollectionView *_collectionView; ASCollectionView *_collectionView;
NSArray *_data;
} }
@end @end
@@ -37,7 +38,7 @@
layout.headerReferenceSize = CGSizeMake(50.0, 50.0); layout.headerReferenceSize = CGSizeMake(50.0, 50.0);
layout.footerReferenceSize = CGSizeMake(50.0, 50.0); layout.footerReferenceSize = CGSizeMake(50.0, 50.0);
_collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout asyncDataFetching:YES]; _collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
_collectionView.asyncDataSource = self; _collectionView.asyncDataSource = self;
_collectionView.asyncDelegate = self; _collectionView.asyncDelegate = self;
_collectionView.backgroundColor = [UIColor whiteColor]; _collectionView.backgroundColor = [UIColor whiteColor];
@@ -45,8 +46,10 @@
[_collectionView registerSupplementaryNodeOfKind:UICollectionElementKindSectionHeader]; [_collectionView registerSupplementaryNodeOfKind:UICollectionElementKindSectionHeader];
[_collectionView registerSupplementaryNodeOfKind:UICollectionElementKindSectionFooter]; [_collectionView registerSupplementaryNodeOfKind:UICollectionElementKindSectionFooter];
#if !SIMULATE_WEB_RESPONSE
self.navigationItem.leftItemsSupplementBackButton = YES; self.navigationItem.leftItemsSupplementBackButton = YES;
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh target:self action:@selector(reloadTapped)]; self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh target:self action:@selector(reloadTapped)];
#endif
return self; return self;
} }
@@ -56,6 +59,31 @@
[super viewDidLoad]; [super viewDidLoad];
[self.view addSubview:_collectionView]; [self.view addSubview:_collectionView];
#if SIMULATE_WEB_RESPONSE
__weak typeof(self) weakSelf = self;
void(^mockWebService)() = ^{
NSLog(@"ViewController \"got data from a web service\"");
ViewController *strongSelf = weakSelf;
if (strongSelf != nil)
{
NSLog(@"ViewController is not nil");
strongSelf->_data = [[NSArray alloc] init];
[strongSelf->_collectionView performBatchUpdates:^{
[strongSelf->_collectionView insertSections:[[NSIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, 100)]];
} completion:nil];
NSLog(@"ViewController finished updating collectionView");
}
else {
NSLog(@"ViewController is nil - won't update collectionView");
}
};
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), mockWebService);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self.navigationController popViewControllerAnimated:YES];
});
#endif
} }
- (void)viewWillLayoutSubviews - (void)viewWillLayoutSubviews
@@ -101,7 +129,11 @@
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{ {
#if SIMULATE_WEB_RESPONSE
return _data == nil ? 0 : 100;
#else
return 100; return 100;
#endif
} }
- (void)collectionViewLockDataSource:(ASCollectionView *)collectionView - (void)collectionViewLockDataSource:(ASCollectionView *)collectionView
@@ -125,4 +157,11 @@
return UIEdgeInsetsMake(20.0, 20.0, 20.0, 20.0); return UIEdgeInsetsMake(20.0, 20.0, 20.0, 20.0);
} }
#if SIMULATE_WEB_RESPONSE
-(void)dealloc
{
NSLog(@"ViewController is deallocing");
}
#endif
@end @end

View File

@@ -130,7 +130,6 @@
AC3C4A5B1A11F47200143C57 /* Frameworks */, AC3C4A5B1A11F47200143C57 /* Frameworks */,
AC3C4A5C1A11F47200143C57 /* Resources */, AC3C4A5C1A11F47200143C57 /* Resources */,
A6902C454C7661D0D277AC62 /* Copy Pods Resources */, A6902C454C7661D0D277AC62 /* Copy Pods Resources */,
EC37EEC9933F5786936BFE7C /* Embed Pods Frameworks */,
); );
buildRules = ( buildRules = (
); );
@@ -201,21 +200,6 @@
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n"; shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n";
showEnvVarsInLog = 0; showEnvVarsInLog = 0;
}; };
EC37EEC9933F5786936BFE7C /* Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Embed Pods Frameworks";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
F868CFBB21824CC9521B6588 /* Check Pods Manifest.lock */ = { F868CFBB21824CC9521B6588 /* Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;