Home > OS >  How to get SwiftUI TextField to show numbers but also accept an empty value
How to get SwiftUI TextField to show numbers but also accept an empty value

Time:05-13

I am trying to get a text field that only accepts numbers but will also accept an empty value (ie. it is an optional field). This is what the textfield looks like:

TextField("Phone Number", value: $phoneNumber, formatter: numberFormatter)
    .disableAutocorrection(true)
    .autocapitalization(.none)

and my number formatter looks like this:

@State private var numberFormatter: NumberFormatter = {
    var nf = NumberFormatter()
    nf.numberStyle = .none
    nf.zeroSymbol = ""    
    return nf
}()

I have tried two different solutions yet both have problems. The first solution is to define phoneNumber as an optional integer Int? so that the TextField will accept a blank input:

@State var phoneNumber: Int? = nil

However, this messes with the TextField so that when I change its value via the app the changes don't update the actual variable phoneNumber. So, when I go to send my data in, phoneNumber still stays at nil. I think this has something to do with the numberFormatter and how it wont't accept nil variables.

So, my second solution is to initialize phoneNumber as an Int like so:

@State var phoneNumber: Int = 0

This solution does update phoneNumber when I change the text in the TextField. However, it will only show a blank space in the TextField box when I type in 0 (because of my .zeroSymbol definition in the numberFormatter). When I try to just put a blank space (ie. delete all the text) and then click out of the TextField, it just reverts back to the number that it was before. This same thing happens when I put a non-numeric entry into the field, which I am okay with because it should only accept numbers, but I want to still allow the user to include a blank entry.

I am using XCode and creating an iOS app. Thank you for any help.

CodePudding user response:

A possible solution is to intercept input/output via proxy binding and and perform needed additional validation/processing.

demo

Tested with Xcode 13.3 / iOS 15.4

Here is main part:

TextField("Phone Number", value: Binding(
    get: { phoneNumber ?? 0},
    set: { phoneNumber = phoneNumber == $0 ? nil : $0 }
    ), formatter: numberFormatter)

Complete test module in project

CodePudding user response:

Another solution that I found is just to make phoneNumber a string with default "". Then, validate it by trying to convert it to an Int via Int(phoneNumber). If Int(phoneNumber) == nil, then raise an error. Otherwise, you now have a new optional integer that can be nil or the integer provided:

var intPhoneNumber: Int?
        if phoneNumber == "" {
            intPhoneNumber = nil
        } else {
            intPhoneNumber = Int(phoneNumber)
            if intPhoneNumber == nil {
                completion(.failure(.custom(errorMessage: "Please enter a valid phone number. Try again.")))
                return
            } else if intPhoneNumber! < 0 {
                completion(.failure(.custom(errorMessage: "Invalid Phone Number. Please enter a valid 10 digit phone number.")))
                return
            } else if numberOfDigits(in: intPhoneNumber!) != 10 {
                completion(.failure(.custom(errorMessage: "Invalid Phone Number. Please enter a valid 10 digit phone number.")))
                return
            }
        }
  • Related