Formatting TextField text as Price - iphone

I want to format the textfield text when user enter a value .. It work perfectly but my problem is ,If user press 1 then 2 after that decimale point . My replacement string is "12." At that time numbar is 12 . i am not able to take decimal point ..
NSNumber* number = [numberFormatter numberFromString:string];
Here is My full code,
#pragma mark textfieldDelgate
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSLocale *locale=[[NSLocale alloc]initWithLocaleIdentifier:#"en_GB"];
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setLocale:locale];
[numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
NSString *text = [textField text];
NSString *replacementText = [text stringByReplacingCharactersInRange:range withString:string];
NSMutableString *newReplacement = [[ NSMutableString alloc ] initWithString:replacementText];
NSString *currencyGroupingSeparator = numberFormatter.currencyGroupingSeparator;
[newReplacement replaceOccurrencesOfString:currencyGroupingSeparator withString:#"" options:NSBackwardsSearch range:NSMakeRange(0, [newReplacement length])];
NSNumber * number = [numberFormatter numberFromString:newReplacement];
if (number == nil && [replacementText length]!=0) {
return NO;
}
NSLog(#"%#",newReplacement);
text = [numberFormatter stringFromNumber:number];
[textField setText:text];
return NO;
}
I have also attached screenshot.
How can I format the string with decimal value......
Thanks in advance

For me the line of code:
NSNumber * number = [numberFormatter numberFromString:newReplacement];
does not returns nil but it removes the decimal entered by user. This is because with numberFormatter id you pass a value like '12.' or '12.0' it will remove the decimal as a part of formatting. For other values like 12.3 it will not remove decimal.
I am not able to understand your requirement for doing all this coding but if you want decimal to be there put some logic to check if number is in format of 12. or 12.0 , then escape formatting. Decimal will remain there.
Please use this code:
NSNumber * number;
if ([newReplacement hasSuffix:#"."] || [newReplacement hasSuffix:#".0"])
{
[textField setText:newReplacement]; return NO;
}
else
{
number = [numberFormatter numberFromString:newReplacement];
}

You'll probably want to use NSNumberFormatterCurrencyStyle instead of NSNumberFormatterDecimalStyle if you're dealing with formatting money. It might Just Work™ after setting that.
But, it might not. Regardless of all that, pretty sure the reason for the behavior you're seeing is that you're not giving it a number from your string; 12 is a number, as is 12.5, but, 12. is not. I'm surprised its not returning nil outright.
If thats the case (and its still broken), than you'll probably want to special-case having the . at the end, and append instead of passing it into the number formatter.

I have tried printing your code in my log. It is working fine for me.I think there is some other type of issue.
NSLocale *locale=[[NSLocale alloc]initWithLocaleIdentifier:#"en_GB"];
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setLocale:locale];
[numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
NSNumber* number = [numberFormatter numberFromString:#"12.4"];
NSLog(#"number is %#",number);
log print-----
number is 12.4

//Add this in .h file
NSNumberFormatter *formatter;
NSInteger currencyScale;
NSString *enteredDigits;
//Add this in .m file`enter code here`
-(void)viewdidload
{
NSNumberFormatter *aFormatter = [[NSNumberFormatter alloc] init];
[aFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
currencyScale = -1 * [aFormatter maximumFractionDigits];
self.formatter = aFormatter;
}
#pragma UItextfield Delegate
- (void)textFieldDidBeginEditing:(UITextField *)textField {
// Keep a pointer to the field, so we can resign it from a toolbar
Field = textField;
if(textField.text==NULL || [textField.text isEqualToString:#""])
{
self.enteredDigits = #"";
//textField.text=#"";
}
}
- (void)textFieldDidEndEditing:(UITextField *)textField {
}
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string { //give tag to text field
if (textField.tag==10) {
// self.priceInput = textField;
// Check the length of the string
int len=[self.enteredDigits length];
NSLog(#"string lenght is %d",len);
if ([string length]) {
self.enteredDigits = [self.enteredDigits stringByAppendingFormat:#"%d", [string integerValue]];
} else {
// This is a backspace
NSUInteger len = [self.enteredDigits length];
if (len > 1) {
self.enteredDigits = [self.enteredDigits substringWithRange:NSMakeRange(0, len - 1)];
} else {
self.enteredDigits = #"";
}
}
NSDecimalNumber *decimal = nil;
if ( ![self.enteredDigits isEqualToString:#""]) {
decimal = [[NSDecimalNumber decimalNumberWithString:self.enteredDigits] decimalNumberByMultiplyingByPowerOf10:currencyScale];
float deci= [decimal floatValue];
NSLog(#"decimall==%f",deci);
} else {
decimal = [NSDecimalNumber zero];
}
// Replace the text with the localized decimal number
float deci= [decimal floatValue];
NSLog(#"decimall==%f",deci);
NSString *temp = [self.formatter stringFromNumber:decimal];
textField.text=temp;
//NSLog(#"Text fielddddddddddddddf=%# %#",numberText,temp);
return NO;
}
return YES;
}
- (BOOL)textFieldShouldClear:(UITextField *)textField
{
textField.text=#"";
self.enteredDigits=#"";
NSLog(#"Clear Clicked");
return YES;
}
I think this code will help you

Related

0 Not working in currency type input

I am taking the input in form of currency like 54 gets converted to 0.54,but When I am trying to enter 100, I get out as 0.1 only.The code is not working for 0. You cannot enter value as 100.00 .The code I am using is
(BOOL)textField:(UITextField *)transactionAmount shouldChangeCharactersInRange:(NSRange)range
replacementString:(NSString *)string
{
NSString *substring = transactionAmount.text;
substring = [substring stringByAppendingString:string];
NSLog(#"Text : %#",substring);
NSString *cleanCentString = [[transactionAmount.text
componentsSeparatedByCharactersInSet:
[[NSCharacterSet decimalDigitCharacterSet] invertedSet]]
componentsJoinedByString:#""];
// Parse final integer value
NSInteger centAmount = cleanCentString.integerValue;
// Check the user input
if (string.length > 0)
{
// Digit added
centAmount = centAmount * 10 + string.integerValue;
}
else
{
// Digit deleted
centAmount = centAmount / 10;
}
// Update call amount value
NSNumber *amount = [[NSNumber alloc] initWithFloat:(float)centAmount / 100.0f];
// Write amount with currency symbols to the textfield
NSNumberFormatter *_currencyFormatter = [[NSNumberFormatter alloc] init];
// [_currencyFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
[_currencyFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
[_currencyFormatter setCurrencyCode:#"USD"];
[_currencyFormatter setNegativeFormat:#"-¤#,##0.00"];
self.transactionAmount.text = [_currencyFormatter stringFromNumber:amount];
// [self SetMainMessage:customTipsValue.text];
return NO;
}
Most of your initial "string cleaning" is wrong and your number formatter isn't correct. It should be something like this:
- (BOOL)textField:(UITextField *)transactionAmount shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
NSString *substring = transactionAmount.text;
substring = [substring stringByReplacingCharactersInRange:range withString:string];
NSLog(#"New Text : %#",substring);
NSString *cleanCentString = [[substring
componentsSeparatedByCharactersInSet:
[[NSCharacterSet decimalDigitCharacterSet] invertedSet]]
componentsJoinedByString:#""];
// Parse final integer value
NSInteger centAmount = cleanCentString.integerValue;
// Update call amount value
NSNumber *amount = [[NSNumber alloc] initWithFloat:centAmount / 100.0f];
// NOTE: make this an instance variable and set it up just once
// Write amount with currency symbols to the textfield
NSNumberFormatter *_currencyFormatter = [[NSNumberFormatter alloc] init];
[_currencyFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
[_currencyFormatter setCurrencyCode:#"USD"];
[_currencyFormatter setNegativeFormat:#"-¤#,##0.00"];
self.transactionAmount.text = [_currencyFormatter stringFromNumber:amount];
return NO;
}
If, for some reason, you want to use Decimal format instead of Currency format, make sure you set the minimum and maximum fraction digits (decimal places) to 2.
Do you really want to hardcode USD? What about people using the app in other countries?
Your original string cleaning didn't properly support a user using cut, copy, or paste. It also used the wrong text to create cleanCentString.
What I understood is
If entered value is 0, you want 0.
If entered value is between 1 and 99, you want 0.01 to 0.99.
If entered value is 1 or more, you want 1.00, like wise.
Why don't you get straight as float requiredCurrency=inputCurrency/100.0f;

How to set character length of TextField by deformatting the formatted number

I used the below code to set character lentgh to 17:
- (BOOL)textField:(UITextField *)theTextField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
// NSString *stringWithoutSpaces = [theTextField.text stringByReplacingOccurrencesOfString:#"," withString:#""];
int limit = 16;
switch (theTextField.tag) {
case 10:
NSLog(#"I m In 10");
//To set maximum No.of Charaters.
return !([theTextField.text length]>limit && [string length] > range.length);
break;
case 11:
NSLog(#"I m In 11");
//To set maximum No.of Charaters.
return !([theTextField.text length]>limit && [string length] > range.length);
break;
default:
NSLog(#"I m In default");
break;
}
return YES;
}
I am formatting the entered charaters in the textfieldShouldEndEditing,
For Example,
I entered 12345678912345678, its limit up to 17 and its formatting to 12,345,678,912,345,678
and we again back to TextField and we edited to 12,345,678,912,345 then it considers , (comma) as one character, so length of the TextField increases.
How to resolve this problem?
Simple solution:
NSString *stringNumber = #"12,345,678,912,345";
NSUInteger length = [[stringNumber stringByReplacingOccurrencesOfString:#"," withString:#""] length];
NSLog(#"length: %lu", length);
NSLog output:
length: 14
Use a number formatter.
Example:
NSString *stringNumber = #"12,345,678,912,345";
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setNumberStyle:NSNumberFormatterDecimalStyle];
NSNumber *number = [formatter numberFromString:stringNumber];
NSLog(#"number: %#", number);
stringNumber = [number stringValue];
NSLog(#"stringNumber: %#", stringNumber);
NSLog output:
number: 12345678912345
stringNumber: 12345678912345

UITextField validation

I am creating a text field.
Whenever the value in text field is greater than 100 I need to display an alert saying "value must be less than 100", then the text field's value should be cleared.
How can I do this?
You can have your view controller conform to the UITextFieldDelegate protocol and implement the -textField:shouldChangeCharactersInRange:replacementString: method:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
if (range.location == 0 && string.length == 0) {
return YES;
}
// Build up the resulting string…
NSMutableString *fullString = [[NSMutableString alloc] init];
[fullString appendString:[textField.text substringWithRange:NSMakeRange(0, range.location)]];
[fullString appendString:string];
// Set up number formatter…
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
NSNumber *replaceNumber = [formatter numberFromString:fullString];
[fullString release];
[formatter release];
return !(replaceNumber == nil || [replaceNumber intValue] > 100);
}
To build the full string you must take into account edits in the middle of the text field too. Made some changes to the code posted above.
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
BOOL result = NO;
// Build the final string...
NSMutableString *fullString = [[NSMutableString alloc] init];
NSUInteger firstChunkLength = range.location;
NSUInteger secondChunkStart = range.location + range.length;
NSUInteger secondChunkLength = [textField.text length] - secondChunkStart;
[fullString appendString:[textField.text substringWithRange:NSMakeRange( 0, firstChunkLength )]];
[fullString appendString:string];
[fullString appendString:[textField.text substringWithRange:NSMakeRange( secondChunkStart, secondChunkLength )]];
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
NSNumber *replaceNumber = [formatter numberFromString:fullString];
NSNumber *originalNumber = [formatter numberFromString:textField.text];
result = ( replaceNumber != nil && replaceNumber != originalNumber );
[fullString release];
[formatter release];
return result;
}
This method is used to validate a UItextfield for only numeric values.
It is a perfect integer validation.
- (BOOL) textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSCharacterSet *nonNumberSet = [[NSCharacterSet characterSetWithCharactersInString:#"0123456789."] invertedSet];
return ([string stringByTrimmingCharactersInSet:nonNumberSet].length > 0);
}
Thankfully, now that we have Swift, this is MUCH easier. You do have to construct the prospective string (hopefully when Apple rewrites Cocoa in Swift this interface will add that).
Meantime, here is a simple one liner to do so:
let constructedString = (self.inputField.text! as NSString).stringByReplacingCharactersInRange(range, withString: string)
Note, self.inputField is of course an outlet to the field in question. Also, the casting to NSString is because the range we are passed is an NSRange.
I have create a class JSInputField (subclass of UItextField) which lets user add validation very easily. For eg.
JSInputField *inputField = [[JSInputField alloc] initWithFrame:CGRectMake(10, 100, 300, 50)];
[self.view addSubview:inputField];
[inputField setPlaceholder:#"Enter Text"];
[inputField setRoundedCorners:UIRectCornerAllCorners];
[inputField addValidationRule:JSCreateRuleNotNullValue]; //This will validate field for null value. It will show error if field is empty.
[inputField addValidationRule:JSCreateRuleNumeric(2)]; //This will validate field for numeric values and restrict to enter value upto 2 decimal places.

Realtime formatting with NSNumberFormatter in a UITextfield

I have UITextfield where a user can put in a dollar amount, I would like the textfield always to be formatted with two decimals ($ .##). The formatting has to be maintained all the time. But i'm stuck on how to append the entered numbers to existing ones in the textfield ?
//This delegate is called everytime a character is inserted in an UITextfield.
- (BOOL) textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if ([textField tag] == amountTag)
{
NSString *amount = string;
//How do I append the already entered numbers in the textfield to the new entered values ?
//??
//Get new formatted value
NSString *newAmount = [self formatCurrencyValue:[amount doubleValue]];
[textField setText:[NSString stringWithFormat:#"%#",newAmount]];
return NO;
}
//Returning yes allows the entered chars to be processed
return YES;
}
-(NSString*) formatCurrencyValue:(double)value
{
NSNumberFormatter *numberFormatter = [[[NSNumberFormatter alloc] init]autorelease];
[numberFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
[numberFormatter setCurrencySymbol:#"$"];
[numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
NSNumber *c = [NSNumber numberWithFloat:value];
return [numberFormatter stringFromNumber:c];
}
Here's the idea...
Store the string value, append to it, then use that for the operation.
Define an NSMutableString in the .h file
NSMutableString *storedValue;
#property (nonatomic, retain) NSMutableString *storedValue;
Synthesize it.
Then do this...
- (BOOL) textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if ([textField tag] == amountTag)
{
[storedValue appendString:string];
NSString *newAmount = [self formatCurrencyValue:([storedValue doubleValue]/100)];
[textField setText:[NSString stringWithFormat:#"%#",newAmount]];
return NO;
}
//Returning yes allows the entered chars to be processed
return YES;
}

What's the best way to validate currency input in UITextField?

My application allows the user to enter a numeric value (currency) in a UITextField control, but the keyboard layout that I wish was available is unfortunately not one of the built-in options, so I had to choose the "Numbers & Punctuation" option in Interface Builder. Here's the corresponding dialog window in IB:
So when my application asks the user for the input, it is displaying the following:
Which is perfectly fine, but look at all of the extra keys available to the user! One could easily enter "12;56!" in the text field, and I assume I have to validate that somehow.
So my question is: how do I validate currency values entered into a UITextField?
I have the urge to answer because this was the first entry I saw when I googled and the highest ranked answer wouldn't allow me to enter a currency.
I'm german, and in germany (and in many other countries) we use , as the decimalseparator.
I just wrote a similar method and this is what I have right now.
- (BOOL) textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
static NSString *numbers = #"0123456789";
static NSString *numbersPeriod = #"01234567890.";
static NSString *numbersComma = #"0123456789,";
//NSLog(#"%d %d %#", range.location, range.length, string);
if (range.length > 0 && [string length] == 0) {
// enable delete
return YES;
}
NSString *symbol = [[NSLocale currentLocale] objectForKey:NSLocaleDecimalSeparator];
if (range.location == 0 && [string isEqualToString:symbol]) {
// decimalseparator should not be first
return NO;
}
NSCharacterSet *characterSet;
NSRange separatorRange = [textField.text rangeOfString:symbol];
if (separatorRange.location == NSNotFound) {
if ([symbol isEqualToString:#"."]) {
characterSet = [[NSCharacterSet characterSetWithCharactersInString:numbersPeriod] invertedSet];
}
else {
characterSet = [[NSCharacterSet characterSetWithCharactersInString:numbersComma] invertedSet];
}
}
else {
// allow 2 characters after the decimal separator
if (range.location > (separatorRange.location + 2)) {
return NO;
}
characterSet = [[NSCharacterSet characterSetWithCharactersInString:numbers] invertedSet];
}
return ([[string stringByTrimmingCharactersInSet:characterSet] length] > 0);
}
While you can putz around in the NSNumberFormatter, I found it easier to just screen out anything but 0-9 and .
This is working well for me:
- (BOOL) textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSCharacterSet *nonNumberSet = [[NSCharacterSet characterSetWithCharactersInString:#"0123456789."] invertedSet];
return ([string stringByTrimmingCharactersInSet:nonNumberSet].length > 0);
}
I found this to be a relatively clean approach. I haven't tested it with non-US currencies but since it uses the NSNumberFormatter's properties I believe it should handle them correctly.
Start by setting up a formatter some place:
formatter = [NSNumberFormatter new];
[formatter setNumberStyle: NSNumberFormatterCurrencyStyle];
[formatter setLenient:YES];
[formatter setGeneratesDecimalNumbers:YES];
Use the formatter to parse and reformat their input. It also handles shifting the number when digits are added and removed.
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSString *replaced = [textField.text stringByReplacingCharactersInRange:range withString:string];
NSDecimalNumber *amount = (NSDecimalNumber*) [formatter numberFromString:replaced];
if (amount == nil) {
// Something screwed up the parsing. Probably an alpha character.
return NO;
}
// If the field is empty (the initial case) the number should be shifted to
// start in the right most decimal place.
short powerOf10 = 0;
if ([textField.text isEqualToString:#""]) {
powerOf10 = -formatter.maximumFractionDigits;
}
// If the edit point is to the right of the decimal point we need to do
// some shifting.
else if (range.location + formatter.maximumFractionDigits >= textField.text.length) {
// If there's a range of text selected, it'll delete part of the number
// so shift it back to the right.
if (range.length) {
powerOf10 = -range.length;
}
// Otherwise they're adding this many characters so shift left.
else {
powerOf10 = [string length];
}
}
amount = [amount decimalNumberByMultiplyingByPowerOf10:powerOf10];
// Replace the value and then cancel this change.
textField.text = [formatter stringFromNumber:amount];
return NO;
}
Perhaps you could attach a UITextFieldDelegate on the control and have it implement textField:shouldChangeCharactersInRange:replacementString: That way, if it sees any characters that you don't want in the field, it can reject them.
In conjunction with the textField:shouldChangeCharactersInRange:replacementString: suggestion made by Marc, you should pass the text through an NSNumberFormatter using an NSNumberFormatterCurrencyStyle. This will handle the quirks of currency formatting and handle locale specific options.
There's a "Data Formatting Programming Guide for Cocoa" section in the iPhone documentation if you search for it. Sadly, most of the UI information here is Mac OS X specific (doesnt work on iPhone) but it'll show you how to use the formatter classes.
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.
I keep seeing that people don't know how to properly determine the resulting string when implementing shouldChangeCharactersInRange.
Here's how you get the new text that would be entered if you returned YES:
NSString *newText = [textField.text stringByReplacingCharactersInRange:range withString:string];
Once you have this new text, it's easy to check if that text is a valid number or not, and return YES or NO accordingly.
Keep in mind that all other solutions, such as those shown here, where one checks the length of "string", and such, may not work properly if the user tries to edit the string using a bluetooth keyboard with cursor keys or using the advanced editing features (select, cut, paste).
If you want them to only be able to enter numbers - you might also consider the number keypad
After finding a quick solution on stack overflow to handle US currency, I rewrote the function to safely handle international currencies as well. This solution will dynamically validate user input from a UITextField, correcting as they type.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
NSNumberFormatter *nf = [[NSNumberFormatter alloc] init];
[numberFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
[numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
[numberFormatter setMaximumFractionDigits:2];
[numberFormatter setMinimumFractionDigits:0];
// Grab the contents of the text field
NSString *text = [textField text];
// the appropriate decimalSeperator and currencySymbol for the current locale
// can be found with help of the
// NSNumberFormatter and NSLocale classes.
NSString *decimalSeperator = numberFormatter.decimalSeparator;
NSString *currencySymbol = numberFormatter.currencySymbol;
NSString *replacementText = [text stringByReplacingCharactersInRange:range withString:string];
NSMutableString *newReplacement = [[ NSMutableString alloc ] initWithString:replacementText];
// whenever a decimalSeperator or currencySymobol 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 the number can't be computed, we ignore the new input.
NSRange decimalRange = [text rangeOfString:decimalSeperator];
if ([string isEqualToString:decimalSeperator] == YES &&
[text rangeOfString:decimalSeperator].length == 0) {
[textField setText:newReplacement];
} else if([string isEqualToString:currencySymbol] == YES&&
[text rangeOfString:currencySymbol].length == 0) {
[textField setText:newReplacement];
} else if([newReplacement isEqualToString:currencySymbol] == YES) {
return YES;
}else {
NSString *currencyGroupingSeparator = numberFormatter.currencyGroupingSeparator;
[newReplacement replaceOccurrencesOfString:currencyGroupingSeparator withString:#"" options:NSBackwardsSearch range:NSMakeRange(0, [newReplacement length])];
[numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
NSNumber *number = [numberFormatter numberFromString:newReplacement];
if([newReplacement length] == 1) {
[numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
number = [numberFormatter numberFromString:newReplacement];
}
if (number == nil) {
[newReplacement release];
return NO;
}
[numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
text = [numberFormatter stringFromNumber:number];
[textField setText:text];
}
[newReplacement release];
return NO; // we return NO because we have manually edited the textField contents.
}
Using an NSNumberFormatter is the correct answer. It will handle validation and converting the string to and from the correct object type.
You can also use Gamma-Point solution and evaluate if *aNumber is Nil, if its Nil, then it means a character that wasnt 0-9 . or , was entered, this way you can validate just numbers, it will nil the variable if any no-number char is entered.
I Couldn't find the appropriate implementation for currency validate. Here is my variant:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
NSString* proposedString = [textField.text stringByReplacingCharactersInRange:range withString:string];
//if there is empty string return YES
if([proposedString length] == 0) {
return YES;
}
//create inverted set for appripriate symbols
NSCharacterSet *nonNumberSet = [[NSCharacterSet characterSetWithCharactersInString:#"0123456789.,"] invertedSet];
//if side symbols is trimmed by nonNumberSet - return NO
if([proposedString stringByTrimmingCharactersInSet:nonNumberSet].length != [proposedString length]) {
return NO;
}
//if there is more than 1 symbol of '.' or ',' return NO
if([[proposedString componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:#".,"]] count] > 2) {
return NO;
}
//finally check is ok, return YES
return YES;
}
Using shouldChangeCharactersInRange screws up the pop-up key board as the backspace button doesn't work.
Number formatter is the way to go. Here's a sample I used to find positive decimals. I call it during the validation check- for e.g. when the user clicks on the save button.
-(BOOL) isPositiveNumber: (NSString *) numberString {
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
NSNumber *aNumber = [numberFormatter numberFromString:numberString];
[numberFormatter release];
if ([aNumber floatValue] > 0) {
NSLog( #"Found positive number %4.2f",[aNumber floatValue] );
return YES;
}
else {
return NO;
}
}