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)")
}
}
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
NavigationStackwithin a SwiftUI view. And they will stack each other. This is a workaround: