UITextView UITextField限制輸入字數以及精準剩餘字數顯示

熊本丸發表於2018-12-08

UITextView UITextField限制輸入字數以及精準剩餘字數顯示

在日常專案開發中,我們經常用到UITextView UITextField這兩個控制元件,今天我們來講講如何方便簡單的限制這兩個控制元件的輸入字數以及精準顯示剩餘字數。

之前在專案中遇到了UITextView的限制輸入以及剩餘字數顯示,因為專案比較老,也有寫好的限制輸入功能,但是當這兩種功能需要同時實現時,需要在原來的基礎上新增很多重複的程式碼,剛好最近也閒下來了,就準備自己動手寫寫。

首先我們先拿UITextView來說吧,UITextView主要用於多行文字的輸入,為了限制它的輸入字數,我使用的方法是自定義一個textView,自定義的textView自身實現UITextViewDelegate方法,子類的屬性有以下幾個:

@property (nonatomic, assign) NSInteger tvLimitNumber;//最大輸入字數

@property (nonatomic, assign, getter=isForbidSystemEmoji) BOOL shouldForbidSystemEmoji;//是否禁止系統表情輸入

@property (nonatomic, assign) BOOL returnKeyDone;//return鍵點選效果是否完成輸入

@property (nonatomic, weak) id<BWTextViewDelegate> tvDelegate;//自定義代理方法
複製程式碼

為了實現限制輸入字數,所用到的主要是以下兩個代理方法:

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text;

- (void)textViewDidChange:(UITextView *)textView;
複製程式碼

第一個代理方法可以實時的獲取到當前輸入的每一個字元,並且控制是否將輸入的字元加入到最後的字串中,我們在代理方法中實現以下內容來限制輸入的字元是否加入到最後的字串中:

//最大限制數量小於0 則不做限制數量限制
    if (self.tvLimitNumber <= 0) {
        return YES;
    }
    //獲取高亮部分
    UITextRange *markedRange = [textView markedTextRange];
    UITextPosition *position = [textView positionFromPosition:markedRange.start offset:0];
    //如果有高亮且當前字數開始位置小於最大限制時允許輸入
    if (markedRange && position) {
        NSInteger startOffset = [textView offsetFromPosition:textView.beginningOfDocument toPosition:markedRange.start];
        NSInteger endOffset = [textView offsetFromPosition:textView.beginningOfDocument toPosition:markedRange.end];
        NSRange offsetRange = NSMakeRange(startOffset, endOffset - startOffset);
        if (offsetRange.location < self.tvLimitNumber) {
            return YES;
        } else {
            return NO;
        }
    }
複製程式碼

在第二個代理方法中,我們需要實現的是判斷textView中的文字是否超過限制字數,如果超出則擷取出子串並賦值給textView.text,實現文字的限制:

//若限制數量小於等於0 則不對輸入做限制
    if (self.tvLimitNumber <= 0) {
        //執行自定義代理方法
        [self textViewValueDidChanged:textView];
        return;
    }
    NSString *lang = [[[UITextInputMode activeInputModes] firstObject] primaryLanguage];//當前的輸入模式
    //獲取高亮部分
    UITextRange *range = [textView markedTextRange];
    UITextPosition *start = range.start;
    UITextPosition*end = range.end;
    NSInteger selectLength = [textView offsetFromPosition:start toPosition:end];
    //存在高亮則不計算高度
    if (range && selectLength) {
        return;
    }
    if ([lang isEqualToString:@"zh-Hans"]) {
        NSInteger contentLength = textView.text.length - selectLength;
        if (contentLength >= self.tvLimitNumber) {
            textView.text = [textView.text substringToIndex:self.tvLimitNumber];
            //文字數量超出最大限制數量
        }
    } else {
        if (textView.text.length >= self.tvLimitNumber) {
            textView.text = [textView.text substringToIndex:self.tvLimitNumber];
            //文字數量超出最大限制數量
        }
    }
    //執行自定義代理方法
    [self textViewValueDidChanged:textView];
複製程式碼

為了讓在外部也能實現UITextView的代理方法,自定義的textView新增一個協議,協議的大致內容都是UITextViewDelegate中的內容,這樣就不用擔心無法在外部不能自定義一些操作了,自定義的代理方法主要有以下幾種:

- (void)bwTextViewDidChanged:(UITextView *)textView;

 - (BOOL)bwTextView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text;

 - (BOOL)bwTextViewShouldBeginEditing:(UITextView *)textView;

 - (BOOL)bwTextViewShouldEndEditing:(UITextView *)textView;

 - (void)bwTextViewDidBeginEditing:(UITextView *)textView;

 - (void)bwTextViewDidEndEditing:(UITextView *)textView;
複製程式碼

接下來講講UITextField,實現的原理是一樣的,不過UITextFieldDelegate中並沒有暴露出類似UITextView:- (void)textViewDidChange:(UITextView *)textView這樣的方法,為了實現類似的功能,我在自定義的UITextField中新增了實時監聽UITextField內容變化的通知,並在通知方法中實現擷取子串的操作:

- (void)setTfLimitNumber:(NSInteger)tfLimitNumber {
    _tfLimitNumber = tfLimitNumber;
    //新增通知
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textFieldDidChanged) name:UITextFieldTextDidChangeNotification object:nil];
}
複製程式碼
- (void)textFieldDidChanged {
    //若限制數量小於等於0 則不對輸入做限制
    if (self.tfLimitNumber <= 0) {
        //執行自定義代理方法
        [self bwTextFieldDidChanged:self];
        return;
    }
    NSString *lang = [[[UITextInputMode activeInputModes] firstObject] primaryLanguage];//當前的輸入模式
    //獲取高亮部分
    UITextRange *range = [self markedTextRange];
    UITextPosition *start = range.start;
    UITextPosition*end = range.end;
    NSInteger selectLength = [self offsetFromPosition:start toPosition:end];
    //存在高亮則不計算高度
    if (range && selectLength) {
        return;
    }
    if ([lang isEqualToString:@"zh-Hans"]) {
        NSInteger contentLength = self.text.length - selectLength;
        if (contentLength >= self.tfLimitNumber) {
            self.text = [self.text substringToIndex:self.tfLimitNumber];
            //文字數量超出最大限制數量
        }
    } else {
        if (self.text.length >= self.tfLimitNumber) {
            self.text = [self.text substringToIndex:self.tfLimitNumber];
            //文字數量超出最大限制數量
        }
    }
    //執行自定義代理方法
    [self bwTextFieldDidChanged:self];
}
複製程式碼

以上就是自定義UITextView UITextField實現限制輸入字數以及精準獲取剩餘字數的功能,實現這類功能只需要初始化時設定需要限制的字數即可,減少了重複實現代理方法的程式碼,大大的增加了程式碼的美觀以及封裝性。

程式碼請戳這裡

相關文章