iosswiftuiswiftui-navigationstackswiftui-navigationsplitviewnavigationsplitview

Determine if a SwiftUI view is presented as sidebar?


I'm using a NavigationSplitView to present a two-column view, with a sidebar and detail content on iPad, which is collapsed into a single-column view on iPhone and when the app is used in Split View and Slide Over.

I'd like to style some of the content in my sidebar differently and reorganize some of the content depending on whether or not it's currently being used as a sidebar alongside a detail view, or if only a single column is visible.

Reading the column visibility (NavigationSplitViewVisibility) doesn't work, as it only seems to update in response to the user toggling the sidebar manually. The .automatic mode is always set, regardless of whether the sidebar is actually visible at the moment.


Solution

  • It seems like you just want to know whether the navigation split view has collapsed into a single column.

    From horizontalSizeClass:

    Several built-in views change their behavior based on this size class. For example, a NavigationView presents a multicolumn view when the horizontal size class is UserInterfaceSizeClass.regular, but a single column otherwise.

    The documentation talks about the deprecated NavigationView. NavigationSplitView presumably works in the same way, and the docs has just not been updated.

    So provided the sidebar column is visible, horizontalSizeClass == .compact means that no other columns are visible, and horizontalSizeClass == .regular means that the detail column is also visible.

    Here is an example where the sidebar displays different texts depending on whether the detail column is also visible.

    struct ContentView: View {
        @State private var selection: Int?
        @Environment(\.horizontalSizeClass) var sizeClass
        
        var body: some View {
            NavigationSplitView {
                List([0,1,2,3], id: \.self, selection: $selection) {
                    if sizeClass == .compact {
                        Text("Compact \($0)")
                    } else {
                        Text("Regular \($0)")
                    }
                }
            } detail: {
                if let selection {
                    Text("Details of \(selection)")
                }
            }
    
        }
    }