iOS 鍵盤聯(lián)想特殊字符問題

話不多說先上圖

[圖片上傳失敗...(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 *)stringstring都是單個字母,中間的空格是后面加上去的,所以這個代理方法不可靠了。修改通知方法:

- (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地址

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

  • 一、姓名存儲和展現(xiàn) 1. 按學(xué)生總表編碼存儲 李洋薛鵬磊按照學(xué)生總表進(jìn)行編碼,并將編碼進(jìn)行存儲或者發(fā)送 左邊是客戶...
    果芽軟件閱讀 5,250評論 0 0
  • 字符串和字符 甲串是一系列字符,如的"hello, world"或"albatross"。Swift字符串由Str...
    Fuuqiu閱讀 1,101評論 0 0
  • 第一章 IO概述 1.1 什么是IO 生活中,你肯定經(jīng)歷過這樣的場景。當(dāng)你編輯一個文本文件,忘記了ctrl+s ,...
    beautiful被注冊了閱讀 215評論 0 0
  • 我們在開發(fā)中很多時候都會遇到字符串截圖,比如文本顯示長度限制、自定義協(xié)議解析都會截取一部分字符串進(jìn)行操作。 但是我...
    鏡像閱讀 1,846評論 0 0
  • 1、為什么要做數(shù)據(jù)壓縮? 2、什么是數(shù)據(jù)壓縮? 3、常見的數(shù)據(jù)壓縮算法 LZW壓縮 LZW壓縮是一種無損壓縮,應(yīng)用...
    算法手記閱讀 1,013評論 0 0

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