SwiftUI泛型

hivapdat  于 2023-05-21  发布在  Swift
关注(0)|答案(2)|浏览(144)

如何将这两个视图组合成一个PickerFromOptions视图,该视图可以接受enum选项或`[String]选项参数?
我意识到我可以把它们作为两个单独的视图,并根据我想要传递的内容调用正确的视图,然而,我试图更好地理解泛型,所以我想看看我如何才能只创建一个视图来处理这两种情况。到目前为止,我一直很难将这些组合成一个视图。
我还意识到我可以将枚举Map到一个字符串数组来传递,但随后将选择存储回数据模型将不得不经历更多的“环”。

struct PickerFromEnumOptions<SelectionType, OptionType>: View where SelectionType: Hashable, OptionType: Identifiable & RawRepresentable & Hashable   {
    var label: String
    var options: [OptionType]
    @Binding var selection: SelectionType
    
    var body: some View {
        Picker(label, selection:$selection) {
            ForEach(options) { option in
                Text("\(option.rawValue as! String)")
                    .tag(option)
            }
        }
    }
}

struct PickerFromStringArrayOptions<SelectionType, OptionType>: View where SelectionType: Hashable, OptionType: StringProtocol {
    var label: String
    var options: [OptionType]
    @Binding var selection: SelectionType
    
    var body: some View {
        Picker(label, selection:$selection) {
            ForEach(options, id:\.self) { option in
                Text("\(option as! String)")
                    .tag(option)
            }
        }
    }
}

我希望能够使用Enum来调用它,如下所示:

enum enumOptions: String, CaseIterable, Identifiable {
        case one, two, three
        
        var id: String { self.rawValue }
    }

@State var enumSelection: enumOptions
    PickerFromOptions(label: "enumOptions", options: enumOptions.allCases, selection: $enumOptionSelection)

或者像这样的字符串选项数组:

let stringOptions = ["one", "two", "three"]
    
    @State var stringSelection: String
    PickerFromOptions(label: "stringOptions", options: stringOptions, selection: $stringSelection)

我的第一个想法是让OptionType符合Hashable,但后来似乎我必须进行运行时类型检查,以确定如何获取id和字符串值。

tzxcd3kk

tzxcd3kk1#

为了做到这一点,我倾向于先对泛型的用途做一些修改,首先我认为最好是为实际的集合使用泛型类型,而不是集合的元素。
使用ForEach的要求是集合符合RandomAccessCollection,因此泛型类型将是

<OptionsType: RandomAccessCollection>

由于我们现在使用的是一个集合,所以不需要第二个泛型类型,因为它将是RandomAccessCollection.Element

struct PickerFromOptions<OptionsType: RandomAccessCollection>: View

我们希望Element类型符合Hashable,这里我也让它符合CustomStringConvertible,这样我们就可以在Picker中显示一些内容

where OptionsType.Element: Hashable, OptionsType.Element: CustomStringConvertible

我也倾向于让selection属性是可选的,这样我们就可以不选择任何东西。
所以通用视图就变成了

struct PickerFromOptions<OptionsType: RandomAccessCollection>: View where OptionsType.Element: Hashable,
                                                                            OptionsType.Element: CustomStringConvertible  {
    var label: String
    var options: OptionsType
    @Binding var selection: OptionsType.Element?

    var body: some View {
        Picker(label, selection:$selection) {
            ForEach(options, id: \.self) { option in
                Text(option.description)
                    .tag(Optional(option))
            }
        }
    }
}

用法示例

@State var enumSelection: enumOptions?
@State var stringSelection: String?

var body: some View {
    VStack {
        PickerFromOptions(label: "enumOptions", options: enumOptions.allCases, selection: $enumSelection)
        PickerFromOptions(label: "stringOptions", options: stringOptions, selection: $stringSelection)
    }
    .padding()
}

如果您不想使用CustomStringConvertible,一个替代的、也许更好的解决方案是引入一个元素需要遵循的协议

protocol OptionsElement: Hashable {
    var name: String { get }
}

然后我们稍微改变一下实现方式

struct PickerFromOptionsCustom<OptionsType: RandomAccessCollection>: View where OptionsType.Element: OptionsElement  {
    var label: String
    var options: OptionsType
    @Binding var selection: OptionsType.Element?

    var body: some View {
        Picker(label, selection:$selection) {
            ForEach(options, id: \.self) { option in
                Text(option.name)
                    .tag(option)
            }
        }
    }
}
cdmah0mi

cdmah0mi2#

EnumString相同时,我会选择Enum,如下所示:

enum Numbers: String, CaseIterable {
    case one, two, three
}

然后像这样使用它:

@State var selection: Numbers

Picker("Picker", selection: $selection) {
    ForEach(Numbers.allCases, id: \.self) { number in
        Text(number.rawValue)
    }
}

这为您提供了不丢失字符的安全性和所有可能选项的概述。

相关问题