iosobjective-c

How do you dynamically format a number to have commas in a UITextField entry?


I want to have commas dynamically added to my numeric UITextField entry while the user is typing.

For example: 123,456 and 12,345,678, but not like this 123,45 or 123,4567.

How does one automatically append the commas while a user is typing a number in Objective-C?

Edit: I'd also like to be able to allow the user to input decimals.


Solution

  • Instead of inserting the commas on your own in shouldChangeCharactersInRange:, you can use an NSNumberFormatterDecimalStyle to handle the comma formatting for you. Even though it's called "decimal" style, it also inserts commas to appropriately group numbers into their thousands digits.

    Note: To simplify matters, I'll assume you only want the text field to accept numeric entries and I'll also add logic to limit the user's input to numbers.

    Edit: I've updated the code to handle decimals also as per the OP's request.

    To utilize NSNumberFormatterDecimalStyle's formatting upon every character entry, try adding this to your shouldChangeCharactersInRange: delegate method:

    - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
    
        if (([string isEqualToString:@"0"] || [string isEqualToString:@""]) && [textField.text rangeOfString:@"."].location < range.location) {
            return YES;
        }
    
        // First check whether the replacement string's numeric...
        NSCharacterSet *cs = [[NSCharacterSet characterSetWithCharactersInString:@"0123456789"] invertedSet];
        NSString *filtered = [[string componentsSeparatedByCharactersInSet:cs] componentsJoinedByString:@""];
        bool isNumeric = [string isEqualToString:filtered];
    
        // Then if the replacement string's numeric, or if it's 
        // a backspace, or if it's a decimal point and the text
        // field doesn't already contain a decimal point,
        // reformat the new complete number using
        // NSNumberFormatterDecimalStyle
        if (isNumeric ||
            [string isEqualToString:@""] ||
            ([string isEqualToString:@"."] &&
             [textField.text rangeOfString:@"."].location == NSNotFound)) {
    
            // Create the decimal style formatter
            NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
            [formatter setNumberStyle:NSNumberFormatterDecimalStyle];
            [formatter setMaximumFractionDigits:10];
    
            // Combine the new text with the old; then remove any
            // commas from the textField before formatting
            NSString *combinedText = [textField.text stringByReplacingCharactersInRange:range withString:string];
            NSString *numberWithoutCommas = [combinedText stringByReplacingOccurrencesOfString:@"," withString:@""];
            NSNumber *number = [formatter numberFromString:numberWithoutCommas];
    
            NSString *formattedString = [formatter stringFromNumber:number];
    
            // If the last entry was a decimal or a zero after a decimal,
            // re-add it here because the formatter will naturally remove
            // it.
            if ([string isEqualToString:@"."] &&
                range.location == textField.text.length) {
                formattedString = [formattedString stringByAppendingString:@"."];
            }
    
            textField.text = formattedString;
    
        }
    
        // Return no, because either the replacement string is not 
        // valid or it is and the textfield has already been updated
        // accordingly
        return NO;
    }