Niels Hoogendoorn
Niels Hoogendoorn

Reputation: 738

SwiftUI - NavigationBar in View from NavigationLink quickly showing then disappearing

I have a ContentView containing a NavigationView that leads to a DestinationView. I want to hide the navigation bar in the ContentView, but show it in the DestinationView. To hide it in the ContentView I set navigationBarHidden to true and give navigationBarTitle an empty string. In the DestinationView I set navigationBarHidden to false and give it the title "DestinationView".

If I run the project and tap on the NavigationLink, the DestinationView shows the NavigationBar but quickly hides it after the view appeared. Can anybody help me with this?

struct ContentView: View {
    var body: some View {
        NavigationView {
            ZStack {
                Color.red.frame(maxWidth: .infinity, maxHeight: .infinity)
                NavigationLink(destination: DestinationView()) {
                    ZStack {
                        Color.green.frame(width: 200, height: 200)
                        Text("Tap me")
                    }
                }
            }
            .navigationBarTitle("")
            .navigationBarHidden(true)
        }
    }
}

struct DestinationView: View {
    var body: some View {
        List {
            Text("1")
            Text("2")
        }
        .navigationBarTitle("DestinationView")
        .navigationBarHidden(false)
    }
}

enter image description here

Upvotes: 7

Views: 3820

Answers (6)

hbr4u7vb2sljksdf2
hbr4u7vb2sljksdf2

Reputation: 318

I run this code on an iOS 14 Simulator and the navigation bar did not hide, so I assume this might be an issue with iOS 13. I had a similar problem and my code which resulted in nav bar disappearing on iOS 13.5 Simulator worked fine on iOS 14.4 Simulator.

Upvotes: 0

cseh_17
cseh_17

Reputation: 237

There ist actually a really simple solution to this problem. After many tries I figured it, that you have to add the .navigationBarHidden(false) directly to the destination view inside the NavigationLink like this:

struct ContentView: View {
    var body: some View {
        NavigationView {
            ZStack {
                Color.red.frame(maxWidth: .infinity, maxHeight: .infinity)
                NavigationLink(destination: DestinationView()
                   .navigationBarHidden(false)) {
                    ZStack {
                        Color.green.frame(width: 200, height: 200)
                        Text("Tap me")
                    }
                }
            }
            .navigationBarTitle("")
            .navigationBarHidden(true)
        }
    }
}

struct DestinationView: View {
    var body: some View {
        List {
            Text("1")
            Text("2")
        }
        .navigationBarTitle("DestinationView")
        .navigationBarHidden(false)
    }
}

It will work like desired, and won't disappear after showing up.

Upvotes: 0

Norman
Norman

Reputation: 3205

For me passing a binding around through the view hierarchy wasn't optimal, adding the state to an environment var was preferable.

class SceneState: ObservableObject {
    @Published var isNavigationBarHidden = true
}


class SceneDelegate: UIResponder, UIWindowSceneDelegate {
...
    var sceneState = SceneState()

...
    let contentView = ContentView().environmentObject(sceneState)
...
}

struct ContentView: View {
    var body: some View {
        NavigationView {
            View1()
        }
    }
}

struct View1: View {
    @EnvironmentObject var sceneState: SceneState
    @State private var showView2: Bool = false

    var body: some View {
        VStack {
            Text("NO nav bar.")
            Button("Go to View2") {
                self.showView2 = true
            }
            NavigationLink(destination: View2(), isActive: $showView2, label: {EmptyView()})
        }
        .navigationBarHidden(self.sceneState.isNavigationBarHidden)
        .navigationBarTitle("")
        .navigationBarBackButtonHidden(self.sceneState.isNavigationBarHidden)
    }
}


struct View2: View {
    @EnvironmentObject var sceneState: SceneState

     var body: some View {
        VStack {
            Text("WITH nav bar.")
        }
            .navigationBarHidden(self.sceneState.isNavigationBarHidden)
            .navigationBarTitle("WWDC")
            .navigationBarBackButtonHidden(self.sceneState.isNavigationBarHidden)
            .onAppear {
                self.sceneState.isNavigationBarHidden = false
        }
    }
}

Upvotes: 1

Drarok
Drarok

Reputation: 3809

Edit: use the accepted answer as it's a much cleaner solution.


I encountered this bug and ended up using UIViewControllerRepresentable to wrap a controller which sets the navigation bar hidden state in its viewDidAppear method:

struct ContentView: View {
    var body: some View {
        NavigationView {
            ZStack {
                Color.red.frame(maxWidth: .infinity, maxHeight: .infinity)
                NavigationLink(destination: DestinationView()) {
                    ZStack {
                        Color.green.frame(width: 200, height: 200)
                        Text("Tap me")
                    }
                }
            }
            .navigationBarTitle("")
            .navigationBarHidden(true)
        }
    }
}

struct DestinationView: View {
    var body: some View {
        List {
            Text("1")
            Text("2")
        }
        .navigationBarTitle("DestinationView")
        .navigationBarHidden(false)
        .background(HorribleHack())
    }
}

struct HorribleHack: UIViewControllerRepresentable {
    func makeUIViewController(context: Context) -> HorribleHackViewController {
        HorribleHackViewController()
    }

    func updateUIViewController(_ uiViewController: HorribleHackViewController, context: Context) {
    }
}

class HorribleHackViewController: UIViewController {
    override func viewDidAppear(_ animated: Bool) {
        DispatchQueue.main.async {
            self.navigationController?.setNavigationBarHidden(false, animated: false)
        }
    }
}

Upvotes: 1

bhargav K
bhargav K

Reputation: 64

There is an issue with the safe area layout guide

struct ContentView: View {
    var body: some View {
        NavigationView {
            ZStack {
                Color.red.frame(maxWidth: .infinity, maxHeight: .infinity)
                VStack {
                    NavigationLink(destination: DestinationView()) {
                        ZStack {
                            Color.green.frame(width: 200, height: 200)
                            Text("Tap me")
                        }
                    }
                }
            }.edgesIgnoringSafeArea(.all)
            .navigationBarHidden(true)
        }
    }
}

struct DestinationView: View {
    var body: some View {
        VStack {
            List {
                Text("1")
                Text("2")
            }
        }.navigationBarTitle("DestinationView")
        .navigationBarHidden(false)
    }
}

Happy Coding...

Upvotes: 1

Jawad Ali
Jawad Ali

Reputation: 14397

You need to use variable to achieve this and bind it with your destination

struct ContentView: View {
         @State var isNavigationBarHidden: Bool = true
        var body: some View {
            NavigationView {

                ZStack {
                    Color.red.frame(maxWidth: .infinity, maxHeight: .infinity)
                    NavigationLink(destination: DestinationView(isNavigationBarHidden: self.$isNavigationBarHidden)) {
                        ZStack {
                            Color.green.frame(width: 200, height: 200)
                            Text("Tap me")
                        }
                    }

                }
                .navigationBarHidden(self.isNavigationBarHidden)
                .navigationBarTitle("")
                .onAppear {
                    self.isNavigationBarHidden = true
                }
            }
        }
    }

    struct DestinationView: View {
        @Binding var isNavigationBarHidden: Bool
        var body: some View {
            List {
                Text("1")
                Text("2")
            }
            .navigationBarTitle("DestinationView")

            .onAppear {
                self.isNavigationBarHidden = false
            }
        }
    }

Upvotes: 21

Related Questions