Local Links

External Links

Contact

Search this site

Validating and filtering UITextField input


A common task in iOS apps is to limit text input in UITextFields, e.g. to only allow numeric input (i.e. limit the input to numbers).

To make sure input from the user remains valid in such a text field, the shouldChangeCharactersInRange delegate method has to be implemented.

However, if you do that, be aware of a very common programming error.

If your code looks like this, you're doing it wrong:

  - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
  {
     if ([string length] < 1)    // non-visible characters are okay
          return YES;
      if ([string stringByTrimmingCharactersInSet:[NSCharacterSet controlCharacterSet]].length == 0)
          return YES;
      NSCharacterSet *nonNumberSet = [[NSCharacterSet characterSetWithCharactersInString:@"0123456789."] invertedSet];
      return [string stringByTrimmingCharactersInSet:nonNumberSet].length > 0;
  }

The mistake here is checking the replacement string and the previous contents separately. While this usually works as long as the user only types in new characters, it'll give you wrong test results as soon as the user selects a range of already existing text and replaces that with a paste or delete operation. Also, the above code lets you input nonsense such as "1.2.3.4".

The correct way to do this goes like this: You want to make sure that what appears in the text field is valid. Meaning you want to look at what would appear in the text field. Since shouldChangeCharactersInRange gets called after the user typed his input but before it actually appears in the text field, all you need to do is to see what the text would look like if you'd allow it go through. You get this result text using the stringByReplacingCharactersInRange method and then validate its contents, like this:

  - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
  {
    // First, create the text that will end up in the input field if you'll return YES:
    NSString *resultString = [textField.text stringByReplacingCharactersInRange:range withString:string];

    // Now, validate the text and return NO if you don't like what it'll contain.
    // You accomplish this by trying to convert it to a number and see if that worked.
    NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
    [numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
    NSNumber* resultingNumber = [numberFormatter numberFromString:resultString];
    [numberFormatter release];
    return resultingNumber != nil;
  }

To see a few good and bad examples of this, see this StackOverflow question.

Page last modified on 2012-04-14, 03:59 EDT
Powered by PmWiki