views:

380

answers:

1

Howdy all,

I'm working with a UITextField that holds a localized currency value. I've seen lots of posts on how to work with this, but my question is: how do I re-apply currency formatting to the UITextField after every key press?

I know that I can set up and use a currency formatter with:

NSNumberFormatter *currencyFormatter = [[NSNumberFormatter alloc] init];
[currencyFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
...
[currencyFormatter stringFromNumber:...];

but I don't know how to hook it up.

For instance, if the value in the field reads "$12,345" and the user taps the "6" key, then the value should change to "$123,456".

Which callback is the "correct" one to do this in (should I use textField:shouldChangeCharactersInRange:replacementString: or a custom target-action) and how do I use the NSNumberFormatter to parse and re-apply formatting to the UITextField's text property?

Any help would be much appreciated! Thanks!

A: 

I've been working on this issue and I think I figured out a nice, clean solution. I'll show you how to appropriately update the textfield on user input, but you'll have to figure out the localization yourself, that part should be easy enough anyway.

In the viewDidLoad I use the following code to initialize my textfield and numberformatter. Both are global variables defined in the header file.

- (void)viewDidLoad {
    [super viewDidLoad];

    textField = [[UITextField alloc] initWithFrame:CGRectMake(10.0f, 10.0f, 300.0f, 30.0f)];
    [textField setBackgroundColor:[UIColor whiteColor]];
    [textField setAutocorrectionType:UITextAutocorrectionTypeNo];
    [textField setAutocapitalizationType:UITextAutocapitalizationTypeNone];
    [textField setTextAlignment:UITextAlignmentCenter];
    [textField becomeFirstResponder];
    [textField setDelegate:self];
    [[self view] addSubview:textField];

    numberFormatter = [[NSNumberFormatter alloc] init]; 
    [numberFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
    [numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
    [numberFormatter setMaximumFractionDigits:10];
    [numberFormatter setMinimumFractionDigits:0];

    number = [[NSNumber alloc] initWithInt:0];  
    [textField setText:[numberFormatter stringFromNumber:number]];
}

This is the code in the UITextFieldDelegate method textField:shouldChangeCharactersInRange:replacementString:

- (BOOL)textField:(UITextField *)textField_ shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {     
    NSString *text = [textField text];  // we'll retrieve the old string from the textField to work with.
    NSString *decimalSeperator = @".";  // the appropriate decimalSeperator for the current localisation can be found with help of the
                                    // NSNumberFormatter and NSLocale classes.

    // we'll define a characterSet to filter all invalid chars. 
    // the entered string will be trimmed down to the valid chars only.

    NSCharacterSet *characterSet = nil;
    NSString *numberChars = @"0123456789";
    if ([text rangeOfString:decimalSeperator].location != NSNotFound) characterSet = [NSCharacterSet characterSetWithCharactersInString:numberChars];
    else characterSet = [NSCharacterSet characterSetWithCharactersInString:[numberChars stringByAppendingString:decimalSeperator]];
    NSCharacterSet *invertedCharSet = [characterSet invertedSet];   
    NSString *trimmedString = [string stringByTrimmingCharactersInSet:invertedCharSet];
    text = [text stringByReplacingCharactersInRange:range withString:trimmedString];

    // whenever a decimalSeperator is entered, we'll just update the textField.
    // whenever other chars are entered, we'll calculate the new number and update the textField accordingly.

    if ([string isEqualToString:decimalSeperator] == YES) {
        [textField setText:text];
    } else {
        NSNumber *number = [numberFormatter numberFromString:text];
        if (number == nil) number = [NSNumber numberWithInt:0];
        text = [numberFormatter stringFromNumber:number];
        [textField setText:text];       
    }

    return NO; // we return NO because we have manually edited the textField contents.
}

Edit: fixed memory leak.

Wolfgang Schreurs