Hide UINavigationController's navigationBar when the root controller is a UIHostingController
Asked Answered
F

2

7

I am struggling to hide the navigationBar, which would properly be hidden if the root controller wasn't a SwiftUI UIHostingController.

I tried the following:

  • Setting navigationController.isNavigationBarHidden = true after creating it, at viewDidLoad and viewWillAppear.

  • Adding both .navigationBarHidden(true) and .navigationBarBackButtonHidden(true) for the UIHostingController's rootView.

Could it be an Apple bug? I am using Xcode 11.6.

All my attempts together:

class LoginController: UINavigationController, ObservableObject
{
    static var newAccount: LoginController
    {
        let controller = LoginController()
        let view = LoginViewStep1()
            .navigationBarHidden(true)
            .navigationBarBackButtonHidden(true)
        controller.viewControllers = [UIHostingController(rootView: view)]
        controller.isNavigationBarHidden = true
        return controller
    }
    
    override func viewWillAppear(_ animated: Bool)
    {
        super.viewWillAppear(animated)
        
        self.isNavigationBarHidden = true
    }

    override func viewDidLoad()
    {
        super.viewDidLoad()

        self.isNavigationBarHidden = true
    }
}

struct LoginViewStep1: View
{
    // ...
    
    var body: some View
    {
        VStack {
            // ...
        }
        .navigationBarHidden(true)
        .navigationBarBackButtonHidden(true)
    }
}
Fivestar answered 11/8, 2020 at 22:41 Comment(2)
Would you show your code?Lorrinelorry
Added my code @Lorrinelorry .Fivestar
L
4

Here is a solution. Tested with Xcode 11.4 / iOS 13.4

demo

Modified your code:

class LoginController: UINavigationController, ObservableObject
{
    static var newAccount: LoginController
    {
        let controller = LoginController()
        let view = LoginViewStep1()
        controller.viewControllers = [UIHostingController(rootView: view)]

        // make it delayed, so view hierarchy become constructed !!!
        DispatchQueue.main.async {
            controller.isNavigationBarHidden = true
        }

        return controller
    }
}

struct LoginViewStep1: View
{
    var body: some View
    {
        VStack {
            Text("Hello World!")
        }
    }
}

tested part in SceneDelegate

if let windowScene = scene as? UIWindowScene {
    let window = UIWindow(windowScene: windowScene)

    window.rootViewController = LoginController.newAccount

    self.window = window
    window.makeKeyAndVisible()
}
Lorrinelorry answered 12/8, 2020 at 13:49 Comment(2)
That worked! Thanks. One would think that viewDidLoad and viewWillAppear would be "delayed" enough.Fivestar
Seems that pushing a second UIHostingController would require a similar hack.Fivestar
C
4

Alternate solution is to use UINavigationControllerDelegate:

func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
    // Required because pushing UIHostingController makes the navigationBar appear again
    navigationController.isNavigationBarHidden = true
}

Tested with iOS 14.5 - Xcode 12.5

Czardom answered 23/4, 2021 at 19:7 Comment(1)
This didn't work for me with Xcode 14.2 and iOS 15.7.3. I was able to get it to work by wrapping the isNavigationBarHidden line in an async dispatch to the main dispatch queue. Kind of a combination of the navigation controller delegate and @Asperi's solution.Carbaugh

© 2022 - 2024 — McMap. All rights reserved.