I found that the shouldChangeCharactersInRange screws up the pop-up keyboard, backspace and "Done" button as well. I found if I handled 0 length strings and allowed control characters though, it worked fine.
I don't like using NSNumberFormatter because it insists that the number is well-formed at all stages while the user is editing and that can be infuriating if you, say, want to have two decimal points in the number for a moment until you delete the one that's in the wrong spot.
Here's the code I used:
- (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;
return ([string stringByTrimmingCharactersInSet:[self.characterSet invertedSet]].length > 0);
}
Where self.characterSet holds the characters that are acceptable, I used this method to create it for a currency:
- (NSCharacterSet *)createCurrencyCharacterSet
{
NSLocale *locale = [NSLocale currentLocale];
NSMutableCharacterSet *currencySet = [NSMutableCharacterSet decimalDigitCharacterSet];
[currencySet addCharactersInString:@"-"]; // negative symbol, can't find a localised version
[currencySet addCharactersInString:[locale objectForKey:NSLocaleCurrencySymbol]];
[currencySet addCharactersInString:[locale objectForKey:NSLocaleGroupingSeparator]];
[currencySet addCharactersInString:[locale objectForKey:NSLocaleDecimalSeparator]];
return [[currencySet copy] autorelease];
}
The somewhat unhappy code [[currencySet copy] autorelease]
returns an immutable NSCharacterSet.
Using [NSCharacterSet decimalDigitCharacterSet]
also includes the Indic and Arabic equivalent characters which hopefully means that people use those languages can use their alphabet's digits to enter numbers.
It's still necessary to check that NSNumberFormatter can parse the user's input and alert if it can't; nonetheless, it makes a nicer experience when only legit characters can be entered.