想對(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;
}