@@ -950,6 +950,10 @@ - (void)addSubnode:(ASDisplayNode *)subnode
950950 _subnodes = [[NSMutableArray alloc ] init ];
951951
952952 [_subnodes addObject: subnode];
953+
954+ // This call will apply our .hierarchyState to the new subnode.
955+ // If we are a managed hierarchy, as in ASCellNode trees, it will also apply our .interfaceState.
956+ [subnode __setSupernode: self ];
953957
954958 if (self.nodeLoaded ) {
955959 // If this node has a view or layer, force the subnode to also create its view or layer and add it to the hierarchy here.
@@ -969,8 +973,6 @@ - (void)addSubnode:(ASDisplayNode *)subnode
969973 if (isMovingEquivalentParents) {
970974 [subnode __decrementVisibilityNotificationsDisabled ];
971975 }
972-
973- [subnode __setSupernode: self ];
974976}
975977
976978/*
@@ -1267,7 +1269,7 @@ - (void)__decrementVisibilityNotificationsDisabled
12671269}
12681270
12691271// This uses the layer hieararchy for safety. Who knows what people might do and it would be bad to have visibilty out of sync
1270- - (BOOL )__hasParentWithVisibilityNotificationsDisabled
1272+ - (BOOL )__selfOrParentHasVisibilityNotificationsDisabled
12711273{
12721274 CALayer *layer = _layer;
12731275 do {
@@ -1287,7 +1289,7 @@ - (void)__enterHierarchy
12871289{
12881290 ASDisplayNodeAssertMainThread ();
12891291 ASDisplayNodeAssert (!_flags.isEnteringHierarchy , @" Should not cause recursive __enterHierarchy" );
1290- if (!self.inHierarchy && !_flags.visibilityNotificationsDisabled && ![self __hasParentWithVisibilityNotificationsDisabled ]) {
1292+ if (!self.inHierarchy && !_flags.visibilityNotificationsDisabled && ![self __selfOrParentHasVisibilityNotificationsDisabled ]) {
12911293 self.inHierarchy = YES ;
12921294 _flags.isEnteringHierarchy = YES ;
12931295 if (self.shouldRasterizeDescendants ) {
@@ -1309,7 +1311,7 @@ - (void)__exitHierarchy
13091311{
13101312 ASDisplayNodeAssertMainThread ();
13111313 ASDisplayNodeAssert (!_flags.isExitingHierarchy , @" Should not cause recursive __exitHierarchy" );
1312- if (self.inHierarchy && !_flags.visibilityNotificationsDisabled && ![self __hasParentWithVisibilityNotificationsDisabled ]) {
1314+ if (self.inHierarchy && !_flags.visibilityNotificationsDisabled && ![self __selfOrParentHasVisibilityNotificationsDisabled ]) {
13131315 self.inHierarchy = NO ;
13141316
13151317 [self .asyncLayer cancelAsyncDisplay ];
@@ -1807,6 +1809,19 @@ - (void)setHierarchyState:(ASHierarchyState)newState
18071809 _hierarchyState = newState;
18081810 }
18091811
1812+ // Entered or exited contents rendering state.
1813+ if ((newState & ASHierarchyStateRangeManaged) != (oldState & ASHierarchyStateRangeManaged)) {
1814+ if (newState & ASHierarchyStateRangeManaged) {
1815+ [self enterInterfaceState: self .supernode.interfaceState];
1816+ } else {
1817+ // The case of exiting a range-managed state should be fairly rare. Adding or removing the node
1818+ // to a view hierarchy will cause its interfaceState to be either fully set or unset (all fields),
1819+ // but because we might be about to be added to a view hierarchy, exiting the interface state now
1820+ // would cause inefficient churn. The tradeoff is that we may not clear contents / fetched data
1821+ // for nodes that are removed from a managed state and then retained but not used (bad idea anyway!)
1822+ }
1823+ }
1824+
18101825 if (newState != oldState) {
18111826 LOG (@" setHierarchyState: oldState = %lu , newState = %lu " , (unsigned long )oldState, (unsigned long )newState);
18121827 }
0 commit comments