The picker doesn't update the number of segments it has when a value is changed during execution

Viewed 44

I have recently started learning swiftUI and I'm facing some issues here. This is the code:

struct ContentView: View {
    @State private var measurementType = 0
    @State private var inputValue = ""
    @State private var inputUnit = 0
    @State private var outputUnit = 1

    var outputValue = ""
    let measurementTypes = ["Temp", "Length", "Time", "Volume"]
    
    
    var typeDictionary = [
        ["Celsius", "Fahrenheit", "Kelvin"],
        ["Meters", "Kilometers", "Feet", "Yards", "Miles"],
        ["Seconds", "Minutes", "Hours", "Days"],
        ["Milliliters", "Liters", "Cups", "Pints", "Gallons"]
    ]

    var body: some View {
        NavigationView {
            Form {
                Section(header: Text("Choose the type of measurement")) {
                    Picker("The type of measurement", selection: $measurementType) {
                        ForEach(0 ..< measurementTypes.count) {
                            Text("\(measurementTypes[$0])")
                        }
                    }
                    .id(measurementType)
                    .pickerStyle(SegmentedPickerStyle())
                }
                
                Section {
                    TextField("Enter the value", text: $inputValue)
                        .keyboardType(.decimalPad)
                }
                
                Section(header: Text("Choose the input unit")) {
                    Picker("The input unit", selection: self.$inputUnit) {
                        ForEach(0 ..< typeDictionary[measurementType].count) {
                            Text("\(typeDictionary[measurementType][$0])")
                        }
                    }
                    .pickerStyle(SegmentedPickerStyle())
                }
                
                Section(header: Text("Choose the output unit")) {
                    
                }
                
                Section(header: Text("Converted Value")) {
                    Text("")
                }
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Whenever I change the measurementType while the code is running, the values of the picker in the input units section update, but the number of segments don't. If I start with the value of measurementType as 1, then the app simply crashes when I choose Temp or Time from the picke.

1 Answers

When using ForEach, you need to provide an id so SwiftUI can uniquely identify each element in the array and know when to redraw the view. You can use id: \.self to use the item's value as its id.

Here's an updated working version:

import SwiftUI

struct ContentView: View {
    @State private var measurementType = 0
    @State private var inputValue = ""
    @State private var inputUnit = 0
    @State private var outputUnit = 1

    var outputValue = ""
    let measurementTypes = ["Temp", "Length", "Time", "Volume"]
    
    
    var typeDictionary = [
        ["Celsius", "Fahrenheit", "Kelvin"],
        ["Meters", "Kilometers", "Feet", "Yards", "Miles"],
        ["Seconds", "Minutes", "Hours", "Days"],
        ["Milliliters", "Liters", "Cups", "Pints", "Gallons"]
    ]

    var body: some View {
        NavigationView {
            Form {
                Section(header: Text("Choose the type of measurement")) {
                    Picker("The type of measurement", selection: $measurementType) {
                        ForEach(0 ..< measurementTypes.count, id: \.self) {
                            Text("\(measurementTypes[$0])")
                        }
                    }
                    .id(measurementType)
                    .pickerStyle(SegmentedPickerStyle())
                }
                
                Section {
                    TextField("Enter the value", text: $inputValue)
                        .keyboardType(.decimalPad)
                }
                
                Section(header: Text("Choose the input unit")) {
                    Picker("The input unit", selection: self.$inputUnit) {
                        ForEach(0 ..< typeDictionary[measurementType].count, id: \.self) {
                            Text("\(typeDictionary[measurementType][$0])")
                        }
                    }
                    .pickerStyle(SegmentedPickerStyle())
                }
                
                Section(header: Text("Choose the output unit")) {
                    
                }
                
                Section(header: Text("Converted Value")) {
                    Text("")
                }
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
Related