提问人:lazarevzubov 提问时间:11/17/2023 更新时间:11/17/2023 访问量:21
带有路径的 NavigationStack 在 UIHostingController 中无法正常工作
NavigationStack with path doesn't work properly inside UIHostingController
问:
当 s 被包装在 中时,通过 的导航不起作用:即使将值附加到 ,也不会推送相应的视图。UIHostingController
rootView
NavigationStack
NavigationStack
path
NavigationPath
奇怪的是,如果用非空初始化,它会正确显示堆栈:NavigationStack
path
(在动画中,带有“首先显示”按钮的屏幕是一个 ,它以模式方式显示一个包装在(带有“下一步打开”按钮的视图)中的 SwiftUI 视图。“打开下一个”修改视图,尝试推送另一个带有“下一步”文本标签的 SwiftUI 视图。UIViewController
UIHostingController
path
这是一个最小的可重现示例,去除了所有不相关的代码:
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")
}
}
但是,同样的方法在纯 SwiftUI 中也有效:如果根视图以模式方式呈现另一个包装在 中的视图,则对相应视图的所有修改都会导致预期行为:NavigationStack
path
下面是工作示例的代码:
@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")
}
}
我怎样才能让它在里面正常工作?UIHostingController
任何指导都是值得赞赏的。
答:
正如 lorem ipsum 为我正确指出的那样,问题出在 中,这在 SwiftUI 视图之外不起作用。如果注入到 的根视图,则导航将按预期工作。@ObservedObject
NavigationModel
UIHostingController
但是,它也迫使我们在视图内移动相应的视图修饰符。这打破了将视图与导航分离的想法,SwiftUI 视图变得“相互感知”。NavigationStack
navigationDestination
为了部分解决硬编码导航的问题,我们可以从外部注入目标块,如下所示:
struct FirstView<Destination: View>: View {
var body: some View {
NavigationStack(path: $navigationModel.navigationPath) {
Button("Open next") {
self.navigationModel.navigationPath.append(HomeViewController.NavigationModel.Path.second)
}
.navigationDestination(for: HomeViewController.NavigationModel.Path.self, destination: navigationDestination)
}
}
@ViewBuilder private let navigationDestination: (HomeViewController.NavigationModel.Path) -> Destination
@ObservedObject private var navigationModel: HomeViewController.NavigationModel
init(navigationModel: HomeViewController.NavigationModel, @ViewBuilder navigationDestination: @escaping (HomeViewController.NavigationModel.Path) -> Destination) {
self.navigationModel = navigationModel
self.navigationDestination = navigationDestination
}
}
当然,这不是一个完美的解决方案,但它解决了视图相互“感知”的问题。
这是与以下内容相关的更新代码:UIHostingController
@objc func presentSecondViewController() {
let view = FirstView(navigationModel: navigationModel) {
switch $0 {
case .second:
SecondView()
}
}
let vc = UIHostingController(rootView: view)
present(vc, animated: true)
}
评论
@ObservedObject
let
SwiftUI.View
@ObservedObject
navigationDestination(for:)
UIHostingController
UIHostingController
NavigationStack
path