I have a Binding to a Double parameter that is set by a SwiftUI TextField
. I use a custom Formatter
that converts to and from a Double value. The TextField
sends an empty string ""
to the Formatter
upon editing so the conversion fails and the Double
parameter is not updated. The struct is called from a parent View which has a @ObjectBinding
parameter and the Double
is a parameter of that object.
I am currently using Xcode 11 beta 3 and macOS Catalina Beta 3. The TextField
works if the parameter is a String
. The problem appears to be that a non-String type, which requires a Formatter
fails to properly update the @Binding
value.
Here is the Formatter
:
public class DoubleFormatter: Formatter {
override public func string(for obj: Any?) -> String? {
var retVal: String?
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
if let dbl = obj as? Double {
retVal = formatter.string(from: NSNumber(value: dbl))
} else {
retVal = nil
}
return retVal
}
override public func getObjectValue(_ obj: AutoreleasingUnsafeMutablePointer<AnyObject?>?, for string: String, errorDescription error: AutoreleasingUnsafeMutablePointer<NSString?>?) -> Bool {
var retVal = true
if let dbl = Double(string), let objok = obj {
objok.pointee = dbl as AnyObject?
retVal = true
} else {
retVal = false
}
return retVal
}
}
Here is the SwiftUI View that takes the Double parameter in a TextField
struct HStackTextTextField : View {
var text: String
@Binding var value: Double
@State var valueState: Double
var body: some View {
HStack {
Text("\(value)") //shows the value failing to update
TextField("Number", value: $value, formatter: DoubleFormatter()) //Still Fails
Text("\(valueState)") //shows valueState updating properly
TextField("Number", value: $valueState, formatter: DoubleFormatter()) //works as expected
}
}
}
I expect the TextField
value to update when I type, but it does not. When I trace the value in the the Formatter. The string provided to getObjectValue
is ""
instead of the value in the TextField
.
UPDATE: As of catalina/Xcode beta 5, this still appears to be an issue when the View
TextField
parameter is defined as @Binding
and passed to the View
. It appears to work as expected if the TextField
parameter is defined as @State
and is local to the View.
I believe this is a bug in SwiftUI. (See my similar question: SwiftUI TextField with formatter not working?)
In beta 2, it didn't work at all. In beta 3, I think you'll find that your result gets passed to your formatter if (and only if) you hit return after typing in the field. Hopefully in beta 4 they'll finish fixing the bug!
SwiftUI 3.0 still facing the same issue when using custom formatter in TextField. In my case I was using number formatter with 2 fractions. Here is my workgaround I found which looks legit and works correctly. I used custom binding.
@Binding var amount: Double?
var body: some View {
VStack {
let amountBinding = Binding(
get: { self.amount ?? 0.0 },
set: { self.amount = Double(String(format:"%.2f", $0))})
TextField("€", value: amountBinding, formatter: NumberFormatters.twoFractionDigits, onEditingChanged: { changed in
// Editing changed
})
Spacer()
}
}
struct NumberFormatters {
static var twoFractionDigits: Formatter {
let formatter = NumberFormatter()
formatter.numberStyle = .none
formatter.maximumFractionDigits = 2
return formatter
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With