SwiftUI中相互依赖的TextField(双向数据绑定)

p8ekf7hl  于 2023-03-17  发布在  Swift
关注(0)|答案(1)|浏览(183)

尝试使用SwiftUI编写温度转换器。
我希望通过在摄氏度输入中键入温度,华氏度立即反映转换后的温度,反之亦然。

我尝试使用onChange,但这会创建一个循环:Celsius -> onChange(Celsius) -> update Fahrenheit -> onChange(Fahrenheit) -> update Celsius,这将导致以摄氏度键入1,并且输入变为1.000,从而无法键入两个数字。
下面的代码几乎是我想要的,它使用了onEditingChange。问题是它需要丢失文本字段上的焦点才能更新其他数字。

import SwiftUI

struct ContentView: View {
    @State private var temperatureCelsius: String = "0"
    @State private var temperatureFahrenheit: String = "32"
    
    var body: some View {
        VStack(alignment: .center) {
            Text("Temperature Converter")
                .font(.system(size: 36))
            Spacer()
            HStack {
                HStack {
                    Spacer()
                    TextField("     ",
                              text: $temperatureCelsius,
                              onEditingChanged: { newValue in
                                temperatureFahrenheit = String(convertToFahrenheit(celsiusTemperature: Float(temperatureCelsius) ?? 0.0))
                              }
                        )
                        .keyboardType(.decimalPad)
                        .multilineTextAlignment(.trailing)
                        .fixedSize()
                    Text("ºC")
                    
                }
                Image(systemName: "arrow.left.arrow.right")
                    .imageScale(.large)
                    .foregroundColor(.secondary)
                HStack {
                    TextField("     ",
                              text: $temperatureFahrenheit,
                              onEditingChanged: { newValue in
                                temperatureCelsius = String(convertToCelsius(fahrenheitTemperature: Float(temperatureFahrenheit) ?? 0.0))
                              }
                        )
                        .keyboardType(.decimalPad)
                        .multilineTextAlignment(.trailing)
                        .fixedSize()
                        
                    Text("ºF")
                    Spacer()
                }
            }
            
            
        }
        .padding()
    }
}

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

理想情况下,应该有一种方法可以知道用户什么时候改变了值,而代码什么时候改变了值,这样我就可以使双向数据绑定工作。

wj8zmpe1

wj8zmpe11#

制作您自己的Binding来 Package @State的作品。

@State var celsiusText = "0"
@State var fahrenheitText = "32"
TextField("     ",
          text: Binding(get: {
                celsiusText
            }, set: {
                celsiusText = $0
                
                let temperatureC = Measurement(value: (try? FloatingPointParseStrategy(format: .number).parse($0)) ?? 0, unit: UnitTemperature.celsius)
                fahrenheitText = temperatureC.converted(to: .fahrenheit).value.formatted()
            }))
TextField("     ",
          text: Binding(get: {
                fahrenheitText
            }, set: {
                fahrenheitText = $0
                let temperatureF = Measurement(value: (try? FloatingPointParseStrategy(format: .number).parse($0)) ?? 0, unit: UnitTemperature.fahrenheit)
                celsiusText = temperatureF.converted(to: .celsius).value.formatted()
            })
    )

注意,我在这里使用了Measurement来进行温度转换,并且使用了一种对区域设置敏感的方法来解析和格式化数字,因为默认的description只会输出太多的小数位:)

相关问题