After updating to iOS 26 beta, navigation titles stopped displaying in some of our SwiftUI views, while others continued to work. The affected views showed no navigation bar - the title, large title display mode, and toolbar items were not visible.
This behavior is different from iOS 18, where the same code displays navigation titles as expected.
- iOS Version: iOS 26 beta (all current betas through beta 7)
- SwiftUI: NavigationStack with
.navigationTitle()
and .navigationBarTitleDisplayMode(.large)
- Specific Pattern: Views where
ScrollView
or Form
is the direct child of the view body
In iOS 26, when a view's body directly returns a ScrollView
or Form
, the navigation title doesn't appear. Here's an example of code that displays the navigation title in iOS 18 but not in iOS 26:
struct DashboardView: View {
var body: some View {
ScrollView {
LazyVStack {
// Your content here
}
}
.navigationTitle("Dashboard")
.navigationBarTitleDisplayMode(.large)
}
}
Similarly with Form:
struct SettingsView: View {
var body: some View {
Form {
Section("Settings") {
// Your settings here
}
}
.navigationTitle("Settings")
.navigationBarTitleDisplayMode(.large)
}
}
Views that continued to work had a different structure - they wrapped their scrollable content in a VStack
or had non-scrolling elements before the scrollable content:
struct WorkingView: View {
var body: some View {
VStack(spacing: 0) {
headerView // Non-scrolling content
ScrollView {
// Scrollable content
}
}
.navigationTitle("This Works")
.navigationBarTitleDisplayMode(.large)
}
}
The fix is to ensure there's at least one non-scrolling element in a VStack
before your scrollable content. Even an invisible element works:
struct FixedDashboardView: View {
var body: some View {
VStack(spacing: 0) {
// Non-scrolling placeholder to force navigation bar rendering in iOS 26
Color.clear.frame(height: 1)
ScrollView {
LazyVStack {
// Your content here
}
}
}
.navigationTitle("Dashboard")
.navigationBarTitleDisplayMode(.large)
}
}
For Form-based views:
struct FixedSettingsView: View {
var body: some View {
VStack(spacing: 0) {
// Non-scrolling placeholder to force navigation bar rendering in iOS 26
Color.clear.frame(height: 1)
Form {
Section("Settings") {
// Your settings here
}
}
}
.navigationTitle("Settings")
.navigationBarTitleDisplayMode(.large)
}
}
In iOS 26, the navigation bar appears to require at least one non-scrolling element to display properly when using scrollable containers.
By adding a VStack
with at least one non-scrolling element (even an invisible one), the navigation title displays correctly in iOS 26 while maintaining compatibility with iOS 18.
Before finding this solution, I tried many other approaches that did NOT fix the issue:
- Adding
.toolbar
with empty content - Using
.navigationBarHidden(false)
- Using
.toolbar(.visible, for: .navigationBar)
- Adding
.searchable()
modifier (though all working views had this) - Restructuring
ScrollViewReader
placement - Adding
.frame(maxWidth: .infinity)
to child views - Various other modifier combinations
This change in behavior affects SwiftUI apps using:
NavigationStack
or NavigationView
- Views where
ScrollView
or Form
is the direct child - Large title display mode (inline mode may also be affected)
Always wrap ScrollView
or Form
in a VStack
with at least one non-scrolling element in iOS 26:
VStack(spacing: 0) {
Color.clear.frame(height: 1) // Minimal workaround
ScrollView { /* content */ } // or Form { /* content */ }
}
This behavior change has been observed consistently across iOS 26 betas. The workaround described above ensures navigation titles display correctly in iOS 26 while maintaining backward compatibility with iOS 18 and earlier versions.
Whether this is an intentional change or will be addressed in future iOS 26 releases remains to be seen. For now, the VStack
wrapper approach provides a reliable solution.