在可可中与货币作斗争
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 方法,您可以,但这不是必需的。
【讨论】:
以上是关于在可可中与货币作斗争的主要内容,如果未能解决你的问题,请参考以下文章