10

According to Apple's documentation regarding Picker in SwiftUI using an Enum, if the enum conforms to the Identifiable protocol in addition to CaseIterable, a picker iterating over all cases should update the bound variable natively.

I tested it, and it does not work as expected.

enum Flavor: String, CaseIterable, Identifiable {
    case chocolate
    case vanilla
    case strawberry

    var id: String { self.rawValue }
}

struct EnumView: View {
    @State private var selectedFlavor = Flavor.chocolate
    var body: some View {
        VStack {
            Picker("Flavor", selection: $selectedFlavor) {
                ForEach(Flavor.allCases) { flavor in
                    Text(flavor.rawValue.capitalized)//.tag(flavor)
                }
            }
        
            Text("Selected flavor: \(selectedFlavor.rawValue)")
        }
    }
}

enter image description here

However, if I pass a tag for each view, it works.

enter image description here

What's happening here? Is the Apple documentation wrong? The selectedFlavor variable expects a value of type Flavor, but the id used in the picker is actually a String.

Thanks.

pawello2222
  • 46,897
  • 22
  • 145
  • 209
alpennec
  • 1,864
  • 3
  • 18
  • 25

2 Answers2

16

For a Picker to work properly, its elements need to be identified.

Note that the selectedFlavor variable is of type Flavor. Which means the options in the Picker should be identified as Flavors (not Strings).

However, in your code your id is of type String:

var id: String { self.rawValue }

You can either:

  • provide a tag (of type Flavor):
Text(flavor.rawValue.capitalized)
    .tag(flavor)
  • conform Flavor to Identifiable by providing a custom id of type Flavor:
var id: Flavor { self }
  • specify the id parameter (of type Flavor) explicitly in the ForEach:
ForEach(Flavor.allCases, id: \.self) { ... }
  • change the selectedFlavor to be a String:
@State private var selectedFlavor = Flavor.chocolate.rawValue
pawello2222
  • 46,897
  • 22
  • 145
  • 209
  • But when the elements conform to Identifiable, the ForEach should automatically assigns a tag to the selection views in the picker, using each option’s id. I don't understand why the picker does not automatically convert the string id used as the tag to the enum with the rawValue of this id used as a tag. – alpennec Oct 09 '20 at 10:07
  • @alpennec I added a more detailed explanation. I hope it's clearer now. – pawello2222 Oct 09 '20 at 12:41
  • thanks for the explanation. It's good to have all the possible options listed. If I understand well, when the items passed to the ForEach loop conform to Identifiable (i.e. we don't need to tell the id to use), the generated tags for each view in the ForEach loop is the id? So if we have an id of type String for an Enum, then the tag for the view is a String, and when the picker selects an item, it selects a String whereas the expected value for the selection is an Enum. Is that correct? – alpennec Oct 09 '20 at 14:40
  • @alpennec Yes, that's right. Basically the issue was about tagging values with Strings and expecting a Flavor as the selected item. – pawello2222 Oct 09 '20 at 14:42
4

This is an addition to @pawello2222 already awesome answer.

Just to clarify that the solution to conform the enum to Identifiable using the below code works to correctly tag the UI picker without explicitly declaring it within the picker code:

var id: Flavor { self }

The official Apple documentation for the swiftUI picker says to id the enum using var id: String { self.rawValue } and does not work as expected using the rest of the example code.

araMara
  • 223
  • 1
  • 8