How to fix Picker not showing selection in SwiftUI

Issue #716

I have an enum that conforms to CaseIterable that I want to show in Picker

1
2
3
4
5
6
7
8
9
10
11
12
13
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

1
2
3
4
5
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

1
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 idfor ForEach

What we can also do is to declare enum case itself as id

1
2
3
4
5
6
7
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

Updated at 2020-12-23 06:05:08

Comments