When UIHostingController
‘s rootView
is wrapped in a NavigationStack
, navigation by means of NavigationStack
‘s path
doesn’t work: Even though a value is appended to the NavigationPath
, the corresponding view is not pushed.
Curiously enough, if NavigationStack
is initialized with a non-empty path
, it shows the stack correctly:
(On the animation, the screen with the “Show first” button is a UIViewController
, which modally presents a SwiftUI view wrapped in a UIHostingController
(the view with the button “Open next”.) “Open next” modifies path
of the view, trying to push another SwiftUI view, with the “Next” text label.
This is a minimal reproducible example, stripped of all irrelevant code:
class HomeViewController: UIViewController {
class NavigationModel: ObservableObject {
enum Path {
case second
}
@Published var navigationPath = NavigationPath()
}
@ObservedObject private var navigationModel = NavigationModel()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
let button = UIButton(type: .system)
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("Show first", for: .normal)
button.addTarget(self, action: #selector(presentSecondViewController), for: .touchUpInside)
view.addSubview(button)
NSLayoutConstraint.activate([button.centerXAnchor.constraint(equalTo: view.centerXAnchor), button.centerYAnchor.constraint(equalTo: view.centerYAnchor)])
}
@objc func presentSecondViewController() {
let view = NavigationStack(path: $navigationModel.navigationPath) {
Button("Open next") { self.navigationModel.navigationPath.append(NavigationModel.Path.second) }
.navigationDestination(for: NavigationModel.Path.self) {
switch $0 {
case .second:
SecondView()
}
}
}
let vc = UIHostingController(rootView: view)
present(vc, animated: true)
}
}
struct SecondView: View {
var body: some View {
Text("Next")
}
}
However, the same approach works in the pure SwiftUI: If the root view modally presents another view wrapped in a NavigationStack
, all modifications of the corresponding path
result in the expected behavior:
Here’s the code of the working example:
@main struct SwiftUIPlaygroundApp: App {
class NavigationModel: ObservableObject {
enum Path {
case second
}
@Published var navigationPath = NavigationPath()
}
var body: some Scene {
WindowGroup {
Button("Show first") { firstShown = true }
.sheet(isPresented: $firstShown) {
NavigationStack(path: $navigationModel.navigationPath) {
Button("Open next") { navigationModel.navigationPath.append(NavigationModel.Path.second) }
.navigationDestination(for: NavigationModel.Path.self) {
switch $0 {
case .second:
SecondView()
}
}
}
}
}
}
@State var firstShown = false
@ObservedObject var navigationModel = NavigationModel()
}
struct SecondView: View {
var body: some View {
Text("Next")
}
}
How could I make it work properly inside UIHostingController
?
Any guidance is appreciated.