'NavigationView causes Unexpected behavior with SwiftUI Picker
I have a SwiftUI Picker, and for some reason when I open the app the picker shows the wrong selection in the picker, but has the correct value stored. I can't tell if this is caused by the geometry reader or the navigation view. If anyone has some advice, or can explain the logic for why this is happening it would be great.
Here is a link to the sample if someone wants to test: https://github.com/mazefest/ExampleIssueProject1
Below is an image of what is happening, the picker contains 7 images of number (1-7). You can select to open the picker on the left, it also stores your last value in the button, as you can see below "three" has been selected, so when opening the picker you would expect to see the image of the 3, but no you get the title for the 3 and the number four.
Below is the code for the Picker:
struct NumberPicker: View {
@State var selection: PickerOption
let onSelection: (PickerOption) -> ()
var body: some View {
GeometryReader { geo in
HStack {
Spacer()
Picker(selection.title, selection: $selection) {
ForEach(PickerOption.allCases) { option in
Image(uiImage: option.image)
.resizable()
.aspectRatio(contentMode: .fit)
}
}
.pickerStyle(.wheel)
.defaultWheelPickerItemHeight(geo.size.height)
.frame(width: geo.size.width * 0.7)
Spacer()
}
.toolbar {
ToolbarItem(placement: .confirmationAction) {
Button("Done") {
onSelection(selection)
}
}
}
}
}
}
What's weird is I was actually able to fix the issue by putting everything in another navigation view. Which I do not want to do because I lose the "done" button. But as you can see below it did indeed work.
Below is the code with the added navigation view:
struct NumberPicker: View {
@State var selection: PickerOption
let onSelection: (PickerOption) -> ()
var body: some View {
NavigationView { // <-- The added navigation view
GeometryReader { geo in
HStack {
Spacer()
Picker(selection.title, selection: $selection) {
ForEach(PickerOption.allCases) { option in
Image(uiImage: option.image)
.resizable()
.aspectRatio(contentMode: .fit)
}
}
.pickerStyle(.wheel)
.defaultWheelPickerItemHeight(geo.size.height)
.frame(width: geo.size.width * 0.7)
Spacer()
}
.toolbar {
ToolbarItem(placement: .confirmationAction) {
Button("Done") {
onSelection(selection)
}
}
}
}
}
}
}
Why is this happening? How can I solve it?
Code for pickerOption:
public enum PickerOption: String, Hashable, CaseIterable, Codable, Identifiable {
case one
case two
case three
case four
case five
case six
case seven
public var id: Self { self }
var title: String {
switch self {
case .one:
return "title for one"
case .two:
return "title for two"
case .three:
return "title for three"
case .four:
return "title for four"
case .five:
return "title for five"
case .six:
return "title for six"
case .seven:
return "title for seven"
}
}
public var image: UIImage {
switch self {
case .one:
return UIImage(named: "one-image")!
case .two:
return UIImage(named: "two-image")!
case .three:
return UIImage(named: "three-image")!
case .four:
return UIImage(named: "four-image")!
case .five:
return UIImage(named: "five-image")!
case .six:
return UIImage(named: "six-image")!
case .seven:
return UIImage(named: "seven-image")!
}
}
}
Solution 1:[1]
I rewrote your code (since I didn't have your images) to just use SFSymbol strings. I stripped away a lot of the other stuff you had like the HStack and the NavigationView and GeometryReader (personally I stay away from GR). This might not be what you're looking for exactly, but maybe this will nudge you in the right direction!
Utilizing the defaultWheelPickerItemHeight gives you control over the height so it works as a nice padding to the images.
struct NumberPicker: View {
@State var selection: PickerOption
let onSelection: (PickerOption) -> ()
var body: some View {
Picker(selection.title, selection: $selection) {
ForEach(PickerOption.allCases) { option in
Image(systemName: option.image)
.font(.largeTitle)
}
}
.pickerStyle(.wheel)
.defaultWheelPickerItemHeight(100)
.toolbar {
ToolbarItem(placement: .confirmationAction) {
Button("Done") {
onSelection(selection)
}
}
}
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | yonodactyl |



