I want to use only 1 view to manage 2 pickers in SwiftUI. The data that are binded are enums:
enum ReferenceStockStatus: String, CustomStringConvertible, CaseIterable {
case all
case stock
case notInStock
var description : String {
switch self {
case .all: return "All"
case .stock: return "In stock"
case .notInStock: return "Not in stock"
}
}
}
enum ReferenceGoneStatus: String, CustomStringConvertible, CaseIterable {
case all
case takenOut
case neverGone
var description : String {
switch self {
case .all: return "All"
case .takenOut: return "Taken out"
case .neverGone: return "Never gone"
}
}
}
The parent view:
struct FiltersContextReferenceView: View {
@ObservedObject var finderViewModel: FinderViewModel
var body: some View {
PickerSegmented(finderViewModel: finderViewModel, selection: $finderViewModel.referenceStockStatus, cases: ReferenceStockStatus.allCases, change: finderViewModel.referenceStockStatus)
PickerSegmented(finderViewModel: finderViewModel, selection: $finderViewModel.referenceGoneStatus, cases: ReferenceGoneStatus.allCases, change: finderViewModel.referenceGoneStatus)
}
}
The child view that receives the binding:
struct PickerSegmented: View {
@ObservedObject var finderViewModel: FinderViewModel
@Binding var selection: String // => all enums are string, so I want to use the string type ?!
var cases: Array<String>
var change: String
var body: some View {
Picker("", selection: $selection) {
ForEach(cases, id: \.self) { option in
Text(option.rawValue)
}
}
.onChange(of: change) { _ in
// do something
}
.pickerStyle(SegmentedPickerStyle())
}
}
So, I logically get this error:
Cannot convert value of type 'Binding<ReferenceStockStatus>' to expected argument type 'Binding<String>'
How can I solve the enum type issue ? Using generics ? More generally how to send different data types to edit in the same view ? Here it's a picker, but it could be a List()...
CodePudding user response:
Here is a simple breakdown of how generics would work.
To start you need to "require" conformance to the protocols the Picker
needs.
CustomStringConvertible & CaseIterable & Hashable
Then just use the available variables
struct PickerSegmented<P>: View where P: CustomStringConvertible & CaseIterable & Hashable{
//tag and selection must match types exactly
typealias TagType = P
//Generic type
@Binding var selection: TagType
var body: some View {
Picker("", selection: $selection) {
//Use custom Case Iterable and Hashable
ForEach(Array(P.allCases), id: \.description) { option in
//Use Custom String Convertible
Text(option.description)
//tag and selection must match types exactly
.tag(option as TagType)
}
}
.pickerStyle(SegmentedPickerStyle())
}
}