skip to Main Content

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.

2

Answers


  1. Chosen as BEST ANSWER

    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
                }
            }
    

  2. 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

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search