我已经阅读了关于这个话题的documentation条,我相信我对它有一个基本的理解. 从理论上讲,这听起来相当不错,而且不太复杂.如果我有一个模型类ModelClass和一个显示模型详细信息的视图ModelDetailsView,我只需将模型实例附加到NavigationPath,并让.navigationDestination(for: ModelClass.self)修改器准备好接收数据和显示详细信息视图.

因此,简化后的项目设置如下所示:

ContentView名:

struct ContentView: View {
    @StateObject private var navigator = Navigator()

    var body: some View {
        WindowGroup {
            NavigationStack(path: $navigator.path) {
                MainMenu()
                    .navigationDestination(for: ModelClass.self) { model in
                        ModelDetailsView(model: model)
                    }
            }
            .environmentObject(navigator)
        }
    }
}

Navigator个班级:

class Navigator: ObservableObject {
    @Published var path = NavigationPath()

    func navigate(with model: any Hashable) {
        path.append(model)
    }
}

我们有MainMenu米的导航:

struct MainMenu: View {
    @EnvironmentObject private var navigator: Navigator
    let model = ModelClass()

    var body: some View {
        Button("Go to details") {
            navigator.navigate(with: model)
        }
    }
}

我对上面提到的方法有很多问题.

首先,在导航部分(navigator.navigate(with: model)),我实际上没有在代码中指定我的意图.从这一行中,不清楚我是要导航到详细信息视图,还是要导航到将这个类实例用作属性的任何其他视图.添加 comments 有点儿 destruct 了快速代码的整洁(至少对我来说是这样).

但不管怎样,如果有必要,我会写 comments 来澄清,这应该是把我带到一个细节视图.但下一个问题要糟糕得多.

那么,如果我有多个将这个类作为属性的视图,会怎样呢?假设我有这ModelDetailsView分,但我也有SecondaryDetailsView分.这两种观点只有一个论点,两种观点都是ModelClass分.现在,我应该如何写.navigationDestination修饰语来区分这两个呢?我能做到

.navigationDestination(for: ModelClass.self) { model in
    if someCondition {
        ModelDetailsView(model: model)
    } else {
        SecondaryDetailsView(model: model)
    }
}

但是,这someCondition必须在ContentView中,这既不实际,也不是一个好的做法.

有些地方(我找不到来源)建议导航时使用String作为标识符.所以导航器会有一个功能

func show<V>(_ viewType: V.Type) where V: View {
    path.append(String(describing: viewType.self))
}

这可以用来像这样

navigator.show(ModelDetailsView.self)
// or
navigator.show(SecondaryDetailsView.self)

这种方法的问题是不能将参数传递给视图.

.navigationDestination(for: String.self) { id in
    if id == String(describing: ModelDetailsView.self) {
        ModelDetailsView(model: ) // how do I get the model for the view?
    }
}

所以我想知道有没有人有一个好的解决方案,可以用在更大的项目中而不会变得太难看.

推荐答案

正如Benzy Neez在 comments 中建议的那样,使用带有关联值的枚举作为导航路径的类型是合适的.每个 case 的名称可以表示视图的类型(无论是details还是secondaryDetails),相关的值可以存储您想要传递给该视图的模型.

enum Destination: Hashable {
    case details(of: ModelClass) // assuming ModelClass is Hashable
    case secondaryDetails(of: ModelClass)
}

然后,navigate方法可以接受Destination:

func navigate(to destination: Destination) {
    path.append(destination)
}

navigate(to: .details(of: someModel))这样的用法将是非常可读性的.

navigationDestination(for:destination:)修饰符可以判断它是哪种大小写的枚举:

.navigationDestination(for: Destination.self) { dest in
    switch dest {
    case let .details(of: model):
        ModelDetailsView(model: model)
    case let .secondaryDetails(of: model):
        SecondaryDetailsView(model: model)
    }
}

Swift相关问答推荐

解析SON数据的API调用有什么问题?

@ MainActor + Combine不一致的编译器错误

MyApp类型不符合协议App''''

动画过程中的SwiftUI重绘视图

RealityKit如何获得两个相对大小相同的ModelEntity?

RealityKit(VisionOS)中的PhysicBodyComponent和DragGesture

在Swift中,是否只有iOS版本超过15才能导入Swift包?

文件命名&NumberForMatter+扩展名&:含义?

SwiftUI-如何使用剩余时间制作倒计时计时器

Swift ui 转换无法按预期工作

如何使用Swift宏和@Observable和@Environment?

Swift Property Wrappers-Initialization 失败并显示无法将‘Double’类型的值转换为预期的参数类型

如何延迟 swift 属性 didSet 使其每秒只触发一次

自定义 DispatchQueue 服务质量

swiftui更改显示的 Select 器值

SwiftUI 4 列表初始化程序在 iOS 中不可用

为什么 Swift Tuples 只能在元素个数小于等于 6 的情况下进行比较?

在 Swift 4 中,如何删除基于块的 KVO 观察者?

是 swift 中另一个日期的同一周、月、年的日期

无法推断泛型参数的参数