I'd like to be able to dynamically update the format of a SwiftUI TextField based on a user's selection from a Picker, but what I've tried doesn't seem to be working.
I have a TextField where the user enters an amount, and a Picker from which the user selects a currency, and I'd like the formatting of the TextField to match the Picker's currency selection.
TextField has a format property (at least in macOS 12/iOS 15) which allows it to be formatted as a specified currency. That mostly works as expected for whatever currency I initially pass to it, but if I change the currency after the View loads the formatting doesn't change. That is despite passing it the same variable that I pass to the Picker's selected argument, and despite the fact that the View regularly updates when the TextField's value changes.
Below is a simplified version of my code to illustrate the issue. The TextField's value will initially be formatted to show a localised representation of GBP £0.00. If you type “100” in the field it will update to format it as British pounds. All good. But if you change the currency from the Picker to, say, USD, or any other currency, the amount will continue to be formatted as GBP.
For the avoidance of any doubt, I know from my actual code (though it can't be seen in this example) that the value of currency (that is being passed to the Picker and the TextField's format property) is changing based on the Picker selection. So that's not the issue. It's just that the TextField doesn't seem to be aware or care that it has changed.
So it seems like whatever is passed to the TextField's format parameter on initialisation sticks forever and cannot be changed. But if I'm doing something wrong or there's a workaround I'd be really grateful for any pointers.
PS: The issue is best illustrated on macOS, but iOS has the same issue only the formatting appears to change as expected when you first change the selected currency in the Picker, but then reverts once you edit the amount field.
PPS: There's a separate issue relating to formatting EUR which I'll post about separately as it seems to be a separate issue.
import SwiftUI
struct ContentView: View {
@State private var amount = Decimal()
@State private var currency: Currency = .GBP
var body: some View {
CurrencyAmount(
title: "Some label",
amount: $amount,
currency: $currency)
}
}
struct CurrencyAmount: View {
let title: String
@Binding var amount: Decimal
@Binding var currency: Currency
let prompt: String = ""
var body: some View {
HStack {
TextField(
title,
value: $amount,
//FIXME: The currency code used in the format does not update when the user selects a different currency from the Picker.
format: .currency(code: currency.rawValue),
prompt: Text(prompt))
CurrencyPicker(selected: $currency)
}
}
}
struct CurrencyPicker: View {
@Binding var selected: Currency
var label = "Currency"
var body: some View {
Picker(selection: $selected,
label: Text(label)
) {
ForEach(Currency.allCases) { code in
Text(code.rawValue).tag(code)
}
}
}
}
enum Currency: String, CaseIterable, Identifiable {
case AUD, CAD, EUR, GBP, NZD, USD
var id: String { self.rawValue }
}
Post
Replies
Boosts
Views
Activity
SwiftUI's TextField's format parameter accepts a ParseableFormatStyle of .currency. This is a super useful improvement for what I imagine is a really common use case. But while it mostly works seamlessly for me, for some reason it doesn't seem to work if the currency is set to Euros (EUR).
If the currency is set to EUR as in my example below, no matter what you enter in the TextField, the value resolves to 0 formatted as a localised representation of EUR 0.00. It's as if the ParseableFormatStyle doesn't recognise any input as being a valid format of EUR. If you change the currency from EUR to any of the other currencies I've tried it works fine.
Note: There is another issue I've encountered with the .currency format of TextField which I've written about in a separate post which you can find here.
import SwiftUI
struct ContentView: View {
@State private var amount = Decimal()
@State private var currency: Currency = .EUR // change this to any other currency to get different (and expected) results
var body: some View {
CurrencyAmount(
title: "Some label",
amount: $amount,
currency: $currency)
}
}
struct CurrencyAmount: View {
let title: String
@Binding var amount: Decimal
@Binding var currency: Currency
let prompt: String = ""
var body: some View {
TextField(
title,
value: $amount,
format: .currency(code: currency.rawValue),
prompt: Text(prompt))
}
}
}
enum Currency: String, CaseIterable, Identifiable {
case AUD, CAD, EUR, GBP, NZD, USD
var id: String { self.rawValue }
}