UITextFiled,UITextView長度限制

長度限制用到的地方不少,可是需求都不同.有的要求所有字符按一個處理,有的要求英文字母按一個,中文按兩個,emoji按四個.這樣就會遇到各類各樣奇怪的問題,再被虐了無數次後,終於解決掉了.下面就來寫寫遇到的各類坑.git

Delegate

首先想到的方法確定是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;
}

簡單地測試了下發現沒什麼問題,不過稍微細緻點就發現了兩個問題:字體

  • 輸入結束後,點擊輸入框上面的候選漢字,不會進入委託,能夠無限的長.
  • 當使用拼音輸入法時,輸入的漢字默認兩個字符長度,當你輸入到上方候選漢字有6位時,實際上還沒超過長度,可是已經沒法輸入,框裏也變成了輸入的字母,十分不方便.

以後網上查了不少,有在delegate裏實現,感受很複雜.仍是用UITextInputCurrentInputModeDidChangeNotification來作更方便點.編碼

Notification
[[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];
}

輸入框上面的高亮部分能夠無限輸,只是長度變化的時候截取.只是有個瑕疵,就是高亮部分能夠無限輸入.設計

emoji表情截取

原本覺得萬事大吉了,可是測試仍是挑出了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地址,歡迎你們交流,提出產品經理的要求,繼續完善.

相關文章
相關標籤/搜索