Reputation: 101
I'm running into a SwiftUI end-user, usability problem using a TextField to enter a currency amount.
The bound field is a double, initially set to 0 and when the text field is displayed, the prompt is $0.00.
The problem is that when the user wants to enter a value, they have to erase the 0.00 with the backspace key, manually. Also, if they accidentally backspace over the $-sign, any value entered thereafter disappears!
When there are multiple currency fields, this is a real nuisance for the end-user.
I've seen suggestions on the Internet to set the formatter.zeroSymbol = ""
in the NumberFormatter, but when numberStyle = .currency
any value entered is lost/destroyed.
If I change the number style to .decimal
, I can use the zeroSymbol
option and it seems to work, but I lose the currency formatting.
Does anyone know how to fix this?
Following is a sample code that you can run that demonstrates this problem.
import SwiftUI
struct ContentView: View {
@State private var amount: Double = 0
let currencyFormat: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .currency
// formatter.zeroSymbol = ""
return formatter
}()
var body: some View {
HStack {
Text("Enter Amount")
Spacer()
TextField("", value: $amount, formatter: currencyFormat)
.keyboardType(.numbersAndPunctuation)
}
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Upvotes: 4
Views: 3408
Reputation: 21
Here's a workaround that worked for me. It uses FocusState to switch between two formatters.
import SwiftUI
struct ContentView: View {
@State private var amount: Double = 0
var body: some View {
HStack {
Text("Enter Amount")
Spacer()
CurrencyTextField(title: "", value: $amount)
}
.padding()
}
}
struct CurrencyTextField: View {
var title: String
@Binding var value: Double
@FocusState private var isFocused: Bool
private let currencyNumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .currency
return formatter
}()
private let decimalNumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.usesGroupingSeparator = false
return formatter
}()
var body: some View {
TextField(title, value: $value, formatter: isFocused ? decimalNumberFormatter : currencyNumberFormatter)
.keyboardType(.decimalPad)
.focused($isFocused)
}
}
#Preview {
ContentView()
}
Upvotes: 2
Reputation: 51
Elaborating on the answer of @Zulqarnain Naveed:
TextField("",value: $amount, format: .currency(code: Locale.current.currency?.identifier ?? "USD"))
Add the following lines of code. This will select all text in the text field when focussed:
.onReceive(NotificationCenter.default.publisher(for: UITextField.textDidBeginEditingNotification)) { obj in
if let textField = obj.object as? UITextField {
textField.selectedTextRange = textField.textRange(from: textField.beginningOfDocument, to: textField.endOfDocument)
}
}
Upvotes: 5
Reputation: 108
Instead of this one:
TextField("", value: $amount, formatter: currencyFormat)
.keyboardType(.numbersAndPunctuation)
Use this one:
TextField("",value: $amount, format: .currency(code: Locale.current.currency?.identifier ?? "USD"))
Upvotes: 1