Преобразование NSString в валюту - полная история - PullRequest
0 голосов
/ 25 декабря 2009

После более чем одного дня возни с этой проблемой я посмотрю, смогу ли я получить какую-то помощь. Этот вопрос более или менее задавался ранее, но, похоже, никто не дает полного ответа, так что, надеюсь, мы сможем получить его сейчас.

Использование UILabel и UITextView (с числовой клавиатурой) Я хочу добиться поведения, похожего на ATM, позволяющего пользователям просто набирать цифры, и оно отформатировано в валюте в метке. Идея в основном изложена здесь:

Как лучше всего вводить числовые значения с десятичными точками?

Единственная проблема заключается в том, что в нем никогда явно не говорится, как мы можем получить целое число, например 123, в текстовом поле и отобразить в метке значение $ 1,23 или 123 ¥ и т. Д. У кого-нибудь есть код, который это делает?

Ответы [ 4 ]

3 голосов
/ 28 декабря 2009

Я нашел решение, и в соответствии с целью этого вопроса я собираюсь дать полный ответ тем, у кого эта проблема возникнет в будущем. Сначала я создал новый вспомогательный класс с именем 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];
}

, который просто берет значение текстового поля и запускает его через метод stringToCurrency, описанный выше.

Мне это кажется хакерским, поэтому, пожалуйста, найдите время, чтобы посмотреть и почистить его, если вы заинтересованы в его использовании. Также я замечаю, что при больших значениях он ломается.

2 голосов
/ 25 декабря 2009

Взгляните на NSNumberFormatter, который отформатирует числовые данные на основе текущей или указанной локали.

0 голосов
/ 23 июля 2012

Мне не очень понравились существующие ответы, поэтому я объединил несколько приемов. Я использовал скрытый 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;
}

У меня есть этот установщик, чем он обрабатывает метку и включает кнопку «Готово»:

-(void)setAmount:(NSDecimalNumber *)amount
{
    amount_ = amount;
    amountLabel.text = [formatter stringFromNumber:amount];
    self.navigationItem.rightBarButtonItem.enabled = [self isValid];
}
0 голосов
/ 15 июля 2011

Поскольку я до сих пор не вижу правильных ответов на этот вопрос, я поделюсь своим решением без использования NSScanner (сканер, кажется, не работает для меня). Является ли комбинация из этого " Каков наилучший способ ввода числовых значений с десятичными точками? " и это " Удалить все, кроме чисел из NSString " ответов.

Сначала я представляю строку NSSt с настройками местной валюты пользователя в 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 :. В зависимости от настроек пользователя будет отображаться строка вроде этой: $ 0.00 (для локальных настроек United Stated). В зависимости от вашей ситуации вы можете представить значение из вашей модели данных.

Когда пользователь прикасается к текстовому полю, я представляю клавиатуру с типом:

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
    // /1099666/udalit-vse-krome-nomerov-iz-nsstring#1099674
    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;
}

Таким образом, введенная пользователем валюта всегда будет правильной, и нет необходимости проверять ее. Независимо от того, какие настройки валюты выбраны, это всегда будет правильно отформатированной валютой.

...