mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-09 07:41:40 +00:00
Merge pull request #1201 from maicki/ASRangeControllerCleanup
[ASRangeController] Remove deprecated range controller implementation that is no longer used.
This commit is contained in:
commit
cae47e23e1
@ -186,10 +186,6 @@
|
||||
257754C21BEE458E00737CA5 /* ASTextKitCoreTextAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 257754BB1BEE458E00737CA5 /* ASTextKitCoreTextAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
257754C31BEE458E00737CA5 /* ASTextNodeTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 257754BC1BEE458E00737CA5 /* ASTextNodeTypes.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
257754C41BEE458E00737CA5 /* ASTextNodeWordKerner.m in Sources */ = {isa = PBXBuildFile; fileRef = 257754BD1BEE458E00737CA5 /* ASTextNodeWordKerner.m */; };
|
||||
258FF4271C0D152600A83844 /* ASRangeHandlerVisible.h in Headers */ = {isa = PBXBuildFile; fileRef = 258FF4251C0D152600A83844 /* ASRangeHandlerVisible.h */; };
|
||||
258FF4281C0D152600A83844 /* ASRangeHandlerVisible.mm in Sources */ = {isa = PBXBuildFile; fileRef = 258FF4261C0D152600A83844 /* ASRangeHandlerVisible.mm */; };
|
||||
25A977EF1C0D2A5500406B62 /* ASRangeHandlerVisible.mm in Sources */ = {isa = PBXBuildFile; fileRef = 258FF4261C0D152600A83844 /* ASRangeHandlerVisible.mm */; };
|
||||
25BAA16F1C0D18D2002747C7 /* ASRangeHandlerVisible.h in Headers */ = {isa = PBXBuildFile; fileRef = 258FF4251C0D152600A83844 /* ASRangeHandlerVisible.h */; };
|
||||
25E327561C16819500A2170C /* ASPagerNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 25E327541C16819500A2170C /* ASPagerNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
25E327571C16819500A2170C /* ASPagerNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 25E327541C16819500A2170C /* ASPagerNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
25E327581C16819500A2170C /* ASPagerNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 25E327551C16819500A2170C /* ASPagerNode.m */; };
|
||||
@ -199,11 +195,6 @@
|
||||
2911485C1A77147A005D0878 /* ASControlNodeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 2911485B1A77147A005D0878 /* ASControlNodeTests.m */; };
|
||||
291B63FB1AA53A7A000A71B3 /* ASScrollDirection.h in Headers */ = {isa = PBXBuildFile; fileRef = 296A0A311A951715005ACEAA /* ASScrollDirection.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
292C599F1A956527007E5DD6 /* ASLayoutRangeType.h in Headers */ = {isa = PBXBuildFile; fileRef = 292C59991A956527007E5DD6 /* ASLayoutRangeType.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
292C59A01A956527007E5DD6 /* ASRangeHandlerPreload.h in Headers */ = {isa = PBXBuildFile; fileRef = 292C599A1A956527007E5DD6 /* ASRangeHandlerPreload.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
292C59A11A956527007E5DD6 /* ASRangeHandlerPreload.mm in Sources */ = {isa = PBXBuildFile; fileRef = 292C599B1A956527007E5DD6 /* ASRangeHandlerPreload.mm */; };
|
||||
292C59A21A956527007E5DD6 /* ASRangeHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 292C599C1A956527007E5DD6 /* ASRangeHandler.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
292C59A31A956527007E5DD6 /* ASRangeHandlerRender.h in Headers */ = {isa = PBXBuildFile; fileRef = 292C599D1A956527007E5DD6 /* ASRangeHandlerRender.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
292C59A41A956527007E5DD6 /* ASRangeHandlerRender.mm in Sources */ = {isa = PBXBuildFile; fileRef = 292C599E1A956527007E5DD6 /* ASRangeHandlerRender.mm */; };
|
||||
2967F9E21AB0A5190072E4AB /* ASBasicImageDownloaderInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 2967F9E11AB0A4CF0072E4AB /* ASBasicImageDownloaderInternal.h */; };
|
||||
296A0A351A951ABF005ACEAA /* ASBatchFetchingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 296A0A341A951ABF005ACEAA /* ASBatchFetchingTests.m */; };
|
||||
299DA1A91A828D2900162D41 /* ASBatchContext.h in Headers */ = {isa = PBXBuildFile; fileRef = 299DA1A71A828D2900162D41 /* ASBatchContext.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
@ -424,11 +415,6 @@
|
||||
B35062251B010EFD0018CF92 /* ASMutableAttributedStringBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = 058D09E9195D050800B7D73C /* ASMutableAttributedStringBuilder.m */; };
|
||||
B35062261B010EFD0018CF92 /* ASRangeController.h in Headers */ = {isa = PBXBuildFile; fileRef = 055F1A3619ABD413004DAFF1 /* ASRangeController.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
B35062271B010EFD0018CF92 /* ASRangeController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 055F1A3719ABD413004DAFF1 /* ASRangeController.mm */; };
|
||||
B35062281B010EFD0018CF92 /* ASRangeHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 292C599C1A956527007E5DD6 /* ASRangeHandler.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
B35062291B010EFD0018CF92 /* ASRangeHandlerPreload.h in Headers */ = {isa = PBXBuildFile; fileRef = 292C599A1A956527007E5DD6 /* ASRangeHandlerPreload.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
B350622A1B010EFD0018CF92 /* ASRangeHandlerPreload.mm in Sources */ = {isa = PBXBuildFile; fileRef = 292C599B1A956527007E5DD6 /* ASRangeHandlerPreload.mm */; };
|
||||
B350622B1B010EFD0018CF92 /* ASRangeHandlerRender.h in Headers */ = {isa = PBXBuildFile; fileRef = 292C599D1A956527007E5DD6 /* ASRangeHandlerRender.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
B350622C1B010EFD0018CF92 /* ASRangeHandlerRender.mm in Sources */ = {isa = PBXBuildFile; fileRef = 292C599E1A956527007E5DD6 /* ASRangeHandlerRender.mm */; };
|
||||
B350622D1B010EFD0018CF92 /* ASScrollDirection.h in Headers */ = {isa = PBXBuildFile; fileRef = 296A0A311A951715005ACEAA /* ASScrollDirection.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
B35062391B010EFD0018CF92 /* ASThread.h in Headers */ = {isa = PBXBuildFile; fileRef = 058D0A12195D050800B7D73C /* ASThread.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
B350623A1B010EFD0018CF92 /* NSMutableAttributedString+TextKitAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 058D09F5195D050800B7D73C /* NSMutableAttributedString+TextKitAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
@ -495,10 +481,6 @@
|
||||
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 */; };
|
||||
DECBD6EA1BE56E1900CF4905 /* ASButtonNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = DECBD6E61BE56E1900CF4905 /* ASButtonNode.mm */; };
|
||||
DECC2ECD1C35C1C600388446 /* ASRangeControllerBeta.h in Headers */ = {isa = PBXBuildFile; fileRef = DECC2ECB1C35C1C600388446 /* ASRangeControllerBeta.h */; };
|
||||
DECC2ECE1C35C1C600388446 /* ASRangeControllerBeta.h in Headers */ = {isa = PBXBuildFile; fileRef = DECC2ECB1C35C1C600388446 /* ASRangeControllerBeta.h */; };
|
||||
DECC2ECF1C35C1C600388446 /* ASRangeControllerBeta.mm in Sources */ = {isa = PBXBuildFile; fileRef = DECC2ECC1C35C1C600388446 /* ASRangeControllerBeta.mm */; };
|
||||
DECC2ED01C35C1C600388446 /* ASRangeControllerBeta.mm in Sources */ = {isa = PBXBuildFile; fileRef = DECC2ECC1C35C1C600388446 /* ASRangeControllerBeta.mm */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@ -688,17 +670,10 @@
|
||||
257754BB1BEE458E00737CA5 /* ASTextKitCoreTextAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASTextKitCoreTextAdditions.h; path = TextKit/ASTextKitCoreTextAdditions.h; sourceTree = "<group>"; };
|
||||
257754BC1BEE458E00737CA5 /* ASTextNodeTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASTextNodeTypes.h; path = TextKit/ASTextNodeTypes.h; sourceTree = "<group>"; };
|
||||
257754BD1BEE458E00737CA5 /* ASTextNodeWordKerner.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ASTextNodeWordKerner.m; path = TextKit/ASTextNodeWordKerner.m; sourceTree = "<group>"; };
|
||||
258FF4251C0D152600A83844 /* ASRangeHandlerVisible.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASRangeHandlerVisible.h; sourceTree = "<group>"; };
|
||||
258FF4261C0D152600A83844 /* ASRangeHandlerVisible.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASRangeHandlerVisible.mm; sourceTree = "<group>"; };
|
||||
25E327541C16819500A2170C /* ASPagerNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = ASPagerNode.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
|
||||
25E327551C16819500A2170C /* ASPagerNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = ASPagerNode.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
|
||||
2911485B1A77147A005D0878 /* ASControlNodeTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASControlNodeTests.m; sourceTree = "<group>"; };
|
||||
292C59991A956527007E5DD6 /* ASLayoutRangeType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASLayoutRangeType.h; sourceTree = "<group>"; };
|
||||
292C599A1A956527007E5DD6 /* ASRangeHandlerPreload.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASRangeHandlerPreload.h; sourceTree = "<group>"; };
|
||||
292C599B1A956527007E5DD6 /* ASRangeHandlerPreload.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASRangeHandlerPreload.mm; sourceTree = "<group>"; };
|
||||
292C599C1A956527007E5DD6 /* ASRangeHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASRangeHandler.h; sourceTree = "<group>"; };
|
||||
292C599D1A956527007E5DD6 /* ASRangeHandlerRender.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASRangeHandlerRender.h; sourceTree = "<group>"; };
|
||||
292C599E1A956527007E5DD6 /* ASRangeHandlerRender.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASRangeHandlerRender.mm; sourceTree = "<group>"; };
|
||||
2967F9E11AB0A4CF0072E4AB /* ASBasicImageDownloaderInternal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ASBasicImageDownloaderInternal.h; sourceTree = "<group>"; };
|
||||
296A0A311A951715005ACEAA /* ASScrollDirection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASScrollDirection.h; path = AsyncDisplayKit/Details/ASScrollDirection.h; sourceTree = SOURCE_ROOT; };
|
||||
296A0A341A951ABF005ACEAA /* ASBatchFetchingTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASBatchFetchingTests.m; sourceTree = "<group>"; };
|
||||
@ -817,8 +792,6 @@
|
||||
DEC146B51C37A16A004A0EE7 /* ASCollectionInternal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ASCollectionInternal.m; path = Details/ASCollectionInternal.m; 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>"; };
|
||||
DECC2ECB1C35C1C600388446 /* ASRangeControllerBeta.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASRangeControllerBeta.h; sourceTree = "<group>"; };
|
||||
DECC2ECC1C35C1C600388446 /* ASRangeControllerBeta.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASRangeControllerBeta.mm; sourceTree = "<group>"; };
|
||||
EFA731F0396842FF8AB635EE /* libPods-AsyncDisplayKitTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-AsyncDisplayKitTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
FB07EABBCF28656C6297BC2D /* Pods-AsyncDisplayKitTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AsyncDisplayKitTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-AsyncDisplayKitTests/Pods-AsyncDisplayKitTests.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
@ -1097,15 +1070,6 @@
|
||||
058D09E9195D050800B7D73C /* ASMutableAttributedStringBuilder.m */,
|
||||
055F1A3619ABD413004DAFF1 /* ASRangeController.h */,
|
||||
055F1A3719ABD413004DAFF1 /* ASRangeController.mm */,
|
||||
DECC2ECB1C35C1C600388446 /* ASRangeControllerBeta.h */,
|
||||
DECC2ECC1C35C1C600388446 /* ASRangeControllerBeta.mm */,
|
||||
292C599C1A956527007E5DD6 /* ASRangeHandler.h */,
|
||||
258FF4251C0D152600A83844 /* ASRangeHandlerVisible.h */,
|
||||
258FF4261C0D152600A83844 /* ASRangeHandlerVisible.mm */,
|
||||
292C599A1A956527007E5DD6 /* ASRangeHandlerPreload.h */,
|
||||
292C599B1A956527007E5DD6 /* ASRangeHandlerPreload.mm */,
|
||||
292C599D1A956527007E5DD6 /* ASRangeHandlerRender.h */,
|
||||
292C599E1A956527007E5DD6 /* ASRangeHandlerRender.mm */,
|
||||
296A0A311A951715005ACEAA /* ASScrollDirection.h */,
|
||||
205F0E111B371BD7007741D0 /* ASScrollDirection.m */,
|
||||
058D0A12195D050800B7D73C /* ASThread.h */,
|
||||
@ -1368,7 +1332,6 @@
|
||||
DECBD6E71BE56E1900CF4905 /* ASButtonNode.h in Headers */,
|
||||
DBC452DB1C5BF64600B16017 /* NSArray+Diffing.h in Headers */,
|
||||
058D0A4C195D05CB00B7D73C /* ASDisplayNode+Subclasses.h in Headers */,
|
||||
258FF4271C0D152600A83844 /* ASRangeHandlerVisible.h in Headers */,
|
||||
058D0A4A195D05CB00B7D73C /* ASDisplayNode.h in Headers */,
|
||||
058D0A84195D060300B7D73C /* ASDisplayNodeExtraIvars.h in Headers */,
|
||||
AC7A2C171BDE11DF0093FE1A /* ASTableViewInternal.h in Headers */,
|
||||
@ -1399,7 +1362,6 @@
|
||||
9C65A72A1BA8EA4D0084DA91 /* ASLayoutOptionsPrivate.h in Headers */,
|
||||
292C599F1A956527007E5DD6 /* ASLayoutRangeType.h in Headers */,
|
||||
257754B61BEE44CD00737CA5 /* ASEqualityHashHelpers.h in Headers */,
|
||||
DECC2ECD1C35C1C600388446 /* ASRangeControllerBeta.h in Headers */,
|
||||
ACF6ED261B17843500DA7C62 /* ASLayoutSpec.h in Headers */,
|
||||
ACF6ED4D1B17847A00DA7C62 /* ASLayoutSpecUtilities.h in Headers */,
|
||||
AC026B6F1BD57DBF00BBC17E /* _ASHierarchyChangeSet.h in Headers */,
|
||||
@ -1412,9 +1374,6 @@
|
||||
055B9FA81A1C154B00035D6D /* ASNetworkImageNode.h in Headers */,
|
||||
ACF6ED2B1B17843500DA7C62 /* ASOverlayLayoutSpec.h in Headers */,
|
||||
055F1A3819ABD413004DAFF1 /* ASRangeController.h in Headers */,
|
||||
292C59A21A956527007E5DD6 /* ASRangeHandler.h in Headers */,
|
||||
292C59A01A956527007E5DD6 /* ASRangeHandlerPreload.h in Headers */,
|
||||
292C59A31A956527007E5DD6 /* ASRangeHandlerRender.h in Headers */,
|
||||
ACF6ED2D1B17843500DA7C62 /* ASRatioLayoutSpec.h in Headers */,
|
||||
AC47D9451B3BB41900AAEE9D /* ASRelativeSize.h in Headers */,
|
||||
291B63FB1AA53A7A000A71B3 /* ASScrollDirection.h in Headers */,
|
||||
@ -1456,13 +1415,11 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
AC026B6A1BD57D6F00BBC17E /* ASChangeSetDataController.h in Headers */,
|
||||
25BAA16F1C0D18D2002747C7 /* ASRangeHandlerVisible.h in Headers */,
|
||||
B35062481B010EFD0018CF92 /* _AS-objc-internal.h in Headers */,
|
||||
B350623C1B010EFD0018CF92 /* _ASAsyncTransaction.h in Headers */,
|
||||
B350623E1B010EFD0018CF92 /* _ASAsyncTransactionContainer+Private.h in Headers */,
|
||||
B350623F1B010EFD0018CF92 /* _ASAsyncTransactionContainer.h in Headers */,
|
||||
B13CA1011C52004900E031AB /* ASCollectionNode+Beta.h in Headers */,
|
||||
DECC2ECE1C35C1C600388446 /* ASRangeControllerBeta.h in Headers */,
|
||||
254C6B7E1BF94DF4003EC431 /* ASTextKitTailTruncater.h in Headers */,
|
||||
B35062411B010EFD0018CF92 /* _ASAsyncTransactionGroup.h in Headers */,
|
||||
B35062491B010EFD0018CF92 /* _ASCoreAnimationExtras.h in Headers */,
|
||||
@ -1541,9 +1498,6 @@
|
||||
B35062061B010EFD0018CF92 /* ASNetworkImageNode.h in Headers */,
|
||||
34EFC76C1B701CED00AD841F /* ASOverlayLayoutSpec.h in Headers */,
|
||||
B35062261B010EFD0018CF92 /* ASRangeController.h in Headers */,
|
||||
B35062281B010EFD0018CF92 /* ASRangeHandler.h in Headers */,
|
||||
B35062291B010EFD0018CF92 /* ASRangeHandlerPreload.h in Headers */,
|
||||
B350622B1B010EFD0018CF92 /* ASRangeHandlerRender.h in Headers */,
|
||||
34EFC76E1B701CF400AD841F /* ASRatioLayoutSpec.h in Headers */,
|
||||
34EFC7651B701CCC00AD841F /* ASRelativeSize.h in Headers */,
|
||||
254C6B741BF94DF4003EC431 /* ASTextNodeWordKerner.h in Headers */,
|
||||
@ -1838,8 +1792,6 @@
|
||||
257754AB1BEE44CD00737CA5 /* ASTextKitEntityAttribute.m in Sources */,
|
||||
055F1A3919ABD413004DAFF1 /* ASRangeController.mm in Sources */,
|
||||
044285091BAA63FE00D16268 /* ASBatchFetching.m in Sources */,
|
||||
292C59A11A956527007E5DD6 /* ASRangeHandlerPreload.mm in Sources */,
|
||||
292C59A41A956527007E5DD6 /* ASRangeHandlerRender.mm in Sources */,
|
||||
257754AE1BEE44CD00737CA5 /* ASTextKitRenderer+Positioning.mm in Sources */,
|
||||
ACF6ED2E1B17843500DA7C62 /* ASRatioLayoutSpec.mm in Sources */,
|
||||
AC47D9461B3BB41900AAEE9D /* ASRelativeSize.mm in Sources */,
|
||||
@ -1847,13 +1799,11 @@
|
||||
D785F6631A74327E00291744 /* ASScrollNode.m in Sources */,
|
||||
058D0A2C195D050800B7D73C /* ASSentinel.m in Sources */,
|
||||
9C8221971BA237B80037F19A /* ASStackBaselinePositionedLayout.mm in Sources */,
|
||||
258FF4281C0D152600A83844 /* ASRangeHandlerVisible.mm in Sources */,
|
||||
251B8EF81BBB3D690087C538 /* ASCollectionDataController.mm in Sources */,
|
||||
ACF6ED301B17843500DA7C62 /* ASStackLayoutSpec.mm in Sources */,
|
||||
257754BE1BEE458E00737CA5 /* ASTextKitHelpers.mm in Sources */,
|
||||
257754A91BEE44CD00737CA5 /* ASTextKitContext.mm in Sources */,
|
||||
ACF6ED501B17847A00DA7C62 /* ASStackPositionedLayout.mm in Sources */,
|
||||
DECC2ECF1C35C1C600388446 /* ASRangeControllerBeta.mm in Sources */,
|
||||
ACF6ED521B17847A00DA7C62 /* ASStackUnpositionedLayout.mm in Sources */,
|
||||
257754A61BEE44CD00737CA5 /* ASTextKitAttributes.mm in Sources */,
|
||||
ACF6ED321B17843500DA7C62 /* ASStaticLayoutSpec.mm in Sources */,
|
||||
@ -1977,20 +1927,16 @@
|
||||
044285101BAA64EC00D16268 /* ASMultidimensionalArrayUtils.mm in Sources */,
|
||||
B35062271B010EFD0018CF92 /* ASRangeController.mm in Sources */,
|
||||
0442850A1BAA63FE00D16268 /* ASBatchFetching.m in Sources */,
|
||||
B350622A1B010EFD0018CF92 /* ASRangeHandlerPreload.mm in Sources */,
|
||||
B350622C1B010EFD0018CF92 /* ASRangeHandlerRender.mm in Sources */,
|
||||
34EFC76F1B701CF700AD841F /* ASRatioLayoutSpec.mm in Sources */,
|
||||
254C6B8B1BF94F8A003EC431 /* ASTextKitShadower.mm in Sources */,
|
||||
34EFC7661B701CD200AD841F /* ASRelativeSize.mm in Sources */,
|
||||
254C6B851BF94F8A003EC431 /* ASTextKitAttributes.mm in Sources */,
|
||||
509E68601B3AED8E009B9150 /* ASScrollDirection.m in Sources */,
|
||||
25A977EF1C0D2A5500406B62 /* ASRangeHandlerVisible.mm in Sources */,
|
||||
B35062091B010EFD0018CF92 /* ASScrollNode.m in Sources */,
|
||||
B35062561B010EFD0018CF92 /* ASSentinel.m in Sources */,
|
||||
9C8221981BA237B80037F19A /* ASStackBaselinePositionedLayout.mm in Sources */,
|
||||
34EFC7721B701D0300AD841F /* ASStackLayoutSpec.mm in Sources */,
|
||||
34EFC7761B701D2A00AD841F /* ASStackPositionedLayout.mm in Sources */,
|
||||
DECC2ED01C35C1C600388446 /* ASRangeControllerBeta.mm in Sources */,
|
||||
34EFC7781B701D3100AD841F /* ASStackUnpositionedLayout.mm in Sources */,
|
||||
AC026B6C1BD57D6F00BBC17E /* ASChangeSetDataController.m in Sources */,
|
||||
34EFC7741B701D0A00AD841F /* ASStaticLayoutSpec.mm in Sources */,
|
||||
|
||||
@ -167,12 +167,9 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
||||
self.strongCollectionNode = collectionNode;
|
||||
}
|
||||
|
||||
_layoutController = [ASDisplayNode shouldUseNewRenderingRange] ?
|
||||
[[ASCollectionViewLayoutControllerBeta alloc] initWithCollectionView:self] :
|
||||
[[ASCollectionViewLayoutControllerStable alloc] initWithCollectionView:self];
|
||||
_layoutController = [[ASCollectionViewLayoutController alloc] initWithCollectionView:self];
|
||||
|
||||
_rangeController = [ASDisplayNode shouldUseNewRenderingRange] ? [[ASRangeControllerBeta alloc] init]
|
||||
: [[ASRangeControllerStable alloc] init];
|
||||
_rangeController = [[ASRangeController alloc] init];
|
||||
_rangeController.dataSource = self;
|
||||
_rangeController.delegate = self;
|
||||
_rangeController.layoutController = _layoutController;
|
||||
|
||||
@ -8,9 +8,6 @@
|
||||
|
||||
@interface ASDisplayNode (Beta)
|
||||
|
||||
+ (BOOL)shouldUseNewRenderingRange;
|
||||
+ (void)setShouldUseNewRenderingRange:(BOOL)shouldUseNewRenderingRange;
|
||||
|
||||
+ (BOOL)usesImplicitHierarchyManagement;
|
||||
+ (void)setUsesImplicitHierarchyManagement:(BOOL)enabled;
|
||||
|
||||
|
||||
@ -256,7 +256,6 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
||||
+ (void)scheduleNodeForRecursiveDisplay:(ASDisplayNode *)node
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
ASDisplayNodeAssert([ASDisplayNode shouldUseNewRenderingRange], @"+scheduleNodeForRecursiveDisplay: should never be called without the new rendering range enabled");
|
||||
static NSMutableSet *nodesToDisplay = nil;
|
||||
static BOOL displayScheduled = NO;
|
||||
static ASDN::RecursiveMutex displaySchedulerLock;
|
||||
@ -1666,18 +1665,6 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
|
||||
return _flags.shouldBypassEnsureDisplay;
|
||||
}
|
||||
|
||||
static BOOL ShouldUseNewRenderingRange = YES;
|
||||
|
||||
+ (BOOL)shouldUseNewRenderingRange
|
||||
{
|
||||
return ShouldUseNewRenderingRange;
|
||||
}
|
||||
|
||||
+ (void)setShouldUseNewRenderingRange:(BOOL)shouldUseNewRenderingRange
|
||||
{
|
||||
ShouldUseNewRenderingRange = shouldUseNewRenderingRange;
|
||||
}
|
||||
|
||||
#pragma mark - For Subclasses
|
||||
|
||||
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
|
||||
@ -1922,7 +1909,7 @@ static BOOL ShouldUseNewRenderingRange = YES;
|
||||
} else {
|
||||
// NOTE: This case isn't currently supported as setInterfaceState: isn't exposed externally, and all
|
||||
// internal use cases are range-managed. When a node is visible, don't mess with display - CA will start it.
|
||||
if ([ASDisplayNode shouldUseNewRenderingRange] && !ASInterfaceStateIncludesVisible(newState)) {
|
||||
if (!ASInterfaceStateIncludesVisible(newState)) {
|
||||
// Check __implementsDisplay purely for efficiency - it's faster even than calling -asyncLayer.
|
||||
if ([self __implementsDisplay]) {
|
||||
if (nowDisplay) {
|
||||
|
||||
@ -11,7 +11,6 @@
|
||||
#import "ASAssert.h"
|
||||
#import "ASBatchFetching.h"
|
||||
#import "ASChangeSetDataController.h"
|
||||
#import "ASCollectionViewLayoutController.h"
|
||||
#import "ASDelegateProxy.h"
|
||||
#import "ASDisplayNode+Beta.h"
|
||||
#import "ASDisplayNode+FrameworkPrivate.h"
|
||||
@ -148,8 +147,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
{
|
||||
_layoutController = [[ASFlowLayoutController alloc] initWithScrollOption:ASFlowLayoutDirectionVertical];
|
||||
|
||||
_rangeController = [ASDisplayNode shouldUseNewRenderingRange] ? [[ASRangeControllerBeta alloc] init]
|
||||
: [[ASRangeControllerStable alloc] init];
|
||||
_rangeController = [[ASRangeController alloc] init];
|
||||
_rangeController.layoutController = _layoutController;
|
||||
_rangeController.dataSource = self;
|
||||
_rangeController.delegate = self;
|
||||
|
||||
@ -63,9 +63,6 @@
|
||||
#import <AsyncDisplayKit/ASLayoutOptions.h>
|
||||
#import <AsyncDisplayKit/ASLog.h>
|
||||
#import <AsyncDisplayKit/ASMutableAttributedStringBuilder.h>
|
||||
#import <AsyncDisplayKit/ASRangeHandler.h>
|
||||
#import <AsyncDisplayKit/ASRangeHandlerPreload.h>
|
||||
#import <AsyncDisplayKit/ASRangeHandlerRender.h>
|
||||
#import <AsyncDisplayKit/ASThread.h>
|
||||
#import <AsyncDisplayKit/CGRect+ASConvenience.h>
|
||||
#import <AsyncDisplayKit/NSMutableAttributedString+TextKitAdditions.h>
|
||||
|
||||
@ -82,13 +82,6 @@ extern BOOL ASRangeTuningParametersEqualToRangeTuningParameters(ASRangeTuningPar
|
||||
|
||||
#pragma mark - Abstract Index Path Range Support
|
||||
|
||||
// FIXME: This method can be removed once ASRangeControllerBeta becomes the main version.
|
||||
- (BOOL)shouldUpdateForVisibleIndexPaths:(NSArray *)indexPaths rangeType:(ASLayoutRangeType)rangeType
|
||||
{
|
||||
ASDisplayNodeAssertNotSupported();
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (NSSet *)indexPathsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType
|
||||
{
|
||||
ASDisplayNodeAssertNotSupported();
|
||||
|
||||
@ -19,10 +19,4 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@end
|
||||
|
||||
@interface ASCollectionViewLayoutControllerStable : ASCollectionViewLayoutController
|
||||
@end
|
||||
|
||||
@interface ASCollectionViewLayoutControllerBeta : ASCollectionViewLayoutController
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@ -49,90 +49,6 @@ typedef struct ASRangeGeometry ASRangeGeometry;
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation ASCollectionViewLayoutControllerStable
|
||||
{
|
||||
std::vector<CGRect> _updateRangeBoundsIndexedByRangeType;
|
||||
}
|
||||
|
||||
- (instancetype)initWithCollectionView:(ASCollectionView *)collectionView
|
||||
{
|
||||
if (!(self = [super initWithCollectionView:collectionView])) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
_updateRangeBoundsIndexedByRangeType = std::vector<CGRect>(ASLayoutRangeTypeCount);
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSSet *)indexPathsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType
|
||||
{
|
||||
ASRangeTuningParameters tuningParameters = [self tuningParametersForRangeMode:rangeMode rangeType:rangeType];
|
||||
ASRangeGeometry rangeGeometry = [self rangeGeometryWithScrollDirection:scrollDirection tuningParameters:tuningParameters];
|
||||
_updateRangeBoundsIndexedByRangeType[rangeType] = rangeGeometry.updateBounds;
|
||||
return [self indexPathsForItemsWithinRangeBounds:rangeGeometry.rangeBounds];
|
||||
}
|
||||
|
||||
- (NSSet *)indexPathsForItemsWithinRangeBounds:(CGRect)rangeBounds
|
||||
{
|
||||
NSMutableSet *indexPathSet = [[NSMutableSet alloc] init];
|
||||
NSArray *layoutAttributes = [_collectionViewLayout layoutAttributesForElementsInRect:rangeBounds];
|
||||
for (UICollectionViewLayoutAttributes *la in layoutAttributes) {
|
||||
if (la.representedElementCategory == UICollectionElementCategoryCell) {
|
||||
[indexPathSet addObject:la.indexPath];
|
||||
}
|
||||
}
|
||||
return indexPathSet;
|
||||
}
|
||||
|
||||
- (ASRangeGeometry)rangeGeometryWithScrollDirection:(ASScrollDirection)scrollDirection
|
||||
tuningParameters:(ASRangeTuningParameters)tuningParameters
|
||||
{
|
||||
CGRect rangeBounds = _collectionView.bounds;
|
||||
CGRect updateBounds = _collectionView.bounds;
|
||||
|
||||
// Scrollable directions can change for non-flow layouts
|
||||
if ([_collectionViewLayout asdk_isFlowLayout] == NO) {
|
||||
_scrollableDirections = [_collectionView scrollableDirections];
|
||||
}
|
||||
|
||||
rangeBounds = CGRectExpandToRangeWithScrollableDirections(rangeBounds, tuningParameters, _scrollableDirections, scrollDirection);
|
||||
|
||||
ASRangeTuningParameters updateTuningParameters = tuningParameters;
|
||||
updateTuningParameters.leadingBufferScreenfuls = MIN(updateTuningParameters.leadingBufferScreenfuls * 0.5, 0.95);
|
||||
updateTuningParameters.trailingBufferScreenfuls = MIN(updateTuningParameters.trailingBufferScreenfuls * 0.5, 0.95);
|
||||
|
||||
updateBounds = CGRectExpandToRangeWithScrollableDirections(updateBounds, updateTuningParameters, _scrollableDirections, scrollDirection);
|
||||
|
||||
return {rangeBounds, updateBounds};
|
||||
}
|
||||
|
||||
- (BOOL)shouldUpdateForVisibleIndexPaths:(NSArray *)indexPaths rangeType:(ASLayoutRangeType)rangeType
|
||||
{
|
||||
CGSize viewportSize = [self viewportSize];
|
||||
CGRect updateRangeBounds = _updateRangeBoundsIndexedByRangeType[rangeType];
|
||||
if (CGRectIsEmpty(updateRangeBounds)) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
CGRect currentBounds = _collectionView.bounds;
|
||||
if (CGRectIsEmpty(currentBounds)) {
|
||||
currentBounds = CGRectMake(0, 0, viewportSize.width, viewportSize.height);
|
||||
}
|
||||
|
||||
if (CGRectContainsRect(updateRangeBounds, currentBounds)) {
|
||||
return NO;
|
||||
} else {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation ASCollectionViewLayoutControllerBeta
|
||||
|
||||
- (NSSet *)indexPathsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType
|
||||
{
|
||||
ASRangeTuningParameters tuningParameters = [self tuningParametersForRangeMode:rangeMode rangeType:rangeType];
|
||||
|
||||
@ -15,8 +15,6 @@
|
||||
#include <vector>
|
||||
#include <cassert>
|
||||
|
||||
static const CGFloat kASFlowLayoutControllerRefreshingThreshold = 0.3;
|
||||
|
||||
@interface ASFlowLayoutController()
|
||||
{
|
||||
ASIndexPathRange _visibleRange;
|
||||
@ -39,32 +37,6 @@ static const CGFloat kASFlowLayoutControllerRefreshingThreshold = 0.3;
|
||||
|
||||
#pragma mark - Visible Indices
|
||||
|
||||
// FIXME: This method can be removed once ASRangeControllerBeta becomes the main version.
|
||||
- (BOOL)shouldUpdateForVisibleIndexPaths:(NSArray *)indexPaths rangeType:(ASLayoutRangeType)rangeType
|
||||
{
|
||||
if (!indexPaths.count || rangeType >= _rangesByType.size()) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
ASIndexPathRange existingRange = _rangesByType[rangeType];
|
||||
ASIndexPathRange newRange = [self indexPathRangeForIndexPaths:indexPaths];
|
||||
|
||||
ASIndexPath maximumStart = ASIndexPathMaximum(existingRange.start, newRange.start);
|
||||
ASIndexPath minimumEnd = ASIndexPathMinimum(existingRange.end, newRange.end);
|
||||
|
||||
if (ASIndexPathEqualToIndexPath(maximumStart, existingRange.start) || ASIndexPathEqualToIndexPath(minimumEnd, existingRange.end)) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
NSInteger newStartDelta = [self flowLayoutDistanceForRange:ASIndexPathRangeMake(_visibleRange.start, newRange.start)];
|
||||
NSInteger existingStartDelta = [self flowLayoutDistanceForRange:ASIndexPathRangeMake(_visibleRange.start, existingRange.start)] * kASFlowLayoutControllerRefreshingThreshold;
|
||||
|
||||
NSInteger newEndDelta = [self flowLayoutDistanceForRange:ASIndexPathRangeMake(_visibleRange.end, newRange.end)];
|
||||
NSInteger existingEndDelta = [self flowLayoutDistanceForRange:ASIndexPathRangeMake(_visibleRange.end, existingRange.end)] * kASFlowLayoutControllerRefreshingThreshold;
|
||||
|
||||
return (newStartDelta > existingStartDelta) || (newEndDelta > existingEndDelta);
|
||||
}
|
||||
|
||||
- (void)setVisibleNodeIndexPaths:(NSArray *)indexPaths
|
||||
{
|
||||
_visibleRange = [self indexPathRangeForIndexPaths:indexPaths];
|
||||
|
||||
@ -31,10 +31,6 @@ FOUNDATION_EXPORT BOOL ASRangeTuningParametersEqualToRangeTuningParameters(ASRan
|
||||
|
||||
- (ASRangeTuningParameters)tuningParametersForRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType;
|
||||
|
||||
// FIXME: This method can be removed once ASRangeControllerBeta becomes the main version.
|
||||
// TODO: Now that it is the main version, can we remove this now?
|
||||
- (BOOL)shouldUpdateForVisibleIndexPaths:(NSArray<NSIndexPath *> *)indexPaths rangeType:(ASLayoutRangeType)rangeType;
|
||||
|
||||
- (NSSet *)indexPathsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType;
|
||||
|
||||
@optional
|
||||
|
||||
@ -77,12 +77,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@end
|
||||
|
||||
@interface ASRangeControllerStable : ASRangeController
|
||||
@end
|
||||
|
||||
@interface ASRangeControllerBeta : ASRangeController
|
||||
@end
|
||||
|
||||
/**
|
||||
* Data source for ASRangeController.
|
||||
*
|
||||
|
||||
@ -10,51 +10,26 @@
|
||||
|
||||
#import "ASAssert.h"
|
||||
#import "ASDisplayNodeExtras.h"
|
||||
#import "ASDisplayNodeInternal.h"
|
||||
#import "ASMultiDimensionalArrayUtils.h"
|
||||
#import "ASRangeHandlerVisible.h"
|
||||
#import "ASRangeHandlerRender.h"
|
||||
#import "ASRangeHandlerPreload.h"
|
||||
#import "ASInternalHelpers.h"
|
||||
#import "ASLayoutController.h"
|
||||
#import "ASLayoutRangeType.h"
|
||||
#import "ASDisplayNode+FrameworkPrivate.h"
|
||||
|
||||
@implementation ASRangeController
|
||||
|
||||
- (void)visibleNodeIndexPathsDidChangeWithScrollDirection:(ASScrollDirection)scrollDirection
|
||||
{
|
||||
}
|
||||
|
||||
- (void)configureContentView:(UIView *)contentView forCellNode:(ASCellNode *)node
|
||||
{
|
||||
}
|
||||
|
||||
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType
|
||||
{
|
||||
[_layoutController setTuningParameters:tuningParameters forRangeMode:rangeMode rangeType:rangeType];
|
||||
}
|
||||
|
||||
- (ASRangeTuningParameters)tuningParametersForRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType
|
||||
{
|
||||
return [_layoutController tuningParametersForRangeMode:rangeMode rangeType:rangeType];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface ASRangeControllerStable ()
|
||||
@interface ASRangeController ()
|
||||
{
|
||||
BOOL _rangeIsValid;
|
||||
|
||||
// keys should be ASLayoutRangeTypes and values NSSets containing NSIndexPaths
|
||||
NSMutableDictionary *_rangeTypeIndexPaths;
|
||||
NSDictionary *_rangeTypeHandlers;
|
||||
BOOL _queuedRangeUpdate;
|
||||
|
||||
BOOL _layoutControllerImplementsSetVisibleIndexPaths;
|
||||
ASScrollDirection _scrollDirection;
|
||||
NSSet<NSIndexPath *> *_allPreviousIndexPaths;
|
||||
ASLayoutRangeMode _currentRangeMode;
|
||||
BOOL _didRegisterForNotifications;
|
||||
CFAbsoluteTime _pendingDisplayNodesTimestamp;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation ASRangeControllerStable
|
||||
@implementation ASRangeController
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
@ -63,19 +38,273 @@
|
||||
}
|
||||
|
||||
_rangeIsValid = YES;
|
||||
_rangeTypeIndexPaths = [NSMutableDictionary dictionary];
|
||||
_rangeTypeHandlers = @{
|
||||
@(ASLayoutRangeTypeDisplay) : [[ASRangeHandlerRender alloc] init],
|
||||
@(ASLayoutRangeTypeFetchData): [[ASRangeHandlerPreload alloc] init],
|
||||
};
|
||||
_currentRangeMode = ASLayoutRangeModeInvalid;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
if (_didRegisterForNotifications) {
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:ASRenderingEngineDidDisplayScheduledNodesNotification object:nil];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Core visible node range managment API
|
||||
|
||||
+ (ASLayoutRangeMode)rangeModeForInterfaceState:(ASInterfaceState)interfaceState
|
||||
currentRangeMode:(ASLayoutRangeMode)currentRangeMode
|
||||
{
|
||||
BOOL isVisible = (ASInterfaceStateIncludesVisible(interfaceState));
|
||||
BOOL isFirstRangeUpdate = (currentRangeMode == ASLayoutRangeModeInvalid);
|
||||
if (!isVisible || isFirstRangeUpdate) {
|
||||
return ASLayoutRangeModeMinimum;
|
||||
}
|
||||
|
||||
return ASLayoutRangeModeFull;
|
||||
}
|
||||
|
||||
- (void)visibleNodeIndexPathsDidChangeWithScrollDirection:(ASScrollDirection)scrollDirection
|
||||
{
|
||||
_scrollDirection = scrollDirection;
|
||||
[self scheduleRangeUpdate];
|
||||
}
|
||||
|
||||
- (void)scheduleRangeUpdate
|
||||
{
|
||||
if (_queuedRangeUpdate) {
|
||||
return;
|
||||
}
|
||||
|
||||
// coalesce these events -- handling them multiple times per runloop is noisy and expensive
|
||||
_queuedRangeUpdate = YES;
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self _updateVisibleNodeIndexPaths];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)setLayoutController:(id<ASLayoutController>)layoutController
|
||||
{
|
||||
_layoutController = layoutController;
|
||||
_layoutControllerImplementsSetVisibleIndexPaths = [_layoutController respondsToSelector:@selector(setVisibleNodeIndexPaths:)];
|
||||
}
|
||||
|
||||
- (void)_updateVisibleNodeIndexPaths
|
||||
{
|
||||
ASDisplayNodeAssert(_layoutController, @"An ASLayoutController is required by ASRangeController");
|
||||
if (!_queuedRangeUpdate || !_layoutController) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Consider if we need to use this codepath, or can rely on something more similar to the data & display ranges
|
||||
// Example: ... = [_layoutController indexPathsForScrolling:_scrollDirection rangeType:ASLayoutRangeTypeVisible];
|
||||
NSArray<NSIndexPath *> *visibleNodePaths = [_dataSource visibleNodeIndexPathsForRangeController:self];
|
||||
|
||||
if (visibleNodePaths.count == 0) { // if we don't have any visibleNodes currently (scrolled before or after content)...
|
||||
_queuedRangeUpdate = NO;
|
||||
return; // don't do anything for this update, but leave _rangeIsValid == NO to make sure we update it later
|
||||
}
|
||||
|
||||
[_layoutController setViewportSize:[_dataSource viewportSizeForRangeController:self]];
|
||||
|
||||
// the layout controller needs to know what the current visible indices are to calculate range offsets
|
||||
if (_layoutControllerImplementsSetVisibleIndexPaths) {
|
||||
[_layoutController setVisibleNodeIndexPaths:visibleNodePaths];
|
||||
}
|
||||
|
||||
// allNodes is a 2D array: it contains arrays for each section, each containing nodes.
|
||||
NSArray<NSArray *> *allNodes = [_dataSource completedNodes];
|
||||
NSUInteger numberOfSections = [allNodes count];
|
||||
|
||||
NSArray<ASDisplayNode *> *currentSectionNodes = nil;
|
||||
NSInteger currentSectionIndex = -1; // Set to -1 so we don't match any indexPath.section on the first iteration.
|
||||
NSUInteger numberOfNodesInSection = 0;
|
||||
|
||||
NSSet<NSIndexPath *> *visibleIndexPaths = [NSSet setWithArray:visibleNodePaths];
|
||||
NSSet<NSIndexPath *> *displayIndexPaths = nil;
|
||||
NSSet<NSIndexPath *> *fetchDataIndexPaths = nil;
|
||||
|
||||
// Prioritize the order in which we visit each. Visible nodes should be updated first so they are enqueued on
|
||||
// the network or display queues before preloading (offscreen) nodes are enqueued.
|
||||
NSMutableOrderedSet<NSIndexPath *> *allIndexPaths = [[NSMutableOrderedSet alloc] initWithSet:visibleIndexPaths];
|
||||
|
||||
ASInterfaceState selfInterfaceState = [_dataSource interfaceStateForRangeController:self];
|
||||
ASLayoutRangeMode rangeMode = [ASRangeController rangeModeForInterfaceState:selfInterfaceState
|
||||
currentRangeMode:_currentRangeMode];
|
||||
|
||||
ASRangeTuningParameters parametersFetchData = [_layoutController tuningParametersForRangeMode:rangeMode
|
||||
rangeType:ASLayoutRangeTypeFetchData];
|
||||
if (ASRangeTuningParametersEqualToRangeTuningParameters(parametersFetchData, ASRangeTuningParametersZero)) {
|
||||
fetchDataIndexPaths = visibleIndexPaths;
|
||||
} else {
|
||||
fetchDataIndexPaths = [_layoutController indexPathsForScrolling:_scrollDirection
|
||||
rangeMode:rangeMode
|
||||
rangeType:ASLayoutRangeTypeFetchData];
|
||||
}
|
||||
|
||||
ASRangeTuningParameters parametersDisplay = [_layoutController tuningParametersForRangeMode:rangeMode
|
||||
rangeType:ASLayoutRangeTypeDisplay];
|
||||
if (ASRangeTuningParametersEqualToRangeTuningParameters(parametersDisplay, ASRangeTuningParametersZero)) {
|
||||
displayIndexPaths = visibleIndexPaths;
|
||||
} else if (ASRangeTuningParametersEqualToRangeTuningParameters(parametersDisplay, parametersFetchData)) {
|
||||
displayIndexPaths = fetchDataIndexPaths;
|
||||
} else {
|
||||
displayIndexPaths = [_layoutController indexPathsForScrolling:_scrollDirection
|
||||
rangeMode:rangeMode
|
||||
rangeType:ASLayoutRangeTypeDisplay];
|
||||
}
|
||||
|
||||
// Typically the fetchDataIndexPaths will be the largest, and be a superset of the others, though it may be disjoint.
|
||||
// Because allIndexPaths is an NSMutableOrderedSet, this adds the non-duplicate items /after/ the existing items.
|
||||
// This means that during iteration, we will first visit visible, then display, then fetch data nodes.
|
||||
[allIndexPaths unionSet:displayIndexPaths];
|
||||
[allIndexPaths unionSet:fetchDataIndexPaths];
|
||||
|
||||
// Add anything we had applied interfaceState to in the last update, but is no longer in range, so we can clear any
|
||||
// range flags it still has enabled. Most of the time, all but a few elements are equal; a large programmatic
|
||||
// scroll or major main thread stall could cause entirely disjoint sets. In either case we must visit all.
|
||||
// Calling "-set" on NSMutableOrderedSet just references the underlying mutable data store, so we must copy it.
|
||||
NSSet<NSIndexPath *> *allCurrentIndexPaths = [[allIndexPaths set] copy];
|
||||
[allIndexPaths unionSet:_allPreviousIndexPaths];
|
||||
_allPreviousIndexPaths = allCurrentIndexPaths;
|
||||
_currentRangeMode = rangeMode;
|
||||
|
||||
if (!_rangeIsValid) {
|
||||
[allIndexPaths addObjectsFromArray:ASIndexPathsForMultidimensionalArray(allNodes)];
|
||||
}
|
||||
|
||||
// TODO Don't register for notifications if this range update doesn't cause any node to enter rendering pipeline.
|
||||
// This can be done once there is an API to observe to (or be notified upon) interface state changes or pipeline enterings
|
||||
[self registerForNotificationsForInterfaceStateIfNeeded:selfInterfaceState];
|
||||
|
||||
#if RangeControllerLoggingEnabled
|
||||
NSMutableArray<NSIndexPath *> *modifiedIndexPaths = (RangeControllerLoggingEnabled ? [NSMutableArray array] : nil);
|
||||
#endif
|
||||
|
||||
for (NSIndexPath *indexPath in allIndexPaths) {
|
||||
// Before a node / indexPath is exposed to ASRangeController, ASDataController should have already measured it.
|
||||
// For consistency, make sure each node knows that it should measure itself if something changes.
|
||||
ASInterfaceState interfaceState = ASInterfaceStateMeasureLayout;
|
||||
|
||||
if (ASInterfaceStateIncludesVisible(selfInterfaceState)) {
|
||||
if ([fetchDataIndexPaths containsObject:indexPath]) {
|
||||
interfaceState |= ASInterfaceStateFetchData;
|
||||
}
|
||||
if ([displayIndexPaths containsObject:indexPath]) {
|
||||
interfaceState |= ASInterfaceStateDisplay;
|
||||
}
|
||||
if ([visibleIndexPaths containsObject:indexPath]) {
|
||||
interfaceState |= ASInterfaceStateVisible;
|
||||
}
|
||||
} else {
|
||||
// If selfInterfaceState isn't visible, then visibleIndexPaths represents what /will/ be immediately visible at the
|
||||
// instant we come onscreen. So, fetch data and display all of those things, but don't waste resources preloading yet.
|
||||
// We handle this as a separate case to minimize set operations for offscreen preloading, including containsObject:.
|
||||
|
||||
// Set Layout, Fetch Data, Display. DO NOT set Visible: even though these elements are in the visible range / "viewport",
|
||||
// our overall container object is itself not visible yet. The moment it becomes visible, we will run the condition above.
|
||||
if ([allCurrentIndexPaths containsObject:indexPath]) {
|
||||
// We might be looking at an indexPath that was previously in-range, but now we need to clear it.
|
||||
// In that case we'll just set it back to MeasureLayout. Only set Display | FetchData if in allCurrentIndexPaths.
|
||||
interfaceState |= ASInterfaceStateDisplay;
|
||||
interfaceState |= ASInterfaceStateFetchData;
|
||||
}
|
||||
}
|
||||
|
||||
NSInteger section = indexPath.section;
|
||||
NSInteger row = indexPath.row;
|
||||
|
||||
if (section >= 0 && row >= 0 && section < numberOfSections) {
|
||||
if (section != currentSectionIndex) {
|
||||
// Often we'll be dealing with indexPaths in the same section, but the set isn't sorted and we may even bounce
|
||||
// between the same ones. Still, this saves dozens of method calls to access the inner array and count.
|
||||
currentSectionNodes = [allNodes objectAtIndex:section];
|
||||
numberOfNodesInSection = [currentSectionNodes count];
|
||||
currentSectionIndex = section;
|
||||
}
|
||||
|
||||
if (row < numberOfNodesInSection) {
|
||||
ASDisplayNode *node = [currentSectionNodes objectAtIndex:row];
|
||||
|
||||
ASDisplayNodeAssert(node.hierarchyState & ASHierarchyStateRangeManaged, @"All nodes reaching this point should be range-managed, or interfaceState may be incorrectly reset.");
|
||||
// Skip the many method calls of the recursive operation if the top level cell node already has the right interfaceState.
|
||||
if (node.interfaceState != interfaceState) {
|
||||
#if RangeControllerLoggingEnabled
|
||||
[modifiedIndexPaths addObject:indexPath];
|
||||
#endif
|
||||
[node recursivelySetInterfaceState:interfaceState];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_didRegisterForNotifications) {
|
||||
_pendingDisplayNodesTimestamp = CFAbsoluteTimeGetCurrent();
|
||||
}
|
||||
|
||||
_rangeIsValid = YES;
|
||||
_queuedRangeUpdate = NO;
|
||||
|
||||
#if RangeControllerLoggingEnabled
|
||||
NSSet *visibleNodePathsSet = [NSSet setWithArray:visibleNodePaths];
|
||||
BOOL setsAreEqual = [visibleIndexPaths isEqualToSet:visibleNodePathsSet];
|
||||
NSLog(@"visible sets are equal: %d", setsAreEqual);
|
||||
if (!setsAreEqual) {
|
||||
NSLog(@"standard: %@", visibleIndexPaths);
|
||||
NSLog(@"custom: %@", visibleNodePathsSet);
|
||||
}
|
||||
|
||||
[modifiedIndexPaths sortUsingSelector:@selector(compare:)];
|
||||
|
||||
for (NSIndexPath *indexPath in modifiedIndexPaths) {
|
||||
ASDisplayNode *node = [_dataSource rangeController:self nodeAtIndexPath:indexPath];
|
||||
ASInterfaceState interfaceState = node.interfaceState;
|
||||
BOOL inVisible = ASInterfaceStateIncludesVisible(interfaceState);
|
||||
BOOL inDisplay = ASInterfaceStateIncludesDisplay(interfaceState);
|
||||
BOOL inFetchData = ASInterfaceStateIncludesFetchData(interfaceState);
|
||||
NSLog(@"indexPath %@, Visible: %d, Display: %d, FetchData: %d", indexPath, inVisible, inDisplay, inFetchData);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#pragma mark - Notification observers
|
||||
|
||||
- (void)registerForNotificationsForInterfaceStateIfNeeded:(ASInterfaceState)interfaceState
|
||||
{
|
||||
if (!_didRegisterForNotifications) {
|
||||
ASLayoutRangeMode nextRangeMode = [ASRangeController rangeModeForInterfaceState:interfaceState
|
||||
currentRangeMode:_currentRangeMode];
|
||||
if (_currentRangeMode != nextRangeMode) {
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(scheduledNodesDidDisplay:)
|
||||
name:ASRenderingEngineDidDisplayScheduledNodesNotification
|
||||
object:nil];
|
||||
_didRegisterForNotifications = YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)scheduledNodesDidDisplay:(NSNotification *)notification
|
||||
{
|
||||
CFAbsoluteTime notificationTimestamp = ((NSNumber *)[notification.userInfo objectForKey:ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp]).doubleValue;
|
||||
if (_pendingDisplayNodesTimestamp < notificationTimestamp) {
|
||||
// The rendering engine has processed all the nodes this range controller scheduled. Let's schedule a range update
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:ASRenderingEngineDidDisplayScheduledNodesNotification object:nil];
|
||||
_didRegisterForNotifications = NO;
|
||||
|
||||
[self scheduleRangeUpdate];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Cell node view handling
|
||||
|
||||
- (void)configureContentView:(UIView *)contentView forCellNode:(ASCellNode *)node
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
ASDisplayNodeAssert(node, @"Cannot move a nil node to a view");
|
||||
ASDisplayNodeAssert(contentView, @"Cannot move a node to a non-existent view");
|
||||
|
||||
if (node.view.superview == contentView) {
|
||||
// this content view is already correctly configured
|
||||
return;
|
||||
@ -86,119 +315,17 @@
|
||||
[view removeFromSuperview];
|
||||
}
|
||||
|
||||
[self moveCellNode:node toView:contentView];
|
||||
[contentView addSubview:node.view];
|
||||
}
|
||||
|
||||
- (void)moveCellNode:(ASCellNode *)node toView:(UIView *)view
|
||||
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
ASDisplayNodeAssert(node, @"Cannot move a nil node to a view");
|
||||
ASDisplayNodeAssert(view, @"Cannot move a node to a non-existent view");
|
||||
|
||||
// force any nodes that are about to come into view to have display enabled
|
||||
if (node.displaySuspended) {
|
||||
[node recursivelySetDisplaySuspended:NO];
|
||||
}
|
||||
|
||||
[view addSubview:node.view];
|
||||
[_layoutController setTuningParameters:tuningParameters forRangeMode:rangeMode rangeType:rangeType];
|
||||
}
|
||||
|
||||
#pragma mark - Core visible node range managment API
|
||||
|
||||
- (void)visibleNodeIndexPathsDidChangeWithScrollDirection:(ASScrollDirection)scrollDirection
|
||||
- (ASRangeTuningParameters)tuningParametersForRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType
|
||||
{
|
||||
_scrollDirection = scrollDirection;
|
||||
|
||||
if (_queuedRangeUpdate) {
|
||||
return;
|
||||
}
|
||||
|
||||
// coalesce these events -- handling them multiple times per runloop is noisy and expensive
|
||||
_queuedRangeUpdate = YES;
|
||||
|
||||
[self performSelector:@selector(_updateVisibleNodeIndexPaths)
|
||||
withObject:nil
|
||||
afterDelay:0
|
||||
inModes:@[ NSRunLoopCommonModes ]];
|
||||
}
|
||||
|
||||
- (void)_updateVisibleNodeIndexPaths
|
||||
{
|
||||
if (!_queuedRangeUpdate) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSArray *visibleNodePaths = [_dataSource visibleNodeIndexPathsForRangeController:self];
|
||||
|
||||
if (visibleNodePaths.count == 0) { // if we don't have any visibleNodes currently (scrolled before or after content)...
|
||||
_queuedRangeUpdate = NO;
|
||||
return ; // don't do anything for this update, but leave _rangeIsValid to make sure we update it later
|
||||
}
|
||||
|
||||
NSSet *visibleNodePathsSet = [NSSet setWithArray:visibleNodePaths];
|
||||
CGSize viewportSize = [_dataSource viewportSizeForRangeController:self];
|
||||
[_layoutController setViewportSize:viewportSize];
|
||||
|
||||
// the layout controller needs to know what the current visible indices are to calculate range offsets
|
||||
if ([_layoutController respondsToSelector:@selector(setVisibleNodeIndexPaths:)]) {
|
||||
[_layoutController setVisibleNodeIndexPaths:visibleNodePaths];
|
||||
}
|
||||
|
||||
for (NSInteger i = 0; i < ASLayoutRangeTypeCount; i++) {
|
||||
ASLayoutRangeType rangeType = (ASLayoutRangeType)i;
|
||||
id rangeKey = @(rangeType);
|
||||
|
||||
// this delegate decide what happens when a node is added or removed from a range
|
||||
id<ASRangeHandler> rangeHandler = _rangeTypeHandlers[rangeKey];
|
||||
|
||||
if (!_rangeIsValid || [_layoutController shouldUpdateForVisibleIndexPaths:visibleNodePaths rangeType:rangeType]) {
|
||||
NSSet *indexPaths = [_layoutController indexPathsForScrolling:_scrollDirection
|
||||
rangeMode:ASLayoutRangeModeFull
|
||||
rangeType:rangeType];
|
||||
|
||||
// Notify to remove indexpaths that are leftover that are not visible or included in the _layoutController calculated paths
|
||||
NSMutableSet *removedIndexPaths = _rangeIsValid ? [_rangeTypeIndexPaths[rangeKey] mutableCopy] : [NSMutableSet set];
|
||||
[removedIndexPaths minusSet:indexPaths];
|
||||
[removedIndexPaths minusSet:visibleNodePathsSet];
|
||||
|
||||
if (removedIndexPaths.count) {
|
||||
NSArray *removedNodes = [_dataSource rangeController:self nodesAtIndexPaths:[removedIndexPaths allObjects]];
|
||||
for (ASCellNode *node in removedNodes) {
|
||||
// since this class usually manages large or infinite data sets, the working range
|
||||
// directly bounds memory usage by requiring redrawing any content that falls outside the range.
|
||||
[rangeHandler node:node exitedRangeOfType:rangeType];
|
||||
}
|
||||
}
|
||||
|
||||
// Notify to add index paths that are not currently in _rangeTypeIndexPaths
|
||||
NSMutableSet *addedIndexPaths = [indexPaths mutableCopy];
|
||||
[addedIndexPaths minusSet:_rangeTypeIndexPaths[rangeKey]];
|
||||
|
||||
// The preload range (for example) should include nodes that are visible
|
||||
// TODO: remove this once we have removed the dependency on Core Animation's -display
|
||||
if ([self shouldSkipVisibleNodesForRangeType:rangeType]) {
|
||||
[addedIndexPaths minusSet:visibleNodePathsSet];
|
||||
}
|
||||
|
||||
if (addedIndexPaths.count) {
|
||||
NSArray *addedNodes = [_dataSource rangeController:self nodesAtIndexPaths:[addedIndexPaths allObjects]];
|
||||
for (ASCellNode *node in addedNodes) {
|
||||
[rangeHandler node:node enteredRangeOfType:rangeType];
|
||||
}
|
||||
}
|
||||
|
||||
// set the range indexpaths so that we can remove/add on the next update pass
|
||||
_rangeTypeIndexPaths[rangeKey] = indexPaths;
|
||||
}
|
||||
}
|
||||
|
||||
_rangeIsValid = YES;
|
||||
_queuedRangeUpdate = NO;
|
||||
}
|
||||
|
||||
- (BOOL)shouldSkipVisibleNodesForRangeType:(ASLayoutRangeType)rangeType
|
||||
{
|
||||
return rangeType == ASLayoutRangeTypeDisplay;
|
||||
return [_layoutController tuningParametersForRangeMode:rangeMode rangeType:rangeType];
|
||||
}
|
||||
|
||||
#pragma mark - ASDataControllerDelegete
|
||||
@ -230,18 +357,6 @@
|
||||
{
|
||||
ASPerformBlockOnMainThread(^{
|
||||
_rangeIsValid = NO;
|
||||
|
||||
// When removing nodes we need to make sure that removed indexPaths are not left in _rangeTypeIndexPaths,
|
||||
// otherwise _updateVisibleNodeIndexPaths may try to retrieve nodes from dataSource that aren't there anymore
|
||||
for (NSInteger i = 0; i < ASLayoutRangeTypeCount; i++) {
|
||||
id rangeKey = @((ASLayoutRangeType)i);
|
||||
NSMutableSet *rangePaths = [_rangeTypeIndexPaths[rangeKey] mutableCopy];
|
||||
for (NSIndexPath *path in indexPaths) {
|
||||
[rangePaths removeObject:path];
|
||||
}
|
||||
_rangeTypeIndexPaths[rangeKey] = rangePaths;
|
||||
}
|
||||
|
||||
[_delegate rangeController:self didDeleteNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
||||
});
|
||||
}
|
||||
@ -259,22 +374,8 @@
|
||||
{
|
||||
ASPerformBlockOnMainThread(^{
|
||||
_rangeIsValid = NO;
|
||||
|
||||
// When removing nodes we need to make sure that removed indexPaths are not left in _rangeTypeIndexPaths,
|
||||
// otherwise _updateVisibleNodeIndexPaths may try to retrieve nodes from dataSource that aren't there anymore
|
||||
for (NSInteger i = 0; i < ASLayoutRangeTypeCount; i++) {
|
||||
id rangeKey = @((ASLayoutRangeType)i);
|
||||
NSMutableSet *rangePaths = [_rangeTypeIndexPaths[rangeKey] mutableCopy];
|
||||
for (NSIndexPath *path in _rangeTypeIndexPaths[rangeKey]) {
|
||||
if ([indexSet containsIndex:path.section]) {
|
||||
[rangePaths removeObject:path];
|
||||
}
|
||||
}
|
||||
_rangeTypeIndexPaths[rangeKey] = rangePaths;
|
||||
}
|
||||
|
||||
[_delegate rangeController:self didDeleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions];
|
||||
});
|
||||
}
|
||||
|
||||
@end
|
||||
@end
|
||||
@ -1,20 +0,0 @@
|
||||
/* Copyright (c) 2014-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in 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>
|
||||
|
||||
#import <AsyncDisplayKit/ASRangeController.h>
|
||||
#import <AsyncDisplayKit/ASCellNode.h>
|
||||
#import <AsyncDisplayKit/ASDataController.h>
|
||||
#import <AsyncDisplayKit/ASLayoutController.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
// ASRangeControllerBeta defined in ASRangeController.h
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@ -1,374 +0,0 @@
|
||||
/* Copyright (c) 2014-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in 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 "ASRangeControllerBeta.h"
|
||||
|
||||
#import "ASAssert.h"
|
||||
#import "ASDisplayNodeExtras.h"
|
||||
#import "ASDisplayNodeInternal.h"
|
||||
#import "ASMultiDimensionalArrayUtils.h"
|
||||
#import "ASRangeHandlerVisible.h"
|
||||
#import "ASRangeHandlerRender.h"
|
||||
#import "ASRangeHandlerPreload.h"
|
||||
#import "ASInternalHelpers.h"
|
||||
#import "ASDisplayNode+FrameworkPrivate.h"
|
||||
|
||||
@interface ASRangeControllerBeta ()
|
||||
{
|
||||
BOOL _rangeIsValid;
|
||||
BOOL _queuedRangeUpdate;
|
||||
BOOL _layoutControllerImplementsSetVisibleIndexPaths;
|
||||
ASScrollDirection _scrollDirection;
|
||||
NSSet<NSIndexPath *> *_allPreviousIndexPaths;
|
||||
ASLayoutRangeMode _currentRangeMode;
|
||||
BOOL _didRegisterForNotifications;
|
||||
CFAbsoluteTime _pendingDisplayNodesTimestamp;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation ASRangeControllerBeta
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if (!(self = [super init])) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
_rangeIsValid = YES;
|
||||
_currentRangeMode = ASLayoutRangeModeInvalid;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
if (_didRegisterForNotifications) {
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:ASRenderingEngineDidDisplayScheduledNodesNotification object:nil];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Core visible node range managment API
|
||||
|
||||
+ (ASLayoutRangeMode)rangeModeForInterfaceState:(ASInterfaceState)interfaceState
|
||||
currentRangeMode:(ASLayoutRangeMode)currentRangeMode
|
||||
{
|
||||
BOOL isVisible = (ASInterfaceStateIncludesVisible(interfaceState));
|
||||
BOOL isFirstRangeUpdate = (currentRangeMode == ASLayoutRangeModeInvalid);
|
||||
if (!isVisible || isFirstRangeUpdate) {
|
||||
return ASLayoutRangeModeMinimum;
|
||||
}
|
||||
|
||||
return ASLayoutRangeModeFull;
|
||||
}
|
||||
|
||||
- (void)visibleNodeIndexPathsDidChangeWithScrollDirection:(ASScrollDirection)scrollDirection
|
||||
{
|
||||
_scrollDirection = scrollDirection;
|
||||
[self scheduleRangeUpdate];
|
||||
}
|
||||
|
||||
- (void)scheduleRangeUpdate
|
||||
{
|
||||
if (_queuedRangeUpdate) {
|
||||
return;
|
||||
}
|
||||
|
||||
// coalesce these events -- handling them multiple times per runloop is noisy and expensive
|
||||
_queuedRangeUpdate = YES;
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self _updateVisibleNodeIndexPaths];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)setLayoutController:(id<ASLayoutController>)layoutController
|
||||
{
|
||||
_layoutController = layoutController;
|
||||
_layoutControllerImplementsSetVisibleIndexPaths = [_layoutController respondsToSelector:@selector(setVisibleNodeIndexPaths:)];
|
||||
}
|
||||
|
||||
- (void)_updateVisibleNodeIndexPaths
|
||||
{
|
||||
ASDisplayNodeAssert(_layoutController, @"An ASLayoutController is required by ASRangeController");
|
||||
if (!_queuedRangeUpdate || !_layoutController) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Consider if we need to use this codepath, or can rely on something more similar to the data & display ranges
|
||||
// Example: ... = [_layoutController indexPathsForScrolling:_scrollDirection rangeType:ASLayoutRangeTypeVisible];
|
||||
NSArray<NSIndexPath *> *visibleNodePaths = [_dataSource visibleNodeIndexPathsForRangeController:self];
|
||||
|
||||
if (visibleNodePaths.count == 0) { // if we don't have any visibleNodes currently (scrolled before or after content)...
|
||||
_queuedRangeUpdate = NO;
|
||||
return; // don't do anything for this update, but leave _rangeIsValid == NO to make sure we update it later
|
||||
}
|
||||
|
||||
[_layoutController setViewportSize:[_dataSource viewportSizeForRangeController:self]];
|
||||
|
||||
// the layout controller needs to know what the current visible indices are to calculate range offsets
|
||||
if (_layoutControllerImplementsSetVisibleIndexPaths) {
|
||||
[_layoutController setVisibleNodeIndexPaths:visibleNodePaths];
|
||||
}
|
||||
|
||||
// allNodes is a 2D array: it contains arrays for each section, each containing nodes.
|
||||
NSArray<NSArray *> *allNodes = [_dataSource completedNodes];
|
||||
NSUInteger numberOfSections = [allNodes count];
|
||||
|
||||
NSArray<ASDisplayNode *> *currentSectionNodes = nil;
|
||||
NSInteger currentSectionIndex = -1; // Set to -1 so we don't match any indexPath.section on the first iteration.
|
||||
NSUInteger numberOfNodesInSection = 0;
|
||||
|
||||
NSSet<NSIndexPath *> *visibleIndexPaths = [NSSet setWithArray:visibleNodePaths];
|
||||
NSSet<NSIndexPath *> *displayIndexPaths = nil;
|
||||
NSSet<NSIndexPath *> *fetchDataIndexPaths = nil;
|
||||
|
||||
// Prioritize the order in which we visit each. Visible nodes should be updated first so they are enqueued on
|
||||
// the network or display queues before preloading (offscreen) nodes are enqueued.
|
||||
NSMutableOrderedSet<NSIndexPath *> *allIndexPaths = [[NSMutableOrderedSet alloc] initWithSet:visibleIndexPaths];
|
||||
|
||||
ASInterfaceState selfInterfaceState = [_dataSource interfaceStateForRangeController:self];
|
||||
ASLayoutRangeMode rangeMode = [ASRangeControllerBeta rangeModeForInterfaceState:selfInterfaceState
|
||||
currentRangeMode:_currentRangeMode];
|
||||
|
||||
ASRangeTuningParameters parametersFetchData = [_layoutController tuningParametersForRangeMode:rangeMode
|
||||
rangeType:ASLayoutRangeTypeFetchData];
|
||||
if (ASRangeTuningParametersEqualToRangeTuningParameters(parametersFetchData, ASRangeTuningParametersZero)) {
|
||||
fetchDataIndexPaths = visibleIndexPaths;
|
||||
} else {
|
||||
fetchDataIndexPaths = [_layoutController indexPathsForScrolling:_scrollDirection
|
||||
rangeMode:rangeMode
|
||||
rangeType:ASLayoutRangeTypeFetchData];
|
||||
}
|
||||
|
||||
ASRangeTuningParameters parametersDisplay = [_layoutController tuningParametersForRangeMode:rangeMode
|
||||
rangeType:ASLayoutRangeTypeDisplay];
|
||||
if (ASRangeTuningParametersEqualToRangeTuningParameters(parametersDisplay, ASRangeTuningParametersZero)) {
|
||||
displayIndexPaths = visibleIndexPaths;
|
||||
} else if (ASRangeTuningParametersEqualToRangeTuningParameters(parametersDisplay, parametersFetchData)) {
|
||||
displayIndexPaths = fetchDataIndexPaths;
|
||||
} else {
|
||||
displayIndexPaths = [_layoutController indexPathsForScrolling:_scrollDirection
|
||||
rangeMode:rangeMode
|
||||
rangeType:ASLayoutRangeTypeDisplay];
|
||||
}
|
||||
|
||||
// Typically the fetchDataIndexPaths will be the largest, and be a superset of the others, though it may be disjoint.
|
||||
// Because allIndexPaths is an NSMutableOrderedSet, this adds the non-duplicate items /after/ the existing items.
|
||||
// This means that during iteration, we will first visit visible, then display, then fetch data nodes.
|
||||
[allIndexPaths unionSet:displayIndexPaths];
|
||||
[allIndexPaths unionSet:fetchDataIndexPaths];
|
||||
|
||||
// Add anything we had applied interfaceState to in the last update, but is no longer in range, so we can clear any
|
||||
// range flags it still has enabled. Most of the time, all but a few elements are equal; a large programmatic
|
||||
// scroll or major main thread stall could cause entirely disjoint sets. In either case we must visit all.
|
||||
// Calling "-set" on NSMutableOrderedSet just references the underlying mutable data store, so we must copy it.
|
||||
NSSet<NSIndexPath *> *allCurrentIndexPaths = [[allIndexPaths set] copy];
|
||||
[allIndexPaths unionSet:_allPreviousIndexPaths];
|
||||
_allPreviousIndexPaths = allCurrentIndexPaths;
|
||||
_currentRangeMode = rangeMode;
|
||||
|
||||
if (!_rangeIsValid) {
|
||||
[allIndexPaths addObjectsFromArray:ASIndexPathsForMultidimensionalArray(allNodes)];
|
||||
}
|
||||
|
||||
// TODO Don't register for notifications if this range update doesn't cause any node to enter rendering pipeline.
|
||||
// This can be done once there is an API to observe to (or be notified upon) interface state changes or pipeline enterings
|
||||
[self registerForNotificationsForInterfaceStateIfNeeded:selfInterfaceState];
|
||||
|
||||
#if RangeControllerLoggingEnabled
|
||||
NSMutableArray<NSIndexPath *> *modifiedIndexPaths = (RangeControllerLoggingEnabled ? [NSMutableArray array] : nil);
|
||||
#endif
|
||||
|
||||
for (NSIndexPath *indexPath in allIndexPaths) {
|
||||
// Before a node / indexPath is exposed to ASRangeController, ASDataController should have already measured it.
|
||||
// For consistency, make sure each node knows that it should measure itself if something changes.
|
||||
ASInterfaceState interfaceState = ASInterfaceStateMeasureLayout;
|
||||
|
||||
if (ASInterfaceStateIncludesVisible(selfInterfaceState)) {
|
||||
if ([fetchDataIndexPaths containsObject:indexPath]) {
|
||||
interfaceState |= ASInterfaceStateFetchData;
|
||||
}
|
||||
if ([displayIndexPaths containsObject:indexPath]) {
|
||||
interfaceState |= ASInterfaceStateDisplay;
|
||||
}
|
||||
if ([visibleIndexPaths containsObject:indexPath]) {
|
||||
interfaceState |= ASInterfaceStateVisible;
|
||||
}
|
||||
} else {
|
||||
// If selfInterfaceState isn't visible, then visibleIndexPaths represents what /will/ be immediately visible at the
|
||||
// instant we come onscreen. So, fetch data and display all of those things, but don't waste resources preloading yet.
|
||||
// We handle this as a separate case to minimize set operations for offscreen preloading, including containsObject:.
|
||||
|
||||
// Set Layout, Fetch Data, Display. DO NOT set Visible: even though these elements are in the visible range / "viewport",
|
||||
// our overall container object is itself not visible yet. The moment it becomes visible, we will run the condition above.
|
||||
if ([allCurrentIndexPaths containsObject:indexPath]) {
|
||||
// We might be looking at an indexPath that was previously in-range, but now we need to clear it.
|
||||
// In that case we'll just set it back to MeasureLayout. Only set Display | FetchData if in allCurrentIndexPaths.
|
||||
interfaceState |= ASInterfaceStateDisplay;
|
||||
interfaceState |= ASInterfaceStateFetchData;
|
||||
}
|
||||
}
|
||||
|
||||
NSInteger section = indexPath.section;
|
||||
NSInteger row = indexPath.row;
|
||||
|
||||
if (section >= 0 && row >= 0 && section < numberOfSections) {
|
||||
if (section != currentSectionIndex) {
|
||||
// Often we'll be dealing with indexPaths in the same section, but the set isn't sorted and we may even bounce
|
||||
// between the same ones. Still, this saves dozens of method calls to access the inner array and count.
|
||||
currentSectionNodes = [allNodes objectAtIndex:section];
|
||||
numberOfNodesInSection = [currentSectionNodes count];
|
||||
currentSectionIndex = section;
|
||||
}
|
||||
|
||||
if (row < numberOfNodesInSection) {
|
||||
ASDisplayNode *node = [currentSectionNodes objectAtIndex:row];
|
||||
|
||||
ASDisplayNodeAssert(node.hierarchyState & ASHierarchyStateRangeManaged, @"All nodes reaching this point should be range-managed, or interfaceState may be incorrectly reset.");
|
||||
// Skip the many method calls of the recursive operation if the top level cell node already has the right interfaceState.
|
||||
if (node.interfaceState != interfaceState) {
|
||||
#if RangeControllerLoggingEnabled
|
||||
[modifiedIndexPaths addObject:indexPath];
|
||||
#endif
|
||||
[node recursivelySetInterfaceState:interfaceState];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_didRegisterForNotifications) {
|
||||
_pendingDisplayNodesTimestamp = CFAbsoluteTimeGetCurrent();
|
||||
}
|
||||
|
||||
_rangeIsValid = YES;
|
||||
_queuedRangeUpdate = NO;
|
||||
|
||||
#if RangeControllerLoggingEnabled
|
||||
NSSet *visibleNodePathsSet = [NSSet setWithArray:visibleNodePaths];
|
||||
BOOL setsAreEqual = [visibleIndexPaths isEqualToSet:visibleNodePathsSet];
|
||||
NSLog(@"visible sets are equal: %d", setsAreEqual);
|
||||
if (!setsAreEqual) {
|
||||
NSLog(@"standard: %@", visibleIndexPaths);
|
||||
NSLog(@"custom: %@", visibleNodePathsSet);
|
||||
}
|
||||
|
||||
[modifiedIndexPaths sortUsingSelector:@selector(compare:)];
|
||||
|
||||
for (NSIndexPath *indexPath in modifiedIndexPaths) {
|
||||
ASDisplayNode *node = [_dataSource rangeController:self nodeAtIndexPath:indexPath];
|
||||
ASInterfaceState interfaceState = node.interfaceState;
|
||||
BOOL inVisible = ASInterfaceStateIncludesVisible(interfaceState);
|
||||
BOOL inDisplay = ASInterfaceStateIncludesDisplay(interfaceState);
|
||||
BOOL inFetchData = ASInterfaceStateIncludesFetchData(interfaceState);
|
||||
NSLog(@"indexPath %@, Visible: %d, Display: %d, FetchData: %d", indexPath, inVisible, inDisplay, inFetchData);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#pragma mark - Notification observers
|
||||
|
||||
- (void)registerForNotificationsForInterfaceStateIfNeeded:(ASInterfaceState)interfaceState
|
||||
{
|
||||
if (!_didRegisterForNotifications) {
|
||||
ASLayoutRangeMode nextRangeMode = [ASRangeControllerBeta rangeModeForInterfaceState:interfaceState
|
||||
currentRangeMode:_currentRangeMode];
|
||||
if (_currentRangeMode != nextRangeMode) {
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(scheduledNodesDidDisplay:)
|
||||
name:ASRenderingEngineDidDisplayScheduledNodesNotification
|
||||
object:nil];
|
||||
_didRegisterForNotifications = YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)scheduledNodesDidDisplay:(NSNotification *)notification
|
||||
{
|
||||
CFAbsoluteTime notificationTimestamp = ((NSNumber *)[notification.userInfo objectForKey:ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp]).doubleValue;
|
||||
if (_pendingDisplayNodesTimestamp < notificationTimestamp) {
|
||||
// The rendering engine has processed all the nodes this range controller scheduled. Let's schedule a range update
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:ASRenderingEngineDidDisplayScheduledNodesNotification object:nil];
|
||||
_didRegisterForNotifications = NO;
|
||||
|
||||
[self scheduleRangeUpdate];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Cell node view handling
|
||||
|
||||
- (void)configureContentView:(UIView *)contentView forCellNode:(ASCellNode *)node
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
ASDisplayNodeAssert(node, @"Cannot move a nil node to a view");
|
||||
ASDisplayNodeAssert(contentView, @"Cannot move a node to a non-existent view");
|
||||
|
||||
if (node.view.superview == contentView) {
|
||||
// this content view is already correctly configured
|
||||
return;
|
||||
}
|
||||
|
||||
// clean the content view
|
||||
for (UIView *view in contentView.subviews) {
|
||||
[view removeFromSuperview];
|
||||
}
|
||||
|
||||
[contentView addSubview:node.view];
|
||||
}
|
||||
|
||||
#pragma mark - ASDataControllerDelegete
|
||||
|
||||
- (void)dataControllerBeginUpdates:(ASDataController *)dataController
|
||||
{
|
||||
ASPerformBlockOnMainThread(^{
|
||||
[_delegate didBeginUpdatesInRangeController:self];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)dataController:(ASDataController *)dataController endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL))completion
|
||||
{
|
||||
ASPerformBlockOnMainThread(^{
|
||||
[_delegate rangeController:self didEndUpdatesAnimated:animated completion:completion];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)dataController:(ASDataController *)dataController didInsertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
||||
{
|
||||
ASDisplayNodeAssert(nodes.count == indexPaths.count, @"Invalid index path");
|
||||
ASPerformBlockOnMainThread(^{
|
||||
_rangeIsValid = NO;
|
||||
[_delegate rangeController:self didInsertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)dataController:(ASDataController *)dataController didDeleteNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
||||
{
|
||||
ASPerformBlockOnMainThread(^{
|
||||
_rangeIsValid = NO;
|
||||
[_delegate rangeController:self didDeleteNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)dataController:(ASDataController *)dataController didInsertSections:(NSArray *)sections atIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
||||
{
|
||||
ASDisplayNodeAssert(sections.count == indexSet.count, @"Invalid sections");
|
||||
ASPerformBlockOnMainThread(^{
|
||||
_rangeIsValid = NO;
|
||||
[_delegate rangeController:self didInsertSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)dataController:(ASDataController *)dataController didDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
||||
{
|
||||
ASPerformBlockOnMainThread(^{
|
||||
_rangeIsValid = NO;
|
||||
[_delegate rangeController:self didDeleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions];
|
||||
});
|
||||
}
|
||||
|
||||
@end
|
||||
@ -1,26 +0,0 @@
|
||||
/* Copyright (c) 2014-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in 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>
|
||||
|
||||
#import <AsyncDisplayKit/ASLayoutRangeType.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class ASDisplayNode;
|
||||
|
||||
@protocol ASRangeHandler <NSObject>
|
||||
|
||||
@required
|
||||
|
||||
- (void)node:(ASDisplayNode *)node enteredRangeOfType:(ASLayoutRangeType)rangeType;
|
||||
- (void)node:(ASDisplayNode *)node exitedRangeOfType:(ASLayoutRangeType)rangeType;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@ -1,19 +0,0 @@
|
||||
/* Copyright (c) 2014-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in 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>
|
||||
|
||||
#import <AsyncDisplayKit/ASRangeHandler.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface ASRangeHandlerPreload : NSObject <ASRangeHandler>
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@ -1,27 +0,0 @@
|
||||
/* Copyright (c) 2014-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in 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 "ASRangeHandlerPreload.h"
|
||||
#import "ASDisplayNode.h"
|
||||
#import "ASDisplayNode+FrameworkPrivate.h"
|
||||
|
||||
@implementation ASRangeHandlerPreload
|
||||
|
||||
- (void)node:(ASDisplayNode *)node enteredRangeOfType:(ASLayoutRangeType)rangeType
|
||||
{
|
||||
ASDisplayNodeAssert(rangeType == ASLayoutRangeTypeFetchData, @"Preload delegate should not handle other ranges");
|
||||
[node enterInterfaceState:ASInterfaceStateFetchData];
|
||||
}
|
||||
|
||||
- (void)node:(ASDisplayNode *)node exitedRangeOfType:(ASLayoutRangeType)rangeType
|
||||
{
|
||||
ASDisplayNodeAssert(rangeType == ASLayoutRangeTypeFetchData, @"Preload delegate should not handle other ranges");
|
||||
[node exitInterfaceState:ASInterfaceStateFetchData];
|
||||
}
|
||||
|
||||
@end
|
||||
@ -1,19 +0,0 @@
|
||||
/* Copyright (c) 2014-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in 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>
|
||||
|
||||
#import <AsyncDisplayKit/ASRangeHandler.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface ASRangeHandlerRender : NSObject <ASRangeHandler>
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@ -1,128 +0,0 @@
|
||||
/* Copyright (c) 2014-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in 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 "ASRangeHandlerRender.h"
|
||||
|
||||
#import "ASDisplayNode.h"
|
||||
#import "ASDisplayNode+Subclasses.h"
|
||||
#import "ASDisplayNode+FrameworkPrivate.h"
|
||||
#import "ASDisplayNode+Beta.h"
|
||||
|
||||
@interface ASRangeHandlerRender ()
|
||||
@property (nonatomic,readonly) UIWindow *workingWindow;
|
||||
@end
|
||||
|
||||
@implementation ASRangeHandlerRender
|
||||
@synthesize workingWindow = _workingWindow;
|
||||
|
||||
- (UIWindow *)workingWindow
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
|
||||
// we add nodes' views to this invisible window to start async rendering
|
||||
// TODO: Replace this with directly triggering display https://github.com/facebook/AsyncDisplayKit/issues/315
|
||||
// Update: Latest attempt is at https://github.com/facebook/AsyncDisplayKit/pull/828
|
||||
|
||||
if (!_workingWindow && ![ASDisplayNode shouldUseNewRenderingRange]) {
|
||||
_workingWindow = [[UIWindow alloc] initWithFrame:CGRectZero];
|
||||
_workingWindow.windowLevel = UIWindowLevelNormal - 1000;
|
||||
_workingWindow.userInteractionEnabled = NO;
|
||||
_workingWindow.hidden = YES;
|
||||
_workingWindow.alpha = 0.0;
|
||||
}
|
||||
|
||||
return _workingWindow;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
if (![ASDisplayNode shouldUseNewRenderingRange]) {
|
||||
for (CALayer *layer in [self.workingWindow.layer.sublayers copy]) {
|
||||
ASDisplayNode *node = layer.asyncdisplaykit_node;
|
||||
[self node:node exitedRangeOfType:ASLayoutRangeTypeDisplay];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)node:(ASDisplayNode *)node enteredRangeOfType:(ASLayoutRangeType)rangeType
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
ASDisplayNodeAssert(rangeType == ASLayoutRangeTypeDisplay, @"Render delegate should not handle other ranges");
|
||||
|
||||
// If a node had previously been onscreen but now is only in the working range,
|
||||
// ensure its view is not orphaned in a UITableViewCell in the reuse pool.
|
||||
if (![node isLayerBacked] && node.view.superview) {
|
||||
[node.view removeFromSuperview];
|
||||
}
|
||||
|
||||
// The node un-suspends display.
|
||||
[node enterInterfaceState:ASInterfaceStateDisplay];
|
||||
|
||||
|
||||
// ASDisplayNodeAssert(![ASDisplayNode shouldUseNewRenderingRange], @"It should no longer be possible to reach this point with the new display range enabled");
|
||||
if ([ASDisplayNode shouldUseNewRenderingRange]) {
|
||||
[node recursivelyEnsureDisplaySynchronously:NO];
|
||||
} else {
|
||||
// Add the node's layer to an off-screen window to trigger display and mark its contents as non-volatile.
|
||||
// Use the layer directly to avoid the substantial overhead of UIView heirarchy manipulations.
|
||||
// Any view-backed nodes will still create their views in order to assemble the layer heirarchy, and they will
|
||||
// also assemble a view subtree for the node, but we avoid the much more significant expense triggered by a view
|
||||
// being added or removed from an onscreen window (responder chain setup, will/DidMoveToWindow: recursive calls, etc)
|
||||
[[[self workingWindow] layer] addSublayer:node.layer];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)node:(ASDisplayNode *)node exitedRangeOfType:(ASLayoutRangeType)rangeType
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
ASDisplayNodeAssert(rangeType == ASLayoutRangeTypeDisplay, @"Render delegate should not handle other ranges");
|
||||
|
||||
// This code is tricky. There are several possible states a node can be in when it reaches this point.
|
||||
// 1. Layer-backed vs view-backed nodes. AS of this writing, only ASCellNodes arrive here, which are always view-backed —
|
||||
// but we maintain correctness for all ASDisplayNodes, including layer-backed ones.
|
||||
// (Note: it would not make sense to pass in a subnode of a rasterized node here, so that is unsupported).
|
||||
// 2. The node's layer may have been added to the workingWindow previously, or it may have never been added, such as if rangeTuningParameter's leading value is 0.
|
||||
// 3. The node's layer may not be present in the workingWindow, even if it was previously added.
|
||||
// This is a common case, as once the node is added to an active cell contentsView (e.g. visible), it is automatically removed from the workingWindow.
|
||||
// The system does this when addSublayer is called, even if removeFromSuperlayer is never explicitly called.
|
||||
// 4. Lastly and most unusually, it is possible for a node to be offscreen, completely outside the heirarchy, and yet considered within the working range.
|
||||
// This happens if the UITableViewCell is reused after scrolling offscreen. Because the node has already been given the opportunity to display, we do not
|
||||
// proactively re-host it within the workingWindow (improving efficiency). Some time later, it may fall outside the working range, in which case calling
|
||||
// -recursivelyClearContents is critical. If the user scrolls back and it is re-hosted in a UITableViewCell, the content will still exist as it is not cleared
|
||||
// by simply being removed from the cell. The code that usually triggers this condition is the -removeFromSuperview in -[ASRangeController configureContentView:forCellNode:].
|
||||
// Condition #4 is suboptimal in some cases, as it is conceivable that memory warnings could trigger clearing content that is inside the working range. However, enforcing the
|
||||
// preservation of this content could result in the app being killed, which is not likely preferable over briefly seeing placeholders in the event the user scrolls backwards.
|
||||
// Nonetheless, future changes to the implementation will likely eliminate this behavior to simplify debugging and extensibility of working range functionality.
|
||||
|
||||
// The node calls clearCurrentContents and suspends display
|
||||
[node exitInterfaceState:ASInterfaceStateDisplay];
|
||||
|
||||
// ASDisplayNodeAssert(![ASDisplayNode shouldUseNewRenderingRange], @"It should no longer be possible to reach this point with the new display range enabled");
|
||||
|
||||
if ([ASDisplayNode shouldUseNewRenderingRange]) {
|
||||
if (![node isLayerBacked]) {
|
||||
[node.view removeFromSuperview];
|
||||
} else {
|
||||
[node.layer removeFromSuperlayer];
|
||||
}
|
||||
} else {
|
||||
if (node.layer.superlayer != [[self workingWindow] layer]) {
|
||||
// In this case, the node has previously passed through the working range (or it is zero), and it has now fallen outside the working range.
|
||||
if (![node isLayerBacked]) {
|
||||
// If the node is view-backed, we need to make sure to remove the view (which is now present in the containing cell contentsView).
|
||||
// Layer-backed nodes will be fully handled by the unconditional removal below.
|
||||
[node.view removeFromSuperview];
|
||||
}
|
||||
}
|
||||
|
||||
// At this point, the node's layer may validly be present either in the workingWindow, or in the contentsView of a cell.
|
||||
[node.layer removeFromSuperlayer];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@ -1,15 +0,0 @@
|
||||
/* Copyright (c) 2014-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in 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>
|
||||
|
||||
#import <AsyncDisplayKit/ASRangeHandler.h>
|
||||
|
||||
@interface ASRangeHandlerVisible : NSObject <ASRangeHandler>
|
||||
|
||||
@end
|
||||
@ -1,25 +0,0 @@
|
||||
/* Copyright (c) 2014-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in 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 "ASRangeHandlerVisible.h"
|
||||
#import "ASDisplayNode.h"
|
||||
#import "ASDisplayNode+FrameworkPrivate.h"
|
||||
|
||||
@implementation ASRangeHandlerVisible
|
||||
|
||||
- (void)node:(ASDisplayNode *)node enteredRangeOfType:(ASLayoutRangeType)rangeType
|
||||
{
|
||||
[node enterInterfaceState:ASInterfaceStateVisible];
|
||||
}
|
||||
|
||||
- (void)node:(ASDisplayNode *)node exitedRangeOfType:(ASLayoutRangeType)rangeType
|
||||
{
|
||||
[node exitInterfaceState:ASInterfaceStateVisible];
|
||||
}
|
||||
|
||||
@end
|
||||
@ -290,13 +290,11 @@
|
||||
|
||||
_messageToViewOrLayer(setNeedsDisplay);
|
||||
|
||||
if ([ASDisplayNode shouldUseNewRenderingRange]) {
|
||||
BOOL nowDisplay = ASInterfaceStateIncludesDisplay(_interfaceState);
|
||||
// FIXME: This should not need to recursively display, so create a non-recursive variant.
|
||||
// The semantics of setNeedsDisplay (as defined by CALayer behavior) are not recursive.
|
||||
if (_layer && !_flags.synchronous && nowDisplay && [self __implementsDisplay]) {
|
||||
[ASDisplayNode scheduleNodeForRecursiveDisplay:self];
|
||||
}
|
||||
BOOL nowDisplay = ASInterfaceStateIncludesDisplay(_interfaceState);
|
||||
// FIXME: This should not need to recursively display, so create a non-recursive variant.
|
||||
// The semantics of setNeedsDisplay (as defined by CALayer behavior) are not recursive.
|
||||
if (_layer && !_flags.synchronous && nowDisplay && [self __implementsDisplay]) {
|
||||
[ASDisplayNode scheduleNodeForRecursiveDisplay:self];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,7 +14,6 @@
|
||||
|
||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
||||
{
|
||||
[ASDisplayNode setShouldUseNewRenderingRange:YES];
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
@ -20,8 +20,6 @@
|
||||
|
||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
||||
{
|
||||
[ASDisplayNode setShouldUseNewRenderingRange:YES];
|
||||
|
||||
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
|
||||
self.window.backgroundColor = [UIColor whiteColor];
|
||||
self.window.rootViewController = [[UINavigationController alloc] init];
|
||||
|
||||
@ -19,9 +19,7 @@
|
||||
@implementation AppDelegate
|
||||
|
||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
||||
{
|
||||
[ASDisplayNode setShouldUseNewRenderingRange:YES];
|
||||
|
||||
{
|
||||
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
|
||||
self.window.backgroundColor = [UIColor whiteColor];
|
||||
self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:[[ViewController alloc] init]];
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user