@@ -198,19 +198,22 @@ + (Class)layerClass
198198 return [_ASDisplayLayer class ];
199199}
200200
201- + (void )scheduleNodeForDisplay : (ASDisplayNode *)node
201+ + (void )scheduleNodeForRecursiveDisplay : (ASDisplayNode *)node
202202{
203203 ASDisplayNodeAssertMainThread ();
204+ ASDisplayNodeAssert ([ASDisplayNode shouldUseNewRenderingRange ], @" +scheduleNodeForRecursiveDisplay: should never be called without the new rendering range enabled" );
204205 static NSMutableSet *nodesToDisplay = nil ;
205206 static BOOL displayScheduled = NO ;
206207 static ASDN::RecursiveMutex displaySchedulerLock;
208+
207209 {
208210 ASDN::MutexLocker l (displaySchedulerLock);
209211 if (!nodesToDisplay) {
210212 nodesToDisplay = [[NSMutableSet alloc ] init ];
211213 }
212214 [nodesToDisplay addObject: node];
213215 }
216+
214217 if (!displayScheduled) {
215218 displayScheduled = YES ;
216219 // It's essenital that any layout pass that is scheduled during the current
@@ -1557,13 +1560,11 @@ - (void)recursivelyEnsureDisplaySynchronously:(BOOL)synchronously
15571560
15581561- (void )setShouldBypassEnsureDisplay : (BOOL )shouldBypassEnsureDisplay
15591562{
1560- ASDN::MutexLocker l (_propertyLock);
15611563 _flags.shouldBypassEnsureDisplay = shouldBypassEnsureDisplay;
15621564}
15631565
15641566- (BOOL )shouldBypassEnsureDisplay
15651567{
1566- ASDN::MutexLocker l (_propertyLock);
15671568 return _flags.shouldBypassEnsureDisplay ;
15681569}
15691570
@@ -1612,7 +1613,7 @@ - (CGSize)calculateSizeThatFits:(CGSize)constrainedSize
16121613- (ASLayoutSpec *)layoutSpecThatFits : (ASSizeRange)constrainedSize
16131614{
16141615 ASDisplayNodeAssertThreadAffinity (self);
1615- return [ASLayoutSpec new ] ;
1616+ return nil ;
16161617}
16171618
16181619- (ASLayout *)calculatedLayout
@@ -1772,7 +1773,7 @@ - (void)setInterfaceState:(ASInterfaceState)newState
17721773 oldState = _interfaceState;
17731774 _interfaceState = newState;
17741775 }
1775-
1776+
17761777 if ((newState & ASInterfaceStateMeasureLayout) != (oldState & ASInterfaceStateMeasureLayout)) {
17771778 // Trigger asynchronous measurement if it is not already cached or being calculated.
17781779 }
@@ -1782,8 +1783,11 @@ - (void)setInterfaceState:(ASInterfaceState)newState
17821783 // Still, the interfaceState should be updated to the current state of the node; just don't act on the transition.
17831784
17841785 // Entered or exited data loading state.
1785- if ((newState & ASInterfaceStateFetchData) != (oldState & ASInterfaceStateFetchData)) {
1786- if (newState & ASInterfaceStateFetchData) {
1786+ BOOL nowFetchData = ASInterfaceStateIncludesFetchData (newState);
1787+ BOOL wasFetchData = ASInterfaceStateIncludesFetchData (oldState);
1788+
1789+ if (nowFetchData != wasFetchData) {
1790+ if (nowFetchData) {
17871791 [self fetchData ];
17881792 } else {
17891793 if ([self supportsRangeManagedInterfaceState ]) {
@@ -1793,21 +1797,42 @@ - (void)setInterfaceState:(ASInterfaceState)newState
17931797 }
17941798
17951799 // Entered or exited contents rendering state.
1796- if ((newState & ASInterfaceStateDisplay) != (oldState & ASInterfaceStateDisplay)) {
1800+ BOOL nowDisplay = ASInterfaceStateIncludesDisplay (newState);
1801+ BOOL wasDisplay = ASInterfaceStateIncludesDisplay (oldState);
1802+
1803+ if (nowDisplay != wasDisplay) {
17971804 if ([self supportsRangeManagedInterfaceState ]) {
1798- if (newState & ASInterfaceStateDisplay ) {
1805+ if (nowDisplay ) {
17991806 // Once the working window is eliminated (ASRangeHandlerRender), trigger display directly here.
18001807 [self setDisplaySuspended: NO ];
18011808 } else {
18021809 [self setDisplaySuspended: YES ];
18031810 [self clearContents ];
18041811 }
1812+ } else {
1813+ // NOTE: This case isn't currently supported as setInterfaceState: isn't exposed externally, and all
1814+ // internal use cases are range-managed. When a node is visible, don't mess with display - CA will start it.
1815+ if ([ASDisplayNode shouldUseNewRenderingRange ] && !ASInterfaceStateIncludesVisible (newState)) {
1816+ // Check __implementsDisplay purely for efficiency - it's faster even than calling -asyncLayer.
1817+ if ([self __implementsDisplay ]) {
1818+ if (nowDisplay) {
1819+ [ASDisplayNode scheduleNodeForRecursiveDisplay: self ];
1820+ } else {
1821+ [[self asyncLayer ] cancelAsyncDisplay ];
1822+ [self clearContents ];
1823+ }
1824+ }
1825+ }
18051826 }
18061827 }
18071828
1808- // Entered or exited data loading state.
1809- if ((newState & ASInterfaceStateVisible) != (oldState & ASInterfaceStateVisible)) {
1810- if (newState & ASInterfaceStateVisible) {
1829+ // Became visible or invisible. When range-managed, this represents literal visibility - at least one pixel
1830+ // is onscreen. If not range-managed, we can't guarantee more than the node being present in an onscreen window.
1831+ BOOL nowVisible = ASInterfaceStateIncludesVisible (newState);
1832+ BOOL wasVisible = ASInterfaceStateIncludesVisible (oldState);
1833+
1834+ if (nowVisible != wasVisible) {
1835+ if (nowVisible) {
18111836 [self visibilityDidChange: YES ];
18121837 } else {
18131838 [self visibilityDidChange: NO ];
@@ -1843,11 +1868,23 @@ - (void)exitInterfaceState:(ASInterfaceState)interfaceState
18431868
18441869- (void )recursivelySetInterfaceState : (ASInterfaceState)interfaceState
18451870{
1871+ ASInterfaceState oldState = self.interfaceState ;
1872+ ASInterfaceState newState = interfaceState;
18461873 ASDisplayNodePerformBlockOnEveryNode (nil , self, ^(ASDisplayNode *node) {
18471874 node.interfaceState = interfaceState;
18481875 });
1849- // FIXME: This should also be called in setInterfaceState: if it isn't being applied recursively.
1850- [ASDisplayNode scheduleNodeForDisplay: self ];
1876+
1877+ if ([self supportsRangeManagedInterfaceState ]) {
1878+ // Instead of each node in the recursion assuming it needs to schedule itself for display,
1879+ // setInterfaceState: skips this when handling range-managed nodes (our whole subtree has this set).
1880+ // If our range manager intends for us to be displayed right now, and didn't before, get started!
1881+
1882+ BOOL nowDisplay = ASInterfaceStateIncludesDisplay (newState);
1883+ BOOL wasDisplay = ASInterfaceStateIncludesDisplay (oldState);
1884+ if (nowDisplay && (nowDisplay != wasDisplay)) {
1885+ [ASDisplayNode scheduleNodeForRecursiveDisplay: self ];
1886+ }
1887+ }
18511888}
18521889
18531890- (ASHierarchyState)hierarchyState
0 commit comments