SwiftUI how to hide navigation bar with TabView

Viewed 2482

I'm using SwiftUI TabView inside NavigationView, But I just can't hide the navigation bar in iOS 13.0 simulator.

Here is the code:

import SwiftUI

struct TestView: View {
    var body: some View {
        ZStack {
            Color.green
            Text("Hello")
        }
    }
}

struct ContentView: View {
    var body: some View {
        NavigationView {
            ZStack {
                Color.red
                TabView(selection: .constant(0),
                        content: {
                            TestView()
                                .tabItem { Text("test") }
                                .tag(0)
                                .navigationBarTitle("")
                                .navigationBarHidden(true)
                        })
            }

        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }

}

Any help? thanks!

enter image description here

6 Answers

Check this

TabView {
        ECHomeView().tabItem {
            VStack {
                Text("Home")
                Image.Home.renderingMode(.template)
            }
        }.navigationBarHidden(true)
        ECMyClaimsView().tabItem {
            VStack {
                Text("My Claims")
                Image.Myclaims.renderingMode(.template)
            }
        }.navigationBarHidden(true).navigationBarTitle("")
        ECAddClaimView().tabItem {
            VStack {
                Text("Create")
                Image.Create.renderingMode(.template)
            }
        }.navigationBarHidden(true).navigationBarTitle("")
        ECMyApprovalsView().tabItem {
            VStack {
                Text("My Approvals")
                Image.MyApprovals.renderingMode(.template)
            }
        }.navigationBarHidden(true).navigationBarTitle("")
        ECMenuView().tabItem {
            VStack {
                Text("Menu")
                Image.Menu.renderingMode(.template)
            }
        }.navigationBarHidden(true).navigationBarTitle("")
    }

I think it's system bug. I had same problem.

Same issue

Here is my Solution

1. Add UIApplication extension

https://stackoverflow.com/a/30858591/12496427

extension UIApplication {
    class func topViewController(controller: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {
        if let navigationController = controller as? UINavigationController {
            return topViewController(controller: navigationController.visibleViewController)
        }
        if let tabController = controller as? UITabBarController {
            if let selected = tabController.selectedViewController {
                return topViewController(controller: selected)
            }
        }
        if let presented = controller?.presentedViewController {
            return topViewController(controller: presented)
        }
        return controller
    }
}

2. Using it in onAppear() function

struct TestView: View {
    var body: some View {
        ZStack {
            Color.green
            Text("Hello")
        }
/////// HERE ////////
        .onAppear { 
            UIApplication.topViewController()?
               .navigationController?.isNavigationBarHidden = true
        }
/////// HERE ////////
    }
}

struct ContentView: View {
    var body: some View {
        NavigationView {
            ZStack {
                Color.red
                TabView(selection: .constant(0),
                        content: {
                            TestView()
                                .tabItem { Text("test") }
                                .tag(0)
                                .navigationBarTitle("")
                                .navigationBarHidden(true)
                        })
            }

        }
    }
}

If you get access to the boolean that sets whether or not you are showing your navigation bar, you can fix this issue. But its not pleasant - I think there must be a bug behind the issue here.

The NavigationView that contains the TabView has a modifier that governs if the nav bar shows. Something like this:

NavigationView {
                NavigationLink(destination:
                                TabBarScreen(domainDependencies: domainDependencies).environmentObject(navState),
                               isActive: $navState.initialNavMainLink) {
                    EmptyView()
                }.isDetailLink(false)
            }
            .navigationViewStyle(StackNavigationViewStyle())
            //the property here governs hiding of nav bar:
            .navigationBarHidden(navState.initialNavBarHidden)

Ive put that property (initialNavBarHidden) within my NavState class, which is an object that contains all the nav links etc for my entire app navigation. That object is placed in the @environment so i can use it anywhere.

On the .onAppear for each tab, if I set that property to false, and then true, using DispatchQueue.main.asyncAfter .. with a short gap between them, then the problem is fixed. As I say, its a hack, but it works. I could get nothing else to work after some days looking at this issue.

So I have a function on my navState class like this:

func tabBarBugFix() {
        DispatchQueue.main.asyncAfter(deadline: .now()) {
            self.initialNavBarHidden = false
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.00001) {
                self.initialNavBarHidden = true
            }
        }
    }

And I call it for onAppear for each tab page, eg:

struct PostPage: View {
    
    @EnvironmentObject var navState: NavState
    
    var body: some View {
        Text("Create a Post")
            .onAppear() {
                navState.tabBarBugFix()
            }
    }
}

You get a tiny visual glitch, which is horrible but brief. As a better fix Im actually going to take my tab bar out of the NavigationView altogether, as thats where the bug happens.

you have already hide the navigation bar with .navigationBarHidden(true). here you are seeing the safe area , so you can let your view expand in to the safe area by using .ignoresSafeArea()

struct ContentView: View {
    var body: some View {
        NavigationView {
            ZStack {
                Color.red
                
                TabView(selection: .constant(0),
                        content: {
                            TestView()
                                .tabItem { Text("test") }
                                .tag(0)
                                .navigationBarTitle("")
                                .navigationBarHidden(true)
                                .ignoresSafeArea() //<-here
                        })
            }
            
        }
    }
}

enter image description here enter image description here

In the View you want to hide the NavigationView use .navigationBarHidden(true) to hide it.

struct TestView: View {
    var body: some View {
        ZStack {
            Color.green
            Text("Hello")
        }
        .navigationBarHidden(true)
    }
}

If you don't want the big NavigationView, use .navigationBarTitleDisplayMode(.inline) to shrink the size, and also keep using ToolBarItems.

How about the negative padding trick?

struct ContentView: View {
  var body: some View {
    NavigationView {
      TabView {
        List(1 ..< 40, id: \.self) {
          Text("A" + $0.description)
        }.tag(1).navigationBarHidden(true).navigationBarTitleDisplayMode(.inline)
        List(1 ..< 40, id: \.self) {
          Text("B" + $0.description)
        }.tag(2).navigationBarHidden(true).navigationBarTitleDisplayMode(.inline)
      }.padding(.bottom, -50) // <- trick
    }
  }
}
Related