UITextField的一些坑


想對(duì)UITextField的輸入內(nèi)容的長(zhǎng)度做一個(gè)限制,初始的代碼大致如下:

- (UITextField *)titleTextField {
    if (!_titleTextField) {
        _titleTextField = [[UITextField alloc] init];
        _titleTextField.delegate = self;
        [_titleTextField addTarget:self action:@selector(titleTextFieldDidChanged:) forControlEvents:UIControlEventEditingChanged];
    }
    
    return _titleTextField;
}

在titleTextFieldDidChanged的處理:

- (void)titleTextFieldDidChanged:(UITextField *)textField {
  // 如果textField.text的長(zhǎng)度超過(guò)指定的長(zhǎng)度,那么就截?cái)?  // 并彈出一個(gè)alertView告知用戶
}

結(jié)果發(fā)現(xiàn)輸入英文的時(shí)候,當(dāng)長(zhǎng)度即將超過(guò)時(shí),如果使用鍵盤輸入,沒有問(wèn)題,但如果選擇鍵盤上方的聯(lián)想單詞時(shí),alertView會(huì)彈出兩次。

調(diào)試發(fā)現(xiàn),如果使用英文鍵盤的聯(lián)想單詞時(shí),會(huì)觸發(fā)兩次UIControlEventEditingChanged,第一次是輸入單詞,第二次是輸入一個(gè)空格。因此會(huì)觸發(fā)兩次titleTextFieldDidChanged的調(diào)用,導(dǎo)致alertView彈出兩次。

中文拼音輸入,選詞時(shí),我們可以通過(guò)textField.markedTextRange,判斷是否處于高亮狀態(tài)來(lái)進(jìn)行處理,同時(shí),中文情況下,如果選擇鍵盤頂部的聯(lián)想單詞時(shí),也只會(huì)觸發(fā)一次UIControlEventEditingChanged(因?yàn)橹形奈膊坎恍枰a(bǔ)空格)。

我目前沒有發(fā)現(xiàn)可以阻止“選擇英文鍵盤頂部的聯(lián)想單詞,觸發(fā)兩次UIControlEventEditingChanged”這個(gè)行為,而且也無(wú)法判斷輸入的空格是單獨(dú)輸入的空格還是“選擇英文鍵盤頂部的聯(lián)想單詞,追加的空格”。

因此,只能曲線救國(guó),即判斷alertView是否已經(jīng)彈出,如果彈出了,則不用在此彈出了。

此外,在截?cái)嘧址畷r(shí),需要考慮是否截?cái)鄀moji等特殊字符(長(zhǎng)度>1),因此需要用到下面兩個(gè)API

- (NSRange)rangeOfComposedCharacterSequenceAtIndex:(NSUInteger)index;
- (NSRange)rangeOfComposedCharacterSequencesForRange:(NSRange)range API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));

即判斷截?cái)辔恢玫淖址欠袷嵌嚅L(zhǎng)度組成的特殊字符。

此外,還有一個(gè)比較特殊的問(wèn)題,即如果在編輯的時(shí)候,用戶在中部插入多個(gè)字符導(dǎo)致超過(guò)長(zhǎng)度時(shí),會(huì)出現(xiàn)插入成功但尾部的字符被截?cái)啵@個(gè)行為看上去也不是那么友好。

這是因?yàn)槲覀冊(cè)赨IControlEventEditingChanged事件里進(jìn)行的處理,此時(shí)TextField的內(nèi)容已經(jīng)被插入完成,形成一個(gè)整體后再進(jìn)行的長(zhǎng)度判斷和截取。

如果需要更精準(zhǔn)的行為,例如當(dāng)前30個(gè)字符,最多允許31個(gè)字符,中部插入4個(gè)字符時(shí),僅插入一個(gè)字符,原30個(gè)字符不變,并提醒用戶不允許超過(guò)31個(gè)字符。如果需要處理成這種行為,那么我們需要在下面這個(gè)代理中進(jìn)行提前處理。

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
    return YES;
}

最終的代碼:

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
    // http://m.itdecent.cn/p/436fef66f6dc
    
    if (string.length <= 0) {
        return YES;
    }

    // 高亮選擇狀態(tài)中,例如中文輸入拼音時(shí)
    UITextRange *selectedRange = textField.markedTextRange;
    if (selectedRange && ![selectedRange isEmpty]) {
        return YES;
    }

    // 先看看更新后的字符串是什么
    NSString *newString = [textField.text stringByReplacingCharactersInRange:range withString:string];

    // 更新后是否長(zhǎng)度溢出,這邊31是最長(zhǎng)長(zhǎng)度,暫時(shí)硬編碼了
    NSInteger overflowedLength = 31 - [newString length];

    if (overflowedLength >= 0) {
        // 沒有溢出,那么就直接返回YES
        return YES;
    } else {
        // 溢出了,那看看允許添加多少長(zhǎng)度的內(nèi)容
        NSInteger allowedLength = [string length] + overflowedLength;
        if (allowedLength > 0) {
            // 原則上,我們把string前allowedLength的字串進(jìn)行替換
            // 但考慮emoji的情況,需要考慮不要截?cái)噙@種特殊字符
            NSString *subString = @"";
            NSRange rangeIndex = [string rangeOfComposedCharacterSequenceAtIndex:(allowedLength - 1)];
            
            if (rangeIndex.location + rangeIndex.length > allowedLength) {
                subString = [string substringToIndex:rangeIndex.location];
            } else {
                subString = [string substringToIndex:allowedLength];
            }
            
            // 最后如果子字符串不為空,則替換
            if (![subString isEqualToString:@""]) {
                [textField setText:[textField.text stringByReplacingCharactersInRange:range withString:subString]];
            }
        }
        
        // 這種情況下由于返回NO,需要在這里把值傳遞給self.addTopicVM.title
        // 其他的由titleTextFieldDidChanged里面處理
        self.addTopicVM.title = textField.text;

        // 給出警告,提示超過(guò)字符串最大容量。
        if (![self.titleAlertView isBeingPresented]) {
            NSString *subTitle = KUFUILocalization(@"UFUIAddTopicViewController.titleCharacterReachesMax.alertView.title");
            [self.titleAlertView showInfo:self title:nil subTitle:subTitle closeButtonTitle:nil duration:1.0f];
        }

        // 返回NO,我們處理完了。
        return NO;
    }
    
    return YES;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容