【问题标题】:Having trouble using NSNumberFormatter for currency conversion in iOS在 iOS 中使用 NSNumberFormatter 进行货币转换时遇到问题
【发布时间】:2013-02-21 23:32:00
【问题描述】:

我有一个 UITextField,它在我的应用程序中接收来自用户的数字输入。然后,在我的 shouldChangeCharactersInRange 委托方法中,使用 NSNumberFormatter 将此文本字段中的值转换为货币格式。当我输入数字“12345678”时,该数字会正确转换为 $123456.78(数字一次输入一位,到目前为止,一切正常)。但是,当我在此之后输入另一个数字(例如 9)时,不是显示“1234567.89”,而是显示数字“1234567.88”。如果我在此之后输入另一个数字,则在此之后完全不同的数字(我使用应用程序中的数字键盘输入数字。这是我拥有的代码:

NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setNumberStyle:NSNumberFormatterCurrencyStyle];
modifiedValue = [formatter stringFromNumber:[NSNumber numberWithFloat:[modifiedValue floatValue]]];
textField.text = modifiedValue;

导致这种异常转换的行是这一行:

modifiedValue = [formatter stringFromNumber:[NSNumber numberWithFloat:[modifiedValue floatValue]]];

谁能看出这是为什么?

【问题讨论】:

  • 你应该展示你所有的shouldChangeCharactersInRange方法。除了float 的有效数字数量有限(请改用double)这一事实之外,您还可能遇到其他问题。

标签: ios uitextfield nsnumberformatter uitextfielddelegate


【解决方案1】:

在进行字符串->浮点转换时可能会出现舍入错误。处理货币时不应使用浮点数。您可以改用NSDecimalNumber

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

您也可以考虑使数字格式化程序宽松 - 通常有助于用户输入数据。

[formatter setLenient:YES];

【讨论】:

  • @syedfa 它对转换应用一定程度的启发式方法,以考虑用户可能(或可能不会)输入的其他字符。使用 lenient = NO,1234567.89 转换为 $0.00,$1234567.89 转换为 $1,234,567.89 .当 lenient = YES 时,1234567.89 和 $1234567.89 都转换为 $1,234,567.89。
  • 信不信由你,你以前的答案比这个更新的答案更好:-) 不知道为什么,但它的行数更少,并且它始终正确地接收输入。
  • 这是您发布的原始代码块: NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init]; [格式化程序 setNumberStyle:NSNumberFormatterCurrencyStyle]; NSDecimalNumber *decimal = [NSDecimalNumber decimalNumberWithString:modifiedValue]; modifiedValue = [formatter stringFromNumber:decimal]; textField.text = modifiedValue;
  • 是的,我不知道您的代码中的 modifiedValue 是否会包含货币格式。如果是这样,那么原始代码块将无法正常工作。如果没有,那么原版就可以了。
【解决方案2】:

当我将数字转换为货币时,我通常会运行以下代码:

- (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.
}

解释这个的链接是Re-Apply currency formatting to a UITextField on a change event

希望这行得通!

【讨论】:

  • 这段代码有问题。如果用户更改其 iOS 设备上的区域格式设置(这会更改当前区域设置),则数字格式化程序将不再有效,即使您收听区域设置更改通知,您也无法重置它。
  • 如果你添加了这个呢? [格式化程序 setLocale:[NSLocale currentLocale]];
  • 我使用了某人不久前发布的以下代码块: NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init]; [格式化程序 setNumberStyle:NSNumberFormatterCurrencyStyle]; NSDecimalNumber *decimal = [NSDecimalNumber decimalNumberWithString:modifiedValue]; modifiedValue = [formatter stringFromNumber:decimal]; textField.text = modifiedValue;它解决了我的问题,但具有讽刺意味的是,该答案不再发布!
  • @iLive 如果你打算这样做,请在调用 alloc/init 之后进行。我的偏好是将格式化程序创建为实例变量或至少是静态文件。然后我可以在收到语言环境更改通知时更新实例或静态。
  • @syedfa 我也不知道为什么这个答案被删除了。对我来说似乎是一个不错的选择。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-17
相关资源
最近更新 更多