首页 > 解决方案 > 在 SwiftUI 中传递视图或高阶组件

问题描述

作为一个了解 React 的人,来到 SwiftUI 之后,我在寻找合适的抽象方面遇到了挑战。这是一个例子,但我的问题更笼统。它与传递视图或 React 社区所称的高阶组件有关。我的例子如下。TLDR:如何在下面的列表视图中抽象和删除重复项?

一些模型(这些最终会有所不同):

struct Apple: Comparable, Identifiable {
    let id: UUID = UUID()
    let label: String

    static func < (lhs: Apple, rhs: Apple) -> Bool {
        lhs.label < rhs.label
    }
}

struct Banana: Comparable, Identifiable {
    let id: UUID = UUID()
    let label: String

    static func < (lhs: Banana, rhs: Banana) -> Bool {
        lhs.label < rhs.label
    }
}

一些基本的细节视图(最终会有所不同):

struct AppleView: View {
    let apple: Apple

    var body: some View {
        Text(apple.label)
    }
}

struct BananaView: View {
    let banana: Banana

    var body: some View {
        Text(banana.label)
    }
}

还有两个有很多重复的列表视图:

struct AppleListView: View {
    let title: String
    let apples: [Apple]

    var body: some View {
        List(apples.sorted()) { apple in
            NavigationLink(destination: AppleView(apple: apple)) {
                Text(apple.label)
                    .padding(.all)
            }
        }
        .navigationBarTitle(Text(title), displayMode: .inline)
    }
}

struct BananaListView: View {
    let title: String
    let bananas: [Banana]

    var body: some View {
        List(bananas.sorted()) { banana in
            NavigationLink(destination: BananaView(banana: banana))
                Text(banana.label)
                    .padding(.all)
            }
        }
        .navigationBarTitle(Text(title), displayMode: .inline)
    }
}

如您所见,它仅在很小的部分上有所不同。集合的类型和destination视图不同。destination当涉及到这个视图时,我想保持灵活性,因为AppleBanana以及它们上面的详细视图最终会有所不同。此外,我可能想Cherry稍后再添加,所以抽象这个列表视图是有价值的。

所以,我的问题是:我怎样才能最好地抽象上面的列表视图并删除其中的重复项?你有什么建议?我的尝试如下,但它给我留下了类型错误。它涉及前面提到的高阶组件思想。

我对类型错误的尝试:

struct AppleListView: View {
    let title: String
    let apples: [Apple]

    var body: some View {
        ListView(
            title: title,
            rows: apples, // it complains about types here -> `Cannot convert value of type '[Apple]' to expected argument type 'Array<_>'`
            rowView: { apple in Text(apple.label) },
            destinationView: { apple in AppleView(apple: apple) }
        )
    }
}

struct BananaListView: View {
    let title: String
    let bananas: [Banana]

    var body: some View {
        ListView(
            title: title,
            rows: bananas, // it complains about types here -> `Cannot convert value of type '[Banana]' to expected argument type 'Array<_>'`
            rowView: { banana in Text(banana.label) },
            destinationView: { banana in BananaView(banana: banana) }
        )
    }
}

struct ListView<Content: View, Row: Comparable & Identifiable>: View {
    let title: String
    let rows: [Row]
    let rowView: (Row) -> Content
    let destinationView: (Row) -> Content

    var body: some View {
        List(rows.sorted()) { row in
            NavigationLink(destination: self.destinationView(row)) {
                self.rowView(row)
                    .padding(.all)
            }
        }
        .navigationBarTitle(Text(title), displayMode: .inline)
    }
}

标签: iosswiftreactjsswiftui

解决方案


这是因为你为标签和目的地做了相同的类型,这里是固定的变体

struct ListView<Target: View, Label: View, Row: Comparable & Identifiable>: View {
    let title: String
    let rows: [Row]
    let rowView: (Row) -> Label
    let destinationView: (Row) -> Target

    var body: some View {
        List(rows.sorted()) { row in
            NavigationLink(destination: self.destinationView(row)) {
                self.rowView(row)
                    .padding(.all)
            }
        }
        .navigationBarTitle(Text(title), displayMode: .inline)
    }
}

推荐阅读