lukecsmith.co.uk

SwiftUI: showing / hiding navbar on complex navstack

Luke, 21 May 2020

TL;DR : Have a single source of truth for whether or not the nav bar is hidden – and pass it down the stack.

The problem : Ive found that various instances of the command to show or hide the navigation bar can conflict, and it can be hard to resolve these conflicts. The standard way to show and hide the navigation bar for a SwiftUI view is to use :

.navigationBarTitle("Home")
.navigationBarHidden(true)

Which is fine, as long as you use both commands together, and set the title first (just setting the hidden property on its own doesnt work – possibly just a bug with current implementation).

The problems occur when you try to do something different on subsequent pages in a navigation stack – say for example, you want the nav bar not showing on a home page, but you do want it showing on a detail page. Simply adding a new call like the above one, to set the title and hidden status on that detail page doesnt work, as it conflicts with the original settings on the view above. So heres how I get round the problem and allow for any amount of showing and hiding on any view as it appears down the line.

First, have an @State Bool property on the view at the root of the stack, that determines whether or not the nav bar is hidden. Use this property as the governer as to whether or not the nav bar is hidden on this main view. Heres an example root view :

struct ContentView: View {
    
    @State private var navBarHidden = true
    
    var body: some View {
        NavigationView {
            NavigationLink(
                destination: DetailView(navBarHidden: self.$navBarHidden)
            ) {
                Text("Go to detail view")
            }
        }
        .navigationBarTitle("")
        .navigationBarHidden(self.navBarHidden)
        .onAppear(perform: {
            self.navBarHidden = true
        })
    }
}

Note how the navBarHidden property is being passed on to the detail view in the initializer. The detail view would look like this, and set the hidden property in the onAppear call :

struct DetailView: View {
    
    @Binding var navBarHidden : Bool
    
    var body: some View {
        Text("Hello Detail View!")
            .navigationBarTitle("Detail")
            .onAppear() {
                self.navBarHidden = false
            }
       
    }
}

So when we set the navBarHidden property, we are only ever setting the original instance from the main content view. But we set the title locally. By setting the property in the onAppear function, it ensures that when we return to the main view, the nav bar is shown/hidden at the right time.

Tagged with: