长度限制用到的地方不少,可是需求都不同.有的要求所有字符按一个处理,有的要求英文字母按一个,中文按两个,emoji按四个.这样就会遇到各类各样奇怪的问题,再被虐了无数次后,终于解决掉了.下面就来写写遇到的各类坑.git
首先想到的方法确定是delegate:github
#define kMaxLength 10 - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{ if (textField.text.length > kMaxLength) { return NO; } return YES; }
结果运行下来有问题,输到第10为的时候连删除也无法接收了,这样确定不行.因而想到了每次都让它输进去,以后截取到第10位.测试
#define kMaxLength 10 - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{ NSString *toString = [textField.text stringByReplacingCharactersInRange:range withString:string]; if (toString.length > kMaxLength) { textField.text = [toString substringToIndex:kMaxLength]; return NO; } return YES; }
简单地测试了下发现没什么问题,不过稍微细致点就发现了两个问题:字体
以后网上查了不少,有在delegate里实现,感受很复杂.仍是用UITextInputCurrentInputModeDidChangeNotification来作更方便点.编码
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textField_textDidChange:) name:UITextFieldTextDidChangeNotification object:textF]; #define kMaxLength 10 - (void)textField_textDidChange:(NSNotification *)notification { UITextField *textField = (UITextField *)notification.object; NSString *toBeString = textField.text; UITextRange *selectedRange = [textField markedTextRange]; //获取高亮部分 UITextPosition *position = [textField positionFromPosition:selectedRange.start offset:0]; // 没有高亮选择的字,则对已输入的文字进行字数统计和限制 if (!position) { if (toBeString.length > kMaxLength) { textField.text = [toBeString substringToIndex:kMaxLength]; } } // 有高亮选择的字符串,则暂不对文字进行统计和限制 else{ } } - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; }
输入框上面的高亮部分能够无限输,只是长度变化的时候截取.只是有个瑕疵,就是高亮部分能够无限输入.设计
原本觉得万事大吉了,可是测试仍是挑出了bug,当前面输入的是字母,最后一个是表情时,表情会被截取,成为一个很奇怪的符号.
原来问题出在了substringToIndex这个方法上.怎么得出这个结论的呢:code
-(unichar)characterAtIndex:(NSUInteger)index typedef unsigned short unichar;
这个方法的返回值unichar是个16位的无符号整型.那么全部对NSString的index位置操做,都是以unichar为单位的.
查阅字符编码能够发现:
server
例如这个emoji表情,字符编码为:
Unicode: U+1F601 (U+D83D U+DE01)
发现了问题所在了,emoji表情有20位啊,16位的unichar根本存不下!原来Unicode编码最初是被设计为16位的,后来为了编码一些冷门的中文日文,Unicode编码扩展到了21位(从U+0000到 U+10FFFF).
缘由是找到了,怎么解决呢?blog
NSString与Unicode,这篇文章把我全部的困惑都解决了,而且附上了解决办法.真要感谢下objc中国,否则让我看原版英文,估计够呛,英文仍是不能丢啊!ci
//通知的方法 #define kMaxLength 8 - (void)textField_textDidChange:(NSNotification *)notification { UITextField *textField = (UITextField *)notification.object; NSString *toBeString = textField.text; UITextRange *selectedRange = [textField markedTextRange]; //获取高亮部分 UITextPosition *position = [textField positionFromPosition:selectedRange.start offset:0]; // 没有高亮选择的字,则对已输入的文字进行字数统计和限制 if (!position) { if (toBeString.length > kMaxLength) { UITextRange *textRange = textField.selectedTextRange; textField.text = [toBeString subStringWithMaxLength:kMaxLength]; textField.selectedTextRange = textRange; } } // 有高亮选择的字符串,则暂不对文字进行统计和限制 else{ } } @implementation NSString (Add) - (NSString *)subStringWithMaxLength:(NSInteger)maxLength { __block NSString *aString = @""; __block int length = 0; [self enumerateSubstringsInRange:NSMakeRange(0, self.length) options:NSStringEnumerationByComposedCharacterSequences usingBlock:^(NSString * _Nullable substring, NSRange substringRange, NSRange enclosingRange, BOOL * _Nonnull stop) { char *p = (char *)[substring cStringUsingEncoding:NSUnicodeStringEncoding]; for (int i = 0; i < [substring lengthOfBytesUsingEncoding:NSUnicodeStringEncoding]; i++) { if (*p && p != '\0') { length++; } p++; } if (length <= maxLength) { aString = [aString stringByAppendingString:substring]; } }]; return aString; } @end
重写个NSString截取方法,之后每次截取都用这个方法,就能够解决最后一个表情被截的问题了.
相信不少人都被产品经理虐过,例如textView,两边文字内的间距调整,增长placeholder,设置placeholder的字体颜色,或者上文讲的文字不超过多少等等.
被虐过千百回,大多数状况也都遇到过了,特意封装了两个category,是textField和textView,基本上解决了大多数情况,只须要设置属性值就好了:
tv.maxLength = 20; tv.placeholder = @"我是textView"; tv.placeholderFont = [UIFont systemFontOfSize:15]; tv.placeholderColor = [UIColor redColor];
是否是很方便,github地址,欢迎你们交流,提出产品经理的要求,继续完善.