UIHostingController中NavigationStack的path导航失效问题
当UIHostingController的根视图被包裹在NavigationStack中时,通过修改NavigationPath进行导航会失效——即使向路径中添加值,目标视图也不会被推入栈中。但如果初始化NavigationStack时传入非空的path,栈能正常显示。
场景说明:带有「Show first」按钮的UIViewController模态呈现一个被UIHostingController包裹的SwiftUI视图(含「Open next」按钮),点击「Open next」尝试修改path以跳转至显示「Next」的视图,但跳转无响应。
复现问题的代码
class HomeViewController: UIViewController { class NavigationModel: ObservableObject { enum Path { case second } @Published var navigationPath = NavigationPath() } @ObservedObject 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") } }
纯SwiftUI环境下正常工作的代码
@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") } }
解决方案
问题出在UIViewController中对ObservableObject的声明方式。在UIKit环境下,使用@ObservedObject持有自行初始化的NavigationModel会导致对象生命周期不稳定,SwiftUI无法正确追踪其@Published属性的变更。
只需将HomeViewController中的@ObservedObject替换为@StateObject,即可确保NavigationModel的生命周期与UIViewController绑定,其发布的路径变更能被NavigationStack正确捕获:
class HomeViewController: UIViewController { class NavigationModel: ObservableObject { enum Path { case second } @Published var navigationPath = NavigationPath() } // 替换为@StateObject @StateObject var navigationModel = NavigationModel() // ... 其余代码保持不变 }
原理说明
@StateObject专门用于持有视图(或UIViewController)自己创建的ObservableObject,会自动管理对象的生命周期,确保对象在视图存活期间保持有效。@ObservedObject则用于观察外部传入的ObservableObject,若用来持有自行初始化的对象,可能因SwiftUI的视图更新机制导致订阅失效,进而无法响应属性变更。
内容的提问来源于stack exchange,提问作者lazarevzubov
相关产品推荐
相关产品推荐

