Facing new challenges everyday

Adding Mask to UITextField

February 19th, 2010

Cocoa Touch is an awesome API, it contains a large set o frameworks that can be used to create fantastic applications for iPhone, iPod Touch and iPad, but if we compare Cocoa Touch with Cocoa API for MacOSX we won’t find some cool features like masking on input controls.

Today I found a way to provide this feature in a UIViewController that contains a UITextField. It’s a very simple task, you only need to make sure that your controller implements textField:shouldChangeCharactersInRange:replacementString: method of UITextFieldDelegate protocol.

Here’s a small example of how you can do it:

Creating the InputFieldWithMaskViewController interface

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#import <UIKit/UIKit.h>
#import "InputFieldViewController.h"

@interface InputFieldWithMaskViewController : UIViewController {
    //The variable holding input mask
    NSString* mask;
}

//The property that can be used to set mask variable
@property (nonatomic, retain) NSString* mask;

//A custom initilizer where the user can set the input mask
- (id)initWithMask:(NSString*)aMask;

@end

In the example above we are defining the interface of UIViewController that contains our UITextField, this interface also declares the property to hold the mask string and a custom initializer that accept a input mask as argument.

The next example contains the implementation of view controller where the mask code does your magic, please read the comments carefully.

Creating the InputFieldWithMaskViewController implementation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#import "InputFieldWithMaskViewController.h"

@implementation InputFieldWithMaskViewController

@synthesize mask;

#pragma mark -
#pragma mark View Lifecycle
- (id)initWithMask:(NSString*)aMask {
    if (self = [super init]) {
        self.mask = aMask;
        self.inputField.delegate = self;
    }
    return self;
}

- (void)viewDidLoad {
    [super viewDidLoad];
}

- (void)viewDidUnload {
    [super viewDidUnload];
}

#pragma mark -
#pragma mark UITextField methods

//This method is responsible for add mask characters properly
- (void)formatInput:(UITextField*)aTextField string:(NSString*)aString range:(NSRange)aRange
{
    //Copying the contents of UITextField to an variable to add new chars later
    NSString* value = aTextField.text;
   
    NSString* formattedValue = value;
   
    //Make sure to retrieve the newly entered char on UITextField
    aRange.length = 1;
   
    NSString* _mask = [self.mask substringWithRange:aRange];
   
    //Checking if there's a char mask at current position of cursor
    if (_mask != nil) {
        NSString *regex = @"[0-9]*";
       
        NSPredicate *regextest = [NSPredicate
                                  predicateWithFormat:@"SELF MATCHES %@", regex];
        //Checking if the character at this position isn't a digit
        if (! [regextest evaluateWithObject:_mask]) {
            //If the character at current position is a special char this char must be appended to the user entered text
            formattedValue = [formattedValue stringByAppendingString:_mask];
        }

        if (aRange.location + 1 < [self.mask length]) {
             _mask =  [self.mask substringWithRange:NSMakeRange(aRange.location + 1, 1)];
             if([_mask isEqualToString:@" "])
                formattedValue = [formattedValue stringByAppendingString:_mask];
        }
    }
    //Adding the user entered character
    formattedValue = [formattedValue stringByAppendingString:aString];
   
    //Refreshing UITextField value      
    aTextField.text = formattedValue;
}

//This method comes from UITextFieldDelegate
//and this is the most important piece of mask
//functionality.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
    //If the length of used entered text is equals to mask length the user input must be cancelled
    if ([textField.text length] == [self.mask length]) {
        if(! [string isEqualToString:@""])
            return NO;
        else
            return YES;
    }
    //If the user has started typing text on UITextField the formatting method must be called
    else if ([textField.text length] || range.location == 0) {
        if (string) {
            if(! [string isEqualToString:@""]) {
                [self formatInput:textField string:string range:range];
                return NO;
            }
            return YES;
        }
        return YES;
    }
   
    return YES;
}

#pragma mark -
#pragma mark Memory management
- (void)dealloc {
    [mask release];
    [super dealloc];
}
@end

As you can see in the example above this view controller only need to implement a method from UITextFieldDelegate where we are doing some user input checking and then we call a local method that evaluates a regular expression on the current char typed by user, if the current position in UITextField belongs to a digit nothing happens, otherwise a special char is appended to user input.

Share

8 Comments »

  1. Wally says

    Wow I’ve been looking for sample code like this for a long time – thanks! Quick question: I’m assuming this can be used for telephone number formatting as the user types in the number? I’m looking for a way to format a telephone number similar to the built-in phone app, or similarly for entering phone numbers in contacts, where it will format as (U.S. style phone#) ###-####, or if more numbers entered, eventually as (###) ###-####. Can the mask be variable like this, or will it need to be specified as the end-result (###) ###-####? Thanks!

    March 22nd, 2010 | #

  2. Wally says

    Is there a way to format with an embedded space like in the phone example above? Could you provide an example of what the mask looks like? Currently, I’m initializing the mask in the viewDidLoad by:

    [self initWithMask@"(999) 999-9999"];

    The desire is to have it automatically insert the space after the “)” is inserted. For example, the area code is 213 and the phone number is 543-9876. As the user types in the digits, it formats as follows:
    User types: TextField Displays:
    2 (2
    1 (21
    3 (213
    5 (213) 5
    4 (213) 54
    3 (213) 543
    9 (213) 543-9
    8 (213) 543-98
    7 (213) 543-987
    6 (213) 543-9876

    March 23rd, 2010 | #

  3. rogerio says

    Is must make the proper changes in the code to get spaces working in masks, let me do it here and I’ll update to code on blog post.

    March 23rd, 2010 | #

  4. Wally says

    Great thanks! Can you also post a short example of setting the mask? Just want to make sure I’m setting it correctly.

    March 23rd, 2010 | #

  5. rogerio says

    To setup the mask you only need to call initWithMask as you did in your previous comment.

    March 23rd, 2010 | #

  6. Wally says

    Works, but needs to be “if(_aRange.location + 1 < [self.mask length]){" on the first line… Thanks!

    March 24th, 2010 | #

  7. Boris says

    First of all thanks a lot for this snippet. I ill add a little functionality to work with spaces :

    1 – add a flag to the begining of :

    - (void)formatInput:(UITextField*)aTextField string:(NSString*)aString range:(NSRange)aRange

    ej:
    int flag =0;

    2.

    if (! [regextest evaluateWithObject:_mask]) {
    set the flag to 1
    flag = 1;

    3.
    in the
    if (aRange.location + 1 < [self.mask length]) {

    set the _mask to
    _mask = (temp ==1) ? [self.mask substringWithRange:NSMakeRange(aRange.location+1, 1)]:[self.mask substringWithRange:NSMakeRange(aRange.location-1, 1)];

    First of all thanks a lot for this snippet. I ill add a little functionality to work with spaces :

    1 – add a flag to the begining of :

    - (void)formatInput:(UITextField*)aTextField string:(NSString*)aString range:(NSRange)aRange

    ej:
    int flag =0;

    2.

    if (! [regextest evaluateWithObject:_mask]) {
    set the flag to 1
    flag = 1;

    3.
    in the
    if (aRange.location + 1 < [self.mask length]) {

    set the _mask to
    _mask = (temp ==1) ? [self.mask substringWithRange:NSMakeRange(aRange.location+1, 1)]:[self.mask substringWithRange:NSMakeRange(aRange.location-1, 1)];

    And voila!
    works great

    And voila!
    works great

    November 1st, 2011 | #

  8. Sarah says

    Thanks for great solution, I needed to use a different mask like the following:
    mask=@”9 (999) 999 99 99″ and I was wondering about the changes I need to make to the code.
    thanks again,

    November 3rd, 2011 | #

Leave a comment

:mrgreen: :neutral: :twisted: :shock: :smile: :???: :cool: :evil: :grin: :oops: :razz: :roll: :wink: :cry: :eek: :lol: :mad: :sad:

 

RSS feed for these comments. | TrackBack URI

Visitors Around the World

Polls

How Is My Site?

View Results

Loading ... Loading ...

Categories

Meta

Links

Advertising

hosted by easy2use.net