英文:
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.
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
格式化会输出太多小数位数
英文:
Making your own Binding
that wrap the @State
s 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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论