【问题标题】:Converting NSString to Currency - The Complete Story将 NSString 转换为货币 - 完整的故事
【发布时间】:2009-12-25 02:33:56
【问题描述】:

在解决这个问题超过一天后,我会看看是否可以得到一些帮助。这个问题以前或多或少有人问过,但似乎没有人给出完整的答案,所以希望我们现在可以得到它。

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

What is the best way to enter numeric values with decimal points?

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

【问题讨论】:

    标签: iphone ios nsstring currency nsnumberformatter


    【解决方案1】:

    我已经找到了解决方案,根据这个问题的目的,我将为将来遇到此问题的人提供完整的答案。首先,我创建了一个名为 NumberFormatting 的新 Helper 类并创建了两个方法。

    //
    //  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 i.e. 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 格式化为货币的字符串表示形式。

    第二种方法相反,它采用类似 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 方法运行它。

    对我来说,这似乎很骇人听闻,所以如果您有兴趣使用它,请花点时间查看并清理它。我还注意到它会破坏较大的值。

    【讨论】:

      【解决方案2】:

      看看NSNumberFormatter,它将根据当前或指定的语言环境格式化数字数据。

      【讨论】:

      • 我已经研究过了,但是当想法是将其转换为 1.23 美元时,它需要 123 并将其转换为 123.00 美元
      • 也许您可以读取字符串的长度,并考虑到语言环境,将其转换为 1.23(格式为 $1.23),而不是 123.00,格式为 $123.00。跨度>
      • 例如,您可以在语言环境的NSNumberFormatter 上调用-currencyDecimalSeparator-currencyGroupingSeparator 以了解如何处理格式。
      【解决方案3】:

      由于我仍然没有看到这个问题的正确答案,我将在不使用 NSScanner 的情况下分享我的解决方案(扫描仪似乎对我不起作用)。是这个“What is the best way to enter numeric values with decimal points?”和这个“Remove all but numbers from NSString”答案的组合。

      首先,我在 UITextField 中呈现一个带有用户本地货币设置的 NSString,如下所示:

      //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: 中执行此操作。 根据用户设置,将显示如下字符串:$0.00(用于美国本地设置)。根据您在此处的情况,您可能希望从数据模型中呈现一个值。

      当用户在文本字段内触摸时,我将显示一个键盘,其类型为:

      costField.keyboardType = UIKeyboardTypeDecimalPad; 
      

      这可以防止用户输入除数字以外的任何其他内容。

      在下面的 UITextField 的委托方法中,我将字符串分开以仅获取数字(这里我避免使用 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;
      }
      

      这样用户输入的币种永远是正确的,无需检查。无论选择哪种货币设置,这始终是格式正确的货币。

      【讨论】:

        【解决方案4】:

        我不太喜欢这里的现有答案,所以我结合了几种技术。我使用隐藏的 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;
        }
        

        这是我用来验证输入并将其转换为的代码

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

        我有这个设置器,而不是处理格式化标签和启用完成按钮:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-10-05
          • 1970-01-01
          • 1970-01-01
          • 2014-08-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2022-12-03
          相关资源
          最近更新 更多