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

发布于 2024-08-15 18:36:24 字数 394 浏览 11 评论 0原文

经过一天多的时间研究这个问题后,我将看看是否可以获得一些帮助。这个问题以前或多或少被问过,但似乎没有人给出完整的答案,所以希望我们现在能得到它。

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

什么是最好的输入方式带小数点的数值?

唯一的问题是,它从未明确说明我们如何在文本字段中包含像 123 这样的整数,并在标签中显示为 $1.23 或 123¥ 等。任何人都有执行此操作的代码?

After over a day of poking around with this problem I will see if I can get some help. This question has been more or less asked before, but it seems no one is giving a full answer so hopefully we can get it now.

Using a UILabel and a UITextView (w/ number keyboard) I want to achieve an ATM like behavior of letting the users just type the numbers and it is formatted as currency in the label. The idea is basically outlined here:

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

The only issue is that it never explicitly says how we can go from having an integer like 123 in the textfield and displaying in the label as $1.23 or 123¥ etc. Anyone have code that does this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

对你的占有欲 2024-08-22 18:36:24

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

//
//  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];
}

该方法仅接受文本字段value 并通过上述 stringToCurrency 方法运行它。

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

I have found a solution, and as per the purpose of this question I am going to provide a complete answer for those who have this problem in the future. First I created a new Helper Class called NumberFormatting and created two methods.

//
//  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

and here is the implementation file:

//
//  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

The first method, stringToCurrency, will take an integer number (passed in from a textfield in this case) and convert it to a decimal value using moving the decimal point as appropriate for the users locale settings. It then returns a string representation formatted as currency using NSNumberFormatter.

The second method does the reverse it takes a value like 1.23 and converts it back to 123 using a similar method.

Here is an example of how I used it

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

Here we set the value of the text field to the decimal value from the data store and then we set a observer to watch for changes to the text field and run the method updateBalance

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

Which simply takes the textfield value and run it through the stringToCurrency method described above.

To me this seems hackish so please take the a moment to look over and clean it up if you are interested in using it. Also I notice for large values it breaks.

墨落画卷 2024-08-22 18:36:24

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

Take a look at NSNumberFormatter, which will format numerical data based on the current or specified locale.

金兰素衣 2024-08-22 18:36:24

由于我仍然没有看到这个问题的正确答案,我将在不使用 NSScanner 的情况下分享我的解决方案(扫描仪似乎不适合我)。这是一个组合“ 输入带小数点的数值的最佳方式是什么? ”和这个“ 从 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;
}

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

Since I still didn't see correct Answers to this question I will share my solution without using the NSScanner (the scanner doesn't seem to work for me). Is's a combination out of this " What is the best way to enter numeric values with decimal points? " and this " Remove all but numbers from NSString " answers.

First I present a NSString with the users local currency settings in a UITextField like this:

//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]];

You might do this in the viewControllers method viewDidLoad:.
Depending on the users settings there will be displayed a string like this: $0.00 (for local settings United Stated). Depending on your situation here you might want to present a value out of your data model.

When the user touches inside the text field I will present a Keyboard with type:

costField.keyboardType = UIKeyboardTypeDecimalPad; 

This prevents the user to enter anything else but digits.

In the following UITextField's delegate method I separate the string to get only the numbers (here I avoid using the NSScanner). This is possible, because I know where to set the decimal separator by using the before specified 'currencyScale' value:

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

In this way the currency entered by the user will always be correct and there is no need to check it. No matter which currency settings is selected, this will always result to be the correctly formatted currency.

灼疼热情 2024-08-22 18:36:24

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

我在 init 处设置了格式化程序:

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

我有这个setter 负责格式化标签并启用完成按钮:

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

I didn't really like the existing answers here so I combined a couple of techniques. I used a hidden UITextField with the number pad keyboard for input, and a visible UILabel for formatting.

I've got to properties that hold on to everything:

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

I've got the amount and a NSNumberFormatter as ivars:

NSDecimalNumber *amount_;
NSNumberFormatter *formatter;

I setup my formatter at init:

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

Here's the code I'm using to validate the input convert it to

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

I've got this setter than handles formatting the label and enabling the done button:

-(void)setAmount:(NSDecimalNumber *)amount
{
    amount_ = amount;
    amountLabel.text = [formatter stringFromNumber:amount];
    self.navigationItem.rightBarButtonItem.enabled = [self isValid];
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文