iosswiftswiftuiswiftui-navigationsplitviewnavigationsplitview

preferredCompactColumn is being passed as NavigationSplitViewColumn.sidebar but still seeing details page on iOS


As we can see in the code preferredColumn is set to NavigationSplitViewColumn.sidebar but when I open this app in iPhone 15 pro preview the NavigationSplitViewColumn.detail is shown and then user have to click back button on top to get to sidebar view

enter image description here

struct ContentView: View {
    @State private var selectedSidebarItem: String? = ""
    @State private var isExpanded: Bool = true
    @State private var preferredColumn = NavigationSplitViewColumn.sidebar


    private var toolbarTitle: String {
        switch selectedSidebarItem {
            case "UrlToSummaryView":
                return "URL to Summary"
            case "GenerateShortStoryView":
                return "Generate a Short Story"
            case "YoutubeVideoToSummaryView":
                return "Youtube Video to Summary"
            case "GenerateIdeasView":
                return "Generate Ideas"
            default:
                return ""
        }
    }
    
    var body: some View {
        NavigationSplitView(preferredCompactColumn: $preferredColumn) {
            List(selection: $selectedSidebarItem) {
                DisclosureGroup("Tasks", isExpanded: $isExpanded) {
                    NavigationLink(value: "UrlToSummaryView") {
                        HStack {
                            Image(systemName: "link")
                            Text("URL to summary")
                        }
                    }
                    NavigationLink(value: "GenerateShortStoryView") {
                        HStack {
                            Image(systemName: "chart.bar.doc.horizontal")
                            Text("Generate a short story")
                        }
                    }
                    NavigationLink(value: "YoutubeVideoToSummaryView") {
                        HStack {
                            Image(systemName: "video")
                            Text("Youtube video to summary")
                        }
                    }
                    NavigationLink(value: "GenerateIdeasView") {
                        HStack {
                            Image(systemName: "brain.filled.head.profile")
                            Text("Generate ideas")
                        }
                    }
                }
            }
            .navigationTitle("Butler")
        } content: {
            Text("Hello")
        } detail: {
            if let selectedSidebarItem {
                switch selectedSidebarItem {
                    case "UrlToSummaryView":
                        ToolbarWrapper(title: "URL to summary") {
                            UrlToSummaryView()
                        }
                    case "GenerateShortStoryView":
                        ToolbarWrapper(title: "Generate a Short Story") {
                            GenerateShortStoryView()
                        }
                    case "YoutubeVideoToSummaryView":
                        ToolbarWrapper(title: "Youtube Video to Summary") {
                            YoutubeVideoToSummaryView()
                        }
                    case "GenerateIdeasView":
                        ToolbarWrapper(title: "Generate Ideas") {
                            GenerateIdeasView()
                        }
                    default:
                        Button("Sidebar") {
                            preferredColumn = NavigationSplitViewColumn.content
                        }
                }
            } else {
                Text("")
            }
        }
        .navigationSplitViewStyle(.balanced)
    }
}

I am expecting the sidebar view to be seen in the iPhone by default.


Solution

  • As you may know, the NavigationSplitView cooperates with List, to navigate to the different columns, depending on the list selection.

    What I suspect is happening here, is that the line

    @State private var selectedSidebarItem: String? = ""
    

    makes NavigationSplitView think something in the List is selected initially, and so shows the content column (the column with "Hello" is not the detail column!), ignoring the preference you set.

    If you set selectedSidebarItem to nil initially, this counts as "nothing is selected in the list", and so the NavigationSplitView respects your preference.

    @State private var selectedSidebarItem: String? = nil