UITextField for Phone Number
Solution 1
Here is my solution.. works great! Formats the phone number in realtime. Note: This is for 10 digit phone numbers. And currently it auto formats it like (xxx) xxx-xxxx.. tweak to your hearts delight.
First in your shouldChangeCharactersInRange
you want to gather the whole string for the phone text field and pass it to the validation/formatting function.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
NSString* totalString = [NSString stringWithFormat:@"%@%@",textField.text,string];
// if it's the phone number textfield format it.
if(textField.tag==102 ) {
if (range.length == 1) {
// Delete button was hit.. so tell the method to delete the last char.
textField.text = [self formatPhoneNumber:totalString deleteLastChar:YES];
} else {
textField.text = [self formatPhoneNumber:totalString deleteLastChar:NO ];
}
return false;
}
return YES;
}
And here is where the phone number is formatted. The regex could probably be cleaned up a bit. But I have tested this code for a while and seems to pass all bells. Notice we also use this function to delete a number in the phone number. Works a little easier here because we already stripped out all the other non digits.
-(NSString*) formatPhoneNumber:(NSString*) simpleNumber deleteLastChar:(BOOL)deleteLastChar {
if(simpleNumber.length==0) return @"";
// use regex to remove non-digits(including spaces) so we are left with just the numbers
NSError *error = NULL;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"[\\s-\\(\\)]" options:NSRegularExpressionCaseInsensitive error:&error];
simpleNumber = [regex stringByReplacingMatchesInString:simpleNumber options:0 range:NSMakeRange(0, [simpleNumber length]) withTemplate:@""];
// check if the number is to long
if(simpleNumber.length>10) {
// remove last extra chars.
simpleNumber = [simpleNumber substringToIndex:10];
}
if(deleteLastChar) {
// should we delete the last digit?
simpleNumber = [simpleNumber substringToIndex:[simpleNumber length] - 1];
}
// 123 456 7890
// format the number.. if it's less then 7 digits.. then use this regex.
if(simpleNumber.length<7)
simpleNumber = [simpleNumber stringByReplacingOccurrencesOfString:@"(\\d{3})(\\d+)"
withString:@"($1) $2"
options:NSRegularExpressionSearch
range:NSMakeRange(0, [simpleNumber length])];
else // else do this one..
simpleNumber = [simpleNumber stringByReplacingOccurrencesOfString:@"(\\d{3})(\\d{3})(\\d+)"
withString:@"($1) $2-$3"
options:NSRegularExpressionSearch
range:NSMakeRange(0, [simpleNumber length])];
return simpleNumber;
}
Solution 2
Here's how you can do it in Swift 4:
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool
{
if (textField == phoneTextField) {
let newString = (textField.text! as NSString).replacingCharacters(in: range, with: string)
let components = newString.components(separatedBy: NSCharacterSet.decimalDigits.inverted)
let decimalString = components.joined(separator: "") as NSString
let length = decimalString.length
let hasLeadingOne = length > 0 && decimalString.hasPrefix("1")
if length == 0 || (length > 10 && !hasLeadingOne) || length > 11 {
let newLength = (textField.text! as NSString).length + (string as NSString).length - range.length as Int
return (newLength > 10) ? false : true
}
var index = 0 as Int
let formattedString = NSMutableString()
if hasLeadingOne {
formattedString.append("1 ")
index += 1
}
if (length - index) > 3 {
let areaCode = decimalString.substring(with: NSMakeRange(index, 3))
formattedString.appendFormat("(%@)", areaCode)
index += 3
}
if length - index > 3 {
let prefix = decimalString.substring(with: NSMakeRange(index, 3))
formattedString.appendFormat("%@-", prefix)
index += 3
}
let remainder = decimalString.substring(from: index)
formattedString.append(remainder)
textField.text = formattedString as String
return false
}
else {
return true
}
}
Solution 3
Updated answer from Vikzilla for Swift 3:
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if textField == phoneTextField {
let newString = (textField.text! as NSString).replacingCharacters(in: range, with: string)
let components = (newString as NSString).components(separatedBy: NSCharacterSet.decimalDigits.inverted)
let decimalString = components.joined(separator: "") as NSString
let length = decimalString.length
let hasLeadingOne = length > 0 && decimalString.character(at: 0) == (1 as unichar)
if length == 0 || (length > 10 && !hasLeadingOne) || length > 11 {
let newLength = (textField.text! as NSString).length + (string as NSString).length - range.length as Int
return (newLength > 10) ? false : true
}
var index = 0 as Int
let formattedString = NSMutableString()
if hasLeadingOne {
formattedString.append("1 ")
index += 1
}
if (length - index) > 3 {
let areaCode = decimalString.substring(with: NSMakeRange(index, 3))
formattedString.appendFormat("(%@)", areaCode)
index += 3
}
if length - index > 3 {
let prefix = decimalString.substring(with: NSMakeRange(index, 3))
formattedString.appendFormat("%@-", prefix)
index += 3
}
let remainder = decimalString.substring(from: index)
formattedString.append(remainder)
textField.text = formattedString as String
return false
} else {
return true
}
}
Solution 4
Below function enforces (999)333-5555 format on the textField:
Swift 3:
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if (textField == self.phone){
let newString = (textField.text! as NSString).replacingCharacters(in: range, with: string)
let components = newString.components(separatedBy: NSCharacterSet.decimalDigits.inverted)
let decimalString = components.joined(separator: "") as NSString
let length = decimalString.length
let hasLeadingOne = length > 0 && decimalString.character(at: 0) == (1 as unichar)
if length == 0 || (length > 10 && !hasLeadingOne) || length > 11 {
let newLength = (textField.text! as NSString).length + (string as NSString).length - range.length as Int
return (newLength > 10) ? false : true
}
var index = 0 as Int
let formattedString = NSMutableString()
if hasLeadingOne {
formattedString.append("1 ")
index += 1
}
if (length - index) > 3 {
let areaCode = decimalString.substring(with: NSMakeRange(index, 3))
formattedString.appendFormat("(%@)", areaCode)
index += 3
}
if length - index > 3 {
let prefix = decimalString.substring(with: NSMakeRange(index, 3))
formattedString.appendFormat("%@-", prefix)
index += 3
}
let remainder = decimalString.substring(from: index)
formattedString.append(remainder)
textField.text = formattedString as String
return false
} else {
return true
}
}
Solution 5
I've been struggling with this for a couple of hours, here's what I have:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSUInteger currentLength = textField.text.length;
NSCharacterSet *numbers = [NSCharacterSet decimalDigitCharacterSet];
if (range.length == 1) {
return YES;
}
if ([numbers characterIsMember:[string characterAtIndex:0]]) {
if ( currentLength == 3 )
{
if (range.length != 1)
{
NSString *firstThreeDigits = [textField.text substringWithRange:NSMakeRange(0, 3)];
NSString *updatedText;
if ([string isEqualToString:@"-"])
{
updatedText = [NSString stringWithFormat:@"%@",firstThreeDigits];
}
else
{
updatedText = [NSString stringWithFormat:@"%@-",firstThreeDigits];
}
[textField setText:updatedText];
}
}
else if ( currentLength > 3 && currentLength < 8 )
{
if ( range.length != 1 )
{
NSString *firstThree = [textField.text substringWithRange:NSMakeRange(0, 3)];
NSString *dash = [textField.text substringWithRange:NSMakeRange(3, 1)];
NSUInteger newLenght = range.location - 4;
NSString *nextDigits = [textField.text substringWithRange:NSMakeRange(4, newLenght)];
NSString *updatedText = [NSString stringWithFormat:@"%@%@%@",firstThree,dash,nextDigits];
[textField setText:updatedText];
}
}
else if ( currentLength == 8 )
{
if ( range.length != 1 )
{
NSString *areaCode = [textField.text substringWithRange:NSMakeRange(0, 3)];
NSString *firstThree = [textField.text substringWithRange:NSMakeRange(4, 3)];
NSString *nextDigit = [textField.text substringWithRange:NSMakeRange(7, 1)];
[textField setText:[NSString stringWithFormat:@"(%@) %@-%@",areaCode,firstThree,nextDigit]];
}
}
}
else {
return NO;
}
return YES;
}
I hope someone can contribute.
Admin
Updated on March 07, 2021Comments
-
Admin about 3 years
I was wondering how I can format the textField that I'm using for a phone number (ie like the "Add New Contact" page on the iPhone. When I enter in a new mobile phone, ex. 1236890987 it formats it as (123) 689-0987.) I already have the keyboard set as the number pad.
-
Admin almost 15 yearsHey goeff, it would be awesome if you could show me the code to do this because I just started coding in objective-c and it's very confusing to me. Thanks in advance!!
-
lreichold over 11 yearsWorked well. Additionally, you can dismiss the keyboard after a full number is entered by adding the following to the formatPhoneNumber: method:
if (simpleNumber.length == 10 && deleteLastChar == NO) { [textField resignFirstResponder]; }
-
SonOfSeuss about 11 yearsHow do you wire this to the actual text field?
-
Jason about 11 yearsTrying to add your method with the following code. Is this right? It's not working.
case 3: { cell.textLabel.text = @"Phone" ; tf = phoneFieldTextField = [self makeTextField:self.phone placeholder:@"xxx-xxx-xxxx"]; phoneFieldTextField.keyboardType = UIKeyboardTypePhonePad; [self formatPhoneNumber:phoneFieldTextField.text deleteLastChar:YES]; [cell addSubview:phoneFieldTextField]; break ; }
-
zingle-dingle almost 11 yearsTo wire it to your textfield in interface builder you need to make sure you have the delegate of the textfield set to file owner (most likely). Then add the 2 above methods to your .m file. Currently it's making sure it's a phone textfield by using the "textField.tag==102" statement. The tag for a textfield (or any other ui object) can be set in interface builder. So in this case I set the tag to 102 in interface builder.
-
zingle-dingle almost 11 yearsBTW: Seems like I should be rewarded the answer? Yes Maybe?
-
mostafa tourad over 10 years@zingle-dingle impossible - the account of the OP does not exist anymore, hence this answer will never be accepted. Ow btw, this is only good for North America as other countries use differing schemes.
-
zingle-dingle over 10 years@Till Yeah, North America only. Tweak to your hearts delight. Won't take much to make it work for other countries.
-
ItalyPaleAle over 10 yearsThe idea looks nice, but it's too localized in my opinion. Only North America uses "(xxx) xxx-xxxx", and for everyone else abroad is really annoying! I suggest to look at code.google.com/p/libphonenumber
-
Hiren almost 10 yearsGreat solution. very well use of regex
-
Alexander Volkov over 9 yearsThanks, vikzilla. It helped very much.
-
Blackwood almost 9 yearsThis question already has several answers, some of them with many upvotes. You should at least explain why you think your answer adds something that is new.
-
Alberto Lopez over 8 yearsMight need to adjust: let decimalString = components.joinWithSeparator("") as NSString
-
Jacob about 8 yearsShould be let hasLeadingOne = length > 0 && decimalString.characterAtIndex(0) == (49 as unichar) or even let hasLeadingOne = length > 0 && decimalString.hasPrefix("1")
-
Pat Niemeyer over 7 yearsgot an extraneous sendButtton reference in there
-
Umair Afzal almost 7 yearsHow Can I change it for this format "(+61) 428-74-5183" ?
-
Umair Afzal almost 7 yearsHow Can I change it for this format "(+61) 428-74-5183" ?
-
Ali Ihsan URAL over 5 yearsDont forgot to add UITextFieldDelegate & in viewDidLoad textfield.delegate = self :)
-
ctietze about 5 yearsYou can get rid of the
deleteLastChar
stuff (which doesn't work for selecting and deleting or replacing) when you use this line instead of the original:NSString* totalString = [textField.text stringByReplacingCharactersInRange:range withString:string];
-
Eric Mentele over 3 yearsFYI This breaks the use of auto suggestion. Great for just typing though.