UITextField文本限制输入
2018-12-05 本文已影响14人
大刘
在项目中UITextField(or UITextView)限制输入的需求普遍存在,在Android的xml UI布局中可以轻松的指定maxLength代表可以最多键入的字符数量,digits代表只允许输入的字符集合,但是在iOS系统中,不得不吐槽的是系统并没有给我们这两个属性去设置,并且键盘遮挡的输入框的事还需要我们自己处理,这确实应当是系统来做的。
我们这次只谈文本输入的限制。
简单的需求:
一个文本输入框只允许输入数字,且长度<=11位
这是一个很简单的需求,但是做起来有些麻烦,我找了很多第三方库经过各种测试后发现都不能很好的解决这个问题,因为要适应多种情况,先来看一些事项:
- 要考虑其他输入法,这就意味着textField.keyboardType = UIKeyboardTypeNumberPad并不能保证用户的键盘上只能有数字
- 要考虑复制-粘贴的情况,不合理的是在复制粘贴文本到textField中时,系统一般会在粘贴的字符串的前和后分别自动添加一个空格,而且如果用户粘贴的文本里有一部分是合法的,一部分是不合法的,要把不合法的去除掉
- 要考虑markedTextRange的情况,因为有可能用户选中了一部分textField中的文本然后再键入
- 要考虑9宫格的情况:iOS后来提供针了针对汉字拼音输入的9宫格输入法,但是当点击键盘时会发现在- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string方法中这个string是➋➌➍➎➏➐➑➒,并不是普通的26个英文字符。还好要求输入的只是数字,这个可以暂不考虑
- ...
Code:
-- ViewController.m --
- (void)viewDidLoad {
[super viewDidLoad];
self.textField = [[UITextField alloc] initWithFrame:CGRectMake(20, 100, self.view.frame.size.width-40, 40)];
self.textField.borderStyle = UITextBorderStyleBezel;
self.textField.delegate = self;
[self.view addSubview:self.textField];
}
#pragma mark - <UITextFieldDelegate>
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
return [InputingValidator validatePhone:textField shouldChangeCharactersInRange:range replacementString:string];
}
-- InputingValidator.m --
#import "InputingValidator.h"
@implementation InputingValidator
+ (BOOL)validatePhone:(UITextField *)inputField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)rangeString {
if ([self isNullOrEmpty:rangeString]) {
return YES;
}
else {
NSString *text = inputField.text;
NSString *replaceString = [self getNumber:rangeString];
if ([self isNullOrEmpty:replaceString]) {
return NO;
}
if (replaceString.length + inputField.text.length <= 11 && [replaceString isEqualToString:rangeString]) {
return YES;
}
NSInteger limitLength = MAX(0, 11 - (text.length - range.length));
if (limitLength <= 0) {
return NO;
}
replaceString = [replaceString substringToIndex:MIN(limitLength, replaceString.length)];
[self textField:inputField replaceString:replaceString forRange:range];
return NO;
}
}
+ (void)textField:(id<UITextInput>)inputField replaceString:(NSString *)replaceString forRange:(NSRange)range {
UITextPosition *beginning = inputField.beginningOfDocument;
UITextPosition *start = [inputField positionFromPosition:beginning offset:range.location];
UITextPosition *end = [inputField positionFromPosition:start offset:range.length];
UITextRange *textRange = [inputField textRangeFromPosition:start toPosition:end];
// this will be the new cursor location after insert/paste/typing
NSInteger cursorOffset = [inputField offsetFromPosition:beginning toPosition:start] + replaceString.length;
[inputField replaceRange:textRange withText:replaceString];
// https://stackoverflow.com/questions/11157791/how-to-move-cursor-in-uitextfield-after-setting-its-value
// setText:或replaceRange:withText:之后马上setCursor位置错乱,Weired.
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
UITextPosition *newCursorPosition = [inputField positionFromPosition:inputField.beginningOfDocument offset:cursorOffset];
UITextRange *newSelectedRange = [inputField textRangeFromPosition:newCursorPosition toPosition:newCursorPosition];
[inputField setSelectedTextRange:newSelectedRange];
});
}
+ (NSString *)getNumber:(NSString *)originStr {
if (!originStr || originStr.length <= 0 || [originStr isKindOfClass:[NSNull class]]) {
return nil;
}
NSMutableString *replaceStr = [NSMutableString stringWithCapacity:originStr.length];
for (NSInteger i = 0; i < originStr.length; i++) {
NSString *subStr = [originStr substringWithRange:NSMakeRange(i, 1)];
if ([self containsOnlyNumbers:subStr]) {
[replaceStr appendString:subStr];
}
}
if ([self isNullOrEmpty:replaceStr]) {
return nil;
}
return replaceStr;
}
// 此代码可放在NSString+XXX中,声明为:- (BOOL)isNullOrEmpty;
+ (BOOL)isNullOrEmpty:(NSString *)string {
if (!string || string.length <= 0 || [string isKindOfClass:[NSNull class]]) {
return YES;
}
return NO;
}
// 此代码可放在NSString+XXX中, 声明为:- (BOOL)containsOnlyNumbers;
+ (BOOL)containsOnlyNumbers:(NSString *)string {
NSCharacterSet *numbers = [[NSCharacterSet characterSetWithCharactersInString:@"0123456789"] invertedSet];
return ([string rangeOfCharacterFromSet:numbers].location == NSNotFound);
}
@end