Dan Eden
Dan Eden

Reputation: 1632

SwiftUI: NavigationSplitView title bar issues

I’ve filed this as a bug with Apple (FB12119822) since I’m fairly certain there’s nothing wrong with my code, but posting here for posterity and in case other folks have found a workaround.

When navigating between list and detail sections in a NavigationSplitView, the title bar unexpectedly jumps between inline and large display modes. In some instances, the title appears to be empty until the navigation segue is complete, after which it suddenly renders in a large display mode, pushing view content down.

Steps to reproduce

  1. Create a NavigationSplitView with a sidebar of navigable List items and a detail view corresponding to the selected item with a navigation bar title
  2. Navigate into the detail view

Expected result

The navigation segue animates the origin title into the “Back” button of the navigation bar, and simultaneously animates the destination’s navigation bar title into a default (large) display mode

Actual result

The navigation segue animates the origin title into the “Back” button of the navigation bar, but no destination title is displayed. When the segue completes, the ”Back” button text is replaced with the default text (“Back”) and the destination title suddenly appears. (Screen recording)

Additional observations

The above behaviour is exhibited when the NavigationSplitView’s List has a selection argument. When instead the navigationDestination modifier is applied, the title bar of both the origin list and destination detail view is impacted. (Screen recording)

Code samples

List(selection:)

import SwiftUI

fileprivate struct Item: Identifiable, Hashable {
    let id = UUID()
}

fileprivate let items = (0...10).map { _ in Item() }

struct ListWithSelectionTitleBarJumpExample: View {
    @State var selectedItem: UUID?

    var body: some View {
        NavigationSplitView {
            List(selection: $selectedItem) {
                Section {
                    ForEach(items) { item in
                        Text(item.id.uuidString)
                            .tag(item.id)
                    }
                } header: {
                    Text("Navigable rows")
                }
            }
            .navigationTitle("Navigation Title Jump")
        } detail: {
            if let selectedItem,
                 let item = items.first(where: { $0.id == selectedItem }) {
                Text(item.id.uuidString)
                    .navigationTitle("Detail View")
            } else {
                Text("No selection")
            }
        }
    }
}

struct ListWithSelectionTitleBarJumpExample_Previews: PreviewProvider {
    static var previews: some View {
        ListWithSelectionTitleBarJumpExample()
    }
}

List.navigationDestination

import SwiftUI

fileprivate struct Item: Identifiable, Hashable {
    let id = UUID()
}

fileprivate let items = (0...10).map { _ in Item() }

struct ListWithNavigationDestinationTitleBarJumpExample: View {
    var body: some View {
        NavigationSplitView {
            List {
                Section {
                    ForEach(items) { item in
                        NavigationLink(value: item) {
                            Text(item.id.uuidString)
                        }
                    }
                } header: {
                    Text("Navigable rows")
                }
            }
            .navigationTitle("Navigation Title Jump")
            .navigationDestination(for: Item.self) { value in
                Text(value.id.uuidString)
                    .navigationTitle("Detail View")
            }
        } detail: {
            Text("No selection")
        }
    }
}

struct ListWithNavigationDestinationTitleBarJumpExample_Previews: PreviewProvider {
    static var previews: some View {
        ListWithNavigationDestinationTitleBarJumpExample()
    }
}

Upvotes: 16

Views: 2434

Answers (1)

zrslv
zrslv

Reputation: 1846

I believe NavigationSplitView gets confused when a detail view has a .navigationBarTitleDisplayMode that is different from the one in the no selection view.

For example, a large title vs no title, an inline title vs a large title, a title with an editor via Binding vs plain text title.

At least your ListWithSelectionTitleBarJumpExample could be "fixed" by adding a placeholder .navigationTitle to the "no selection" view:

// ...
} else {
    Text("No selection")
        // The workaround: 
        .navigationTitle(" ")
}

This is, obviously, still a bug, NavigationStack has no problem switching between title modes.

Upvotes: 10

Related Questions