繁体   English   中英

SwiftUI MVVM 协调器/路由器/导航链接

[英]SwiftUI MVVM Coordinator/Router/NavigationLink

我在将 UIKit 架构模式转换为 SwiftUI 时遇到问题。 我目前的模式主要是带有协调器/路由器的 MVVM。 添加@ObservableObject/@Published 后,MVVM 部分看起来非常简单自然。 但是协调/路由似乎不直观。 视图和协调(导航)功能在 SwiftUI 中紧密耦合。 似乎真的不可能将它们与使用辅助结构AnyView分开。

这里有一个例子:我想在 SwiftUI 中创建一个可重复使用的行/单元格。 假设生产中的这一行非常复杂,因此我想重用它。 我也想把它放在另一个模块中,这样我就可以在多个目标中重用它。 (如 iOS、macCatalyst 等...)

在此处输入图像描述

现在我想控制当用户点击该视图或该视图中的按钮时会发生什么。 根据上下文,我需要导航到不同的目的地。 据我所知,可能的 NavigationLink 目标必须硬连线到视图中,或者必须将AnyView传递到视图中。

这里有一些示例代码。 此单元格/行包含两个按钮。 我想导航到依赖于上下文而不是硬连线到代码中的其他视图:

struct ProductFamilyRow: View {
    @State private var selection: Int? = 0
    let item: ProductFamilyItem

    let destinationView1: AnyView
    let destinationView2: AnyView

    var body: some View {
        VStack {
            NavigationLink(
                destination: destinationView1,
                tag: 1,
                selection: self.$selection
            ) {
                EmptyView()
            }

            NavigationLink(
                destination: destinationView2,
                tag: 2,
                selection: self.$selection
            ) {
                EmptyView()
            }

            HStack {
                Text(item.title)
                Button("Destination 1") {
                    self.selection = 1
                }.foregroundColor(Color.blue)

                Button("Destination 2") {
                    self.selection = 2
                }.foregroundColor(Color.blue)
            }

            //Image(item.image)
        }.buttonStyle(PlainButtonStyle())
    }
}

这似乎是 SwiftUI 中的一个主要设计缺陷。 除了使用AnyView hack 之外,基本上不可能使用带有导航链接的可重用组件。 据我所知, AnyView仅用于需要类型擦除并且具有相当多的性能缺陷的特定用例。 所以我不认为这是使用 SwiftUI 创建可重用、可导航视图的惯用解决方案。

这真的是唯一的解决方案吗? 也许我完全错了,这无论如何都是错误的方向。 我在某处读到(再也找不到帖子了..)关于使用一些中央 state 指示要显示哪个视图,但我没有看到如何执行此操作的具体示例。

第二个挑战:我也不希望单元格对任何其他点击做出反应,然后对按钮做出反应。 但是,如果点击,似乎无法控制单元格导航到的位置。 (所以不要点击其中一个按钮,而是点击单元格中的任何位置)在当前示例代码中,它(出于任何原因)导航到“Destination 2”。

提前致谢。

最好使用 generics 作为您的行,如下所示(使用 Xcode 11.4 测试)

使用示例:

ProductFamilyRow(item: ProductFamilyItem(title: "Test"),
    destinationView1: { Text("Details1") },
    destinationView2: { Text("Details2") })

界面:

更新- 为行高亮添加块。 列表对行内的按钮或链接具有自动检测功能,如果存在任何标准(.key),则突出显示,所以。 要禁用这种行为,它需要隐藏自定义按钮样式下的所有内容。

struct ProductFamilyRowStyle: ButtonStyle {

    func makeBody(configuration: Self.Configuration) -> some View {
        configuration.label
            .colorMultiply(configuration.isPressed ? 
                 Color.white.opacity(0.5) : Color.white) // any effect you want
    }
}

struct ProductFamilyRow<D1: View, D2: View>: View {
    let item: ProductFamilyItem
    let destinationView1: () -> D1
    let destinationView2: () -> D2

    init(item: ProductFamilyItem, @ViewBuilder destinationView1: @escaping () -> D1,
        @ViewBuilder destinationView2: @escaping () -> D2)
    {
        self.item = item
        self.destinationView1 = destinationView1
        self.destinationView2 = destinationView2
    }

    @State private var selection: Int? = 0

    var body: some View {
        VStack {
            HStack {
                Text(item.title)
                Button(action: {
                    self.selection = 1
                }) {
                    Text("Destination 1")
                        .background( // hide link inside button !!
                            NavigationLink(destination: destinationView1(),
                                tag: 1, selection: self.$selection) { EmptyView() }
                        )
                }.foregroundColor(Color.blue)

                Button(action: {
                    self.selection = 2
                }) {
                    Text("Destination 2")
                        .background(
                            NavigationLink(destination: destinationView2(),
                                tag: 2, selection: self.$selection) { EmptyView() }
                        )
                }.foregroundColor(Color.blue)
            }

            //Image(item.image)
        }.frame(maxWidth: .infinity) // to have container centered
        .buttonStyle(ProductFamilyRowStyle())
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM