Thursday, January 28, 2010

Auto-formatting phone number UITextField on the iPhone

In this post, we'll use our PhoneNumberFormatter to implement an auto-formatting phone number text field in an attempt to mimic the behavior of iPhone's native apps, like Phone and Contacts.

So, let's call the text field in question myTextField. We start by calling addTarget on the text field to make it call the autoFormatTextField method in our view controller whenever it gets updated, either by the user or some other piece of code. If your view is ready, you should declare the handler as an IBAction and bind through IB. The method would then update the contents of the text fields to the formatted string returned by the formatter. If we do it that way, we'll also need to use a semaphore to prevent the method from being called endlessly.

The implementation outline would be:

// declarations

UITextField *myTextField;
int myTextFieldSemaphore;
PhoneNumberFormatter *myPhoneNumberFormatter;
NSString *myLocale; //@"us"

// init semaphore
myTextFieldSemaphore = 0;

// bind events programatically, skip when using IB.
[myTextField addTarget:self
action:@selector(autoFormatTextField:)
forControlEvents:UIControlEventValueChanged
];

// handle events
- (void)autoFormatTextField:(id)sender {
if(myTextFieldSemaphore) return;
myTextFieldSemaphore = 1;
myTextField.text = [phoneNumberFormatter format:myTextField.text withLocale:myLocale];
myTextFieldSemaphore = 0;
}
Update: As pointed out in the comments, for newer SDKs you may need to bind to UIControlEventEditingChanged. Thanks for your feedback!
//bind events

[myTextField addTarget:self
action:@selector(autoFormatTextField:)
forControlEvents:UIControlEventEditingChanged
];

12 comments:

Anonymous said...

To get this to work, I needed to change the constant to UIControlEventEditingChanged.

Anonymous said...

I concur with the comment above.

Anonymous said...

How do you get the user locale?

Ahmed Abdelkader said...

@Dec03: I didn't need to. If you check Tony's comment on the previous post, you'll find country codes can be used for this purpose. Let us know how that works for you!

Wayfarer said...

Brilliant. Works perfectly, thank you!

Charles Bandes said...

Hi Ahmed,

Thanks so much for your articles. I'm pretty new to iOS and I'm wondering if you could add a little more detail to the implementation outline.

Specifically - how would I attach this to a specific UITextField? Also, when I tried just a cut-n-paste of your code (subbing in my variable names) I found that my text field no longer seemed to accept input.

Ahmed Abdelkader said...

Hi Charles,
First, you need to add the variables and handler method to your view controller and initialize the semaphore to zero. Then, binding can be performed on view setup or maybe in the IB. This should be it. I hope this helps!

Charles Bandes said...

Wow! I bound it through interface builder, and it works beautifully. The only thing I can't figure out is how to get the locale from the device instead of hardcoding it to @"us"

On stackoverflow a guy recommended that I do this:


myTextField.text = [phoneNumberFormatter format:myTextField.text withLocale:[[NSLocale currentLocale] localeIdentifier]];

but that didn't work - could it be as simple as the localeIdentifier not matching the strings you're expecting in the class?

Ahmed Abdelkader said...

Only {us, uk, jp} were included in the previous post. You can add more locales using UIPhoneFormats.plist file, but make sure the implementation works correctly for the new cases you add as it's not perfect. I recommend you check out libphonenumber.

Anonymous said...

In my attempt to use this, I get:

"Unknown type name 'PhoneNumberFormatter'; did you mean 'NSNumberFormatter'?"

on the Line:

PhoneNumberFormatter* phoneNumberFormatter = [[PhoneNumberFormatter alloc] init];
What am I missing? Thanks in advance.

JJW

Anonymous said...

JJW,

On top of the page, there is a link for PhoneNumberFormatter class that you have to include in your project. Send me an email at szafar@soundviewtech.com and I will send you the sample project.

With due respect to all the experienced developers/bloggers, kindly consider newbies like me also. It becomes difficult to track the code without a working copy. It is always a good idea to include a sample project in the main article. But thanks for the good work anyway, even without the sample project.

Yashmeen Singh said...

how do wee get the locale of the code