【问题标题】:SwiftUI Picker in reusable component with protocol cannot conform to Hashable具有协议的可重用组件中的 SwiftUI Picker 不能符合 Hashable
【发布时间】:2022-11-20 05:55:22
【问题描述】:

我正在尝试构建一个可重用组件,其中包含一个 SwiftUI Picker,它可以在我的应用程序的多个位置使用不同类型。我创建了一个符合 Hashable 的 Pickable 协议,但是当我尝试使用它时,Picker 和 ForEach 抱怨 Type 'any Pickable' cannot conform to 'Hashable'

import SwiftUI

struct PickerRow: View {
    let title: String
    let options: [any Pickable]
    @State var selection: any Pickable
    
    var body: some View {
        HStack {
            Spacer()
            Text(title)
                .font(.subHeading)
            Picker(title, selection: $selection, content: {
                ForEach(options, id: \.self) {
                    Text($0.name)
                }
            }).pickerStyle(.menu)
        }
    }
}

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

有没有办法在不指定具体类型的情况下让这样的东西工作?

【问题讨论】:

  • SwiftUI 包装器/视图需要具体类型

标签: ios swift xcode swiftui swift-protocols


【解决方案1】:

如果你考虑一下,这是有道理的。

如果该代码有效并且您这样使用它,您希望发生什么?

struct ContentView: View {
    let options = [PickableA(), PickableB()]
    @State var selection = PickableC()
    var body: some View {
        PickerRow(title: "Choose one", options: options, selection: $selection)
    }
}

那不可能行得通吧?

您需要的是一种方法来确保存在强制 options 和 selection 具有相同具体类型的约束(例如,考虑 Equatable,String 和 Int 都符合要求,但是你无法比较它们)。

一种可能的解决方案是在结构声明中使用通用约束(另请注意 @Binding 而不是 @State,因为我们修改了外部值):

struct PickerRow<Option: Pickable>: View {
    let title: String
    let options: [Option]
    @Binding var selection: Option
    
    var body: some View {
        HStack {
            Spacer()
            Text(title)
                .font(.subheadline)
            Picker(title, selection: $selection) {
                ForEach(options, id: .self) {
                    Text($0.name)
                }
            }.pickerStyle(.menu)
        }
    }
}

你可以这样使用:

struct Person: Pickable {
    let name: String
}

struct ContentView: View {
    let options = [Person(name: "Bob"), Person(name: "Alice")]
    @State var selection = Person(name: "Bob")
    var body: some View {
        PickerRow(title: "Choose one", options: options, selection: $selection)
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-07-11
    • 1970-01-01
    • 1970-01-01
    • 2021-02-04
    • 1970-01-01
    • 1970-01-01
    • 2019-11-23
    • 1970-01-01
    相关资源
    最近更新 更多