Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI TextField binding to Double not working with custom Formatter

Tags:

swiftui

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.

like image 425
G J Avatar asked Jul 09 '19 18:07

G J


2 Answers

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!

like image 99
KRH Avatar answered Oct 23 '22 03:10

KRH


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
    }
}
like image 25
Egzon P. Avatar answered Oct 23 '22 05:10

Egzon P.