SwiftUI large title disappears after tapping the selected tab again

·4 mins

The Settings large title disappeared after tapping the already-selected tab again. Its space stayed empty; the small title still appeared when scrolling, and the accessibility check passed:

titleBefore=true titleAfter=true

An opaque navigation-bar background was painting over the title. The eventual repair removed a screen modifier’s writes to UINavigationBar.appearance(). Hiding the affected screen’s bar background was an intermediate workaround that introduced a font regression.

The title still existed

The prototype varied the tab view, loading state, scroll-view wrapper, and swipe-back helper. Its accessibility existence checks stayed green. Adding the app’s opaque navigation-bar appearance also produced green checks, even though the title vanished visually.

Counting dark pixels in the prototype’s title region exposed the difference:

AppearanceAfter first tapAfter re-tap
Default1295512955
Opaque129550

Those are diagnostic counts from one fixed light-background rendering, not a portable visibility threshold. They contradicted the existence assertion directly.

Before-and-after hierarchy dumps from the running app showed the same title view moving from the navigation bar into the scroll view. The bar’s opaque background then covered it. Initially, the screen displayed a loader without a scroll view, so UIKit built the title inside the bar. Re-tapping the selected tab triggered the transition that exposed the problem on the investigated simulator.

This establishes a rendering mechanism in that environment. It does not establish identical title placement on every supported runtime.

A local modifier changed later navigation bars

The offending modifier intended to remove a divider and set one screen’s background. Its essential mistake looked like this generalized fragment inside a SwiftUI .onAppear closure:

let appearance = UINavigationBarAppearance()
appearance.configureWithOpaqueBackground()
appearance.backgroundColor = .secondarySystemBackground
appearance.shadowColor = .clear

UINavigationBar.appearance().standardAppearance = appearance
UINavigationBar.appearance().scrollEdgeAppearance = appearance

The closure belongs to a screen; the appearance proxy configures all instances of that class. UIKit applies appearance changes when a view enters a window. A different tab’s later navigation bar can inherit those settings, so screen order becomes part of the bug’s setup.

The repair deleted this global writer. The calling screens already declared their backgrounds through SwiftUI’s per-screen toolbar styling; the investigated runtimes did not need a replacement divider-hiding implementation. Their existing snapshots passed without re-recording. Appearance save-and-restore workarounds around a system-presented screen went away with the leak.

A focused test supplied stronger evidence than the original title lookup: show a styled screen, then create another navigation bar and check its background. It failed before the deletion because the next bar inherited a background, then passed afterward. Separate checks guarded the calling screen’s color and the next large title’s font.

Why the one-line hotfix was reversed

The initial workaround used .toolbarBackground(.hidden, for: .navigationBar) on Settings. It kept the prototype’s title visible under the opaque appearance.

The attempted app snapshot never reproduced the original disappearance. The real screen reloaded into a spinner first, unlike the prototype’s scroll-view-first case. Capturing the loaded window still showed the title without the fix. We dropped that test; it couldn’t serve as a regression guard.

Later checks found that hiding the background also made the large title use the system font instead of the intended custom font. A snapshot cropped to the bar failed with that modifier present and passed after its removal. Once the global opaque write was gone, the workaround no longer had a reason to stay.

Verify the interaction, not just the label

Builds and broad test plans passed after the cleanup. Final manual checks in the running app remained open, and older supported runtimes were untested. The font snapshot and next-bar test do not close that gap.

For release verification:

  • Visit the screens that previously wrote global appearance, then open Settings. Repeat with a different screen order.
  • Tap the selected Settings tab again. Capture the title before and after, then scroll through its large-to-inline transition.
  • Check actual title pixels and contrast against the background. Use the hierarchy to explain occlusion; accessibility existence, or isHittable alone, is not universal proof that text is painted visibly.
  • Repeat on supported runtimes, checking pushed screens’ bar styling too.

Audit screen-level UINavigationBar.appearance() writes before adding another visibility workaround.