diff --git a/AsyncDisplayKit/ASDisplayNode+Beta.h b/AsyncDisplayKit/ASDisplayNode+Beta.h index 608f7be71b..98efc3d08a 100644 --- a/AsyncDisplayKit/ASDisplayNode+Beta.h +++ b/AsyncDisplayKit/ASDisplayNode+Beta.h @@ -8,6 +8,9 @@ @interface ASDisplayNode (Beta) ++ (BOOL)shouldUseNewRenderingRange; ++ (void)setShouldUseNewRenderingRange:(BOOL)shouldUseNewRenderingRange; + /** @name Layout */ diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 19c972f790..8d0089ce8e 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -1560,6 +1560,17 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock) return _flags.shouldBypassEnsureDisplay; } +static BOOL ShouldUseNewRenderingRange = NO; + ++ (BOOL)shouldUseNewRenderingRange +{ + return ShouldUseNewRenderingRange; +} ++ (void)setShouldUseNewRenderingRange:(BOOL)shouldUseNewRenderingRange +{ + ShouldUseNewRenderingRange = shouldUseNewRenderingRange; +} + #pragma mark - For Subclasses - (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize diff --git a/AsyncDisplayKit/Details/ASRangeHandlerRender.mm b/AsyncDisplayKit/Details/ASRangeHandlerRender.mm index 4872707e7e..bb4de3c092 100644 --- a/AsyncDisplayKit/Details/ASRangeHandlerRender.mm +++ b/AsyncDisplayKit/Details/ASRangeHandlerRender.mm @@ -11,15 +11,15 @@ #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 - -#if USE_WORKING_WINDOW @synthesize workingWindow = _workingWindow; + - (UIWindow *)workingWindow { ASDisplayNodeAssertMainThread(); @@ -28,7 +28,7 @@ // 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) { + if (!_workingWindow && ![ASDisplayNode shouldUseNewRenderingRange]) { _workingWindow = [[UIWindow alloc] initWithFrame:CGRectZero]; _workingWindow.windowLevel = UIWindowLevelNormal - 1000; _workingWindow.userInteractionEnabled = NO; @@ -41,12 +41,13 @@ - (void)dealloc { - for(CALayer *layer in [self.workingWindow.layer.sublayers copy]) { - ASDisplayNode *node = layer.asyncdisplaykit_node; - [self node:node exitedRangeOfType:ASLayoutRangeTypeRender]; + if (![ASDisplayNode shouldUseNewRenderingRange]) { + for (CALayer *layer in [self.workingWindow.layer.sublayers copy]) { + ASDisplayNode *node = layer.asyncdisplaykit_node; + [self node:node exitedRangeOfType:ASLayoutRangeTypeRender]; + } } } -#endif - (void)node:(ASDisplayNode *)node enteredRangeOfType:(ASLayoutRangeType)rangeType { @@ -63,16 +64,16 @@ [node enterInterfaceState:ASInterfaceStateDisplay]; -#if USE_WORKING_WINDOW - // 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]; -#else - [node recursivelyEnsureDisplay]; // Need to do this without waiting -#endif + if (![ASDisplayNode shouldUseNewRenderingRange]) { + [node recursivelyEnsureDisplay]; // Need to do this without waiting + } 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 @@ -100,25 +101,25 @@ // The node calls clearCurrentContents and suspends display [node exitInterfaceState:ASInterfaceStateDisplay]; -#if USE_WORKING_WINDOW - 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 (![ASDisplayNode shouldUseNewRenderingRange]) { 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]; + } else { + [node.layer removeFromSuperlayer]; } - } - - // 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]; -#else - if (![node isLayerBacked]) { - [node.view removeFromSuperview]; } 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]; } -#endif } @end diff --git a/AsyncDisplayKit/Private/ASDisplayNode+FrameworkPrivate.h b/AsyncDisplayKit/Private/ASDisplayNode+FrameworkPrivate.h index 0c016d009c..5834bc3b9a 100644 --- a/AsyncDisplayKit/Private/ASDisplayNode+FrameworkPrivate.h +++ b/AsyncDisplayKit/Private/ASDisplayNode+FrameworkPrivate.h @@ -20,10 +20,6 @@ NS_ASSUME_NONNULL_BEGIN -// Project-wide control for whether the offscreen UIWindow is used for display, or if -// ASDK's internal system for coalescing and triggering display events is used. -#define USE_WORKING_WINDOW 1 - /** Hierarchy state is propogated from nodes to all of their children when certain behaviors are required from the subtree. Examples include rasterization and external driving of the .interfaceState property. diff --git a/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm b/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm index d14c042c0b..1b4dd81788 100644 --- a/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm +++ b/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm @@ -13,6 +13,7 @@ #import "ASDisplayNodeInternal.h" #import "ASDisplayNode+Subclasses.h" #import "ASDisplayNode+FrameworkPrivate.h" +#import "ASDisplayNode+Beta.h" #import "ASEqualityHelpers.h" /** @@ -248,11 +249,11 @@ _messageToViewOrLayer(setNeedsDisplay); -#if !USE_WORKING_WINDOW - if (_layer && !self.isSynchronous) { - [ASDisplayNode scheduleNodeForDisplay:self]; + if ([ASDisplayNode shouldUseNewRenderingRange]) { + if (_layer && !self.isSynchronous) { + [ASDisplayNode scheduleNodeForDisplay:self]; + } } -#endif } }