Issue #716
I have an enum that conforms to CaseIterable
that I want to show in Picker
enum Position: String, Codable, CaseIterable, Identifiable {
var id: String { rawValue }
case left
case right
case bottom
case top
}
Picker(selection: $preference.position, label: Text("Position")) {
ForEach(Preference.Position.allCases) { position in
Text(position.rawValue)
}
}
It compiles and runs just fine, but Picker does not show current selection regardless of any Picker style I choose. It does not update Binding
at all.
The fix is to specify id
, it looks redundant because of enum conforms to Identifiable
, but it fixes the problem
Picker(selection: $preference.position, label: Text("Position")) {
ForEach(Preference.Position.allCases, id: \.self) { position in
Text(position.rawValue)
}
}
Mismatch between rawValue and enum case itself
Reading ForEach once again
init(_ data: Data, content: @escaping (Data.Element) -> Content)
Available when Data conforms to RandomAccessCollection, ID is Data.Element.ID, Content conforms to View, and Data.Element conforms to Identifiable.
So in our case, we use rawValue
as id
for Identifiable
, so there’s mismatch between our selection
being enum case
and items in ForEach
, which uses rawValue
to uniquely identifies items. So our fix is to explicitly state that we want to use the enum case itself \.self
as id
for ForEach
What we can also do is to declare enum case itself as id
enum Position: String, Codable, CaseIterable, Identifiable {
var id: Position { self }
case left
case right
case bottom
case top
}
The lesson learned here is we need to ensure the underlying type of selection
in List
and id
used in ForEach
are the same