我的应用程序允许用户在UITextField
控件中输入数值(货币),但遗憾的是我希望的键盘布局不是内置选项之一,所以我必须选择Interface Builder中的“Numbers& Punctuation”选项。这是IB中相应的对话窗口:
因此,当我的应用程序询问用户输入时,它显示以下内容:
哪个非常好,但请查看用户可用的所有额外密钥!人们可以很容易地输入“12; 56!”在文本字段中,我假设我必须以某种方式验证。
所以我的问题是:如何验证输入UITextField
的货币值?
答案 0 :(得分:27)
我有回答的冲动,因为这是我用Google搜索时看到的第一个条目,排名最高的答案不允许我输入货币。
我是德国人,在德国(以及许多其他国家),我们使用,
作为十进制分隔符。
我刚刚写了一个类似的方法,这就是我现在所拥有的。
- (BOOL) textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
static NSString *numbers = @"0123456789";
static NSString *numbersPeriod = @"01234567890.";
static NSString *numbersComma = @"0123456789,";
//NSLog(@"%d %d %@", range.location, range.length, string);
if (range.length > 0 && [string length] == 0) {
// enable delete
return YES;
}
NSString *symbol = [[NSLocale currentLocale] objectForKey:NSLocaleDecimalSeparator];
if (range.location == 0 && [string isEqualToString:symbol]) {
// decimalseparator should not be first
return NO;
}
NSCharacterSet *characterSet;
NSRange separatorRange = [textField.text rangeOfString:symbol];
if (separatorRange.location == NSNotFound) {
if ([symbol isEqualToString:@"."]) {
characterSet = [[NSCharacterSet characterSetWithCharactersInString:numbersPeriod] invertedSet];
}
else {
characterSet = [[NSCharacterSet characterSetWithCharactersInString:numbersComma] invertedSet];
}
}
else {
// allow 2 characters after the decimal separator
if (range.location > (separatorRange.location + 2)) {
return NO;
}
characterSet = [[NSCharacterSet characterSetWithCharactersInString:numbers] invertedSet];
}
return ([[string stringByTrimmingCharactersInSet:characterSet] length] > 0);
}
答案 1 :(得分:21)
虽然你可以在NSNumberFormatter中使用它,但我发现除了0-9之外,我更容易筛选出任何东西。
这对我很有用:
- (BOOL) textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSCharacterSet *nonNumberSet = [[NSCharacterSet characterSetWithCharactersInString:@"0123456789."] invertedSet];
return ([string stringByTrimmingCharactersInSet:nonNumberSet].length > 0);
}
答案 2 :(得分:10)
我发现这是一种相对干净的方法。我没有用非美国货币测试它,但由于它使用了NSNumberFormatter的属性,我相信它应该正确处理它们。
首先在某个地方设置格式化程序:
formatter = [NSNumberFormatter new];
[formatter setNumberStyle: NSNumberFormatterCurrencyStyle];
[formatter setLenient:YES];
[formatter setGeneratesDecimalNumbers:YES];
使用格式化程序解析和重新格式化其输入。它还可以在添加和删除数字时处理数字的移位。
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSString *replaced = [textField.text stringByReplacingCharactersInRange:range withString:string];
NSDecimalNumber *amount = (NSDecimalNumber*) [formatter numberFromString:replaced];
if (amount == nil) {
// Something screwed up the parsing. Probably an alpha character.
return NO;
}
// If the field is empty (the initial case) the number should be shifted to
// start in the right most decimal place.
short powerOf10 = 0;
if ([textField.text isEqualToString:@""]) {
powerOf10 = -formatter.maximumFractionDigits;
}
// If the edit point is to the right of the decimal point we need to do
// some shifting.
else if (range.location + formatter.maximumFractionDigits >= textField.text.length) {
// If there's a range of text selected, it'll delete part of the number
// so shift it back to the right.
if (range.length) {
powerOf10 = -range.length;
}
// Otherwise they're adding this many characters so shift left.
else {
powerOf10 = [string length];
}
}
amount = [amount decimalNumberByMultiplyingByPowerOf10:powerOf10];
// Replace the value and then cancel this change.
textField.text = [formatter stringFromNumber:amount];
return NO;
}
答案 3 :(得分:7)
也许您可以在控件上添加 UITextFieldDelegate
,并让它实现 textField:shouldChangeCharactersInRange:replacementString:
这样,如果它看到任何你不喜欢的字符不想在现场,它可以拒绝它们。
答案 4 :(得分:7)
结合Marc提出的textField:shouldChangeCharactersInRange:replacementString:
建议,您应该使用NSNumberFormatter
将文字传递给NSNumberFormatterCurrencyStyle
。这将处理货币格式的怪癖并处理特定于语言环境的选项。
如果你搜索它,iPhone文档中有一个“Cocoa数据格式编程指南”部分。遗憾的是,这里的大部分UI信息都是特定于Mac OS X(在iPhone上无法运行),但它会向您展示如何使用格式化程序类。
答案 5 :(得分:6)
我发现shouldChangeCharactersInRange也会弹出弹出式键盘,退格键和“完成”按钮。我发现如果我处理了0个长度字符串并允许控制字符,它工作正常。
我不喜欢使用NSNumberFormatter,因为它坚持在用户编辑时所有阶段的数字都是格式良好的,如果你想要在数字中暂时有两个小数点,这可能会令人愤怒直到你删除错误位置的那个。
这是我使用的代码:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if ([string length] < 1) // non-visible characters are okay
return YES;
if ([string stringByTrimmingCharactersInSet:[NSCharacterSet controlCharacterSet]].length == 0)
return YES;
return ([string stringByTrimmingCharactersInSet:[self.characterSet invertedSet]].length > 0);
}
其中self.characterSet包含可接受的字符,我使用此方法为货币创建它:
- (NSCharacterSet *)createCurrencyCharacterSet
{
NSLocale *locale = [NSLocale currentLocale];
NSMutableCharacterSet *currencySet = [NSMutableCharacterSet decimalDigitCharacterSet];
[currencySet addCharactersInString:@"-"]; // negative symbol, can't find a localised version
[currencySet addCharactersInString:[locale objectForKey:NSLocaleCurrencySymbol]];
[currencySet addCharactersInString:[locale objectForKey:NSLocaleGroupingSeparator]];
[currencySet addCharactersInString:[locale objectForKey:NSLocaleDecimalSeparator]];
return [[currencySet copy] autorelease];
}
有些不愉快的代码[[currencySet copy] autorelease]
返回一个不可变的NSCharacterSet。
使用[NSCharacterSet decimalDigitCharacterSet]
还包括印度语和阿拉伯语等效字符,希望人们使用这些语言可以使用字母数字输入数字。
仍然需要检查NSNumberFormatter是否可以解析用户的输入并在不能的情况下发出警报;尽管如此,只有输入合法字符才能获得更好的体验。
答案 6 :(得分:5)
我一直看到人们在实现shouldChangeCharactersInRange时不知道如何正确地确定结果字符串。
如果您返回YES:
,您将获得输入的新文本NSString *newText = [textField.text stringByReplacingCharactersInRange:range withString:string];
获得此新文本后,可以轻松检查该文本是否为有效数字,并相应地返回YES或NO。
请记住,如果用户尝试使用带光标键的蓝牙键盘编辑字符串,则所有其他解决方案(例如此处显示的解决方案,其中一个检查“字符串”的长度等)可能无法正常工作使用高级编辑功能(选择,剪切,粘贴)。
答案 7 :(得分:2)
如果您希望他们只能输入数字 - 您也可以考虑数字键盘
答案 8 :(得分:2)
在找到堆栈溢出的快速解决方案以处理美元后,我重写了该函数以安全地处理国际货币。此解决方案将动态验证来自UITextField的用户输入,并在键入时进行更正。
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
NSNumberFormatter *nf = [[NSNumberFormatter alloc] init];
[numberFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
[numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
[numberFormatter setMaximumFractionDigits:2];
[numberFormatter setMinimumFractionDigits:0];
// Grab the contents of the text field
NSString *text = [textField text];
// the appropriate decimalSeperator and currencySymbol for the current locale
// can be found with help of the
// NSNumberFormatter and NSLocale classes.
NSString *decimalSeperator = numberFormatter.decimalSeparator;
NSString *currencySymbol = numberFormatter.currencySymbol;
NSString *replacementText = [text stringByReplacingCharactersInRange:range withString:string];
NSMutableString *newReplacement = [[ NSMutableString alloc ] initWithString:replacementText];
// whenever a decimalSeperator or currencySymobol is entered, we'll just update the textField.
// whenever other chars are entered, we'll calculate the new number and update the textField accordingly.
// If the number can't be computed, we ignore the new input.
NSRange decimalRange = [text rangeOfString:decimalSeperator];
if ([string isEqualToString:decimalSeperator] == YES &&
[text rangeOfString:decimalSeperator].length == 0) {
[textField setText:newReplacement];
} else if([string isEqualToString:currencySymbol] == YES&&
[text rangeOfString:currencySymbol].length == 0) {
[textField setText:newReplacement];
} else if([newReplacement isEqualToString:currencySymbol] == YES) {
return YES;
}else {
NSString *currencyGroupingSeparator = numberFormatter.currencyGroupingSeparator;
[newReplacement replaceOccurrencesOfString:currencyGroupingSeparator withString:@"" options:NSBackwardsSearch range:NSMakeRange(0, [newReplacement length])];
[numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
NSNumber *number = [numberFormatter numberFromString:newReplacement];
if([newReplacement length] == 1) {
[numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
number = [numberFormatter numberFromString:newReplacement];
}
if (number == nil) {
[newReplacement release];
return NO;
}
[numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
text = [numberFormatter stringFromNumber:number];
[textField setText:text];
}
[newReplacement release];
return NO; // we return NO because we have manually edited the textField contents.
}
答案 9 :(得分:1)
使用NSNumberFormatter是正确的答案。它将处理验证并将字符串转换为正确的对象类型。
答案 10 :(得分:1)
您也可以使用Gamma-Point解决方案并评估* aNumber是否为Nil,如果其为Nil,则表示该字符不是0-9。或者,输入后,这样你就可以只验证数字,如果输入任何无数字字符,它将使变量为零。
答案 11 :(得分:1)
我无法找到货币验证的相应实施方案。这是我的变体:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
NSString* proposedString = [textField.text stringByReplacingCharactersInRange:range withString:string];
//if there is empty string return YES
if([proposedString length] == 0) {
return YES;
}
//create inverted set for appripriate symbols
NSCharacterSet *nonNumberSet = [[NSCharacterSet characterSetWithCharactersInString:@"0123456789.,"] invertedSet];
//if side symbols is trimmed by nonNumberSet - return NO
if([proposedString stringByTrimmingCharactersInSet:nonNumberSet].length != [proposedString length]) {
return NO;
}
//if there is more than 1 symbol of '.' or ',' return NO
if([[proposedString componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@".,"]] count] > 2) {
return NO;
}
//finally check is ok, return YES
return YES;
}
答案 12 :(得分:0)
使用shouldChangeCharactersInRange拧紧弹出式键盘,因为退格按钮不起作用。
数字格式化器是要走的路。这是我用来查找正小数的示例。我在验证检查期间将其称为例如当用户点击保存按钮时。
-(BOOL) isPositiveNumber: (NSString *) numberString {
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
NSNumber *aNumber = [numberFormatter numberFromString:numberString];
[numberFormatter release];
if ([aNumber floatValue] > 0) {
NSLog( @"Found positive number %4.2f",[aNumber floatValue] );
return YES;
}
else {
return NO;
}
}