36

I'm looking for a way to format a string into currency without using the TextField hack.

For example, i'd like to have the number "521242" converted into "5,212.42" Or if I have a number under 1$, I would like it to look like this: "52" -> "0.52"

Thanks

François Marceau
  • 592
  • 1
  • 4
  • 10

8 Answers8

67

You probably want something like this (assuming currency is a float):

NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setNumberStyle: NSNumberFormatterCurrencyStyle];
NSString *numberAsString = [numberFormatter stringFromNumber:[NSNumber numberWithFloat:currency]];

From your requirements to treat 52 as .52 you may need to divide by 100.0.

The nice thing about this approach is that it will respect the current locale. So, where appropriate it will format your example as "5.212,42".

Update: I was, perhaps, a little speedy in posting my example. As pointed out by Conrad Shultz below, when dealing with currency amounts, it would be preferable to store the quantities as NSDecimalNumbers. This will greatly reduce headaches with rounding errors. If you do this the above code snippet becomes (assuming currency is a NSDecimalNumber*):

NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setNumberStyle: NSNumberFormatterCurrencyStyle];
NSString *numberAsString = [numberFormatter stringFromNumber:currency];
idz
  • 12,825
  • 1
  • 29
  • 40
  • 31
    Never use floats for exact values (especially currency)! Use NSDecimalNumber/NSDecimal. – Conrad Shultz Aug 03 '12 at 00:57
  • Awesome! I had it working but was struggling with the double decimal.. It was so obvious that I missed it. Thank you! – François Marceau Aug 03 '12 at 00:58
  • 1
    @ConradShultz (+1) A good point, but I tend avoid such sweeping statements. Things which may be exactly representable in decimal can quickly become in exact (e.g I take your "exact" number and divide it by three). The performance overhead of NSDecimalNumber/NSDecimal can soon outweigh the benefits. – idz Aug 03 '12 at 01:26
  • 2
    @idz Sure - which is why you then need a policy for handling those cases. But using a float *will* break at some point, and when you least expect it. When you're working with currency, where every penny counts, float is unacceptable. (FWIW, `NSDecimalNumber` is pretty lightweight; I've never seen any problems even working with thousands of calculations in real-time.) – Conrad Shultz Aug 03 '12 at 01:34
  • @ConradShultz - Sorry had to play Devil's Advocate ;-) – idz Aug 03 '12 at 01:36
  • My $0.02: If one is, say, building a tool that calculates *estimates* only (as opposed to precise transaction amounts), then floating point arithmetic (e.g. doubles) may be acceptable. But, yes, it is important to understand the trade-off and consequences. – Chris W. Rea May 23 '13 at 18:25
  • One idea for putting money into JSON is to use cents (or whatever your lowest divisible unit is). Square does this, for example. This beats your JSON containing 25.000001 Dollarpounds. – Graham Perks Feb 15 '16 at 15:12
12

I use this code. This work for me

1) Add UITextField Delegate to header file

2) Add this code (ARC enabled)

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {

NSString *cleanCentString = [[textField.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 setCurrencyCode:@"USD"];
[_currencyFormatter setNegativeFormat:@"-¤#,##0.00"];
textField.text = [_currencyFormatter stringFromNumber:amount];
return NO; }
AAV
  • 3,785
  • 8
  • 32
  • 59
  • 7
    Never use floats for exact values (especially currency)! Use NSDecimalNumber/NSDecimal. – Conrad Shultz Aug 03 '12 at 00:57
  • 1
    How would it be possible to ensure that e.g. Japanese formatting for YEN is correct when you hardcode the division by 100 since YEN do not have something like cents... Compare `Locale Awareness` on https://nshipster.com/nsformatter/ If you format 1000 to Japanese YEN it becomes "¥1,000". Do I really have to maintain a mapping table with division factors for each supported country or is there a NumberFormatter option? – blackjacx Jun 29 '18 at 15:15
6

swift 2.0 version:

    let _currencyFormatter : NSNumberFormatter = NSNumberFormatter()
    _currencyFormatter.numberStyle = NSNumberFormatterStyle.CurrencyStyle
    _currencyFormatter.currencyCode = "EUR"
    textField.text = _currencyFormatter.stringFromNumber(amount);
Bill Chan
  • 3,199
  • 36
  • 32
3

Use the following code and it will resolve all your issues....

NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setNumberStyle: NSNumberFormatterCurrencyStyle];
NSString *numberAsString = [numberFormatter stringFromNumber:[NSNumber numberWithDouble:[currency doubleValue]]];
Dharmbir Singh
  • 17,485
  • 5
  • 50
  • 66
james lobo
  • 463
  • 4
  • 10
2

This is what I have found reworking AAV answer using NSDecimalNumbers.

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {

NSString *cleanCentString = [[textField.text
                              componentsSeparatedByCharactersInSet:
                              [[NSCharacterSet decimalDigitCharacterSet] invertedSet]]
                             componentsJoinedByString:@""];


// Parse final integer value
NSDecimalNumber *price = [NSDecimalNumber decimalNumberWithMantissa:[cleanCentString integerValue]
                                                           exponent:-2
                                                         isNegative:NO];

NSDecimalNumber *entry = [NSDecimalNumber decimalNumberWithMantissa:[string integerValue]
                                                           exponent:-2
                                                         isNegative:NO];

NSDecimalNumber *multiplier = [NSDecimalNumber decimalNumberWithMantissa:1
                                                            exponent:1
                                                          isNegative:NO];

NSDecimalNumberHandler *handler = [NSDecimalNumberHandler decimalNumberHandlerWithRoundingMode:NSRoundPlain
                                                                                         scale:2
                                                                              raiseOnExactness:NO
                                                                               raiseOnOverflow:NO
                                                                              raiseOnUnderflow:NO
                                                                           raiseOnDivideByZero:NO];
NSDecimalNumber *result;

// Check the user input
if (string.length > 0)
{
    // Digit added
    result = [price decimalNumberByMultiplyingBy:multiplier withBehavior:handler];
    result = [result decimalNumberByAdding:entry];
}
else
{
    // Digit deleted
    result = [price decimalNumberByDividingBy:multiplier withBehavior:handler];
}

// Write amount with currency symbols to the textfield
NSNumberFormatter *_currencyFormatter = [[NSNumberFormatter alloc] init];
[_currencyFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
[_currencyFormatter setCurrencyCode:@"USD"];
textField.text = [_currencyFormatter stringFromNumber:result];

return NO;
}
Joe Collins
  • 458
  • 3
  • 12
1
func getCurrencyFormat(price:String)->String{
    let convertPrice = NSNumber(double: Double(price)!)
    let formatter = NSNumberFormatter()
    formatter.numberStyle = .CurrencyStyle
    formatter.currencyCode = "USD"        

    let convertedPrice = formatter.stringFromNumber(convertPrice)       
    return convertedPrice!
}

Note:- A currency code is a three-letter code that is, in most cases, composed of a country’s two-character Internet country code plus an extra character to denote the currency unit. For example, the currency code for the Australian dollar is “AUD”.

Vikram Pote
  • 5,433
  • 4
  • 33
  • 37
1

For Swift tested code (ref from AAV's code)

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool{

    let strMain : NSString = string

    let arrTemp : NSArray = (textField.text?.componentsSeparatedByCharactersInSet(NSCharacterSet.decimalDigitCharacterSet().invertedSet))!
    let str: NSString = arrTemp.componentsJoinedByString("")

    //NSInteger centAmount = cleanCentString.integerValue;
    var centAmount : NSInteger = str.integerValue

    if (string.length > 0)
    {
        // Digit added
        centAmount = centAmount * 10 + strMain.integerValue;
    }
    else {
        // Digit deleted
        centAmount = centAmount / 10;
    }

    let amount = (Double(centAmount) / 100.0)

    let currencyFormatter = NSNumberFormatter()
    currencyFormatter.numberStyle = .CurrencyStyle
    currencyFormatter.currencyCode = "USD"
    currencyFormatter.negativeFormat = "-¤#,##0.00"
    let convertedPrice = currencyFormatter.stringFromNumber(amount)

    print(convertedPrice)

    txtAmount.text = convertedPrice! //set text to your textfiled
    return false //return false for exact out put
}

note : if you want to remove the default currency symbol from input you can use currencySymbol to blank as below

currencyFormatter.currencyCode = nil
currencyFormatter.currencySymbol = ""

Happy coding!

Hardik Thakkar
  • 15,269
  • 2
  • 94
  • 81
0

One thing about using formatters is they are expensive to create. It is best to create them once per view or viewController. Using lazy is the perfect solution for avoiding this overhead. This also moves a lot of boilerplate up out of the way of your main code.

class MyViewController: UIViewController {
    @IBOutlet var priceLabel: UILabel!

    private lazy var currencyFormatter: NumberFormatter = {
        let formatter = NumberFormatter()
        formatter.currencySymbol = "$"
        formatter.numberStyle = .currency
        return formatter
    }()

     // rest of class
 }

To use it, as noted elsewhere it is best to use Decimal for currency, Double is an approximation.

priceLabel.text = currencyFormatter.string(for: Decimal(9.48))

Output:

$9.48
possen
  • 8,596
  • 2
  • 39
  • 48