Issue #844
Use custom Binding and validate the text. In case of zero, return empty string to let TextField display placeholder
var textField: some View {
let text = Binding<String>(
get: {
state.amount > 0 ? "$\(state.amount)" : ""
},
set: { text in
let text = text.replacingOccurrences(of: "$", with: "")
state.amount = Int(text) ?? 0
}
)
return TextField("$0", text: text)
.keyboardType(.numberPad)
.font(.system(size: 50, weight: .medium, design: .monospaced))
.foregroundColor(Asset.textPrimary.color)
.multilineTextAlignment(.center)
.accentColor(Asset.primary.color)
}
We can also use NumberFormatter for more customizable control
private extension NumberFormatter {
static let currency: NumberFormatter = {
let formatter = NumberFormatter()
formatter.maximumFractionDigits = 2
formatter.minimumFractionDigits = 0
return formatter
}()
}
For decimal pad, and to avoid overflow, we can have some checks
let text = Binding<String>(
get: {
if state.payment.amount > 0 {
let number = NSNumber(value: state.payment.amount)
let string = NumberFormatter.currency.string(from: number) ?? ""
return "$\(string)"
} else {
return ""
}
},
set: { text in
let text = text.replacingOccurrences(of: "$", with: "")
if text.hasSuffix(".") {
return
}
let amount = Amount(text) ?? 0
// Avoid TextField to overgrow outside frame
if amount < 10_000 {
state.payment.amount = amount
} else {
state.payment.amount = state.payment.amount
}
}
)