You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

UIHostingController中NavigationStack的path导航失效问题

UIHostingController嵌套NavigationStack时路径导航失效的解决方法

当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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 03:42:39