'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)
                    }
                }
            }
        }
    }
}

enter image description here

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)
                        }
                    }
                }
            }
        }
    }
}

enter image description here

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)
                }
            }
        }
    }
}

Example of using defaultWheelPickerItemHeight

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