SwiftUI TextField with formatter not working?

Plan B. Since using value: and NumberFormatter doesn’t work, we can use a customised TextField. I have wrapped the TextField inside a struct, so that you can use it as transparently as possible.

I am very new to both Swift and SwiftUI, so there is no doubt a more elegant solution.

struct IntField: View {
    @Binding var int: Int
    @State private var intString: String  = ""
    var body: some View {
        return TextField("", text: $intString)
        .onReceive(Just(intString)) { value in
            if let i = Int(value) { int = i }
            else { intString = "\(int)" }
        }
        .onAppear(perform: {
            intString = "\(int)"
        })
    }
}

and in the ContentView:

struct ContentView: View {
    @State var testInt: Int = 0
    var body: some View {
        return HStack {
            Text("Number:")
            IntField(int: $testInt);
            Text("Value: \(testInt)")
        }
    }
}

Basically, we work with a TextField("…", text: …), which behaves as desired, and use a proxy text field.

Unlike the version using value: and NumberFormatter, the .onReceive method responds immeditately, and we use it to set the real integer value, which is bound. While we’re at it, we check whether the text really yields an integer.

The .onAppear method is used to fill the string from the integer.

You can do the same with FloatField.

This might do the job until Apple finishes the job.


It seems while using value: as an input, SwiftUI does not reload the view for any key that users tap on. And, as you mentioned, it reloads the view when users exit the field or commit it.

On the other hand, SwiftUI reloads the view (immediately) using text: as an input whenever a key is pressed. Nothing else comes to my mind.

in my case, I did it for someNumber2 as below:

struct ContentView: View {

@State var someNumber = 123.0
@State var someNumber2 = "123"


var formattedNumber : NSNumber {

    let formatter = NumberFormatter()

    guard let number = formatter.number(from: someNumber2) else {
        print("not valid to be converted")
        return 0
    }

    return number
}

var body: some View {

    VStack {

        TextField("Number", value: $someNumber, formatter: NumberFormatter())
        TextField("Number2", text: $someNumber2)

        Text("number: \(self.someNumber)")
        Text("number: \(self.formattedNumber)")
    }
  }
}

You can use Binding to convert Double<-->String for TextField

struct TestView: View {
    @State var someNumber = 123.0

    var body: some View {
        let someNumberProxy = Binding<String>(
            get: { String(format: "%.02f", Double(self.someNumber)) },
            set: {
                if let value = NumberFormatter().number(from: $0) {
                    self.someNumber = value.doubleValue
                }
            }
        )

        return VStack {
            TextField("Number", text: someNumberProxy)

            Text("number: \(someNumber)")
        }
      }
}

You can use computed property way to solve this issue. (thanks @ iComputerfreak)

struct TestView: View {
    @State var someNumber = 123.0

    var someNumberProxy: Binding<String> {
        Binding<String>(
            get: { String(format: "%.02f", Double(self.someNumber)) },
            set: {
                if let value = NumberFormatter().number(from: $0) {
                    self.someNumber = value.doubleValue
                }
            }
        )
    }

    var body: some View {
        VStack {
            TextField("Number", text: someNumberProxy)

            Text("number: \(someNumber)")
        }
      }
}

Tags:

Swiftui