setHidesBackButton not working with UIHostingController

55 views Asked by At

I've encountered a weird bug while setting hidesBackButton to true when using a UIHostingController. I set hidesBackButton to true during viewWillAppear. This remains true until a viewWillLayoutSubviews changes the value (without my intervention). I suspect this is a bug on Apple's end with UIHostingController, unless I am overlooking something.

Console output:

viewDidLoad's hidesBackButton: false
viewWillAppear's hidesBackButton: true
viewIsAppearing's hidesBackButton: true
viewWillLayoutSubviews's hidesBackButton: true
viewWillLayoutSubviews's hidesBackButton: false
viewDidAppear's hidesBackButton: false
viewWillLayoutSubviews's hidesBackButton: false

Reproducible demo code:

import SwiftUI
import UIKit

class TestViewController: UIViewController {

    lazy var advanceButton: UIButton = {
        let button = UIButton()
        button.setTitle("Show UIHostingController", for: .normal)
        button.setTitleColor(.link, for: .normal)
        button.addTarget(self, action: #selector(showHostingController), for: .touchUpInside)
        button.translatesAutoresizingMaskIntoConstraints = false
        return button
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        view.addSubview(advanceButton)
        NSLayoutConstraint.activate([
            advanceButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            advanceButton.centerYAnchor.constraint(equalTo: view.centerYAnchor),
        ])
    }

    @objc func showHostingController() {
        let hostingController = CustomHostingController(rootView: Rectangle())
        navigationController?.pushViewController(hostingController, animated: true)
    }

}

class CustomHostingController<Content: View>: UIHostingController<Content> {

    override public func viewDidLoad() {
        super.viewDidLoad()
        print(" viewDidLoad's hidesBackButton: \(navigationItem.hidesBackButton)")
    }

    override public func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        // Hide the back button
        navigationItem.setHidesBackButton(true, animated: false)
        print(" viewWillAppear's hidesBackButton: \(navigationItem.hidesBackButton)")
    }

    override public func viewIsAppearing(_ animated: Bool) {
        super.viewIsAppearing(animated)
        print(" viewIsAppearing's hidesBackButton: \(navigationItem.hidesBackButton)")
    }

    override public func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()
        print(" viewWillLayoutSubviews's hidesBackButton: \(navigationItem.hidesBackButton)")
    }

    override public func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        print(" viewDidAppear's hidesBackButton: \(navigationItem.hidesBackButton)")
    }

}
1

There are 1 answers

0
son On

It seems like another SwiftUI bug, you will often see this kind of navigationBar appearance bug. i.e., when pushing from ViewController that has navigationController to NavigationStack within a SwiftUI view. And they will stack each other. This is a workaround:

//I think they're two NavigationBar separately
let hostingController = CustomHostingController(
    rootView: Rectangle()
        .navigationBarBackButtonHidden(true)
)
navigationController?.navigationItem.setHidesBackButton(true, animated: false)