I am trying to understand why when I push UIHostingViewController with SwiftUI View in it to my navigation stack, there's a slight delay when the navigation title appears, as you can see from this demo:
Code:
struct SwiftUIView: View {
var body: some View {
EmptyView()
.navigationTitle("SwiftUI View")
}
}
Versus using UIViewController where the title is visible right away:
One of the solutions that I can think of is to add .title
before pushing UIHostingViewController
to the stack, but I am wondering if there's a way to fix it from SwiftUI component?
Here's the project that illustrates what I am describing: https://github.com/ignotusverum/NavigationTitlesTesting
And full code:
class ViewController: UIViewController {
override func loadView() {
let view = UIView()
view.backgroundColor = .white
let button = UIButton(type: .system)
button.addTarget(self,
action: #selector(buttonTapped),
for: .touchUpInside)
button.setTitle("Push SwiftUI View", for: .normal)
let button2 = UIButton(type: .system)
button2.addTarget(self,
action: #selector(button2Tapped),
for: .touchUpInside)
button2.setTitle("Push UIViewController", for: .normal)
let stackView = UIStackView(arrangedSubviews: [button, button2])
stackView.axis = .vertical
stackView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stackView)
NSLayoutConstraint.activate([
stackView.heightAnchor.constraint(equalToConstant: 120),
stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
])
self.view = view
}
@objc func buttonTapped() {
let hostingController = UIHostingController(rootView: SwiftUIView())
navigationController?.pushViewController(hostingController,
animated: true)
}
@objc func button2Tapped() {
let viewController = UIViewController()
viewController.view.backgroundColor = .white
viewController.title = "UIViewController"
navigationController?.pushViewController(viewController,
animated: true)
}
override func viewDidLoad() {
super.viewDidLoad()
title = "Navigation Test"
navigationItem.backButtonTitle = "Back"
}
}
struct SwiftUIView: View {
var body: some View {
EmptyView()
.navigationTitle("SwiftUI View")
}
}
PS:
It seems like this issue arises because the large title updates its appearance asynchronously. The delay occurs when the UIHostingController reads the navigation title from the SwiftUI view's environment.
It seems to be an issue when using the UIHostingViewController
. Set the title of the hosting view controller before pushing it:
let vc = UIHostingController(rootView: SomeSwiftUiScreen())
// fix for navigation bar title glitch when pushing a SwiftUI screen from UIKit
vc.title = "My Title"
navigationController?.pushViewController(vc, animated: true)