Ausgabe
Ich versuche, eine wiederverwendbare Komponente zu erstellen, die einen SwiftUI-Picker enthält, der an mehreren Stellen in meiner App mit verschiedenen Typen arbeiten kann. Ich habe ein Pickable-Protokoll erstellt, das Hashable entspricht, aber wenn ich versuche, es zu verwenden, beschweren sich Picker und ForEach, dass Type ‘any Pickable’ nicht ‘Hashable’ entsprechen kann.
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 }
}
Gibt es eine Möglichkeit, so etwas zum Laufen zu bringen, ohne einen konkreten Typ anzugeben?
Lösung
Wenn man darüber nachdenkt, macht es Sinn.
Was würden Sie erwarten, wenn dieser Code gültig wäre und Sie ihn so verwenden würden?
struct ContentView: View {
let options = [PickableA(), PickableB()]
@State var selection = PickableC()
var body: some View {
PickerRow(title: "Choose one", options: options, selection: $selection)
}
}
Das kann unmöglich funktionieren, oder?
Was Sie brauchen, ist eine Möglichkeit, um sicherzustellen, dass es eine Einschränkung gibt, die zwingt , options
und selection
vom gleichen konkreten Typ zu sein (denken Sie Equatable
beispielsweise daran, dass sowohl String
als Int
auch konform sind, aber Sie können sie nicht vergleichen).
Eine mögliche Lösung wäre eine generische Einschränkung in der Deklaration Ihrer Struktur (beachten Sie auch das @Binding
statt, @State
da wir externe Werte ändern):
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)
}
}
}
die du so verwenden könntest:
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)
}
}
Beantwortet von – Alladinian
Antwort geprüft von – Clifford M. (FixError Volunteer)