【发布时间】:2010-09-21 12:59:16
【问题描述】:
在我的应用中,用户需要能够输入带小数位的数值。 iPhone 不提供专门用于此目的的键盘 - 仅提供数字键盘和带有数字和符号的键盘。
有没有一种简单的方法来使用后者并防止输入任何非数字输入而无需正则表达式最终结果?
谢谢!
【问题讨论】:
标签: iphone cocoa-touch
在我的应用中,用户需要能够输入带小数位的数值。 iPhone 不提供专门用于此目的的键盘 - 仅提供数字键盘和带有数字和符号的键盘。
有没有一种简单的方法来使用后者并防止输入任何非数字输入而无需正则表达式最终结果?
谢谢!
【问题讨论】:
标签: iphone cocoa-touch
根据具体的应用程序,在 iphone 上提供一个可供用户选择位置的滑块可能是更好的选择。那么根本不需要输入数字。
【讨论】:
您可能希望使用滑块(如 Martin v. Löwis 所建议的那样)或 UIPickerView,每个数字都有一个单独的轮子。
【讨论】:
我想做完全相同的事情,除了使用货币而不是直接的十进制值。
我最终创建了一个包含 UITextField 和 UILabel 的自定义视图。标签覆盖了文本字段,但文本字段仍会收到触摸。我使用 UITextFieldTextDidChange 通知来观察文本字段的变化(我使用 NSNumberFormatter 将结果数字转换为格式化的货币值)来更新标签。
要禁用允许用户重新定位插入点的放大镜,您需要使用自定义 UITextField 子类并覆盖 touchesBegan:withEvent: 并将其设置为不执行任何操作。
我的解决方案可能与您需要的不同,因为小数点始终是固定的——我使用系统的货币设置来确定小数点后应该有多少位数。但是,数字小键盘上没有小数点。而且您无法在键盘上添加任何按钮(这尤其令人讨厌,因为键盘左下角有一个空白按钮非常适合小数点!)所以我没有解决方案,很遗憾。
【讨论】:
becomeFirstResponder。这样我就不用担心禁用放大镜了。
一个更优雅的解决方案恰好也是最简单的。
您不需要小数分隔键
为什么?因为你可以简单地从用户的输入中推断出来。例如,在美国地区,当您输入 1.23 美元时,您首先输入数字 1-2-3(按该顺序)。在系统中,每输入一个字符,就会被识别为:
请注意我们如何根据用户的输入推断小数点分隔符。现在,如果用户想要输入 1.00 美元,他们只需输入数字 1-0-0。
为了让您的代码处理不同区域设置的货币,您需要获取货币的最大小数位数。这可以通过以下代码 sn-p 来完成:
NSNumberFormatter *currencyFormatter = [[NSNumberFormatter alloc] init];
[currencyFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
int currencyScale = [currencyFormatter maximumFractionDigits];
例如,日元的最大小数位数为 0。因此,在处理日元输入时,没有小数分隔符,因此甚至不必担心小数。
这种解决问题的方法允许您使用 Apple 提供的库存数字输入键盘,而无需担心自定义键盘、正则表达式验证等。
【讨论】:
这是已接受答案中建议的解决方案的示例。这不处理其他货币或任何东西 - 在我的情况下,我只需要支持美元,无论语言环境/货币是什么,所以这对我来说没问题:
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range
replacementString:(NSString *)string {
double currentValue = [textField.text doubleValue];
//Replace line above with this
//double currentValue = [[textField text] substringFromIndex:1] doubleValue];
double cents = round(currentValue * 100.0f);
if ([string length]) {
for (size_t i = 0; i < [string length]; i++) {
unichar c = [string characterAtIndex:i];
if (isnumber(c)) {
cents *= 10;
cents += c - '0';
}
}
} else {
// back Space
cents = floor(cents / 10);
}
textField.text = [NSString stringWithFormat:@"%.2f", cents / 100.0f];
//Add this line
//[textField setText:[NSString stringWithFormat:@"$%@",[textField text]]];
return NO;
}
四舍五入和楼层很重要 a) 因为浮点表示有时会丢失 .00001 或其他任何内容,并且 b) 格式字符串会向上舍入我们在退格部分删除的任何精度。
【讨论】:
我构建了一个带小数点的自定义数字键盘视图控制器...检查一下:
http://sites.google.com/site/psychopupnet/iphone-sdk/tenkeypadcustom10-keypadwithdecimal
【讨论】:
从 iOS4.1 开始,有一个新的键盘类型 UIKeyboardTypeNumberPad,但不幸的是,到目前为止,它似乎还没有出现在 Interface Builder 选择列表中。
【讨论】:
以下是如何在不使用与货币无关的方式中使用浮点数、round() 或 ceil() 的情况下做到这一点。
在您的视图控制器中,设置以下实例变量(如果这是您的包,请使用关联的@property 语句):
@interface MyViewController : UIViewController <UITextFieldDelegate> {
@private
UITextField *firstResponder;
NSNumberFormatter *formatter;
NSInteger currencyScale;
NSString *enteredDigits;
}
@property (nonatomic, readwrite, assign) UITextField *firstResponder;
@property (nonatomic, readwrite, retain) NSNumberFormatter *formatter;
@property (nonatomic, readwrite, retain) NSString *enteredDigits;
@end
您的 viewDidLoad 方法应包含以下内容:
- (void)viewDidLoad {
[super viewDidLoad];
NSNumberFormatter *aFormatter = [[NSNumberFormatter alloc] init];
[aFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
currencyScale = -1 * [aFormatter maximumFractionDigits];
self.formatter = aFormatter;
[aFormatter release];
}
然后按如下方式实现您的 UITextFieldDelegate 方法:
#pragma mark -
#pragma mark UITextFieldDelegate methods
- (void)textFieldDidBeginEditing:(UITextField *)textField {
// Keep a pointer to the field, so we can resign it from a toolbar
self.firstResponder = textField;
self.enteredDigits = @"";
}
- (void)textFieldDidEndEditing:(UITextField *)textField {
if ([self.enteredDigits length] > 0) {
// Get the amount
NSDecimalNumber *result = [[NSDecimalNumber decimalNumberWithString:self.enteredDigits] decimalNumberByMultiplyingByPowerOf10:currencyScale];
NSLog(@"result: %@", result);
}
}
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
// Check the length of the string
if ([string length]) {
self.enteredDigits = [self.enteredDigits stringByAppendingFormat:@"%d", [string integerValue]];
} else {
// This is a backspace
NSUInteger len = [self.enteredDigits length];
if (len > 1) {
self.enteredDigits = [self.enteredDigits substringWithRange:NSMakeRange(0, len - 1)];
} else {
self.enteredDigits = @"";
}
}
NSDecimalNumber *decimal = nil;
if ( ![self.enteredDigits isEqualToString:@""]) {
decimal = [[NSDecimalNumber decimalNumberWithString:self.enteredDigits] decimalNumberByMultiplyingByPowerOf10:currencyScale];
} else {
decimal = [NSDecimalNumber zero];
}
// Replace the text with the localized decimal number
textField.text = [self.formatter stringFromNumber:decimal];
return NO;
}
只用英镑和便士对此进行了测试,但它也应该适用于日元。如果您想为非货币目的格式化小数,那么只需阅读NSNumberFormatter 上的文档并设置您想要的任何格式/maximumFractionDigits。
【讨论】:
self.formatter = [[NSNumberFormatter alloc] init];的泄漏
我认为最好指出从 iOS 4.1 开始您可以使用新的UIKeyboardTypeDecimalPad。
所以现在你只需要:
myTextField.keyboardType=UIKeyboardTypeDecimalPad;
【讨论】:
Mike Weller 帖子的 Swift 2 实现,也只有美元:
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
guard let str = textField.text else {
textField.text = "0.00"
return false
}
let value = (str as NSString).doubleValue
var cents = round(value * 100)
if string.characters.count > 0 {
for c in string.characters {
if let num = Int(String(c)) {
cents *= 10
cents += Double(num)
}
}
}
else {
cents = floor(cents / 10)
}
textField.text = NSString(format: "%.2f", cents/100) as String
return false
}
【讨论】:
您可以使用STATextField 并将currencyRepresentation 设置为YES:
确保输入的小数点不超过一位,并且小数点右侧不超过两位。
还有STAATMTextField默认支持货币模式和 ATM文本输入:
提供一个模拟 ATM 机输入行为的文本字段。
【讨论】: