将NSString转换为货币 – 完整的故事

经过了一天的这个问题,我会看看我能否得到一些帮助。 这个问题之前或多或less都有问题,但似乎没有人给出完整的答案,所以希望我们现在能够得到答案。

使用UILabel和UITextView(带数字键盘)我想实现一种类似ATM的行为,让用户只需键入数字,并将其格式化为标签中的货币。 这个想法基本上概述在这里:

用小数点input数值的最佳方法是什么?

唯一的问题是,它从来没有明确说过我们如何可以在文本字段中使用像123这样的整数,并在标签中显示为$ 1.23或123¥等。任何人都有代码这样做?

我find了一个解决scheme,根据这个问题的目的,我将为那些将来有这个问题的人提供一个完整的答案。 首先我创build了一个名为NumberFormatting的新的Helper类,并创build了两个方法。

// // NumberFormatting.h // Created by Noah Hendrix on 12/26/09. // #import <Foundation/Foundation.h> @interface NumberFormatting : NSObject { } -(NSString *)stringToCurrency:(NSString *)aString; -(NSString *)decimalToIntString:(NSDecimalNumber *)aDecimal; @end 

这里是执行文件:

 // // NumberFormatting.m // Created by Noah Hendrix on 12/26/09. // #import "NumberFormatting.h" @implementation NumberFormatting -(NSString *)stringToCurrency:(NSString *)aString { NSNumberFormatter *currencyFormatter = [[NSNumberFormatter alloc] init]; [currencyFormatter setGeneratesDecimalNumbers:YES]; [currencyFormatter setNumberStyle:NSNumberFormatterCurrencyStyle]; if ([aString length] == 0) aString = @"0"; //convert the integer value of the price to a decimal number ie 123 = 1.23 //[currencyFormatter maximumFractionDigits] gives number of decimal places we need to have //multiply by -1 so the decimal moves inward //we are only dealing with positive values so the number is not negative NSDecimalNumber *value = [NSDecimalNumber decimalNumberWithMantissa:[aString integerValue] exponent:(-1 * [currencyFormatter maximumFractionDigits]) isNegative:NO]; return [currencyFormatter stringFromNumber:value]; } -(NSString *)decimalToIntString:(NSDecimalNumber *)aDecimal { NSNumberFormatter *currencyFormatter = [[NSNumberFormatter alloc] init]; [currencyFormatter setGeneratesDecimalNumbers:YES]; [currencyFormatter setNumberStyle:NSNumberFormatterCurrencyStyle]; if (aDecimal == nil) aDecimal = [NSDecimalNumber zero]; NSDecimalNumber *price = [NSDecimalNumber decimalNumberWithMantissa:[aDecimal integerValue] exponent:([currencyFormatter maximumFractionDigits]) isNegative:NO]; return [price stringValue]; } @end 

第一种方法stringToCurrency将取整数(在这种情况下从文本字段中传入),并根据用户区域设置的相应值移动小数点,将其转换为十进制值。 然后它返回一个使用NSNumberFormatter格式化为货币的string表示。

第二种方法做相反的处理,如1.23所示,并使用类似的方法将其转换回123。

这是我如何使用它的一个例子

 ... self.accountBalanceCell.textField.text = [[NumberFormatting alloc] decimalToIntString:account.accountBalance]; ... [self.accountBalanceCell.textField addTarget:self action:@selector(updateBalance:) forControlEvents:UIControlEventEditingChanged]; 

在这里,我们将文本字段的值设置为数据存储中的十进制值,然后设置观察者来监视文本字段的更改并运行方法updateBalance

 - (void)updateBalance:(id)sender { UILabel *balanceLabel = (UILabel *)[accountBalanceCell.contentView viewWithTag:1000]; NSString *value = ((UITextField *)sender).text; balanceLabel.text = [[NumberFormatting alloc] stringToCurrency:value]; } 

它只是取得文本字段的值,并通过上面描述的stringToCurrency方法运行它。

对我来说,这似乎有些ha so,所以请花一点时间仔细检查并清理它,如果你有兴趣使用它。 另外我注意到它的大值打破。

看看NSNumberFormatter ,它将基于当前或指定的区域设置格式化数字数据。

由于我仍然没有看到这个问题的正确答案,我将分享我的解决scheme,而不使用NSScanner(扫描仪似乎并没有为我工作)。 是这个“ 使用小数点input数值的最好方法是什么? ”和这个“ 从NSString中除去数字以外的所有数据 ”的答案。

首先,我将用户本地货币设置的NSString提供给UITextField,如下所示:

 //currencyFormatter is of type NSNumberFormatter if (currencyFormatter == nil) { currencyFormatter = [[NSNumberFormatter alloc] init]; [currencyFormatter setLocale:[NSLocale currentLocale]]; [currencyFormatter setNumberStyle:NSNumberFormatterCurrencyStyle]; //[currencyFormatter setGeneratesDecimalNumbers:YES]; decimalSeperator = [currencyFormatter decimalSeparator]; //NSString currencyScale = [currencyFormatter maximumFractionDigits]; //short //[currencyFormatter release]; don't forget to release the Formatter at one point } //costField is of type UITextField NSDecimalNumber *nullValue = [NSDecimalNumber decimalNumberWithMantissa:0 exponent:currencyScale isNegative:NO]; [costField setText:[currencyFormatter stringFromNumber:nullValue]]; 

你可以在viewControllers方法viewDidLoad中做到这一点。 根据用户设置,将显示如下所示的string:$ 0.00(本地设置United Stated)。 根据您的情况,您可能需要从数据模型中提供一个值。

当用户触摸文本字段时,我将显示一个types为键盘的键盘:

 costField.keyboardType = UIKeyboardTypeDecimalPad; 

这样可以防止用户input除数字以外的其他内容。

在下面的UITextField的委托方法中,我将string分开以仅获取数字(这里我避免使用NSScanner)。 这是可能的,因为我知道在哪里通过使用之前指定的“currencyScale”值来设置小数点分隔符:

 -(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string { if (textField == costField) { //if for what ever reason ther currency scale is not available set it to 2 //which is the most common scale value if (!currencyScale) { currencyScale = 2; } // separate string from all but numbers // https://stackoverflow.com/questions/1129521/remove-all-but-numbers-from-nsstring/1163595#1163595 NSString *aString = [textField text]; NSMutableString *strippedString = [NSMutableString stringWithCapacity:10]; for (int i=0; i<[aString length]; i++) { if (isdigit([aString characterAtIndex:i])) { [strippedString appendFormat:@"%c",[aString characterAtIndex:i]]; } } //add the newly entered character as a number // https://stackoverflow.com/questions/276382/what-is-the-best-way-to-enter-numeric-values-with-decimal-points/2636699#2636699 double cents = [strippedString doubleValue]; NSLog(@"Cents:%f ",[strippedString doubleValue]); if ([string length]) { for (size_t i = 0; i < [string length]; i++) { unichar c = [string characterAtIndex:i]; if (isnumber(c)) { cents *= 10; //multiply by 10 to add a 0 at the end cents += c - '0'; // makes a number out of the charactor and replace the 0 (see ASCII Table) } } } else { // back Space if the user delete a number cents = floor(cents / 10); } //like this you could save the value as a NSDecimalNumber in your data model //costPerHour is of type NSDecimalNumber self.costPerHour = [NSDecimalNumber decimalNumberWithMantissa:cents exponent:-currencyScale isNegative:NO]; //creat the string with the currency symbol and the currency separator [textField setText:[currencyFormatter stringFromNumber:costPerHour]]; return NO; } return YES; } 

用这种方式用户input的货币总是正确的,不需要检查。 无论select哪种货币设置,总是会导致格式正确的货币。

我真的不喜欢现有的答案,所以我结合了一些技巧。 我用数字键盘input一个隐藏的UITextField和一个可见的UILabel格式。

我必须坚持一切的财产:

 @property (weak, nonatomic) IBOutlet UILabel *amountLabel; @property (weak, nonatomic) IBOutlet UITextField *amountText; @property (retain, nonatomic) NSDecimalNumber *amount; 

我有金额和一个NSNumberFormatter作为ivars:

 NSDecimalNumber *amount_; NSNumberFormatter *formatter; 

我在初始化设置我的格式化程序:

 - (id)initWithCoder:(NSCoder *)aDecoder { self = [super initWithCoder:aDecoder]; if (self) { // Custom initialization formatter = [NSNumberFormatter new]; formatter.numberStyle = NSNumberFormatterCurrencyStyle; } return self; } 

这里是我用来validationinput转换的代码

 -(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string { NSString *asText = [textField.text stringByReplacingCharactersInRange:range withString:string]; if ([asText length] == 0) { [self setAmount:[NSDecimalNumber zero]]; return YES; } // We just want digits so cast the string to an integer then compare it // to itself. If it's unchanged then it's workable. NSInteger asInteger = [asText integerValue]; NSNumber *asNumber = [NSNumber numberWithInteger:asInteger]; if ([[asNumber stringValue] isEqualToString:asText]) { // Convert it to a decimal and shift it over by the fractional part. NSDecimalNumber *newAmount = [NSDecimalNumber decimalNumberWithDecimal:[asNumber decimalValue]]; [self setAmount:[newAmount decimalNumberByMultiplyingByPowerOf10:-formatter.maximumFractionDigits]]; return YES; } return NO; } 

我有这个setter,而不是格式化标签,并启用完成button:

 -(void)setAmount:(NSDecimalNumber *)amount { amount_ = amount; amountLabel.text = [formatter stringFromNumber:amount]; self.navigationItem.rightBarButtonItem.enabled = [self isValid]; }