In SwiftUI, how do I pass a a NavigationSplitViewVisibility to a subview?

192 Views Asked by At

My app uses a NavigationSplitView to provide a 3 column (sidebar, content, detail) layout. The sidebar is implemented in one view (SideBarView). The content view is implemented in a separate view (ContentView). When an entry (Server) in the sidebar is selected, it is passed down to ContentView as a parameter to its initializer. All of this works great.

Now I want to give ContentView the ability to programmatically change the NavigationSplitView's columns using an instance of NavigationSplitViewVisibility. Somehow I need to pass the NavigationSplitViewVisibility instance from SideBarView down to ContentView. How do I do this?

I seem to get caught up in the fact that NavigationSplitViewVisibility does not conform to the ObservableObject protocol.

struct SideBarView: View {
    @State var columnVisibility: NavigationSplitViewVisibility = .automatic
    @State private var selectedServer: Server?
    
    var body: some View {
        NavigationSplitView(columnVisibility: $columnVisibility, sidebar: {
            List (selection: $selectedServer, content: {
                //More SwiftUI Code here
            }
        }
    }, content: {
        if let server = selectedServer {
            // Here is where I want to pass columnVisibility to ContentView so
            // that it can update the configuration of the NavigationSplitView
            // within SideBarView
            ContentView(server: server)
        }
    }, detail: {})
}

struct ContentView: View {
    @ObservedObject var server: Server
    
    init(_with aServer: Server) {
        server = aServer
    }
}
1

There are 1 best solutions below

0
malhal On

Same was as you pass down read/write access to any @State value, with @Binding var, e.g.

ChildView(server: $server, columnVisibility: $columnVisibility)

struct ContentView: View {
    @Binding var server: Server // assumes Server is struct which it normally would be in Swift.
    // let server: Server // if you only need read access.
    @Binding var columnVisibility: NavigationSplitViewVisibility
    ...

Remember and use let if you only need read access. In either case, body is called when the value has changed since the last time the View was init.