話不多說先上圖
[圖片上傳失敗...(image-3146a2-1657457047127)]
做了個需求,產(chǎn)品要求右邊字?jǐn)?shù)限制是字節(jié)數(shù)限制,中文漢子2字節(jié),英文字母、數(shù)字都是1字節(jié)。我們用GBK編碼來計算字節(jié)數(shù)。
需求不難,計算字節(jié)方法如下:
// 計算字節(jié)的個數(shù)
- (NSUInteger)convertToByte:(NSString *)str {
NSStringEncoding encoding = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);
NSUInteger strLength = 0;
char *p = (char *)[str cStringUsingEncoding:encoding];
NSUInteger lengthOfBytes = [str lengthOfBytesUsingEncoding:encoding];
for (int i = 0; i < lengthOfBytes; i++) {
if (*p) {
p++;
strLength++;
} else {
p++;
}
}
return strLength;
}
實現(xiàn)這個功能也很簡單,只需要在UITextField或者UITextView代理方法中設(shè)置即可。
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSString *newStr = [textField.text stringByReplacingCharactersInRange:range withString:string];
if ([self convertToByte:newStr] > 30) {
return NO;
}
self.remarkNameLabel.text = [NSString stringWithFormat:@"%@ %@", [JWDataHelper sharedDataHelper].user.name, newStr];
if ([newStr isEqualToString:self.remark] || (!self.remark && newStr.length == 0)) {
[self.navigationItem.rightBarButtonItem setTintColor:[UIColor jw_colorWithHex:0xb0b0b0]];
self.navigationItem.rightBarButtonItem.enabled = NO;
} else {
[self.navigationItem.rightBarButtonItem setTintColor:[UIColor jw_colorWithHex:0x333333]];
self.navigationItem.rightBarButtonItem.enabled = YES;
}
self.countLabel.text = [NSString stringWithFormat:@"%@/30", @([self convertToByte:newStr])];
return YES;
}
一切看上去很完美,但是用系統(tǒng)輸入法輸入中文時,發(fā)現(xiàn)計算字節(jié)長度不對
比如輸入dgzsg文本顯示如圖:
[圖片上傳失敗...(image-b82596-1657457047127)]
發(fā)現(xiàn)誒?不對啊,明明幾個英文加空格,怎么后面顯示17/30,應(yīng)該是9/30啊。
打斷點看UITextField代理方法,打印newStr如下圖:
[圖片上傳失敗...(image-9b598a-1657457047127)]
也沒問題啊,再看計算字節(jié)長度,發(fā)現(xiàn)轉(zhuǎn)化完,p內(nèi)容不對啊,怎么多了\x816\xa42這種不知道什么字符,如圖:
[圖片上傳失敗...(image-604e1b-1657457047127)]
這個應(yīng)該是個特殊字符,但是不知道是什么,我們用編碼查詢,把newStr內(nèi)容放上去,進(jìn)行編碼查詢,如下圖:
[圖片上傳失敗...(image-b7f55c-1657457047127)]
我們直接輸入個空格,進(jìn)行編碼查詢
[圖片上傳失敗...(image-4f553f-1657457047127)]
空格的unicode編碼是0020,但是我們復(fù)制出來的空格unicode編碼是2006,這就是問題所在了,系統(tǒng)鍵盤輸入時,空格不是空格而是特殊字符。
找到問題,優(yōu)化一下我們計算字節(jié)的方法,把這個特殊字符變成空格來計算我們長度。
// 計算字節(jié)的個數(shù)
- (NSUInteger)convertToByte:(NSString *)str {
str = [str stringByReplacingOccurrencesOfString:@"\u2006" withString:@" "];
NSStringEncoding encoding = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);
NSUInteger strLength = 0;
char *p = (char *)[str cStringUsingEncoding:encoding];
NSUInteger lengthOfBytes = [str lengthOfBytesUsingEncoding:encoding];
for (int i = 0; i < lengthOfBytes; i++) {
if (*p) {
p++;
strLength++;
} else {
p++;
}
}
return strLength;
}
完成以上還有個細(xì)節(jié)需要處理,就是如果輸入英文長度沒達(dá)到限制,選擇聯(lián)想漢字長度超出時,按上面的字節(jié)數(shù)不會變化,如圖
[圖片上傳失敗...(image-72eab3-1657457047127)]
需要添加個通知:[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textFieldChange) name:UITextFieldTextDidChangeNotification object:nil];
- (void)textFieldChange
{
// 獲取高亮內(nèi)容的范圍
UITextRange *selectedRange = [self.textField markedTextRange];
// 這行代碼 可以認(rèn)為是 獲取高亮內(nèi)容的長度
NSInteger markedTextLength = [self.textField offsetFromPosition:selectedRange.start toPosition:selectedRange.end];
// 沒有高亮內(nèi)容時,對已輸入的文字進(jìn)行操作
if (markedTextLength == 0) {
self.countLabel.text = [NSString stringWithFormat:@"%@/30", @([self convertToByte:self.textField.text])];
}
}
本以為這樣大功告成了,后來仔細(xì)測試發(fā)現(xiàn)自己太天真了。
發(fā)現(xiàn)兩個bug。。。。
[圖片上傳失敗...(image-6ec142-1657457047127)]
4個字母3個空格,應(yīng)該是7/30為什么顯示6/30呢?
[圖片上傳失敗...(image-71b044-1657457047127)]
13個漢字,3個字母,2個空格,應(yīng)該是31/30,而且超了限制。
發(fā)現(xiàn)每次輸入鍵盤時,- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string的string都是單個字母,中間的空格是后面加上去的,所以這個代理方法不可靠了。修改通知方法:
- (void)textFieldChange
{
NSInteger bitCount = [self convertToByte:self.textField.text];
self.countLabel.text = [NSString stringWithFormat:@"%@/30", @(bitCount)];
if (bitCount > 30) {
[self.textField resignFirstResponder];
[self.textField becomeFirstResponder];
}
}
如果發(fā)現(xiàn)限制超了,直接resignFirstResponder把選中字母賦值上去,并且becomeFirstResponder,這樣鍵盤就不會隱藏了。
最后附上demo:
Demo地址