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