Skip to content

Commit

Permalink
[ASDisplayNode] Stop infinite layout in _u_measureNodeWithBoundsIfNec…
Browse files Browse the repository at this point in the history
…essary (#1434)

We came across an infinite layout loop in `_u_measureNodeWithBoundsIfNecessary`. After requesting a layout from above, the sizes between pending and caluclated layout still do not match. We continue to prefer to use the pending layout and ask for another layout loop from above. We can’t seem to break out of this loop. The solution (thanks to Huy for the guidance) was to nil out the pending layout we get from requesting the layout from above.

I was only able to reproduce this when working with a node in a `UINavigationBarItem’s` `titleView`. I think that UIKit must be doing something sneaky with setting the frame on the view. While I was not able to create a unit test to catch this issue (I tried for a long time, and can post what I’ve come up with to see if anyone has any suggestions), I was able to create a pretty simple example project that shows the behavior:
https://github.com/rcancro/TextureLayoutLoopExample
  • Loading branch information
rcancro authored and nguyenhuy committed Apr 1, 2019
1 parent c50e509 commit 4534655
Showing 1 changed file with 10 additions and 0 deletions.
10 changes: 10 additions & 0 deletions Source/ASDisplayNode+Layout.mm
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,16 @@ - (void)_u_measureNodeWithBoundsIfNecessary:(CGRect)bounds
__instanceLock__.lock();
}

// If we request that our root layout we may generate a new _pendingDisplayNodeLayout.layout which has
// requestedLayoutFromAbove set to NO. If the pending layout has a different constrained size than nextLayout's
// and the layout sizes don't change we could end up back here asking the root to layout again causing an
// infinite layout loop. Instead, we nil out the _pendingDisplayNodeLayout.layout here because it can be
// considered an undesired artifact of the layout request. nextLayout will become _calculatedDisplayNodeLayout
// when the pending layout transition which will be created later in this method is applied.
// We will use _calculatedLayout the next time around, so requestedLayoutFromAbove will be set to YES and we
// will break out of this layout loop.
_pendingDisplayNodeLayout.layout = nil;

// Update the layout's version here because _u_setNeedsLayoutFromAbove calls __setNeedsLayout which in turn increases _layoutVersion
// Failing to do this will cause the layout to be invalid immediately
nextLayout.version = _layoutVersion;
Expand Down

0 comments on commit 4534655

Please sign in to comment.