Home > other >  Use same view to edit different enums with binding in SwiftUI
Use same view to edit different enums with binding in SwiftUI

Time:12-13

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())
    }
}
  • Related