Search code examples
iosswiftswiftuiios13picker

SwiftUI : Picker does not update correctly when changing datasource


I have just started learning SwiftUI and got stuck somewhere!

I am trying to change segment styled picker datasource when changing value of another segment. But somehow it is not working as expected! Or else I might have coded something wrong. Can anyone figure it out please?

Here is my piece of code:

import SwiftUI

struct ContentView: View {    

@State var selectedType = 0
@State var inputUnit = 0
@State var outputUnit = 1

let arrTypes = ["Temperature", "Length"]

var arrData: [String] {
    switch self.selectedType {
    case 0:
        return ["Celsius", "Fahrenheit", "Kelvin"] //Temperature
    case 1:
        return ["meters", "kilometers", "feet", "yards", "miles"] //Length        
    default:
        return ["Celsius", "Fahrenheit", "Kelvin"]
    }        
}


var body: some View {
    NavigationView{
        Form
        {
            Section(header: Text("Choose type"))
            {
                Picker("Convert", selection: $selectedType) {
                    ForEach(0 ..< 2, id: \.self)
                    { i in
                        Text(self.arrTypes[i])
                    }
                }
                .pickerStyle(SegmentedPickerStyle())
            }

            Section(header: Text("From"))
            {
                Picker("", selection: $inputUnit) {
                    ForEach(0 ..< arrData.count, id: \.self)
                    {
                        Text(self.arrData[$0])
                    }
                }
                .pickerStyle(SegmentedPickerStyle())                    
            }

            Section(header: Text("To"))
            {
                Picker("", selection: $outputUnit) {
                    ForEach(0 ..< arrData.count, id: \.self)
                    {
                        Text(self.arrData[$0])
                    }
                }
                .pickerStyle(SegmentedPickerStyle())
            }                

        }
    }
}
}

When I change segment from Length back to Temperature it merges the array somehow. I tried to debug and print the arrData count in log, then it prints correct result but not updating the UI!

First segment selected by default: enter image description here

Change segment:

enter image description here

Change segment back to first:

enter image description here

Any help or suggestion would greatly be appreciated.


Solution

  • Nick Polychronakis solved it in this fork: https://github.com/nickpolychronakis/100DaysOfSwiftUI/tree/master/UnitCoverter

    The solution is to add .id(:identifier:) to your picker so it is unique.

    Observable var:

    @State var unit = 0
    

    Main picker:

    Picker("Length", selection: $unit) {
                        ForEach(0 ..< inputUnitTypes.count) {
                            Text("\(self.inputUnitTypes[$0].description)")
                        }
                    }
                    .pickerStyle(SegmentedPickerStyle())
    

    One of secondary pickers which content is determined by the unit variable.

    Picker("Length", selection: $inputUnit) {
                            ForEach(0 ..< selected.count) {
                                Text("\(self.selected[$0].description)")
                            }
                        }
                        .id(unit)