From 69e674c1c85c64d0186fff2f6fba1010cea5aa76 Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Mon, 1 Feb 2016 23:42:13 -0800 Subject: [PATCH] Range controller registers to rendering engine and extern to full range if needed --- AsyncDisplayKit/ASDisplayNode.mm | 3 + .../Details/ASAbstractLayoutController.mm | 70 ++++++------- .../ASCollectionViewLayoutController.mm | 8 +- .../Details/ASFlowLayoutController.mm | 4 +- AsyncDisplayKit/Details/ASLayoutController.h | 29 ++++-- AsyncDisplayKit/Details/ASRangeController.mm | 4 +- .../Details/ASRangeControllerBeta.mm | 99 ++++++++++++++----- .../Private/ASDisplayNodeInternal.h | 2 + 8 files changed, 143 insertions(+), 76 deletions(-) diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 5f0cdfd548..7d8e40c217 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -29,6 +29,7 @@ #import "ASCellNode.h" NSInteger const ASDefaultDrawingPriority = ASDefaultTransactionPriority; +NSString * const ASRenderingEngineDidDisplayScheduledNodesNotification = @"ASRenderingEngineDidDisplayScheduledNodes"; @interface _ASDisplayNodePosition : NSObject @@ -279,6 +280,8 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) for (ASDisplayNode *node in displayingNodes) { [node __recursivelyTriggerDisplayAndBlock:NO]; } + [[NSNotificationCenter defaultCenter] postNotificationName:ASRenderingEngineDidDisplayScheduledNodesNotification + object:nil]; }); } } diff --git a/AsyncDisplayKit/Details/ASAbstractLayoutController.mm b/AsyncDisplayKit/Details/ASAbstractLayoutController.mm index 7a10ff1f38..7b1f107413 100644 --- a/AsyncDisplayKit/Details/ASAbstractLayoutController.mm +++ b/AsyncDisplayKit/Details/ASAbstractLayoutController.mm @@ -11,8 +11,7 @@ #include @interface ASAbstractLayoutController () { - std::vector _tuningParameters; - std::vector _minimumTuningParameters; + std::vector> _tuningParameters; CGSize _viewportSize; } @end @@ -25,33 +24,33 @@ return nil; } - _tuningParameters = std::vector(ASLayoutRangeTypeCount); - _tuningParameters[ASLayoutRangeTypeVisible] = { + _tuningParameters = std::vector> (ASLayoutRangeModeCount, std::vector (ASLayoutRangeTypeCount)); + + _tuningParameters[ASLayoutRangeModeMinimum][ASLayoutRangeTypeVisible] = { .leadingBufferScreenfuls = 0, .trailingBufferScreenfuls = 0 }; - _tuningParameters[ASLayoutRangeTypeDisplay] = { - .leadingBufferScreenfuls = 1.5, - .trailingBufferScreenfuls = 0.75 - }; - _tuningParameters[ASLayoutRangeTypeFetchData] = { - .leadingBufferScreenfuls = 3, - .trailingBufferScreenfuls = 2 - }; - - _minimumTuningParameters = std::vector(ASLayoutRangeTypeCount); - _minimumTuningParameters[ASLayoutRangeTypeVisible] = { - .leadingBufferScreenfuls = 0, - .trailingBufferScreenfuls = 0 - }; - _minimumTuningParameters[ASLayoutRangeTypeDisplay] = { + _tuningParameters[ASLayoutRangeModeMinimum][ASLayoutRangeTypeDisplay] = { .leadingBufferScreenfuls = 0.25, .trailingBufferScreenfuls = 0.25 }; - _minimumTuningParameters[ASLayoutRangeTypeFetchData] = { + _tuningParameters[ASLayoutRangeModeMinimum][ASLayoutRangeTypeFetchData] = { .leadingBufferScreenfuls = 1, .trailingBufferScreenfuls = 1 }; + + _tuningParameters[ASLayoutRangeModeFull][ASLayoutRangeTypeVisible] = { + .leadingBufferScreenfuls = 0, + .trailingBufferScreenfuls = 0 + }; + _tuningParameters[ASLayoutRangeModeFull][ASLayoutRangeTypeDisplay] = { + .leadingBufferScreenfuls = 1.5, + .trailingBufferScreenfuls = 0.75 + }; + _tuningParameters[ASLayoutRangeModeFull][ASLayoutRangeTypeFetchData] = { + .leadingBufferScreenfuls = 3, + .trailingBufferScreenfuls = 2 + }; return self; } @@ -60,33 +59,28 @@ - (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType { - ASDisplayNodeAssert(rangeType < _tuningParameters.size(), @"Requesting a range that is OOB for the configured tuning parameters"); - return _tuningParameters[rangeType]; + return [self tuningParametersForRangeMode:ASLayoutRangeModeFull rangeType:rangeType]; } - (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType { - ASDisplayNodeAssert(rangeType < _tuningParameters.size(), @"Requesting a range that is OOB for the configured tuning parameters"); - ASDisplayNodeAssert(rangeType != ASLayoutRangeTypeVisible, @"Must not set Visible range tuning parameters (always 0, 0)"); - _tuningParameters[rangeType] = tuningParameters; + return [self setTuningParameters:tuningParameters forRangeMode:ASLayoutRangeModeFull rangeType:rangeType]; } -- (ASRangeTuningParameters)minimumTuningParametersForRangeType:(ASLayoutRangeType)rangeType +- (ASRangeTuningParameters)tuningParametersForRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType { - ASDisplayNodeAssert(rangeType < _minimumTuningParameters.size(), @"Requesting a range that is OOB for the configured minimum tuning parameters"); - return _minimumTuningParameters[rangeType]; + ASDisplayNodeAssert(rangeMode < _tuningParameters.size() && rangeType < _tuningParameters[rangeMode].size(), + @"Requesting a range that is OOB for the configured tuning parameters"); + return _tuningParameters[rangeMode][rangeType]; } -- (void)setMinimumTuningParameters:(ASRangeTuningParameters)minimumTuningParameters forRangeType:(ASLayoutRangeType)rangeType +- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType { - ASDisplayNodeAssert(rangeType < _minimumTuningParameters.size(), @"Requesting a range that is OOB for the configured minimum tuning parameters"); - ASDisplayNodeAssert(rangeType != ASLayoutRangeTypeVisible, @"Must not set Visible range minimum tuning parameters (always 0, 0)"); - _minimumTuningParameters[rangeType] = minimumTuningParameters; -} - -- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType isFullRange:(BOOL)isFullRange -{ - return isFullRange ? [self tuningParametersForRangeType:rangeType] : [self minimumTuningParametersForRangeType:rangeType]; + ASDisplayNodeAssert(rangeMode < _tuningParameters.size() && rangeType < _tuningParameters[rangeMode].size(), + @"Setting a range that is OOB for the configured tuning parameters"); + ASDisplayNodeAssert(rangeType != ASLayoutRangeTypeVisible, + @"Must not set Visible range minimum tuning parameters (always 0, 0)"); + _tuningParameters[rangeMode][rangeType] = tuningParameters; } #pragma mark - Abstract Index Path Range Support @@ -98,7 +92,7 @@ return NO; } -- (NSSet *)indexPathsForScrolling:(ASScrollDirection)scrollDirection rangeType:(ASLayoutRangeType)rangeType shouldUseFullRange:(BOOL)shouldUseFullRange +- (NSSet *)indexPathsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType { ASDisplayNodeAssertNotSupported(); return nil; diff --git a/AsyncDisplayKit/Details/ASCollectionViewLayoutController.mm b/AsyncDisplayKit/Details/ASCollectionViewLayoutController.mm index 6777c2d3de..bf591b2d93 100644 --- a/AsyncDisplayKit/Details/ASCollectionViewLayoutController.mm +++ b/AsyncDisplayKit/Details/ASCollectionViewLayoutController.mm @@ -66,9 +66,9 @@ typedef struct ASRangeGeometry ASRangeGeometry; return self; } -- (NSSet *)indexPathsForScrolling:(ASScrollDirection)scrollDirection rangeType:(ASLayoutRangeType)rangeType shouldUseFullRange:(BOOL)shouldUseFullRange +- (NSSet *)indexPathsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType { - ASRangeTuningParameters tuningParameters = [self tuningParametersForRangeType:rangeType isFullRange:shouldUseFullRange]; + ASRangeTuningParameters tuningParameters = [self tuningParametersForRangeMode:rangeMode rangeType:rangeType]; ASRangeGeometry rangeGeometry = [self rangeGeometryWithScrollDirection:scrollDirection tuningParameters:tuningParameters]; _updateRangeBoundsIndexedByRangeType[rangeType] = rangeGeometry.updateBounds; return [self indexPathsForItemsWithinRangeBounds:rangeGeometry.rangeBounds]; @@ -133,9 +133,9 @@ typedef struct ASRangeGeometry ASRangeGeometry; @implementation ASCollectionViewLayoutControllerBeta -- (NSSet *)indexPathsForScrolling:(ASScrollDirection)scrollDirection rangeType:(ASLayoutRangeType)rangeType shouldUseFullRange:(BOOL)shouldUseFullRange +- (NSSet *)indexPathsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType { - ASRangeTuningParameters tuningParameters = [self tuningParametersForRangeType:rangeType isFullRange:shouldUseFullRange]; + ASRangeTuningParameters tuningParameters = [self tuningParametersForRangeMode:rangeMode rangeType:rangeType]; CGRect rangeBounds = [self rangeBoundsWithScrollDirection:scrollDirection rangeTuningParameters:tuningParameters]; return [self indexPathsForItemsWithinRangeBounds:rangeBounds]; } diff --git a/AsyncDisplayKit/Details/ASFlowLayoutController.mm b/AsyncDisplayKit/Details/ASFlowLayoutController.mm index d2b3a62bdc..cbcde0f011 100644 --- a/AsyncDisplayKit/Details/ASFlowLayoutController.mm +++ b/AsyncDisplayKit/Details/ASFlowLayoutController.mm @@ -74,7 +74,7 @@ static const CGFloat kASFlowLayoutControllerRefreshingThreshold = 0.3; * IndexPath array for the element in the working range. */ -- (NSSet *)indexPathsForScrolling:(ASScrollDirection)scrollDirection rangeType:(ASLayoutRangeType)rangeType shouldUseFullRange:(BOOL)shouldUseFullRange +- (NSSet *)indexPathsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType { CGFloat viewportScreenMetric; ASScrollDirection leadingDirection; @@ -92,7 +92,7 @@ static const CGFloat kASFlowLayoutControllerRefreshingThreshold = 0.3; leadingDirection = ASScrollDirectionUp; } - ASRangeTuningParameters tuningParameters = [self tuningParametersForRangeType:rangeType isFullRange:shouldUseFullRange]; + ASRangeTuningParameters tuningParameters = [self tuningParametersForRangeMode:rangeMode rangeType:rangeType]; CGFloat backScreens = scrollDirection == leadingDirection ? tuningParameters.leadingBufferScreenfuls : tuningParameters.trailingBufferScreenfuls; CGFloat frontScreens = scrollDirection == leadingDirection ? tuningParameters.trailingBufferScreenfuls : tuningParameters.leadingBufferScreenfuls; diff --git a/AsyncDisplayKit/Details/ASLayoutController.h b/AsyncDisplayKit/Details/ASLayoutController.h index 1a3d89f263..cdf205dbe9 100644 --- a/AsyncDisplayKit/Details/ASLayoutController.h +++ b/AsyncDisplayKit/Details/ASLayoutController.h @@ -16,6 +16,12 @@ NS_ASSUME_NONNULL_BEGIN @class ASCellNode; +typedef NS_ENUM(NSUInteger, ASLayoutRangeMode) { + ASLayoutRangeModeMinimum = 0, + ASLayoutRangeModeFull, + ASLayoutRangeModeCount +}; + typedef struct { CGFloat leadingBufferScreenfuls; CGFloat trailingBufferScreenfuls; @@ -24,23 +30,30 @@ typedef struct { @protocol ASLayoutController /** - * Tuning parameters for the range. + * Tuning parameters for the range type in full mode. This method is deprecated. + * Instead, use -setTuningParameters:forRangeMode:rangeType: + * + * @see setTuningParameters:forRangeMode:rangeType: */ -- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType; +- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType ASDISPLAYNODE_DEPRECATED; -- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType; +/** + * Get tuning parameters for the range type in full mode. This method is deprecated. + * Instead, use -tuningParametersForRangeMode:rangeType: + * + * @see tuningParametersForRangeMode:rangeType: + */ +- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType ASDISPLAYNODE_DEPRECATED; -- (void)setMinimumTuningParameters:(ASRangeTuningParameters)minimumTuningParameters forRangeType:(ASLayoutRangeType)rangeType; +- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType; -- (ASRangeTuningParameters)minimumTuningParametersForRangeType:(ASLayoutRangeType)rangeType; - -- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType isFullRange:(BOOL)isFullRange; +- (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 *)indexPaths rangeType:(ASLayoutRangeType)rangeType; -- (NSSet *)indexPathsForScrolling:(ASScrollDirection)scrollDirection rangeType:(ASLayoutRangeType)rangeType shouldUseFullRange:(BOOL)shouldUseFullRange; +- (NSSet *)indexPathsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType; @optional diff --git a/AsyncDisplayKit/Details/ASRangeController.mm b/AsyncDisplayKit/Details/ASRangeController.mm index 99a6492290..b728916a62 100644 --- a/AsyncDisplayKit/Details/ASRangeController.mm +++ b/AsyncDisplayKit/Details/ASRangeController.mm @@ -153,7 +153,9 @@ id rangeHandler = _rangeTypeHandlers[rangeKey]; if (!_rangeIsValid || [_layoutController shouldUpdateForVisibleIndexPaths:visibleNodePaths rangeType:rangeType]) { - NSSet *indexPaths = [_layoutController indexPathsForScrolling:_scrollDirection rangeType:rangeType shouldUseFullRange:YES]; + 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]; diff --git a/AsyncDisplayKit/Details/ASRangeControllerBeta.mm b/AsyncDisplayKit/Details/ASRangeControllerBeta.mm index a8d77c2301..63b92c3fe7 100644 --- a/AsyncDisplayKit/Details/ASRangeControllerBeta.mm +++ b/AsyncDisplayKit/Details/ASRangeControllerBeta.mm @@ -10,6 +10,7 @@ #import "ASAssert.h" #import "ASDisplayNodeExtras.h" +#import "ASDisplayNodeInternal.h" #import "ASMultiDimensionalArrayUtils.h" #import "ASRangeHandlerVisible.h" #import "ASRangeHandlerRender.h" @@ -17,12 +18,6 @@ #import "ASInternalHelpers.h" #import "ASDisplayNode+FrameworkPrivate.h" -typedef NS_ENUM(NSUInteger, ASRangeTypeUsed) { - ASRangeTypeUsedNone, - ASRangeTypeUsedMinimum, - ASRangeTypeUsedFull, -}; - @interface ASRangeControllerBeta () { BOOL _rangeIsValid; @@ -30,7 +25,8 @@ typedef NS_ENUM(NSUInteger, ASRangeTypeUsed) { BOOL _layoutControllerImplementsSetVisibleIndexPaths; ASScrollDirection _scrollDirection; NSSet *_allPreviousIndexPaths; - ASRangeTypeUsed _rangeTypeUsed; + ASLayoutRangeMode _currentRangeMode; + BOOL _didRegisterForNotifications; } @end @@ -44,13 +40,41 @@ typedef NS_ENUM(NSUInteger, ASRangeTypeUsed) { } _rangeIsValid = YES; - _rangeTypeUsed = ASRangeTypeUsedNone; + _currentRangeMode = ASLayoutRangeModeCount; return self; } +- (void)dealloc +{ + if (_didRegisterForNotifications) { + [[NSNotificationCenter defaultCenter] removeObserver:self]; + } +} + #pragma mark - Core visible node range managment API ++ (ASLayoutRangeMode)rangeModeForInterfaceState:(ASInterfaceState)interfaceState + scrollDirection:(ASScrollDirection)scrollDirection + currentRangeMode:(ASLayoutRangeMode)currentRangeMode +{ + // If we used full mode, don't switch to minimum mode. That will destroy all the hard work done before. + if (currentRangeMode == ASLayoutRangeModeFull) { + return ASLayoutRangeModeFull; + } + + BOOL isVisible = (ASInterfaceStateIncludesVisible(interfaceState)); + BOOL isScrolling = (scrollDirection != ASScrollDirectionNone); + BOOL isUsingMinimumRangeMode = (currentRangeMode == ASLayoutRangeModeMinimum); + // If we are already visible and scrolling, get busy! Better get started on preloading before the user scrolls more... + // If we are already visible and finished displaying minimum mode, extend to full mode + if (isVisible && (isScrolling || isUsingMinimumRangeMode)) { + return ASLayoutRangeModeFull; + } + + return ASLayoutRangeModeMinimum; +} + - (void)visibleNodeIndexPathsDidChangeWithScrollDirection:(ASScrollDirection)scrollDirection { _scrollDirection = scrollDirection; @@ -62,7 +86,7 @@ typedef NS_ENUM(NSUInteger, ASRangeTypeUsed) { if (_queuedRangeUpdate) { return; } - + // coalesce these events -- handling them multiple times per runloop is noisy and expensive _queuedRangeUpdate = YES; @@ -117,25 +141,25 @@ typedef NS_ENUM(NSUInteger, ASRangeTypeUsed) { NSMutableOrderedSet *allIndexPaths = [[NSMutableOrderedSet alloc] initWithSet:visibleIndexPaths]; ASInterfaceState selfInterfaceState = [_dataSource interfaceStateForRangeController:self]; - BOOL selfIsVisible = (ASInterfaceStateIncludesVisible(selfInterfaceState)); - BOOL selfIsScrolling = (_scrollDirection != ASScrollDirectionNone); - BOOL didUseMinimumRange = (_rangeTypeUsed == ASRangeTypeUsedMinimum); - BOOL didUseFullRange = (_rangeTypeUsed == ASRangeTypeUsedFull); - // If we are already visible and scrolling, get busy! Better get started on preloading before the user scrolls more... - // If we are already visible and did finish displaying minimum range, extend to full range - // If we used full range, don't switch to minimum range now. That will destroy all the hard work done before. - BOOL useFullRange = ((selfIsVisible && (selfIsScrolling || didUseMinimumRange)) || didUseFullRange); - NSLog(@"%@ range: %@", useFullRange ? @"Full" : @"Minimum", [((ASCollectionView *)_delegate).asyncDelegate description]); + ASLayoutRangeMode rangeMode = [ASRangeControllerBeta rangeModeForInterfaceState:selfInterfaceState + scrollDirection:_scrollDirection + currentRangeMode:_currentRangeMode]; - fetchDataIndexPaths = [_layoutController indexPathsForScrolling:_scrollDirection rangeType:ASLayoutRangeTypeFetchData shouldUseFullRange:useFullRange]; + fetchDataIndexPaths = [_layoutController indexPathsForScrolling:_scrollDirection + rangeMode:rangeMode + rangeType:ASLayoutRangeTypeFetchData]; - ASRangeTuningParameters parametersDisplay = [_layoutController tuningParametersForRangeType:ASLayoutRangeTypeDisplay isFullRange:useFullRange]; - ASRangeTuningParameters parametersFetchData = [_layoutController tuningParametersForRangeType:ASLayoutRangeTypeFetchData isFullRange:useFullRange]; + ASRangeTuningParameters parametersDisplay = [_layoutController tuningParametersForRangeMode:rangeMode + rangeType:ASLayoutRangeTypeDisplay]; + ASRangeTuningParameters parametersFetchData = [_layoutController tuningParametersForRangeMode:rangeMode + rangeType:ASLayoutRangeTypeFetchData]; if (parametersDisplay.leadingBufferScreenfuls == parametersFetchData.leadingBufferScreenfuls && parametersDisplay.trailingBufferScreenfuls == parametersFetchData.trailingBufferScreenfuls) { displayIndexPaths = fetchDataIndexPaths; } else { - displayIndexPaths = [_layoutController indexPathsForScrolling:_scrollDirection rangeType:ASLayoutRangeTypeDisplay shouldUseFullRange:useFullRange]; + 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. @@ -151,11 +175,13 @@ typedef NS_ENUM(NSUInteger, ASRangeTypeUsed) { NSSet *allCurrentIndexPaths = [[allIndexPaths set] copy]; [allIndexPaths unionSet:_allPreviousIndexPaths]; _allPreviousIndexPaths = allCurrentIndexPaths; - _rangeTypeUsed = useFullRange ? ASRangeTypeUsedFull : ASRangeTypeUsedMinimum; + _currentRangeMode = rangeMode; if (!_rangeIsValid) { [allIndexPaths addObjectsFromArray:ASIndexPathsForMultidimensionalArray(allNodes)]; } + + [self registerForNotificationsIfNeeded]; // This array is only used if logging is enabled. NSMutableArray *modifiedIndexPaths = (RangeControllerLoggingEnabled ? [NSMutableArray array] : nil); @@ -240,6 +266,33 @@ typedef NS_ENUM(NSUInteger, ASRangeTypeUsed) { #endif } +#pragma mark - Notification observers + +- (void)registerForNotificationsIfNeeded +{ + if (!_didRegisterForNotifications) { + BOOL selfInterfaceState = [_dataSource interfaceStateForRangeController:self]; + ASLayoutRangeMode nextRangeMode = [ASRangeControllerBeta rangeModeForInterfaceState:selfInterfaceState + scrollDirection:_scrollDirection + currentRangeMode:_currentRangeMode]; + if (_currentRangeMode != nextRangeMode) { + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(scheduledNodesDidDisplay) + name:ASRenderingEngineDidDisplayScheduledNodesNotification + object:nil]; + _didRegisterForNotifications = YES; + } + } +} + +- (void)scheduledNodesDidDisplay +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; + _didRegisterForNotifications = NO; + + [self scheduleRangeUpdate]; +} + #pragma mark - Cell node view handling - (void)configureContentView:(UIView *)contentView forCellNode:(ASCellNode *)node diff --git a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h index e0740fd3fc..7bb47cf8a0 100644 --- a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h +++ b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h @@ -37,6 +37,8 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides) @class _ASPendingState; @class _ASDisplayNodePosition; +FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayScheduledNodesNotification; + // Allow 2^n increments of begin disabling hierarchy notifications #define VISIBILITY_NOTIFICATIONS_DISABLED_BITS 4