Bugfix: Stabilize NavigationStack Layout + Reduce NavigationBar Spacing on Sheets#475
Bugfix: Stabilize NavigationStack Layout + Reduce NavigationBar Spacing on Sheets#475fhasse95 wants to merge 4 commits into
Conversation
|
This code is extremely finicky, and has tests all over the showcase. I think it would help for you to list out exactly which playgrounds you tested and how. One that you didn't explicitly call out is the SafeArea playground --> Geometry padding playground, which has facilities for hiding and showing the Navigation Bar, both with and without a sheet. |
Yes, I totally agree. Since this code is central and used in many places, I wanted to be extra careful. That's why I tried out the changes in various scenarios. In Skip Showcase, I validated the changes using the following playgrounds:
During implementation, I first tested the changes in my own app across a variety of scenarios, refining the implementation iteratively based on my observations. Besides fixing the original issue, I specifically looked at things like switching between tabs, verifying the initial layout pass of the navigation bar, and the layout behavior of sheets and full-screen covers. I also made sure that neither the navigation bar nor the underlying content jumped during transitions. Most of my testing was done on my Google Pixel 9, where I also verified gesture-driven interactions, such as slowly dismissing a sheet using the back gesture, to ensure the navigation bar behaves correctly throughout the interactive transition. Once everything looked good there, I built Skip Showcase against my local SkipUI branch and went through all of the playgrounds above. To make sure I hadn't introduced any regressions, I repeatedly switched between my bugfix branch and the current That said, I obviously can't completely rule out that I may have missed an edge case. So please feel free to try it with either your own app(s) or with Skip Showcase, and let me know if you spot anything that doesn't look right 😊 |
|
@dfabulich I spent some more time testing this change extensively and found one additional edge case, which is now fixed by the latest commit. The issue occurred when the actual toolbar content was taller than the initially reserved navigation bar height. In that situation, the sheet could still jump slightly during the interactive Android back gesture while the sheet or full-screen cover was shrinking. I encountered this in my own app while testing different localizations. For example, the Arabic version of my Save button resulted in slightly taller toolbar content than the default content used in some other languages:
This caused the measured toolbar height to exceed the initial estimate, making the transition visible during the gesture. The latest commit accounts for this case, and I can no longer reproduce the jump. To further clarify the main issue addressed by this PR: Before this change, the navigation bar was initially laid out with a height of zero and would then expand to its final measured size during a later layout pass. With smaller or simpler views, this is often difficult to notice because layout completes very quickly. In larger and more complex applications, particularly in debug builds, however, the intermediate layout state becomes clearly visible. This PR avoids that intermediate state by reserving an estimated navigation bar height until the actual toolbar height has been been measured. Once the measurement is available, the estimate is seamlessly replaced with the final value. This behavior can be observed whenever a view containing a For example:
Both behaviors can also be reproduced in the Skip Showcase app. Since it is a relatively simple application, the layout issues are only briefly visible and are easiest to spot by recording the screen and stepping through the captured frames. In more complex applications, however, they become much more obvious. The tab-switching issue is visible when opening a tab for the first time, and the presentation issue can be observed in the Sheet Playground. I have attached relevant frame-by-frame screenshots from Skip Showcase below: Switching Tabs [Before]
Switching Tabs [After]
Presenting Sheets [Before]
Presenting Sheets [After]
Presenting Fullscreen Covers [Before]
Presenting Fullscreen Covers [After]
Below are also a few screen recordings from my own Skip app running in debug mode. Since the UI is more complex (icons, gradients, additional drawing work, etc.), the delayed layout becomes much more noticeable than in the Skip Showcase app, as mentioned earlier. Switching Tabs [Before]
Switching Tabs [After]
Presenting Fullscreen Covers [Before]
Presenting Fullscreen Covers [After]
I hope the additional screenshots and explanations help clarify the issue. If you have any further questions or concerns, please let me know 😊 |
marcprux
left a comment
There was a problem hiding this comment.
This looks good to me on the surface, but I do share @dfabulich's concerns over the (lamentable) intricacy of navigation on Android, so I'd like to get his 2nd opinion before I merge.
|
@marcprux @dfabulich FYI: I merged this branch with the latest While testing, I noticed two additional issues. First, my previous version still contained a bug that caused the bottom safe area inset to be applied incorrectly for sheets. Second, I found another layout issue in SafeAreaPlayground → Sheet → Bottom Toolbar during the interactive Android back dismiss gesture, which is also present on the current
Both issues are fixed in the latest commit. |























This PR stabilizes
NavigationStacklayout behavior so that the navigation bar and the content below it no longer jump when the navigation bar height is measured or updated.Before this change, the navigation bar could be laid out with an initial height of zero or an incomplete height, and then expand to its final size after the first layout/measurement pass. Visually, this made the navigation bar appear collapsed immediately after a tab switch and then "slide" or jump into place once layout completed.
In release builds with simple views this is often hard to notice because the layout pass completes very quickly. With more complex views, or in debug builds, however, the intermediate state becomes visible. This is also reproducible in the Skip Showcase app in a debug build when taking a closer look.
To reproduce this in the Skip Showcase app:
AbouttabShowcasetabShowcaseviewNote: The jump only happens during the first layout of that view. After the
Showcasetab has been laid out once, switching away and back again usually does not reproduce the issue since the navigation bar has already been measured.Below is a before/after comparison of this example in the Skip Showcase app, slowed down to make the issue easier to see:
Before:

After:

As shown above, before the change, the navigation bar is initially collapsed after switching tabs and becomes visible only after layout catches up. After the change, the navigation bar is present at the correct size immediately.
This is achieved by reserving an estimated top-bar height before the first real measurement is available. Once the actual top-bar size has been measured, the layout switches to the measured value. This keeps the content and navigation bar stable during initial layout and prevents the visible jump.
I also manually verified additional cases that previously showed navigation-bar jumps during initial layout, including
NavigationStackinside.sheetand.fullScreenCover, using my own Skip app with more complex views and navigation flows. Those cases are also fixed with this change.As part of the same layout improvements, this PR also reduces unnecessary empty space between the navigation bar and the top edge of sheets, making better use of the available sheet area:
Large Title (Before / After):


Inline Title (Before / After):


Thank you for contributing to the Skip project! Please review the contribution guide at https://skip.dev/docs/contributing/ for advice and guidance on making high-quality PRs.
Use this space to describe your change and add any labels (bug, enhancement, documentation, etc.) to help categorize your contribution.
Skip Pull Request Checklist:
swift testI used Codex to iteratively investigate and refine the fix for the NavigationStack layout issue. To verify the results, I tested multiple playground views in the Skip Showcase app and my own Skip app, which contains several navigation scenarios, including sheets, full-screen covers, tab bars, and nested navigation. All of these scenarios work properly after the bug fix.