You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI单位转换APP:切换首个Picker后无法更新后续Picker状态求助

单位转换应用Picker状态更新问题解决

问题描述

开发单位转换应用时,第一个Picker选择转换类型(温度、长度、体积),以此控制第二、第三个Picker的单位选项。但切换第一个Picker时,无法同步更新第二、第三个Picker的@State选中值,导致转换公式无法正常切换。

问题原因

使用.onSubmit触发状态重置的时机不准确,且当转换类型切换后,原选中的单位字符串可能与新Picker的选项不匹配,导致UI选中状态异常。

解决方案

  1. 替换状态监听方式:用.onChange(of:)监听selectionOfTypeOfConversion的变化,确保每次转换类型切换都能触发单位选中状态重置
  2. 同步默认单位与Picker选项:重置时设置的默认单位必须属于当前转换类型对应的枚举,保证与Picker的tag值完全匹配
  3. 完善转换计算逻辑:在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 20:40:47