長度限制用到的地方不少,可是需求都不同.有的要求所有字符按一個處理,有的要求英文字母按一個,中文按兩個,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地址,歡迎你們交流,提出產品經理的要求,繼續完善.