Question

I have created a text field to enter an amount of money. I want the user to be able to enter only one decimal point. I implemented that in the -(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string method.

In the general case it works fine, but if backspace is pressed and the single decimal point is deleted, after that it still assumes that a decimal point has been entered, and so does not accept the decimal point again.

I need to reset the decimalPointEntered flag whenever the decimal point is deleted by pressing backspace. How to do that ?

Was it helpful?

Solution

This works fine for me. Try this code:

-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
    NSString *newString = [textField.text stringByReplacingCharactersInRange:range withString:string];

    NSArray *sep = [newString componentsSeparatedByString:@"."];
    if([sep count] >= 2)
    { 
        NSString *sepStr=[NSString stringWithFormat:@"%@",[sep objectAtIndex:1]];
        return !([sepStr length]>1);
    }
    return YES;
}

OTHER TIPS

Here is what I use:

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
    if (!numberFormatter) {
        numberFormatter = [[NSNumberFormatter alloc]init];
        //[currencyFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];

    }
    NSNumber *test = [numberFormatter numberFromString:[self.text stringByAppendingString: string]];  // in case we entered two decimals

    return (test != nil);

}

I am limiting my user to input amount should be like this

  • User should be able to enter only 0-9 and decimal . characters
  • Only one decimal . character allowed
  • Max 5 digit before decimal .
  • Max 2 digit after decimal .

40543.48

So I updated shouldChangeCharactersInRange method this way

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string;   {

    if (textField == self.txtFieldTransferAmount)
    {
        NSString *updatedText = [textField.text stringByReplacingCharactersInRange:range withString:string];
        NSArray *stringsArray = [updatedText componentsSeparatedByString:@"."];

        // Before decimal only 5 digit allowed
        if (stringsArray.count > 0)
        {
            NSString *dollarAmount = stringsArray[0];
            if (dollarAmount.length > 5)
                return NO;
        }

        // After decimal only 2 digit allowed
        if (stringsArray.count > 1)
        {
            NSString *centAmount = stringsArray[1];
            if (centAmount.length > 2)
                return NO;
        }

        // Only one . decimal allowed
        if (stringsArray.count > 2)
            return NO;

        if (textField.text.length < 8) {
            //User should able to enter only 0-9 and . characters
            NSCharacterSet *cs = [[NSCharacterSet characterSetWithCharactersInString:@"0123456789."] invertedSet];
            NSString *filtered = [[string componentsSeparatedByCharactersInSet:cs] componentsJoinedByString:@""];
            return [string isEqualToString:filtered];
        }
        else
        {
            if (range.length > 0)
            {
                return true;
            }
            else{
                return false;
            }
        }
    }
    return true;
}

I have a very similar solution. Worked better for me. It also validates text input.

-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
    NSLog(@"in shouldChangeCharactersInRange");


    if([string length]==0){
        return YES;
    }

    //Validate Character Entry
    NSCharacterSet *myCharSet = [NSCharacterSet characterSetWithCharactersInString:@"0123456789,."];
    for (int i = 0; i < [string length]; i++) {
        unichar c = [string characterAtIndex:i];

        if ([myCharSet characterIsMember:c]) {

            //now check if string already has 1 decimal mark
            NSString *newString = [textField.text stringByReplacingCharactersInRange:range withString:string];
            NSArray *sep = [newString componentsSeparatedByString:@"."];
            NSLog(@"sep: %@ count: %d" ,sep,[sep count]);
            if([sep count]>2) return NO;
            return YES;

        }

    }

    return NO;
}

May this help someone ..

NSString *newString = [textField.text stringByReplacingCharactersInRange:range withString:string];
        // if . at the beggining
        if ([newString isEqualToString:@"."] && newString.length== 1) {
            textField.text = @"0.";
            return NO;
        }

        // do not allow . more then once
        NSArray *components = [newString componentsSeparatedByString:@"."];
        if([components count] > 2)
        {
            return NO;
        }

Sounds like you need an NSNumberFormatter.

Try this it is a swift code but you can refer the logic:-

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {


    if(string == "." ){
        let countdots = textField.text!.componentsSeparatedByString(".").count - 1

        if countdots > 0 && string == "."
        {
            return false
        }
    }
    return true 
}

This is the easiest way is to do this.


-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
    NSString *newString = [textField.text stringByReplacingCharactersInRange:range withString:string];
    NSArray *explodedNumbersArray = [newString componentsSeparatedByString:@"."];

    // We check if the array has 3 or more items which means that it has 2 decimal places or more
    // we return NO if that happens so that the textfield will not recognize input
    if([explodedNumbersArray count] >= 3) {
        return NO;
    }

    return YES;
}

Note: Make sure that a view or view controller that should receive the delegate notifications should conform to the UITextFieldDelegate.

I use this method to limit the number of decimal characters to one AND allow the user to enter negative amounts if they need to (i.e. for discounts):

NSCharacterSet *cs = [[NSCharacterSet characterSetWithCharactersInString:@"1234567890.-"] invertedSet];
if ([string rangeOfCharacterFromSet:cs].location == NSNotFound) {

    NSString *newString = [[textField text] stringByReplacingCharactersInRange:range withString:string];

    // check number of decimal chars used (maximum of one)
    NSArray *array = [newString componentsSeparatedByString:@"."];
    if ([array count] <= 2) {

        // check negative chars (maximum of one AND it must be at the front)
        array = [newString componentsSeparatedByString:@"-"];

        if (1 == [array count]) {
            return YES; // no negatives
        } else if (2 == [array count]) {
            return ('-' == [newString characterAtIndex:0]); // return YES if first char is negative only
        } else {
            return NO; // too many negative chars
        }
    } else {
        return NO; // too many decimal chars
    }
} else {
    return NO; // invalid character
}

I would always prefer a regex over some piece of code based rule for such kinds of scenario.

To restrict one decimal point and one character after it, use below;

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
   // Only allow one decimal point and 1 digit after it
   NSString *newString = [textField.text stringByReplacingCharactersInRange:range withString:string];
   NSString *expression = @"^[0-9]$*((\\.|,)[0-9]{0,1})?$";
   NSError *error = nil;
   NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:expression options:NSRegularExpressionCaseInsensitive error:&error];
   NSUInteger numberOfMatches = [regex numberOfMatchesInString:newString options:0 range:NSMakeRange(0, [newString length])];
   return numberOfMatches != 0;
   return YES;
 }

The above code will make sure that the text field only contains one decimal point and only one character after it.

You can play with this regex and create rule based on your requirement.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top