SwiftUI单位转换APP:切换首个Picker后无法更新后续Picker状态求助
单位转换应用Picker状态更新问题解决
问题描述
开发单位转换应用时,第一个Picker选择转换类型(温度、长度、体积),以此控制第二、第三个Picker的单位选项。但切换第一个Picker时,无法同步更新第二、第三个Picker的@State选中值,导致转换公式无法正常切换。
问题原因
使用.onSubmit触发状态重置的时机不准确,且当转换类型切换后,原选中的单位字符串可能与新Picker的选项不匹配,导致UI选中状态异常。
解决方案
- 替换状态监听方式:用
.onChange(of:)监听selectionOfTypeOfConversion的变化,确保每次转换类型切换都能触发单位选中状态重置 - 同步默认单位与Picker选项:重置时设置的默认单位必须属于当前转换类型对应的枚举,保证与Picker的tag值完全匹配
- 完善转换计算逻辑:在
output属性中根据当前转换类型和选中单位执行对应计算
修改后的完整代码
struct ContentView: View { @State private var userInput = 0.0 @State var selectionOfTypeOfConversion: Conversions = .temperature @State var selectionFromConversion: String = TemperatureConversions.celsius.rawValue @State var selectionToConversion: String = TemperatureConversions.celsius.rawValue @FocusState private var inputIsFocused: Bool var output: Double { // 根据当前转换类型和单位执行对应计算 switch selectionOfTypeOfConversion { case .temperature: guard let fromUnit = TemperatureConversions(rawValue: selectionFromConversion), let toUnit = TemperatureConversions(rawValue: selectionToConversion) else { return 0.0 } // 温度转换逻辑示例 switch (fromUnit, toUnit) { case (.celsius, .fahrenheit): return userInput * 9/5 + 32 case (.celsius, .kelvin): return userInput + 273.15 case (.fahrenheit, .celsius): return (userInput - 32) * 5/9 case (.fahrenheit, .kelvin): return (userInput - 32) * 5/9 + 273.15 case (.kelvin, .celsius): return userInput - 273.15 case (.kelvin, .fahrenheit): return (userInput - 273.15) * 9/5 + 32 default: return userInput } case .length: guard let fromUnit = LengthConversions(rawValue: selectionFromConversion), let toUnit = LengthConversions(rawValue: selectionToConversion) else { return 0.0 } // 长度转换逻辑示例 let metersValue: Double switch fromUnit { case .meters: metersValue = userInput case .feet: metersValue = userInput * 0.3048 case .miles: metersValue = userInput * 1609.34 } switch toUnit { case .meters: return metersValue case .feet: return metersValue / 0.3048 case .miles: return metersValue / 1609.34 } case .volume: guard let fromUnit = VolumeConversions(rawValue: selectionFromConversion), let toUnit = VolumeConversions(rawValue: selectionToConversion) else { return 0.0 } // 体积转换逻辑示例 let mlValue: Double switch fromUnit { case .milliliters: mlValue = userInput case .pints: mlValue = userInput * 473.176 case .gallons: mlValue = userInput * 3785.41 } switch toUnit { case .milliliters: return mlValue case .pints: return mlValue / 473.176 case .gallons: return mlValue / 3785.41 } } } var body: some View { VStack { NavigationView { Form { Section { Picker("Type of conversion", selection: $selectionOfTypeOfConversion) { ForEach(Conversions.allCases, id: \.self) { Text($0.rawValue) } } // 监听转换类型变化,重置单位选中状态 .onChange(of: selectionOfTypeOfConversion) { _ in checkConvention() } TextField("", value: $userInput, format: .number) .keyboardType(.decimalPad) .focused($inputIsFocused) Picker("From conversion", selection: $selectionFromConversion) { switch selectionOfTypeOfConversion { case .temperature: ForEach(TemperatureConversions.allCases, id: \.self) { Text($0.rawValue).tag($0.rawValue) } case .length: ForEach(LengthConversions.allCases, id: \.self) { Text($0.rawValue).tag($0.rawValue) } case .volume: ForEach(VolumeConversions.allCases, id: \.self) { Text($0.rawValue).tag($0.rawValue) } } } } header: { Text("From") } Section { Picker("To conversion", selection: $selectionToConversion) { switch selectionOfTypeOfConversion { case .temperature: ForEach(TemperatureConversions.allCases, id: \.self) { Text($0.rawValue).tag($0.rawValue) } case .length: ForEach(LengthConversions.allCases, id: \.self) { Text($0.rawValue).tag($0.rawValue) } case .volume: ForEach(VolumeConversions.allCases, id: \.self) { Text($0.rawValue).tag($0.rawValue) } } } Text(output, format: .number) } header: { Text("To") } } .toolbar { ToolbarItemGroup(placement: .keyboard) { Spacer() Button("Done") { inputIsFocused = false } } } } } } private func checkConvention() { switch selectionOfTypeOfConversion { case .temperature: selectionFromConversion = TemperatureConversions.celsius.rawValue selectionToConversion = TemperatureConversions.celsius.rawValue case .length: selectionFromConversion = LengthConversions.meters.rawValue selectionToConversion = LengthConversions.meters.rawValue case .volume: selectionFromConversion = VolumeConversions.milliliters.rawValue selectionToConversion = VolumeConversions.milliliters.rawValue } } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } } enum Conversions: String, CaseIterable { case temperature = "temperature" case length = "length" case volume = "volume" } enum TemperatureConversions: String, CaseIterable { var id: Self { self } case celsius = "celsius" case fahrenheit = "fahrenheit" case kelvin = "kelvin" } enum LengthConversions: String, CaseIterable { var id: Self { self } case meters = "meters" case feet = "feet" case miles = "miles" } enum VolumeConversions: String, CaseIterable { var id: Self { self } case milliliters = "ml" case pints = "pints" case gallons = "gallons" }
关键修改点
- 将原Picker的
.onSubmit替换为.onChange(of: selectionOfTypeOfConversion),确保转换类型切换时立即触发单位重置 - 完善
output属性,添加了完整的温度、长度、体积转换逻辑,实现类型切换时自动切换计算公式 - 保持
checkConvention()的逻辑,确保切换类型后单位Picker默认选中对应类型的基础单位
内容的提问来源于stack exchange,提问作者Anstasia Kotova
相关产品推荐
相关产品推荐

