NavigationSplitView without detail information

Hi,

I have a working program with categories, lists and detail views. All connected via NavigationLink inside a NavigationStack.

It looks a bit strange on an iPad, so I replaced the NavigationStack with a NavigationSplitView. The categories went into the sidebar and the lists into the content section.

The detail section of the NavigationSplitView is empty and it still works. Is that the correct behavior? I don't mind it, because it works.

example code:

var body: some View {
        NavigationSplitView {
            List(selection: $listSelection) {
                Section("Section 1") {
                    NavigationLink(value: EnumValue.V1) {
                        CardView("Card 1")
                    }
                    NavigationLink(value: EnumValue.V2) {
                        CardView("Card 2")
                    }
                    NavigationLink(value: EnumValue.V3) {
                        CardView("Card 3")
                    }
                }
                
                Section("Section 2") {
                    NavigationLink(value: EnumValue.V4) {
                        CardView("Card 4")
                    }
                    NavigationLink(value: EnumValue.V5) {
                        CardView("Card 5")
                    }
                }
            }
            .scrollIndicators(.hidden)
            .navigationTitle("Detail Empty")
        } content: {
            if let mt = listSelection {
                switch mt {
                case .V1:
                    ListView1()
                case .V2:
                    ListView2()
                case .V3:
                    ListView3()
                case .V4:
                    ListView4()
                case .V5:
                    ListView5()
                }
            }
        } detail: {
            // empty
        }        
    }

In each ListView, I assign the ID to the EditorView so that the EditorView is updated.

.navigationDestination(for: ViewListTransfer.self) { vl in
        switch vl {
        case .trend(bp: let bp):
            EditorView(value: bp)
                .id(bp.Id)
        case .trendNew:
            EditorView(val: nil)
        }
}

It seems to work because everything is linked via NavigationLinks, but it does look strange that the detail section is empty. The details section is mandatory; therefore, it cannot be omitted.

Is this behavior something for a bug report?

Christian

Answered by DTS Engineer in 908039022

Hello Christian,

Thanks for using the Developer Forums!

Even though your detail: contains no additional code, SwiftUI adds in an EmptyView for you. The detail parameter is a @ViewBuilder closure, and a builder with no statements returns EmptyView. That makes the detail column a blank placeholder until something is selected.

The detail still fills in because of how NavigationLink works in a split view. A link in the sidebar or content column presents its destination in the next column. When you tap a row in your content list, that link's destination replaces the empty placeholder in the detail column, so the closure doesn't need any content of its own.

If you want something visible before the first selection, put it in detail: as a placeholder, for example ContentUnavailableView.

For more information, see NavigationSplitView, which you can also quickly see by Option + Left Click on a NavigationSplitView in Xcode to see the same information, which can be helpful for quickly explaining things like this.

You might also find these resources helpful:

The SwiftUI cookbook for navigation

Split views (HIG)

 Travis

Hello Christian,

Thanks for using the Developer Forums!

Even though your detail: contains no additional code, SwiftUI adds in an EmptyView for you. The detail parameter is a @ViewBuilder closure, and a builder with no statements returns EmptyView. That makes the detail column a blank placeholder until something is selected.

The detail still fills in because of how NavigationLink works in a split view. A link in the sidebar or content column presents its destination in the next column. When you tap a row in your content list, that link's destination replaces the empty placeholder in the detail column, so the closure doesn't need any content of its own.

If you want something visible before the first selection, put it in detail: as a placeholder, for example ContentUnavailableView.

For more information, see NavigationSplitView, which you can also quickly see by Option + Left Click on a NavigationSplitView in Xcode to see the same information, which can be helpful for quickly explaining things like this.

You might also find these resources helpful:

The SwiftUI cookbook for navigation

Split views (HIG)

 Travis

Hello Travis,

thank you for your answer.

I chose this approach because I wanted to minimize the effort involved in switching to NavigationSplitView.

I had assumed that the content and detail views would be set automatically thanks to NavigationLink and navigationDestination. However, I haven't found any examples that follow the pattern I used.

I was just wondering why the detail area is mandatory, even if it’s empty. I infer that my approach isn't quite right from the behavior I'm seeing.

I had already implemented your placeholder suggestion, and it indicates that something is wrong: the placeholder only appears once. When I switch categories, the placeholder doesn't show up; instead, a warning triangle appears.

I’ve watched the video several times and also downloaded and carefully examined the accompanying source code.

I also noticed today that the link to "Bringing robust navigation structure to your SwiftUI app" has been changed, as the old link no longer works.

The link points to .../swiftui/bringing_robust_navigation_structure_to_your_swiftui_app, but the correct link is .../swiftui/bringing-robust-navigation-structure-to-your-swiftui-app. Underscores were replaced with hyphens.

I might just stick with the two-column solution.

Christian