Search code examples
iosswiftnumber-formatting

NumberFormatter strange behavior in UITextField


i'm trying to add spacing to a number on input, but after 1000 the formatter resets the value

amountTextField.addTarget(self, action: #selector(textFieldFilter(_:)), for: .editingChanged)
@objc private func textFieldFilter(_ textField: UITextField) {
        guard let text = textField.text else {return}
        print("String text\(text)")
        print("Integer text\(Int(text))")
        let formatter = NumberFormatter()
        formatter.groupingSeparator = " "
        formatter.usesGroupingSeparator = true
        formatter.groupingSize = 3
        formatter.numberStyle = .decimal
        textField.text = formatter.string(for: Int(text))
    }

In console prints this:

String text 1
Integer text Optional(1)
String text 10
Integer text Optional(10)
String text 100
Integer text Optional(100)
String text 1000
Integer text Optional(1000)
String text 1 0000
Integer text nil

Solution

  • The initialiser Int(_ text: String)? does not use your formatter to parse the text you pass to it. Hence "1 0000" is not parsed as a valid number and Int("1 0000") returns nil. It also doesn't help that, in the last case, your group size is 4, not 3.

    Here is some code that works

    let text = "1 000"
    print("String text \(text)")
    print("Integer text \(Int(text) ?? -1)") // ?? -1 silences the warning for a nil result
    let formatter = NumberFormatter()
    formatter.groupingSeparator = " "
    formatter.usesGroupingSeparator = true
    formatter.groupingSize = 3
    formatter.numberStyle = .decimal
    if let number = formatter.number(from: text)
    {
        print("number \(number)")
        let numberAsString = formatter.string(from: number)
        print("number as string '\(numberAsString ?? "nil")'")
        let numberAsInt = Int(truncating: number)
        print("numberAsInt \(numberAsInt)")
        let numberAsIntAsString = formatter.string(from: numberAsInt as NSNumber)
        print("numberAsIntAsString '\(numberAsIntAsString ?? "nil")'")
    
    }
    

    prints

    String text 1 000
    Integer text -1
    number 1000
    number as string '1 000'
    numberAsInt 1000
    numberAsIntAsString '1 000'
    

    The first line is the text (which I assign directly and you get from the field.

    The second line is the result of Int(text) which is -1 because Int(text) is nil but I use ?? to assign a default.

    The third line is the result of using the formatter to get an NSNumber from the field

    The fourth line is the result of feeding that number back into the formatter.

    The fifth line prints the NSNumber converted to an Int

    The last line prints the Int converted back to an NSNumber and then back to a string with the formatter.