Co-dependent TextField in SwiftUI (双向数据绑定)

huangapple go评论138阅读模式
英文:

Co-dependent TextField in SwiftUI (two way data binding)

问题

以下是您要翻译的代码部分:

Attempting to write a temperature converter using SwiftUI.

I want that by typing a temperature in the Celsius input the Fahrenheit reflects the converted temperature immediately and vice-versa.

[![UI](https://i.stack.imgur.com/rNqvN.png)](https://i.stack.imgur.com/rNqvN.png)

I attempted to use `onChange` but that creates a loop: `Celsius -> onChange(Celsius) -> update Fahrenheit -> onChange(Fahrenheit) -> update Celsius`, which results in typing `1` in Celsius and the input becoming `1.000` making it impossible to type two digits.

The following code is almost what I want, it uses `onEditingChange`. The issue is that it requires the focus on the TextField to be lost in order to update the other number.

``` lang-swift
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()
    }
}

希望这个翻译对您有所帮助。如果您有其他问题,可以随时向我提问。

英文:

Attempting to write a temperature converter using SwiftUI.

I want that by typing a temperature in the Celsius input the Fahrenheit reflects the converted temperature immediately and vice-versa.

Co-dependent TextField in SwiftUI (双向数据绑定)

I attempted to use onChange but that creates a loop: Celsius -> onChange(Celsius) -> update Fahrenheit -> onChange(Fahrenheit) -> update Celsius, which results in typing 1 in Celsius and the input becoming 1.000 making it impossible to type two digits.

The following code is almost what I want, it uses onEditingChange. The issue is that it requires the focus on the TextField to be lost in order to update the other number.

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()
    }
}

Ideally there would be a way to know when the user changed the value vs the code changed the value. This way I could make this two way data binding work.

答案1

得分: 2

制作自己的Binding,包装@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格式化会输出太多小数位数 Co-dependent TextField in SwiftUI (双向数据绑定)

英文:

Making your own Binding that wrap the @States works.

@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()
            })
    )

Note that I have used a Measurement here to do the temperature conversion, and I have used a locale-sensitive way of parsing and formatting the numbers, because the default description formatting just outputs way too many decimal places Co-dependent TextField in SwiftUI (双向数据绑定)

huangapple
  • 本文由 发表于 2023年3月12日 10:39:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/75710784.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定