diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index 05a9ebf937..e08de9ab04 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -138,6 +138,12 @@ 05F20AA41A15733C00DCA68A /* ASImageProtocols.h in Headers */ = {isa = PBXBuildFile; fileRef = 05F20AA31A15733C00DCA68A /* ASImageProtocols.h */; settings = {ATTRIBUTES = (Public, ); }; }; 1950C4491A3BB5C1005C8279 /* ASEqualityHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = 1950C4481A3BB5C1005C8279 /* ASEqualityHelpers.h */; }; 2911485C1A77147A005D0878 /* ASControlNodeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 2911485B1A77147A005D0878 /* ASControlNodeTests.m */; }; + 292C599F1A956527007E5DD6 /* ASLayoutRangeType.h in Headers */ = {isa = PBXBuildFile; fileRef = 292C59991A956527007E5DD6 /* ASLayoutRangeType.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 292C59A01A956527007E5DD6 /* ASPreloadRangeDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 292C599A1A956527007E5DD6 /* ASPreloadRangeDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 292C59A11A956527007E5DD6 /* ASPreloadRangeDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 292C599B1A956527007E5DD6 /* ASPreloadRangeDelegate.mm */; }; + 292C59A21A956527007E5DD6 /* ASRangeDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 292C599C1A956527007E5DD6 /* ASRangeDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 292C59A31A956527007E5DD6 /* ASRenderRangeDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 292C599D1A956527007E5DD6 /* ASRenderRangeDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 292C59A41A956527007E5DD6 /* ASRenderRangeDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 292C599E1A956527007E5DD6 /* ASRenderRangeDelegate.mm */; }; 3C9C128519E616EF00E942A0 /* ASTableViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3C9C128419E616EF00E942A0 /* ASTableViewTests.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; 464052201A3F83C40061C0BA /* ASDataController.h in Headers */ = {isa = PBXBuildFile; fileRef = 464052191A3F83C40061C0BA /* ASDataController.h */; settings = {ATTRIBUTES = (Public, ); }; }; 464052211A3F83C40061C0BA /* ASDataController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4640521A1A3F83C40061C0BA /* ASDataController.mm */; }; @@ -284,6 +290,12 @@ 05F20AA31A15733C00DCA68A /* ASImageProtocols.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASImageProtocols.h; sourceTree = ""; }; 1950C4481A3BB5C1005C8279 /* ASEqualityHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASEqualityHelpers.h; sourceTree = ""; }; 2911485B1A77147A005D0878 /* ASControlNodeTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASControlNodeTests.m; sourceTree = ""; }; + 292C59991A956527007E5DD6 /* ASLayoutRangeType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASLayoutRangeType.h; sourceTree = ""; }; + 292C599A1A956527007E5DD6 /* ASPreloadRangeDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASPreloadRangeDelegate.h; sourceTree = ""; }; + 292C599B1A956527007E5DD6 /* ASPreloadRangeDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASPreloadRangeDelegate.mm; sourceTree = ""; }; + 292C599C1A956527007E5DD6 /* ASRangeDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASRangeDelegate.h; sourceTree = ""; }; + 292C599D1A956527007E5DD6 /* ASRenderRangeDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASRenderRangeDelegate.h; sourceTree = ""; }; + 292C599E1A956527007E5DD6 /* ASRenderRangeDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASRenderRangeDelegate.mm; sourceTree = ""; }; 3C9C128419E616EF00E942A0 /* ASTableViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASTableViewTests.m; sourceTree = ""; }; 464052191A3F83C40061C0BA /* ASDataController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASDataController.h; sourceTree = ""; }; 4640521A1A3F83C40061C0BA /* ASDataController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASDataController.mm; sourceTree = ""; }; @@ -463,12 +475,21 @@ 058D09E7195D050800B7D73C /* ASHighlightOverlayLayer.mm */, 05F20AA31A15733C00DCA68A /* ASImageProtocols.h */, 4640521D1A3F83C40061C0BA /* ASLayoutController.h */, + + 292C59991A956527007E5DD6 /* ASLayoutRangeType.h */, 4640521E1A3F83C40061C0BA /* ASMultidimensionalArrayUtils.h */, 4640521F1A3F83C40061C0BA /* ASMultidimensionalArrayUtils.mm */, 058D09E8195D050800B7D73C /* ASMutableAttributedStringBuilder.h */, 058D09E9195D050800B7D73C /* ASMutableAttributedStringBuilder.m */, 055F1A3619ABD413004DAFF1 /* ASRangeController.h */, 055F1A3719ABD413004DAFF1 /* ASRangeController.mm */, + 292C599A1A956527007E5DD6 /* ASPreloadRangeDelegate.h */, + 292C599B1A956527007E5DD6 /* ASPreloadRangeDelegate.mm */, + 055F1A3619ABD413004DAFF1 /* ASRangeController.h */, + 055F1A3719ABD413004DAFF1 /* ASRangeController.mm */, + 292C599C1A956527007E5DD6 /* ASRangeDelegate.h */, + 292C599D1A956527007E5DD6 /* ASRenderRangeDelegate.h */, + 292C599E1A956527007E5DD6 /* ASRenderRangeDelegate.mm */, 058D09EA195D050800B7D73C /* ASTextNodeCoreTextAdditions.h */, 058D09EB195D050800B7D73C /* ASTextNodeCoreTextAdditions.m */, 058D09EC195D050800B7D73C /* ASTextNodeRenderer.h */, @@ -574,6 +595,7 @@ 058D0A51195D05CB00B7D73C /* ASTextNode.h in Headers */, 058D0A52195D05CB00B7D73C /* ASTextNode.mm in Headers */, 055F1A3819ABD413004DAFF1 /* ASRangeController.h in Headers */, + 292C59A31A956527007E5DD6 /* ASRenderRangeDelegate.h in Headers */, 055F1A3419ABD3E3004DAFF1 /* ASTableView.h in Headers */, 0574D5E219C110940097DC25 /* ASTableViewProtocols.h in Headers */, 055F1A3C19ABD43F004DAFF1 /* ASCellNode.h in Headers */, @@ -594,6 +616,7 @@ 058D0A61195D05DC00B7D73C /* ASTextNodeTextKitHelpers.h in Headers */, 058D0A62195D05DC00B7D73C /* ASTextNodeTextKitHelpers.mm in Headers */, 058D0A63195D05DC00B7D73C /* ASTextNodeTypes.h in Headers */, + 292C599F1A956527007E5DD6 /* ASLayoutRangeType.h in Headers */, 464052251A3F83C40061C0BA /* ASMultidimensionalArrayUtils.h in Headers */, 058D0A64195D05DC00B7D73C /* ASTextNodeWordKerner.h in Headers */, 058D0A65195D05DC00B7D73C /* ASTextNodeWordKerner.m in Headers */, @@ -615,6 +638,7 @@ 058D0A83195D060300B7D73C /* ASBaseDefines.h in Headers */, 058D0A84195D060300B7D73C /* ASDisplayNodeExtraIvars.h in Headers */, AC3C4A511A1139C100143C57 /* ASCollectionView.h in Headers */, + 292C59A01A956527007E5DD6 /* ASPreloadRangeDelegate.h in Headers */, 055B9FA81A1C154B00035D6D /* ASNetworkImageNode.h in Headers */, 054963491A1EA066000F8E56 /* ASBasicImageDownloader.h in Headers */, AC3C4A541A113EEC00143C57 /* ASCollectionViewProtocols.h in Headers */, @@ -634,6 +658,7 @@ 058D0A7B195D05F900B7D73C /* ASDisplayNodeInternal.h in Headers */, 058D0A7C195D05F900B7D73C /* ASImageNode+CGExtras.h in Headers */, 058D0A7D195D05F900B7D73C /* ASImageNode+CGExtras.m in Headers */, + 292C59A21A956527007E5DD6 /* ASRangeDelegate.h in Headers */, 058D0A7F195D05F900B7D73C /* ASSentinel.h in Headers */, 058D0A80195D05F900B7D73C /* ASSentinel.m in Headers */, 058D0A81195D05F900B7D73C /* ASThread.h in Headers */, @@ -770,8 +795,10 @@ 464052261A3F83C40061C0BA /* ASMultidimensionalArrayUtils.mm in Sources */, 055B9FA91A1C154B00035D6D /* ASNetworkImageNode.mm in Sources */, 058D0A1D195D050800B7D73C /* ASTextNodeRenderer.mm in Sources */, + 292C59A41A956527007E5DD6 /* ASRenderRangeDelegate.mm in Sources */, 058D0A2A195D050800B7D73C /* ASDisplayNode+UIViewBridge.mm in Sources */, AC3C4A521A1139C100143C57 /* ASCollectionView.mm in Sources */, + 292C59A11A956527007E5DD6 /* ASPreloadRangeDelegate.mm in Sources */, 058D0A20195D050800B7D73C /* ASTextNodeWordKerner.m in Sources */, 058D0A1A195D050800B7D73C /* ASHighlightOverlayLayer.mm in Sources */, 464052231A3F83C40061C0BA /* ASFlowLayoutController.mm in Sources */, diff --git a/AsyncDisplayKit/ASCollectionView.h b/AsyncDisplayKit/ASCollectionView.h index fbc42c76e0..34e5f10039 100644 --- a/AsyncDisplayKit/ASCollectionView.h +++ b/AsyncDisplayKit/ASCollectionView.h @@ -39,7 +39,7 @@ * Defaults to the render range having one sceenful both leading and trailing and the preload range having two * screenfuls in both directions. */ -- (ASRangeTuningParameters)tuningParametersForRange:(ASLayoutRange)range; +- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType; /** * Set the tuning parameters for a range. @@ -47,7 +47,7 @@ * @param tuningParameters The tuning parameters to store for a range. * @param range The range to set the tuning parameters for. */ -- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRange:(ASLayoutRange)range; +- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType; /** * Initializer. diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 13cf2a59ba..6d5bf11e87 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -199,24 +199,24 @@ static BOOL _isInterceptedSelector(SEL sel) super.delegate = (id)_proxyDelegate; } -- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRange:(ASLayoutRange)range +- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType { - [_layoutController setTuningParameters:tuningParameters forRange:range]; + [_layoutController setTuningParameters:tuningParameters forRangeType:rangeType]; } -- (ASRangeTuningParameters)tuningParametersForRange:(ASLayoutRange)range +- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType { - return [_layoutController tuningParametersForRange:range]; + return [_layoutController tuningParametersForRangeType:rangeType]; } - (ASRangeTuningParameters)rangeTuningParameters { - return [self tuningParametersForRange:ASLayoutRangeRender]; + return [self tuningParametersForRangeType:ASLayoutRangeTypeRender]; } - (void)setRangeTuningParameters:(ASRangeTuningParameters)tuningParameters { - [self setTuningParameters:tuningParameters forRange:ASLayoutRangeRender]; + [self setTuningParameters:tuningParameters forRangeType:ASLayoutRangeTypeRender]; } - (CGSize)calculatedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath diff --git a/AsyncDisplayKit/ASDisplayNode+Subclasses.h b/AsyncDisplayKit/ASDisplayNode+Subclasses.h index f551639afe..40de2a0047 100644 --- a/AsyncDisplayKit/ASDisplayNode+Subclasses.h +++ b/AsyncDisplayKit/ASDisplayNode+Subclasses.h @@ -185,6 +185,14 @@ */ - (void)displayDidFinish ASDISPLAYNODE_REQUIRES_SUPER; +/** + * @abstract Indicates that the node should fetch any external data, such as images. + * + * @discussion Subclasses may override this method to be notified when they should begin to fetch data. Fetching + * should be done asynchronously. The node is also responsible for managing the memory of any data. + */ +- (void)fetchRemoteData ASDISPLAYNODE_REQUIRES_SUPER; + /** * @abstract Indicates that the receiver is about to display its subnodes. This method is not called if there are no * subnodes present. @@ -312,13 +320,20 @@ - (void)didExitHierarchy ASDISPLAYNODE_REQUIRES_SUPER; /** - * Provides an opportunity to clear backing store and other memory-intensive intermediates, such as text layout managers - * or downloaded content that can be written to a disk cache. + * Provides an opportunity to clear backing store and other memory-intensive intermediates, such as text layout managers. * - * @discussion Called by -recursivelyReclaimMemory. Base class implements self.contents = nil, clearing any backing + * @discussion Called by -recursivelyClearRendering. Base class implements self.contents = nil, clearing any backing * store, for asynchronous regeneration when needed. */ -- (void)reclaimMemory ASDISPLAYNODE_REQUIRES_SUPER; +- (void)clearRendering ASDISPLAYNODE_REQUIRES_SUPER; + +/** + * Provides an opportunity to clear any remote data for only the current node. + * + * @discussion This will not clear data recursively for all subnodes. Either call -recursivelyClearRemoteData or + * selectively clear remote data. + */ +- (void)clearRemoteData ASDISPLAYNODE_REQUIRES_SUPER; /** @name Placeholders */ diff --git a/AsyncDisplayKit/ASDisplayNode.h b/AsyncDisplayKit/ASDisplayNode.h index a7cf542883..c977223128 100644 --- a/AsyncDisplayKit/ASDisplayNode.h +++ b/AsyncDisplayKit/ASDisplayNode.h @@ -312,7 +312,7 @@ typedef CALayer *(^ASDisplayNodeLayerBlock)(); - (void)recursivelySetDisplaySuspended:(BOOL)flag; /** - * @abstract Calls -reclaimMemory on the receiver and its subnode hierarchy. + * @abstract Calls -clearRendering on the receiver and its subnode hierarchy. * * @discussion Clears backing stores and other memory-intensive intermediates. * If the node is removed from a visible hierarchy and then re-added, it will automatically trigger a new asynchronous display, @@ -322,7 +322,18 @@ typedef CALayer *(^ASDisplayNodeLayerBlock)(); * @see displaySuspended and setNeedsDisplay */ -- (void)recursivelyReclaimMemory; +- (void)recursivelyClearRendering; + +/** + * @abstract Calls -clearRemoteData on the receiver and its subnode hierarchy. + * + * @discussion Clears any memory-intensive fetched content. + * This method is used to notify the node that it should purge any content that is both expensive to fetch and to + * retain in memory. + * + * @see fetchRemoteData + */ +- (void)recursivelyClearRemoteData; /** * @abstract Toggle displaying a placeholder over the node that covers content until the node and all subnodes are @@ -539,5 +550,9 @@ typedef CALayer *(^ASDisplayNodeLayerBlock)(); @end @interface ASDisplayNode (Deprecated) + +- (void)reclaimMemory ASDISPLAYNODE_DEPRECATED; +- (void)recursivelyReclaimMemory ASDISPLAYNODE_DEPRECATED; @property (nonatomic, assign) BOOL placeholderFadesOut ASDISPLAYNODE_DEPRECATED; + @end diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 5404b4239d..73817598c4 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -90,7 +90,7 @@ void ASDisplayNodePerformBlockOnMainThread(void (^block)()) // Subclasses should never override these ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(calculatedSize)), @"Subclass %@ must not override calculatedSize method", NSStringFromClass(self)); ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(measure:)), @"Subclass %@ must not override measure method", NSStringFromClass(self)); - ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(recursivelyReclaimMemory)), @"Subclass %@ must not override recursivelyReclaimMemory method", NSStringFromClass(self)); + ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(recursivelyClearRendering)), @"Subclass %@ must not override recursivelyClearRendering method", NSStringFromClass(self)); } + (BOOL)layerBackedNodesEnabled @@ -1328,18 +1328,36 @@ static NSInteger incrementIfFound(NSInteger i) { [self __exitedHierarchy]; } -- (void)reclaimMemory +- (void)clearRendering { self.layer.contents = nil; _placeholderLayer.contents = nil; } -- (void)recursivelyReclaimMemory +- (void)recursivelyClearRendering { for (ASDisplayNode *subnode in self.subnodes) { - [subnode recursivelyReclaimMemory]; + [subnode recursivelyClearRendering]; } - [self reclaimMemory]; + [self clearRendering]; +} + +- (void)fetchRemoteData +{ + // subclass override +} + +- (void)clearRemoteData +{ + // subclass override +} + +- (void)recursivelyClearRemoteData +{ + for (ASDisplayNode *subnode in self.subnodes) { + [subnode recursivelyClearRemoteData]; + } + [self clearRemoteData]; } - (void)layout @@ -1460,6 +1478,7 @@ static NSInteger incrementIfFound(NSInteger i) { } } + #pragma mark - Pending View State - (_ASPendingState *)pendingViewState { @@ -1786,4 +1805,14 @@ static const char *ASDisplayNodeAssociatedNodeKey = "ASAssociatedNode"; return self.placeholderFadeDuration > 0.0; } +- (void)reclaimMemory +{ + [self clearRendering]; +} + +- (void)recursivelyReclaimMemory +{ + [self recursivelyClearRendering]; +} + @end diff --git a/AsyncDisplayKit/ASMultiplexImageNode.mm b/AsyncDisplayKit/ASMultiplexImageNode.mm index 9abf6b8b2f..b618228ce4 100644 --- a/AsyncDisplayKit/ASMultiplexImageNode.mm +++ b/AsyncDisplayKit/ASMultiplexImageNode.mm @@ -162,9 +162,9 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent } #pragma mark - ASDisplayNode Overrides -- (void)reclaimMemory +- (void)clearRendering { - [super reclaimMemory]; // This actually clears the contents, so we need to do this first for our displayedImageIdentifier to be meaningful. + [super clearRendering]; // This actually clears the contents, so we need to do this first for our displayedImageIdentifier to be meaningful. [self _setDisplayedImageIdentifier:nil withImage:nil]; if (_downloadIdentifier) { @@ -177,6 +177,13 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent { [super displayWillStart]; + [self fetchRemoteData]; +} + +- (void)fetchRemoteData +{ + [super fetchRemoteData]; + [self _loadImageIdentifiers]; } diff --git a/AsyncDisplayKit/ASNetworkImageNode.mm b/AsyncDisplayKit/ASNetworkImageNode.mm index b5f8946f91..270586a988 100644 --- a/AsyncDisplayKit/ASNetworkImageNode.mm +++ b/AsyncDisplayKit/ASNetworkImageNode.mm @@ -123,9 +123,16 @@ return _delegate; } -- (void)reclaimMemory +- (void)displayWillStart { - [super reclaimMemory]; + [super displayWillStart]; + + [self fetchRemoteData]; +} + +- (void)clearRemoteData +{ + [super clearRemoteData]; { ASDN::MutexLocker l(_lock); @@ -136,10 +143,10 @@ } } -- (void)displayWillStart +- (void)fetchRemoteData { - [super displayWillStart]; - + [super fetchRemoteData]; + { ASDN::MutexLocker l(_lock); [self _lazilyLoadImageIfNecessary]; diff --git a/AsyncDisplayKit/ASTableView.h b/AsyncDisplayKit/ASTableView.h index d3b4d5cc45..9b4118c8c7 100644 --- a/AsyncDisplayKit/ASTableView.h +++ b/AsyncDisplayKit/ASTableView.h @@ -32,22 +32,22 @@ /** * Tuning parameters for a range. * - * @param range The range to get the tuning parameters for. + * @param rangeType The range to get the tuning parameters for. * * @returns A tuning parameter value for the given range. * * Defaults to the render range having one sceenful both leading and trailing and the preload range having two * screenfuls in both directions. */ -- (ASRangeTuningParameters)tuningParametersForRange:(ASLayoutRange)range; +- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType; /** * Set the tuning parameters for a range. * * @param tuningParameters The tuning parameters to store for a range. - * @param range The range to set the tuning parameters for. + * @param rangeType The range to set the tuning parameters for. */ -- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRange:(ASLayoutRange)range; +- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType; /** * initializer. diff --git a/AsyncDisplayKit/ASTableView.mm b/AsyncDisplayKit/ASTableView.mm index 2a4f529689..46f23b8cd8 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -201,24 +201,24 @@ static BOOL _isInterceptedSelector(SEL sel) [_dataController reloadDataWithAnimationOption:UITableViewRowAnimationNone]; } -- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRange:(ASLayoutRange)range +- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType { - [_layoutController setTuningParameters:tuningParameters forRange:range]; + [_layoutController setTuningParameters:tuningParameters forRangeType:rangeType]; } -- (ASRangeTuningParameters)tuningParametersForRange:(ASLayoutRange)range +- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType { - return [_layoutController tuningParametersForRange:range]; + return [_layoutController tuningParametersForRangeType:rangeType]; } - (ASRangeTuningParameters)rangeTuningParameters { - return [self tuningParametersForRange:ASLayoutRangeRender]; + return [self tuningParametersForRangeType:ASLayoutRangeTypeRender]; } - (void)setRangeTuningParameters:(ASRangeTuningParameters)tuningParameters { - [self setTuningParameters:tuningParameters forRange:ASLayoutRangeRender]; + [self setTuningParameters:tuningParameters forRangeType:ASLayoutRangeTypeRender]; } - (ASCellNode *)nodeForRowAtIndexPath:(NSIndexPath *)indexPath diff --git a/AsyncDisplayKit/ASTextNode.mm b/AsyncDisplayKit/ASTextNode.mm index 8004ef6b91..80201548a6 100644 --- a/AsyncDisplayKit/ASTextNode.mm +++ b/AsyncDisplayKit/ASTextNode.mm @@ -212,12 +212,12 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f) [self _invalidateRenderer]; } -- (void)reclaimMemory +- (void)clearRendering { // We discard the backing store and renderer to prevent the very large // memory overhead of maintaining these for all text nodes. They can be // regenerated when layout is necessary. - [super reclaimMemory]; // ASDisplayNode will set layer.contents = nil + [super clearRendering]; // ASDisplayNode will set layer.contents = nil [self _invalidateRenderer]; } diff --git a/AsyncDisplayKit/Details/ASDataController.h b/AsyncDisplayKit/Details/ASDataController.h index cc58603329..996d84427f 100644 --- a/AsyncDisplayKit/Details/ASDataController.h +++ b/AsyncDisplayKit/Details/ASDataController.h @@ -1,4 +1,10 @@ -// Copyright 2004-present Facebook. All Rights Reserved. +/* Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * 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 #import diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 29ba47a5f9..ed70155727 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -1,4 +1,10 @@ -// Copyright 2004-present Facebook. All Rights Reserved. +/* Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * 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 "ASDataController.h" diff --git a/AsyncDisplayKit/Details/ASFlowLayoutController.h b/AsyncDisplayKit/Details/ASFlowLayoutController.h index 632ba312bf..2524c30509 100644 --- a/AsyncDisplayKit/Details/ASFlowLayoutController.h +++ b/AsyncDisplayKit/Details/ASFlowLayoutController.h @@ -1,4 +1,10 @@ -// Copyright 2004-present Facebook. All Rights Reserved. +/* Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * 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 #import @@ -14,9 +20,9 @@ typedef NS_ENUM(NSUInteger, ASFlowLayoutDirection) { */ @interface ASFlowLayoutController : NSObject -- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRange:(ASLayoutRange)range; +- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType; -- (ASRangeTuningParameters)tuningParametersForRange:(ASLayoutRange)range; +- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType; @property (nonatomic, readonly, assign) ASFlowLayoutDirection layoutDirection; diff --git a/AsyncDisplayKit/Details/ASFlowLayoutController.mm b/AsyncDisplayKit/Details/ASFlowLayoutController.mm index 60a62c7d6c..b093a05d16 100644 --- a/AsyncDisplayKit/Details/ASFlowLayoutController.mm +++ b/AsyncDisplayKit/Details/ASFlowLayoutController.mm @@ -1,4 +1,10 @@ -// Copyright 2004-present Facebook. All Rights Reserved. +/* Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * 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 "ASFlowLayoutController.h" @@ -33,17 +39,14 @@ static const CGFloat kASFlowLayoutControllerRefreshingThreshold = 0.3; _layoutDirection = layoutDirection; - _tuningParameterMap = { - { - // Render - .leadingBufferScreenfuls = 1, - .trailingBufferScreenfuls = 1 - }, - { - // Preload - .leadingBufferScreenfuls = 2, - .trailingBufferScreenfuls = 2 - } + _tuningParameterMap = std::vector(2); + _tuningParameterMap[ASLayoutRangeTypePreload] = { + .leadingBufferScreenfuls = 2, + .trailingBufferScreenfuls = 1 + }; + _tuningParameterMap[ASLayoutRangeTypeRender] = { + .leadingBufferScreenfuls = 3, + .trailingBufferScreenfuls = 2 }; return self; @@ -51,28 +54,28 @@ static const CGFloat kASFlowLayoutControllerRefreshingThreshold = 0.3; #pragma mark - Tuning Parameters -- (ASRangeTuningParameters)tuningParametersForRange:(ASLayoutRange)range +- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType { - ASDisplayNodeAssert(range < _tuningParameterMap.size(), @"Requesting a range that is OOB for the configured tuning parameters"); - return _tuningParameterMap[range]; + ASDisplayNodeAssert(rangeType < _tuningParameterMap.size(), @"Requesting a range that is OOB for the configured tuning parameters"); + return _tuningParameterMap[rangeType]; } -- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRange:(ASLayoutRange)range +- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType { - ASDisplayNodeAssert(range < _tuningParameterMap.size(), @"Requesting a range that is OOB for the configured tuning parameters"); - _tuningParameterMap[range] = tuningParameters; + ASDisplayNodeAssert(rangeType < _tuningParameterMap.size(), @"Requesting a range that is OOB for the configured tuning parameters"); + _tuningParameterMap[rangeType] = tuningParameters; } // Support for the deprecated tuningParameters property - (ASRangeTuningParameters)tuningParameters { - return [self tuningParametersForRange:ASLayoutRangeRender]; + return [self tuningParametersForRangeType:ASLayoutRangeTypeRender]; } // Support for the deprecated tuningParameters property - (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters { - [self setTuningParameters:tuningParameters forRange:ASLayoutRangeRender]; + [self setTuningParameters:tuningParameters forRangeType:ASLayoutRangeTypeRender]; } #pragma mark - Editing @@ -120,7 +123,7 @@ static const CGFloat kASFlowLayoutControllerRefreshingThreshold = 0.3; #pragma mark - Visible Indices -- (BOOL)shouldUpdateForVisibleIndexPaths:(NSArray *)indexPaths viewportSize:(CGSize)viewportSize range:(ASLayoutRange)range +- (BOOL)shouldUpdateForVisibleIndexPaths:(NSArray *)indexPaths viewportSize:(CGSize)viewportSize rangeType:(ASLayoutRangeType)rangeType { if (!indexPaths.count) { return NO; @@ -128,9 +131,9 @@ static const CGFloat kASFlowLayoutControllerRefreshingThreshold = 0.3; std::pair rangeStartPos, rangeEndPos; - if (range < _rangeStartPos.size() && range < _rangeEndPos.size()) { - rangeStartPos = _rangeStartPos[range]; - rangeEndPos = _rangeEndPos[range]; + if (rangeType < _rangeStartPos.size() && rangeType < _rangeEndPos.size()) { + rangeStartPos = _rangeStartPos[rangeType]; + rangeEndPos = _rangeEndPos[rangeType]; } std::pair startPos, endPos; @@ -147,7 +150,7 @@ static const CGFloat kASFlowLayoutControllerRefreshingThreshold = 0.3; - (BOOL)shouldUpdateForVisibleIndexPath:(NSArray *)indexPaths viewportSize:(CGSize)viewportSize { - return [self shouldUpdateForVisibleIndexPaths:indexPaths viewportSize:viewportSize range:ASLayoutRangeRender]; + return [self shouldUpdateForVisibleIndexPaths:indexPaths viewportSize:viewportSize rangeType:ASLayoutRangeTypeRender]; } - (void)setVisibleNodeIndexPaths:(NSArray *)indexPaths @@ -159,7 +162,7 @@ static const CGFloat kASFlowLayoutControllerRefreshingThreshold = 0.3; * IndexPath array for the element in the working range. */ -- (NSSet *)indexPathsForScrolling:(enum ASScrollDirection)scrollDirection viewportSize:(CGSize)viewportSize range:(ASLayoutRange)range +- (NSSet *)indexPathsForScrolling:(enum ASScrollDirection)scrollDirection viewportSize:(CGSize)viewportSize rangeType:(ASLayoutRangeType)rangeType { CGFloat viewportScreenMetric; ASScrollDirection leadingDirection; @@ -176,12 +179,12 @@ static const CGFloat kASFlowLayoutControllerRefreshingThreshold = 0.3; leadingDirection = ASScrollDirectionUp; } - ASRangeTuningParameters tuningParameters = [self tuningParametersForRange:range]; + ASRangeTuningParameters tuningParameters = [self tuningParametersForRangeType:rangeType]; CGFloat backScreens = scrollDirection == leadingDirection ? tuningParameters.leadingBufferScreenfuls : tuningParameters.trailingBufferScreenfuls; CGFloat frontScreens = scrollDirection == leadingDirection ? tuningParameters.trailingBufferScreenfuls : tuningParameters.leadingBufferScreenfuls; - std::pair startIter = ASFindIndexForRange(_nodeSizes, _visibleRangeStartPos, - backScreens * viewportSize.height, _layoutDirection); - std::pair endIter = ASFindIndexForRange(_nodeSizes, _visibleRangeEndPos, frontScreens * viewportSize.height, _layoutDirection); + std::pair startIter = ASFindIndexForRange(_nodeSizes, _visibleRangeStartPos, - backScreens * viewportScreenMetric, _layoutDirection); + std::pair endIter = ASFindIndexForRange(_nodeSizes, _visibleRangeEndPos, frontScreens * viewportScreenMetric, _layoutDirection); NSMutableSet *indexPathSet = [[NSMutableSet alloc] init]; @@ -203,7 +206,7 @@ static const CGFloat kASFlowLayoutControllerRefreshingThreshold = 0.3; - (NSSet *)indexPathsForScrolling:(enum ASScrollDirection)scrollDirection viewportSize:(CGSize)viewportSize { - return [self indexPathsForScrolling:scrollDirection viewportSize:viewportSize range:ASLayoutRangeRender]; + return [self indexPathsForScrolling:scrollDirection viewportSize:viewportSize rangeType:ASLayoutRangeTypeRender]; } #pragma mark - Utility diff --git a/AsyncDisplayKit/Details/ASLayoutController.h b/AsyncDisplayKit/Details/ASLayoutController.h index 9891090db0..60482922fc 100644 --- a/AsyncDisplayKit/Details/ASLayoutController.h +++ b/AsyncDisplayKit/Details/ASLayoutController.h @@ -1,8 +1,15 @@ -// Copyright 2004-present Facebook. All Rights Reserved. +/* Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * 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 #import +#import typedef struct { CGFloat leadingBufferScreenfuls; @@ -17,11 +24,6 @@ typedef NS_ENUM(NSInteger, ASScrollDirection) { ASScrollDirectionDown, }; -typedef NS_ENUM(NSInteger, ASLayoutRange) { - ASLayoutRangeRender, - ASLayoutRangePreload -}; - @protocol ASLayoutController /** @@ -29,7 +31,7 @@ typedef NS_ENUM(NSInteger, ASLayoutRange) { * * Defaults to a trailing buffer of one screenful and a leading buffer of two screenfuls. */ -- (ASRangeTuningParameters)tuningParametersForRange:(ASLayoutRange)range; +- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType; - (void)insertNodesAtIndexPaths:(NSArray *)indexPaths withSizes:(NSArray *)nodeSizes; @@ -41,9 +43,9 @@ typedef NS_ENUM(NSInteger, ASLayoutRange) { - (void)setVisibleNodeIndexPaths:(NSArray *)indexPaths; -- (BOOL)shouldUpdateForVisibleIndexPaths:(NSArray *)indexPaths viewportSize:(CGSize)viewportSize range:(ASLayoutRange)range; +- (BOOL)shouldUpdateForVisibleIndexPaths:(NSArray *)indexPaths viewportSize:(CGSize)viewportSize rangeType:(ASLayoutRangeType)rangeType; -- (NSSet *)indexPathsForScrolling:(enum ASScrollDirection)scrollDirection viewportSize:(CGSize)viewportSize range:(ASLayoutRange)range; +- (NSSet *)indexPathsForScrolling:(enum ASScrollDirection)scrollDirection viewportSize:(CGSize)viewportSize rangeType:(ASLayoutRangeType)rangeType; @property (nonatomic, assign) ASRangeTuningParameters tuningParameters ASDISPLAYNODE_DEPRECATED; diff --git a/AsyncDisplayKit/Details/ASLayoutRangeType.h b/AsyncDisplayKit/Details/ASLayoutRangeType.h new file mode 100644 index 0000000000..128b88b9fe --- /dev/null +++ b/AsyncDisplayKit/Details/ASLayoutRangeType.h @@ -0,0 +1,15 @@ +/* Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +typedef NS_ENUM(NSInteger, ASLayoutRangeType) { + ASLayoutRangeTypeRender, + ASLayoutRangeTypePreload, + ASLayoutRangeTypeCount +}; diff --git a/AsyncDisplayKit/Details/ASMultidimensionalArrayUtils.h b/AsyncDisplayKit/Details/ASMultidimensionalArrayUtils.h index 1a3c2ed2f2..b7fbbd8c3f 100644 --- a/AsyncDisplayKit/Details/ASMultidimensionalArrayUtils.h +++ b/AsyncDisplayKit/Details/ASMultidimensionalArrayUtils.h @@ -1,4 +1,10 @@ -// Copyright 2004-present Facebook. All Rights Reserved. +/* Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * 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 diff --git a/AsyncDisplayKit/Details/ASMultidimensionalArrayUtils.mm b/AsyncDisplayKit/Details/ASMultidimensionalArrayUtils.mm index 345494880b..af0f4991ef 100644 --- a/AsyncDisplayKit/Details/ASMultidimensionalArrayUtils.mm +++ b/AsyncDisplayKit/Details/ASMultidimensionalArrayUtils.mm @@ -1,4 +1,10 @@ -// Copyright 2004-present Facebook. All Rights Reserved. +/* Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * 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 "ASAssert.h" #import "ASMultidimensionalArrayUtils.h" diff --git a/AsyncDisplayKit/Details/ASPreloadRangeDelegate.h b/AsyncDisplayKit/Details/ASPreloadRangeDelegate.h new file mode 100644 index 0000000000..672c854d97 --- /dev/null +++ b/AsyncDisplayKit/Details/ASPreloadRangeDelegate.h @@ -0,0 +1,15 @@ +/* Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +@interface ASPreloadRangeDelegate : NSObject + +@end diff --git a/AsyncDisplayKit/Details/ASPreloadRangeDelegate.mm b/AsyncDisplayKit/Details/ASPreloadRangeDelegate.mm new file mode 100644 index 0000000000..c8a83881fc --- /dev/null +++ b/AsyncDisplayKit/Details/ASPreloadRangeDelegate.mm @@ -0,0 +1,26 @@ +/* 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 "ASPreloadRangeDelegate.h" + +#import "ASDisplayNode.h" +#import "ASDisplayNode+Subclasses.h" + +@implementation ASPreloadRangeDelegate + +- (void)node:(ASDisplayNode *)node enteredRangeType:(ASLayoutRangeType)rangeType +{ + [node fetchRemoteData]; +} + +- (void)node:(ASDisplayNode *)node exitedRangeType:(ASLayoutRangeType)rangeType +{ + [node clearRemoteData]; +} + +@end diff --git a/AsyncDisplayKit/Details/ASRangeController.h b/AsyncDisplayKit/Details/ASRangeController.h index e50be5706d..10a2ccd061 100644 --- a/AsyncDisplayKit/Details/ASRangeController.h +++ b/AsyncDisplayKit/Details/ASRangeController.h @@ -20,7 +20,7 @@ * Working range controller. * * Used internally by ASTableView and potentially by a future ASCollectionView. Observes the visible range, maintains - * a working range, and is responsible for handling AsyncDisplayKit machinery (sizing cell nodes, enqueueing and + * a working range, and is responsible for handling AsyncDisplayKit machinery (sizing cell nodes,x enqueueing and * cancelling their asynchronous layout and display, and so on). */ @interface ASRangeController : ASDealloc2MainObject diff --git a/AsyncDisplayKit/Details/ASRangeController.mm b/AsyncDisplayKit/Details/ASRangeController.mm index 4159e71c4b..ea6695d9b2 100644 --- a/AsyncDisplayKit/Details/ASRangeController.mm +++ b/AsyncDisplayKit/Details/ASRangeController.mm @@ -11,62 +11,16 @@ #import "ASAssert.h" #import "ASDisplayNodeExtras.h" #import "ASDisplayNodeInternal.h" -#import "ASLayoutController.h" - #import "ASMultiDimensionalArrayUtils.h" - -@interface ASDisplayNode (ASRangeController) - -- (void)display; -- (void)recursivelyDisplay; - -@end - -@implementation ASDisplayNode (ASRangeController) - -- (void)display -{ - if (![self __shouldLoadViewOrLayer]) { - return; - } - - ASDisplayNodeAssertMainThread(); - ASDisplayNodeAssert(self.nodeLoaded, @"backing store must be loaded before calling -display"); - - CALayer *layer = self.layer; - - // rendering a backing store requires a node be laid out - [layer setNeedsLayout]; - [layer layoutIfNeeded]; - - if (layer.contents) { - return; - } - - [layer setNeedsDisplay]; - [layer displayIfNeeded]; -} - -- (void)recursivelyDisplay -{ - if (![self __shouldLoadViewOrLayer]) { - return; - } - - for (ASDisplayNode *node in self.subnodes) { - [node recursivelyDisplay]; - } - - [self display]; -} - -@end +#import "ASRenderRangeDelegate.h" +#import "ASPreloadRangeDelegate.h" @interface ASRangeController () { - NSSet *_workingRangeIndexPaths; - NSSet *_workingRangeNodes; - BOOL _workingRangeIsValid; - + BOOL _rangeIsValid; + + // keys should be ASLayoutRangeTypes and values NSSets containing NSIndexPaths + NSMutableDictionary *_rangeTypeIndexPaths; + NSDictionary *_rangeTypeDelegates; BOOL _queuedRangeUpdate; ASScrollDirection _scrollDirection; @@ -79,63 +33,32 @@ - (instancetype)init { if (self = [super init]) { - _workingRangeIndexPaths = [NSSet set]; - _workingRangeIsValid = YES; + _rangeIsValid = YES; + _rangeTypeIndexPaths = [[NSMutableDictionary alloc] init]; + + _rangeTypeDelegates = @{ + @(ASLayoutRangeTypeRender): [[ASRenderRangeDelegate alloc] init], + @(ASLayoutRangeTypePreload): [[ASPreloadRangeDelegate alloc] init], + }; } return self; } -#pragma mark - View manipulation. -- (void)discardNode:(ASCellNode *)node -{ - ASDisplayNodeAssertMainThread(); - ASDisplayNodeAssert(node, @"invalid argument"); - - if ([_workingRangeNodes containsObject:node]) { - // move the node's view to the working range area, so its rendering persists - [self addNodeToWorkingRange:node]; - } else { - // this node isn't in the working range, remove it from the view hierarchy - [self removeNodeFromWorkingRange:node]; - } -} - -- (void)removeNodeFromWorkingRange:(ASCellNode *)node -{ - ASDisplayNodeAssertMainThread(); - ASDisplayNodeAssert(node, @"invalid argument"); - - [node recursivelySetDisplaySuspended:YES]; - [node.view removeFromSuperview]; - - // 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. - [node recursivelyReclaimMemory]; -} - -- (void)addNodeToWorkingRange:(ASCellNode *)node -{ - ASDisplayNodeAssertMainThread(); - ASDisplayNodeAssert(node, @"invalid argument"); - - // if node is in the working range it should not actively be in view - [node.view removeFromSuperview]; - - [node recursivelyDisplay]; -} +#pragma mark - View manipulation - (void)moveNode:(ASCellNode *)node toView:(UIView *)view { ASDisplayNodeAssertMainThread(); - ASDisplayNodeAssert(node && view, @"invalid argument, did you mean -removeNodeFromWorkingRange:?"); + ASDisplayNodeAssert(node, @"Cannot move a nil node to a view"); + ASDisplayNodeAssert(view, @"Cannot move a node to a non-existent view"); [view addSubview:node.view]; } -#pragma mark - -#pragma mark API. + +#pragma mark - API - (void)visibleNodeIndexPathsDidChangeWithScrollDirection:(ASScrollDirection)scrollDirection { @@ -159,42 +82,64 @@ return; } - NSArray *indexPaths = [_delegate rangeControllerVisibleNodeIndexPaths:self]; + NSArray *visibleNodePaths = [_delegate rangeControllerVisibleNodeIndexPaths:self]; + NSSet *visibleNodePathsSet = [NSSet setWithArray:visibleNodePaths]; CGSize viewportSize = [_delegate rangeControllerViewportSize:self]; - if ([_layoutController shouldUpdateForVisibleIndexPaths:indexPaths viewportSize:viewportSize range:ASLayoutRangeRender]) { - [_layoutController setVisibleNodeIndexPaths:indexPaths]; - NSSet *workingRangeIndexPaths = [_layoutController indexPathsForScrolling:_scrollDirection viewportSize:viewportSize range:ASLayoutRangeRender]; - NSSet *visibleRangeIndexPaths = [NSSet setWithArray:indexPaths]; + // the layout controller needs to know what the current visible indices are to calculate range offsets + [_layoutController setVisibleNodeIndexPaths:visibleNodePaths]; - NSMutableSet *removedIndexPaths = _workingRangeIsValid ? [_workingRangeIndexPaths mutableCopy] : [NSMutableSet set]; - [removedIndexPaths minusSet:workingRangeIndexPaths]; - [removedIndexPaths minusSet:visibleRangeIndexPaths]; - if (removedIndexPaths.count) { - NSArray *removedNodes = [_delegate rangeController:self nodesAtIndexPaths:[removedIndexPaths allObjects]]; - [removedNodes enumerateObjectsUsingBlock:^(ASCellNode *node, NSUInteger idx, BOOL *stop) { - [self removeNodeFromWorkingRange:node]; - }]; + 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 rangeDelegate = _rangeTypeDelegates[rangeKey]; + + if ([_layoutController shouldUpdateForVisibleIndexPaths:visibleNodePaths viewportSize:viewportSize rangeType:rangeType]) { + NSSet *indexPaths = [_layoutController indexPathsForScrolling:_scrollDirection viewportSize:viewportSize rangeType:rangeType]; + + // Notify to remove indexpaths that are leftover that are not visible or included in the _layoutController calculated paths + NSMutableSet *removedIndexPaths = _rangeIsValid ? [[_rangeTypeIndexPaths objectForKey:rangeKey] mutableCopy] : [NSMutableSet set]; + [removedIndexPaths minusSet:indexPaths]; + [removedIndexPaths minusSet:visibleNodePathsSet]; + if (removedIndexPaths.count) { + NSArray *removedNodes = [_delegate rangeController:self nodesAtIndexPaths:[removedIndexPaths allObjects]]; + [removedNodes enumerateObjectsUsingBlock:^(ASCellNode *node, NSUInteger idx, BOOL *stop) { + [rangeDelegate node:node exitedRangeType:rangeType]; + }]; + } + + // Notify to add indexpaths that are not currently in _rangeTypeIndexPaths + NSMutableSet *addedIndexPaths = [indexPaths mutableCopy]; + [addedIndexPaths minusSet:[_rangeTypeIndexPaths objectForKey:rangeKey]]; + + // The preload range (for example) should include nodes that are visible + if ([self shouldRemoveVisibleNodesFromRangeType:rangeType]) { + [addedIndexPaths minusSet:visibleNodePathsSet]; + } + + if (addedIndexPaths.count) { + NSArray *addedNodes = [_delegate rangeController:self nodesAtIndexPaths:[addedIndexPaths allObjects]]; + [addedNodes enumerateObjectsUsingBlock:^(ASCellNode *node, NSUInteger idx, BOOL *stop) { + [rangeDelegate node:node enteredRangeType:rangeType]; + }]; + } + + // set the range indexpaths so that we can remove/add on the next update pass + [_rangeTypeIndexPaths setObject:indexPaths forKey:rangeKey]; } - - NSMutableSet *addedIndexPaths = [workingRangeIndexPaths mutableCopy]; - [addedIndexPaths minusSet:_workingRangeIndexPaths]; - [addedIndexPaths minusSet:visibleRangeIndexPaths]; - if (addedIndexPaths.count) { - NSArray *addedNodes = [_delegate rangeController:self nodesAtIndexPaths:[addedIndexPaths allObjects]]; - [addedNodes enumerateObjectsUsingBlock:^(ASCellNode *node, NSUInteger idx, BOOL *stop) { - [self addNodeToWorkingRange:node]; - }]; - } - - _workingRangeIndexPaths = workingRangeIndexPaths; - _workingRangeNodes = [NSSet setWithArray:[_delegate rangeController:self nodesAtIndexPaths:[workingRangeIndexPaths allObjects]]]; - _workingRangeIsValid = YES; } + _rangeIsValid = YES; _queuedRangeUpdate = NO; } +- (BOOL)shouldRemoveVisibleNodesFromRangeType:(ASLayoutRangeType)rangeType +{ + return rangeType != ASLayoutRangeTypePreload; +} + - (void)configureContentView:(UIView *)contentView forCellNode:(ASCellNode *)cellNode { [cellNode recursivelySetDisplaySuspended:NO]; @@ -204,21 +149,15 @@ return; } + // clean the content view for (UIView *view in contentView.subviews) { - ASDisplayNode *node = view.asyncdisplaykit_node; - if (node) { - // plunk this node back into the working range, if appropriate - ASDisplayNodeAssert([node isKindOfClass:[ASCellNode class]], @"invalid node"); - [self discardNode:(ASCellNode *)node]; - } else { - // if it's not a node, it's something random UITableView added to the hierarchy. kill it. - [view removeFromSuperview]; - } + [view removeFromSuperview]; } [self moveNode:cellNode toView:contentView]; } + #pragma mark - ASDataControllerDelegete - (void)dataControllerBeginUpdates:(ASDataController *)dataController { @@ -252,7 +191,7 @@ ASDisplayNodePerformBlockOnMainThread(^{ [_layoutController insertNodesAtIndexPaths:indexPaths withSizes:nodeSizes]; [_delegate rangeController:self didInsertNodesAtIndexPaths:indexPaths withAnimationOption:animationOption]; - _workingRangeIsValid = NO; + _rangeIsValid = NO; }); } @@ -268,7 +207,7 @@ ASDisplayNodePerformBlockOnMainThread(^{ [_layoutController deleteNodesAtIndexPaths:indexPaths]; [_delegate rangeController:self didDeleteNodesAtIndexPaths:indexPaths withAnimationOption:animationOption]; - _workingRangeIsValid = NO; + _rangeIsValid = NO; }); } @@ -296,7 +235,7 @@ ASDisplayNodePerformBlockOnMainThread(^{ [_layoutController insertSections:sectionNodeSizes atIndexSet:indexSet]; [_delegate rangeController:self didInsertSectionsAtIndexSet:indexSet withAnimationOption:animationOption]; - _workingRangeIsValid = NO; + _rangeIsValid = NO; }); } @@ -312,7 +251,7 @@ ASDisplayNodePerformBlockOnMainThread(^{ [_layoutController deleteSectionsAtIndexSet:indexSet]; [_delegate rangeController:self didDeleteSectionsAtIndexSet:indexSet withAnimationOption:animationOption]; - _workingRangeIsValid = NO; + _rangeIsValid = NO; }); } diff --git a/AsyncDisplayKit/Details/ASRangeDelegate.h b/AsyncDisplayKit/Details/ASRangeDelegate.h new file mode 100644 index 0000000000..404f973bd6 --- /dev/null +++ b/AsyncDisplayKit/Details/ASRangeDelegate.h @@ -0,0 +1,22 @@ +/* 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 + +#import + +@class ASDisplayNode; + +@protocol ASRangeDelegate + +@required + +- (void)node:(ASDisplayNode *)node enteredRangeType:(ASLayoutRangeType)rangeType; +- (void)node:(ASDisplayNode *)node exitedRangeType:(ASLayoutRangeType)rangeType; + +@end diff --git a/AsyncDisplayKit/Details/ASRenderRangeDelegate.h b/AsyncDisplayKit/Details/ASRenderRangeDelegate.h new file mode 100644 index 0000000000..fbea7f1d97 --- /dev/null +++ b/AsyncDisplayKit/Details/ASRenderRangeDelegate.h @@ -0,0 +1,15 @@ +/* Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +@interface ASRenderRangeDelegate : NSObject + +@end diff --git a/AsyncDisplayKit/Details/ASRenderRangeDelegate.mm b/AsyncDisplayKit/Details/ASRenderRangeDelegate.mm new file mode 100644 index 0000000000..b02125ab7f --- /dev/null +++ b/AsyncDisplayKit/Details/ASRenderRangeDelegate.mm @@ -0,0 +1,88 @@ +/* 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 "ASRenderRangeDelegate.h" + +#import "ASDisplayNode.h" +#import "ASDisplayNode+Subclasses.h" +#import "ASDisplayNodeInternal.h" + +@interface ASDisplayNode (ASRenderRangeDelegate) + +- (void)display; +- (void)recursivelyDisplay; + +@end + +@implementation ASDisplayNode (ASRenderRangeDelegate) + +- (void)display +{ + if (![self __shouldLoadViewOrLayer]) { + return; + } + + ASDisplayNodeAssertMainThread(); + ASDisplayNodeAssert(self.nodeLoaded, @"backing store must be loaded before calling -display"); + + CALayer *layer = self.layer; + + // rendering a backing store requires a node be laid out + [layer setNeedsLayout]; + [layer layoutIfNeeded]; + + if (layer.contents) { + return; + } + + [layer setNeedsDisplay]; + [layer displayIfNeeded]; +} + +- (void)recursivelyDisplay +{ + if (![self __shouldLoadViewOrLayer]) { + return; + } + + for (ASDisplayNode *node in self.subnodes) { + [node recursivelyDisplay]; + } + + [self display]; +} + +@end + +@implementation ASRenderRangeDelegate + +- (void)node:(ASDisplayNode *)node enteredRangeType:(ASLayoutRangeType)rangeType +{ + ASDisplayNodeAssertMainThread(); + ASDisplayNodeAssert(node, @"invalid argument"); + + // if node is in the working range it should not actively be in view + [node.view removeFromSuperview]; + + [node recursivelyDisplay]; +} + +- (void)node:(ASDisplayNode *)node exitedRangeType:(ASLayoutRangeType)rangeType +{ + ASDisplayNodeAssertMainThread(); + ASDisplayNodeAssert(node, @"invalid argument"); + + [node recursivelySetDisplaySuspended:YES]; + [node.view removeFromSuperview]; + + // 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. + [node recursivelyClearRendering]; +} + +@end diff --git a/examples/Kittens/Sample/ViewController.m b/examples/Kittens/Sample/ViewController.m index 0de62f2823..c29978186f 100644 --- a/examples/Kittens/Sample/ViewController.m +++ b/examples/Kittens/Sample/ViewController.m @@ -17,7 +17,7 @@ #import "BlurbNode.h" #import "KittenNode.h" -static const NSInteger kLitterSize = 20; +static const NSInteger kLitterSize = 200; @interface ViewController ()