SwiftUI : Picker does not update correctly when changing datasource

非 Y 不嫁゛ 提交于 2019-11-29 15:04:03

问题


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:

Change segment:

Change segment back to first:

Any help or suggestion would greatly be appreciated.


回答1:


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)



回答2:


I'm not sure why SwiftUI behaves like this, seems like a bug to me (Correct me if I'm wrong). All I can suggest is to add separate pickers for temperature and length and hide those based on the current selected type. For code re-usability I've added the picker to another file.

MyCustomPicker

struct MyCustomPicker: View {
    var pickerData: [String]
    @Binding var binding: Int
    var body: some View {
        Picker("Convert", selection: $binding) {
            ForEach(0 ..< pickerData.count, id: \.self)
            { i in
                Text(self.pickerData[i])
            }
        }
        .pickerStyle(SegmentedPickerStyle())
    }
}

ContentView

struct ContentView: View {

    @State var selectedType = 0
    @State var inputTempUnit = 0
    @State var outputTempUnit = 1
    @State var inputLenUnit = 0
    @State var outputLenUnit = 1

    let arrTypes = ["Temperature", "Length"]
    let tempData = ["Celsius", "Fahrenheit", "Kelvin"]
    let lenData  = ["meters", "kilometers", "feet", "yards", "miles"]


    var body: some View {
        NavigationView {
            Form {
                Section(header: Text("Choose type")) {
                    MyCustomPicker(pickerData: arrTypes, binding: $selectedType)
                }

                Section(header: Text("From")) {
                    if selectedType == 0 {
                        MyCustomPicker(pickerData: tempData, binding: $inputTempUnit)
                    } else {
                        MyCustomPicker(pickerData: lenData, binding: $inputLenUnit)
                    }
                }

                Section(header: Text("To")) {
                    if selectedType == 0 {
                        MyCustomPicker(pickerData: tempData, binding: $outputTempUnit)
                    } else {
                        MyCustomPicker(pickerData: lenData, binding: $outputLenUnit)
                    }
                }
            }
        }
    }
}

Note: You have to use different state variables to keep track the temperature and length selection.




回答3:


Combining the two earlier answers:

ContentView

    ...

    var units: [String] {
        symbols[unitType]
    }

    ...

            Section(header: Text("Unit Type")) {
                UnitPicker(units: unitTypes, unit: $unitType)
            }

            Section(header: Text("From Unit")) {
                UnitPicker(units: units, unit: $inputUnit)
                    .id(unitType)
            }

            Section(header: Text("To Unit")) {
                UnitPicker(units: units, unit: $outputUnit)
                    .id(unitType)
            }

    ...

UnitPicker

struct UnitPicker: View {
    var units: [String]

    @Binding var unit: Int

    var body: some View {
        Picker("", selection: $unit) {
            ForEach(units.indices, id: \.self) { index in
                Text(self.units[index]).tag(index)
            }
        }
        .pickerStyle(SegmentedPickerStyle())
        .font(.largeTitle)
    }
}

See https://github.com/hugofalkman/UnitConverter.git



来源:https://stackoverflow.com/questions/58352798/swiftui-picker-does-not-update-correctly-when-changing-datasource

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!