UITextField for Phone Number
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.
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;
}
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
}
}
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
}
}
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.
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
}
}