在可可中与货币作斗争

Posted

技术标签:

【中文标题】在可可中与货币作斗争【英文标题】:Struggling with currency in Cocoa 【发布时间】:2009-02-27 01:45:22 【问题描述】:

我正在尝试做一些我认为相当简单的事情:让用户输入一个美元金额,将该金额存储在一个 NSNumber(NSDecimalNumber?)中,然后在稍后再次显示该金额格式化为货币.

我的麻烦不在于 setNumberStyle:NSNumberFormatterCurrencyStyle 并将浮点数显示为货币。问题更多在于所说的 numberFormatter 如何与这个 UITextField 一起工作。我可以找到几个例子。 11 月的This thread 和this one 给了我一些想法,但给我留下了更多问题。

我正在使用 UIKeyboardTypeNumberPad 键盘,并且了解我可能应该在显示时在字段中显示 $0.00(或任何本地货币格式),然后当用户输入数字以移动小数位时:

从显示 $0.00 开始 点击 2 键:显示 $0.02 点击 5 键:显示 $0.25 点击 4 键:显示 $2.54 点击 3 键:显示 $25.43

然后 [numberFormatter numberFromString:textField.text] 应该给我一个可以存储在我的 NSNumber 变量中的值。

遗憾的是,我仍在苦苦挣扎:这真的是最好/最简单的方法吗?如果是这样,那么也许有人可以帮助我实施?我觉得 UITextField 可能需要一个代表来响应每个按键,但不确定是什么、在哪里以及如何实现它?!任何示例代码?我将不胜感激!我找遍了高低……

Edit1: 所以我正在研究 NSFormatter 的 stringForObjectValue: 以及我能找到的最接近 benzado 推荐的内容:UITextViewTextDidChangeNotification。很难找到其中任何一个的示例代码......所以如果你知道在哪里找,请告诉我?

【问题讨论】:

标题好萌,但是以后有类似问题的人就找不到了。 我想过这个,但“货币”在任何地方以及标签中都被提及。 我还是改了。如果你回滚,我不会和你战斗。 对我来说,货币好像在亲吻你:P (我知道这很老了,但是对于后来的搜索者)你可能会发现这个小班很有帮助:github.com/iosptl/ios6ptl/tree/master/ch17/Money/Money 【参考方案1】:

我的解决方案:


- (BOOL)textField:(UITextField *)textField
    shouldChangeCharactersInRange:(NSRange)range 
    replacementString:(NSString *)string

  // Clear all characters that are not numbers
  // (like currency symbols or dividers)
  NSString *cleanCentString = [[textField.text
    componentsSeparatedByCharactersInSet:
    [[NSCharacterSet decimalDigitCharacterSet] invertedSet]]
      componentsJoinedByString:@""];
  // Parse final integer value
  NSInteger centAmount = cleanCentString.integerValue;
  // Check the user input
  if (string.length > 0)
  
    // Digit added
    centAmount = centAmount * 10 + string.integerValue;
  
  else
  
    // Digit deleted
    centAmount = centAmount / 10;
  
  // Update call amount value
  [_amount release];
  _amount = [[NSNumber alloc] initWithFloat:(float)centAmount / 100.0f];
  // Write amount with currency symbols to the textfield
  NSNumberFormatter *_currencyFormatter = [[NSNumberFormatter alloc] init];
  [_currencyFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
  [_currencyFormatter setCurrencyCode:@"USD"];
  [_currencyFormatter setNegativeFormat:@"-¤#,##0.00"];
  textField.text = [_currencyFormatter stringFromNumber:_amount];
  [_currencyFormatter release]
  // Since we already wrote our changes to the textfield
  // we don't want to change the textfield again
  return NO;

【讨论】:

【参考方案2】:

如果我现在必须写,这是我会使用的粗略攻击计划。诀窍是在隐藏的 UITextField 中键入内容,并在用户键入时使用格式化值更新 UILabel。

    创建一个 UITextField,将其隐藏,为其分配一个委托,然后使其成为召唤键盘的第一响应者。 在您的委托中,通过获取文本字段的新值并将其转换为数字来响应 textDidChange: 消息(懒得查找确切名称)。确保空字符串转换为零。 通过格式化程序运行数字,并使用该格式化货币值更新 UILabel。

每次按键时,标签都会更新,因此当用户真正编辑隐藏的文本字段时,她会感觉好像她正在编辑格式化的值。多么鬼鬼祟祟!

【讨论】:

这真的很令人沮丧,因为似乎很多箍只是接受一个货币金额?没有更简单的方法吗? NSNumberFormatter 不应该能够处理接受数字并将它们显示为货币......与将显示的货币转换为 NSNumber 进行存储相反吗? 这个过程并不难。 Cocoa 等价物是 10-20 行。 UITextField 不支持像 NSTextField(在 Mac OS X 上)那样的格式化程序。对不起,如果这意味着你的工作并不容易。 如果不是很明显,我还是很疑惑。我正在寻找的只是一些示例代码的链接(你还记得学习 Cocoa 吗?)。但是,感谢您让我觉得自己更像个白痴并投了反对票,因为新手很清楚这是唯一没有按时间顺序排列的论坛。 例如,我可以在您的回复中找到与第 2 步最接近的内容是 UITextField 类参考中的 UITextFieldTextDidChangeNotification。但我发现很少/没有这方面的文档。【参考方案3】:

嗯,我觉得这样更好:

- (void)viewWillDisappear:(BOOL)animated [[NSNotificationCenter defaultCenter] removeObserver:self  name:UITextFieldTextDidChangeNotification object:nil]; 

-(void)viewDidAppear:(BOOL)animated
 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textDidChanged:) name:UITextFieldTextDidChangeNotification object:nil];


-(void)textDidChanged:(NSNotification *)notification
 [[NSNotificationCenter defaultCenter] removeObserver:self  name:UITextFieldTextDidChangeNotification object:nil];
 UITextField * textField= [notification object];
 NSString * sinPesos= [textField.text stringByReplacingOccurrencesOfString:@"$" withString:@""];
 NSString * sinPuntos= [sinPesos stringByReplacingOccurrencesOfString:@"." withString:@""];

 float monto = [sinPuntos floatValue]; 

 monto= monto/100;

 NSString * cardText= [[self montoFormatter] stringFromNumber:[NSNumber numberWithDouble:monto]]; 

 textField.text = ([cardText isEqualToString: @"0"] ? @"":cardText);
 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textDidChanged:) name:UITextFieldTextDidChangeNotification object:nil];
 

-(NSNumberFormatter *)montoFormatter
 NSNumberFormatter* numberFormatter = [[NSNumberFormatter alloc] init];
 [numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
 [numberFormatter setMaximumFractionDigits:2];
 return [numberFormatter autorelease];

试试看:)

【讨论】:

很高兴看到一个解释为什么这可能会更好【参考方案4】:

由于我很懒,而且我无法忍受即时重新格式化我的输入“以帮助我”,我说:

让他们输入一个十进制数。当他们离开现场时,重新格式化。

【讨论】:

【参考方案5】:

好吧,这有点晚了,但我想我还是会试一试。这可能更像是一种解决方法,并且可能是混乱的代码,但它对我有用。我在 IB 中连接了一个标签和一个隐藏的文本字段,并使用 UITextFieldDelegate 委托编写了这段代码。

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string 
    if (textField == fineHiddenTextField) 
        NSString *fineText = [textField.text stringByReplacingCharactersInRange:range withString:string];
        if ([fineText length] == 0) 
            NSString *emptyFine = @"0.00";
            float fineValue = [emptyFine floatValue];
            fineEntryLabel.text = [NSString stringWithFormat:@"%.2f", fineValue];
         else if ([fineText length] == 1) 
            NSString *firstDec = [NSString stringWithFormat:@"0.0%@", fineText];
            float fineValue = [firstDec floatValue];
            fineEntryLabel.text = [NSString stringWithFormat:@"%.2f", fineValue];
         else if ([fineText length] == 2) 
            NSString *secondDec = [NSString stringWithFormat:@"0.%@", fineText];
            float fineValue = [secondDec floatValue];
            fineEntryLabel.text = [NSString stringWithFormat:@"%.2f", fineValue];
         else 
            int decimalPlace = [fineText length] - 2;
            NSString *fineDollarAmt = [fineText substringToIndex:decimalPlace];
            NSString *fineCentsAmt = [fineText substringFromIndex:decimalPlace];
            NSString *finalFineValue = [NSString stringWithFormat:@"%@.%@", fineDollarAmt, fineCentsAmt];
            float fineValue = [finalFineValue floatValue];
            fineEntryLabel.text = [NSString stringWithFormat:@"%.2f", fineValue];
        


        //fineEntryLabel.text = [NSString stringWithFormat:@"%.2f", fineValue];
        return YES;
    

不完全是最整洁的,但它确实完成了工作。最初的 if 语句只是为了确保这只发生在这个特定的 textField 上(因为在同一页面中有多个。)这段代码用于输入钱(支付的罚款金额),我想要它简单易用。只需将您的标签设置为从右侧对齐即可。

我现在对咖啡有点痴迷,但我会回答你可能有的任何问题。 :)

【讨论】:

【参考方案6】:

这是我的解决方案!

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
    float valorUnit = [textField.text floatValue];

    if( string.length > 0)
    
        float incremento = [string floatValue];

        valorUnit = valorUnit * 10.f + (incremento/100.f);
        NSString* strVal = [NSString stringWithFormat:@"%f", valorUnit];

        if (valorUnit > 0.f && valorUnit < 10.f) 
            textField.text  = [strVal substringToIndex:3];
        
        else if (valorUnit < 100.f && valorUnit >= 10.f)
        
            textField.text  = [strVal substringToIndex:4];
        
        else if (valorUnit >=100.f && valorUnit <1000.f)
        
            textField.text  = [strVal substringToIndex:5];
        
        else 
            return NO;
        

    
    else 
        valorUnit = (valorUnit/10.f);
        NSString* strVal = [NSString stringWithFormat:@"%f", valorUnit];
        if (valorUnit > 0.f && valorUnit < 10.f) 
            textField.text  = [strVal substringToIndex:5];
        
        else if (valorUnit < 100.f && valorUnit >= 10.f)
        
            textField.text  = [strVal substringToIndex:6];
        
        else if (valorUnit >=100.f && valorUnit <1000.f)
        
            textField.text  = [strVal substringToIndex:7];
        
        else 
            return NO;
        
    

return YES;

【讨论】:

您应该使用localizedStringWithFormat: 来显示正确的小数分隔符。此解决方案是否正确处理非小数货币(例如日元)?它是否正确处理没有 ¹⁄₁₀₀ 个单位的货币?【参考方案7】:

我写了一个开源的UITextField 子类来处理这个问题,在这里可以找到:

https://github.com/TomSwift/TSCurrencyTextField

我采用的方法类似于 Lars Schneider 在他的流行回答中建议的方法。但是我的版本完全封装在一个可以在任何地方使用的可重用组件中,就像 UITextField 一样。如果您选择实现任何 UITextFieldDelegate 方法,您可以,但这不是必需的。

【讨论】:

以上是关于在可可中与货币作斗争的主要内容,如果未能解决你的问题,请参考以下文章

在 Rust 中与生命周期的子类型关系作斗争

数字货币...决定大国命运与美三场斗争即将开启

美国考虑发 央行数字货币,

在报告页脚中与组求和

三类数字货币的投资配置建议

在片段中与 onItemClick 斗争