Having trouble using NSNumberFormatter for currency conversion in iOS - ios

I have a UITextField that receives numeric input from the user in my application. The values from this textfield then get converted into currency format using NSNumberFormatter within my shouldChangeCharactersInRange delegate method. When I enter the number "12345678", the number gets correctly converted to $123456.78 (the numbers are entered one digit at a time, and up to this point, everything works smoothly). However, when I enter another digit after this (e.g. 9), rather than displaying "1234567.89", the number "1234567.88" is displayed. If I enter another number after that, a totally different numbers after this (I'm using the number key pad in the application to enter the numbers. Here is the code that I have:
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setNumberStyle:NSNumberFormatterCurrencyStyle];
modifiedValue = [formatter stringFromNumber:[NSNumber numberWithFloat:[modifiedValue floatValue]]];
textField.text = modifiedValue;
The line that causes this unusual conversion is this one:
modifiedValue = [formatter stringFromNumber:[NSNumber numberWithFloat:[modifiedValue floatValue]]];
Can anyone see why this is?

It's likely to be a rounding error when doing the string->float conversion. You shouldn't use floats when dealing with currency. You could use a NSDecimalNumber instead.
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setNumberStyle:NSNumberFormatterCurrencyStyle];
// Below 2 lines if converting from a "currency" string
NSNumber *modifiedNumber = [formatter numberFromString:modifiedValue]; // To convert from the currency string to a number object
NSDecimalNumber *decimal = [NSDecimalNumber decimalNumberWithDecimal:[modifiedNumber decimalValue]];
// OR the below line if converting from a non-currency string
NSDecimalNumber *decimal = [NSDecimalNumber decimalNumberWithString:modifiedValue];
modifiedValue = [formatter stringFromNumber:decimal]; // Convert the new decimal back to a currency string
You may also consider making the number formatter lenient - often helps with user entered data.
[formatter setLenient:YES];

When I'm running number conversions to currency, I usually run this code:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSString *text = _textField.text;
NSString *decimalSeperator = #".";
NSCharacterSet *charSet = nil;
NSString *numberChars = #"0123456789";
// the number formatter will only be instantiated once ...
static NSNumberFormatter *numberFormatter;
if (!numberFormatter)
{
[numberFormatter setLocale:[NSLocale currentLocale]];
numberFormatter = [[NSNumberFormatter alloc] init];
numberFormatter.numberStyle = NSNumberFormatterCurrencyStyle;
numberFormatter.maximumFractionDigits = 10;
numberFormatter.minimumFractionDigits = 0;
numberFormatter.decimalSeparator = decimalSeperator;
numberFormatter.usesGroupingSeparator = NO;
}
// create a character set of valid chars (numbers and optionally a decimal sign) ...
NSRange decimalRange = [text rangeOfString:decimalSeperator];
BOOL isDecimalNumber = (decimalRange.location != NSNotFound);
if (isDecimalNumber)
{
charSet = [NSCharacterSet characterSetWithCharactersInString:numberChars];
}
else
{
numberChars = [numberChars stringByAppendingString:decimalSeperator];
charSet = [NSCharacterSet characterSetWithCharactersInString:numberChars];
}
// remove amy characters from the string that are not a number or decimal sign ...
NSCharacterSet *invertedCharSet = [charSet 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.text = text;
}
else
{
NSNumber *number = [numberFormatter numberFromString:text];
if (number == nil)
{
number = [NSNumber numberWithInt:0];
}
textField.text = isDecimalNumber ? text : [numberFormatter stringFromNumber:number];
}
return NO; // we return NO because we have manually edited the textField contents.
}
The link explaining this is Re-Apply currency formatting to a UITextField on a change event
Hope this works!

Related

Convert numbers to currency

I have number 36381129. I need number 36.381,129
I tried this code, but it doesn't work.
int number = 36381129;
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setNumberStyle: NSNumberFormatterCurrencyStyle];
NSString *numberAsString = [numberFormatter stringFromNumber: [NSNumber numberWithInt:number]];
I give this number.
36.381.129,00 $
I think this is BRAZILIAN REAL CURRENCY Format. You have to call this method with your price in float value, and this method returns your string into your format. Like if we pass 123456789, then it will return 123,456,789.00.
//Convert Price to Your Price Format
+(NSString*)convertFormat:(float)value{
NSString * convertedString = [NSString stringWithFormat:#"%.2f", value];
NSString * leftPart;
NSString * rightPart;
if (([convertedString rangeOfString:#"."].location != NSNotFound)) {
rightPart = [[convertedString componentsSeparatedByString:#"."] objectAtIndex:1];
leftPart = [[convertedString componentsSeparatedByString:#"."] objectAtIndex:0];
}
//NSLog(#"%d",[leftPart length]);
NSMutableString *mu = [NSMutableString stringWithString:leftPart];
if ([mu length] > 3) {
[mu insertString:#"." atIndex:[mu length] - 3];
//NSLog(#"String is %# and length is %d", mu, [mu length]);
}
for (int i=7; i<[mu length]; i=i+4) {
[mu insertString:#"." atIndex:[mu length] - i];
//NSLog(#"%d",mu.length);
}
convertedString = [[mu stringByAppendingString:#","] stringByAppendingString:rightPart];
return convertedString;
}
For more details, refer this blog.
Hope, this is what you're looking for. Any concern get back to me.
Welcome to SO. Your question is pretty vague.
Currency formats depend on the user's locale. It's generally better to either use the default locale of the device, or set a locale, and then let the currency formatter create that string that's appropriate for that locale.
If you set up a hard-coded currency format then it will be wrong for some users. (For example in the US we use a "." as a decimal separator and commas as a grouping symbol. In most of Europe they use a comma as a decimal separator and the period as a grouping symbol. Some countries put the currency symbol at the end of a currency amount, and others put it at the beginning.)
You can use this code:
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setNumberStyle:NSNumberFormatterCurrencyStyle];
NSString *groupingSeparator = [[NSLocale currentLocale] objectForKey:NSLocaleGroupingSeparator];
[formatter setGroupingSeparator:groupingSeparator];
[formatter setGroupingSize:3];
[formatter setAlwaysShowsDecimalSeparator:NO];
[formatter setUsesGroupingSeparator:YES];
and use it this way:
NSString *formattedString = [formatter stringFromNumber:[NSNumber numberWithFloat:rev];
This is a generic solution and will work for any country according to their grouping separator
Taken from: https://stackoverflow.com/a/5407103/2082569

Format a UITextField for currency

I have a UITextField on my aplication that receives only numeric input from the user. This numeric input represents currency and have the default value of 0.00.
I would like to create something like a mask to format the UITextField as the user enter the numbers. For example:
9 becomes $0,09
99 becomes $0,99
999 becomes $999,99
The code below works great, but as I'm using integer and float values the app will eventually display wrong values afeter a certain point. For example:
999999999 becomes 100000000
That happens because flot and integer aren't precise enough and NSDEcimalNumber should be used. The point is that I can't figure out how to replace my integers and float values to NSDecimalNumber ones.
Does anyone could give me a hand to solve this? I spend some time searching the web for a solution but didn't find that suits my needs and tons of people with the same problem.
Heres the code:
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if (textField.tag == 1){
NSString *cleanCentString = [[textField.text componentsSeparatedByCharactersInSet: [[NSCharacterSet decimalDigitCharacterSet] invertedSet]] componentsJoinedByString:#""];
NSInteger centValue= cleanCentString.integerValue;
if (string.length > 0)
{
centValue = centValue * 10 + string.integerValue;
}
else
{
centValue = centValue / 10;
}
NSNumber *formatedValue;
formatedValue = [[NSNumber alloc] initWithFloat:(float)centValue / 100.0f];
NSNumberFormatter *_currencyFormatter = [[NSNumberFormatter alloc] init];
[_currencyFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
textField.text = [_currencyFormatter stringFromNumber:formatedValue];
return NO;
}
if (textField.tag == 2){
// Nothing for now
}
return YES;
}
Implement UITextFieldDelegate and add next methods:
Swift:
let currencySign = "$"
// Adds $ before the text, e.g. "1" -> "$1" and allows "." and ","
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool
{
var value = textField.text
var newString = (textField.text as NSString).stringByReplacingCharactersInRange(range, withString: string)
var components = newString.componentsSeparatedByCharactersInSet(NSCharacterSet(charactersInString: "1234567890,.").invertedSet)
var decimalString = "".join(components) as NSString
var length = decimalString.length
if length > 0 {
value = "\(currencySign)\(decimalString)"
}
else {
value = ""
}
textField.text = value
}
// Formats final value using current locale, e.g. "130,50" -> "$130", "120.70" -> "$120.70", "5" -> "$5.00"
func textFieldDidEndEditing(textField: UITextField) {
var value = textField.text
var components = value.componentsSeparatedByCharactersInSet(NSCharacterSet(charactersInString: "1234567890,.").invertedSet)
var decimalString = "".join(components) as NSString
let number = NSDecimalNumber(string: decimalString, locale:NSLocale.currentLocale())
var formatter = NSNumberFormatter()
formatter.numberStyle = NSNumberFormatterStyle.CurrencyStyle
formatter.locale = NSLocale.currentLocale()
if let formatedValue = formatter.stringFromNumber(number) {
textField.text = formatedValue
}
else {
textField.text = ""
}
}
You can convert integer or double/float value to string by following:
NSString *str = [#(myInt) stringValue];
NSString *str1 = [[NSNumber numberWithFloat:myFloat] stringValue];
After that you can convert string to NSDecimalNumber by many ways. Like:
NSDecimalNumber *number = [NSDecimalNumber decimalNumberWithString:#"100.1"];
NSLog(#"%#", number);
NSDecimalNumber *num = [NSDecimalNumber decimalNumberWithString:#"100.1" locale:NSLocale.currentLocale];
NSLog(#"%#",num);
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setNumberStyle:NSNumberFormatterDecimalStyle];
[formatter setLocale:[NSLocale currentLocale]];
NSLog(#"%#", [formatter stringFromNumber:number]);
NSNumberFormatter will help you with the right formatting. Please read apples page for NSDecimalNumber and NSNumberFormatter.
NSNumberFormatter contains a section named"Configuring the Format of Currency". I didn't try it, but it seems something that can help you.
Let me know if this helps.. :)

Formating a single textfield so that it display is different to all the others

How would i set the currency in a text field to display it as a localized currency, with a leading 0. If someone types in 16.25 pence it would be formated as 0.1625£ respectively. I am using delegation and formating all text fields so only numbers can be passed in, this field should also be localized.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string { // First, create the text that will end up in the input field if you'll return YES:
NSString *resultString = [textField.text stringByReplacingCharactersInRange:range withString:string];
// Now, validate the text and return NO if you don't like what it'll contain.
// You accomplish this by trying to convert it to a number and see if that worked.
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
NSNumber* resultingNumber = [numberFormatter numberFromString:resultString];
//[numberFormatter release];
return resultingNumber != nil;
I do not want this to change, as it formats all my fields. Just want textField1 to have the relevant format,how would i go about doing this, i think it lies in viewdidload method and setting the text property to be localized to a floating point, but i cant seem to work out how to do it.
You can specify which textField you want to format in the delegate method above.
if (textField == textField1) {
// Do Something....
} else {
// Do whatever you want with the other text fields
}
For floating point formatting, use something like this -
[myTextField setText:[NSString stringWithFormat:#"%.2f", myFloat]];
(void)textFieldDidEndEditing:(UITextField *)textField
{
if (textfield1) {
NSString *txt = self.textfield1.text;
double num1 = [txt doubleValue];
double tCost = num1 /100;
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setNumberStyle: NSNumberFormatterCurrencyStyle];
NSString *numberAsString = [numberFormatter stringFromNumber:[NSNumber numberWithFloat:tCost]];
self.textfield1.text = [NSString stringWithFormat:#"%#",numberAsString];
}
}

NSNumberFormatter removing spaces before currency

I am using NSNumberFormatter to format my numbers to strings.
I have a device with Hebrew (israel) region format (settings->General->International->Region Format).
When I try to format the number 100 for instance I get 100 $.
My goal is to remove the space before the currency sign and get just 100$
I ended up changing positiveSuffix and negativeSuffix properties
by removing the spaces from them
because my NSNumberFormatter is static in my application I set them to nil at the end of each use
static NSNumberFormatter *currencyFormatter;
if (!currencyFormatter) {
currencyFormatter = [[NSNumberFormatter alloc] init];
[currencyFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
[currencyFormatter setNegativeFormat:#""];
}
// remove spaces at the suffix
currencyFormatter.positiveSuffix = [currencyFormatter.positiveSuffix stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
currencyFormatter.negativeSuffix = [currencyFormatter.negativeSuffix stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
// get the return number
NSString* retNum = [currencyFormatter stringFromNumber:val];
// this code is for the next time using currencyFormatter
currencyFormatter.positiveSuffix = nil;
currencyFormatter.negativeSuffix = nil;
return retNum;
How about just removing all spaces from the string before running it through NSNumberFormatter? (as answered in this question)
NSString *stringWithoutSpaces = [myString
stringByReplacingOccurrencesOfString:#" " withString:#""];

How to insert grouping comma in NSString as typed?

A user enters a numerical string in a UILabel and the text is displayed as the user types.
NSString *input = [[sender titleLabel] text];
[display_ setText:[[display_ text] stringByAppendingString:input]];
This works fine and I format the display using NSNumberFormatter so that if 1000000 is entered it is converted to 1,000,000 upon tapping another button.
However, I'd like to get those grouping commas to be displayed as the user types. I can understand how to insert things into strings, but how to do it as the user types is not clear to me. Would this require a mutable string?
Maybe somehow monitor the string length and split it into groups of three and make and display a new string with the commas inserted? I could probably do that, but it is the "as it is typed" part that has me stymied.
Another thought is to append and display the string, then read the display into a new NSString and format it and display it again right away. So I tried that, and it almost works:
if (userIsEntering)
{
NSNumberFormatter *fmtr = [[NSNumberFormatter alloc] init];
[fmtr setNumberStyle:NSNumberFormatterDecimalStyle];
[fmtr setGroupingSeparator:#","];
[fmtr setDecimalSeparator:#"."];
NSString *out = [[display_ text] stringByAppendingString:digit];
NSNumber *num = [fmtr numberFromString:out];
NSString* formattedResult = [fmtr stringFromNumber:num];
[display_ setText: [NSString stringWithFormat:#"%#", formattedResult]];
[fmtr release];
}
And, along with the fact that the formatter is created and released with every digit entered, after 4 digits it returns null.
UPDATE: I figured out how to do it in a label (with some help from #Michael-Frederick). It uses an NSNotification.
This works perfectly for non-decimal numbers, but when I try to enter a decimal point it is ignored and removed. If I do not invoke this method, the decimal point is accepted and all works well.
Numeric entry is as follows (from a button):
NSString *digit = [[sender titleLabel] text];
if (userIsStillWorking_)
{
[display_ setText:[[display_ text] stringByAppendingString:digit]];
}
else
{
[display_ setText: digit];
userIsStillWorking_ = YES;
}
[[NSNotificationCenter defaultCenter] postNotificationName:#"updateDisplay" object:nil];
And the updateDisplay method called by the notification is:
{
NSString *unformattedValue = [display_.text stringByReplacingOccurrencesOfString:
#"," withString:#""];
unformattedValue = [unformattedValue stringByReplacingOccurrencesOfString:
#"." withString:#""];
NSDecimalNumber *amount = [NSDecimalNumber decimalNumberWithString:unformattedValue];
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setGroupingSeparator:#","];
[formatter setDecimalSeparator:#"."];
[display_ setText: [ formatter stringFromNumber:amount]];
[formatter release];
}
I've tried commenting out
unformattedValue = [unformattedValue stringByReplacingOccurrencesOfString:
#"." withString:#""];
but that makes no difference.
EDIT:
A user cannot type into a uilabel. You need to use either a uitextfield or a uitextview.
If you want to use a uitextfield, do something like this...
- (void) viewDidLoad {
[super viewDidLoad];
[textField addTarget:self action:#selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];
}
- (void) textFieldDidChange:(UITextField *)textField {
NSString *unformattedValue = [textField.text stringByReplacingOccurrencesOfString:#"," withString:#""];
unformattedValue = [unformattedValue stringByReplacingOccurrencesOfString:#"." withString:#""];
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setNumberStyle:NSNumberFormatterDecimalStyle];
[formatter setGroupingSeparator:#","];
[formatter setDecimalSeparator:#"."];
NSNumber *amount = [NSNumber numberWithInteger:[unformattedValue intValue]];
textField.text = [formatter stringFromNumber:amount];
[formatter release];
}
Note that you are correct that NSNumberFormatter should be declared outside of the textFieldDidChange method. Note that this code would actually be for an integer. You could have to switch intValue to floatValue if need be. This code is untested, it is more of a general guide.
The best way to do this is to use 2 UIlabels. 1 of the labels is used to feed your NSNumberFormatter object by using [NSString stringByAppendingString:digit]; The other label is actually displayed. The trick is to set the label that is unformatted to hidden and the other label is set as an output for the number formatter. By feeding the hidden label to the number formatter, and outputting the displayed label from the number formatter, the number formatter should be set to the NSDecimalNumber style. Setting it all up this way, the result displayed is automatic commas while typing.

Resources