UITextField輸入字?jǐn)?shù)限制

一開始鬼贱,我以為這是一個屬性設(shè)置問題或者是一個類似helloworld的函數(shù)移怯,但是最后發(fā)現(xiàn),這呀的是一個不小的坑吩愧。

1芋酌、哪些問題需要考慮?

  • 輸入字?jǐn)?shù)有哪些雁佳?數(shù)字脐帝、英文同云、中文、emoji堵腹?
  • 字?jǐn)?shù)限制怎么定義炸站?一個中文/emoji算一個字?
  • 如果限制的是字節(jié)數(shù)呢疚顷?
  • 會出現(xiàn)字符截斷問題旱易?
  • (中文輸入法)在剩余的輸入字?jǐn)?shù)為1的情況下,是否還允許用戶輸入一堆拼音字符腿堤?
  • 要是粘貼進(jìn)一大段文字阀坏,能正確處理?
  • ...

2笆檀、測試用例

在介紹用例之前忌堂,我們先來看看微信(iOS V6.5.12.32)是怎么做的。進(jìn)入微信的名字修改界面~

  • 純數(shù)字輸入酗洒,允許輸入32個數(shù)字士修。
  • 純emoji輸入,允許輸入8個emoji樱衷。
  • 純中文輸入棋嘲,允許輸入16個中文
  • 先輸入15個中文,然后再輸入一個中文矩桂。是的沸移,此時你只能輸入一個拼音字母(也即前面的“h”問題)。
  • 存在上述提到的黏貼問題
    基本可以斷定微信的名字是用字節(jié)數(shù)做了限制耍鬓,并且使用的是3.1提到的方法阔籽。

下面是具體的測試用例(最基本的用例就不贅述了):(如無特殊說明,限定輸入字?jǐn)?shù)為10個)
case1:輸入10個中文牲蜀,此時是否還允許有高亮的拼音字母輸入笆制?
case2:輸入10個中文,將鍵盤切換為九宮格英文輸入模式涣达,快速點擊某個字母在辆,最后一個中文是否會被替換?
case3:在剩余的輸入字?jǐn)?shù)為1的情況下度苔,是否還允許用戶輸入一堆高亮拼音字符匆篓?
case4:輸入字?jǐn)?shù)已經(jīng)滿了,此時是否允許粘貼寇窑?鸦概??甩骏?窗市?
case5:限制輸入字符為11個先慷,輸入10個中文,再輸入一個emoji表情咨察,emoji會被截斷论熙?

3、網(wǎng)上參考做法

網(wǎng)上很多博客介紹了相關(guān)的方法摄狱,但是歸根到底就是兩種:
3.1 實現(xiàn) UITextFieldDelegate 協(xié)議中的接口脓诡,這里有說明

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
    // Prevent crashing undo bug – see note below.
    if(range.length + range.location > textField.text.length)
    {
        return NO;
    }
    NSUInteger newLength = [textField.text length] + [string length] - range.length;
    return newLength <= kMaxLength;
}

該方法最簡單,在只允許輸入數(shù)字/英文/emoji的情況下媒役,基本能滿足祝谚。但該方法在黏貼文本長度超過允許值時,直接黏貼失斂蕖(也許我們更期待是自動截扔桓)踩验。
在允許輸入中文的情況鸥诽,該方法的缺陷更明顯。假如還剩下一個字可以輸入箕憾,你打算輸入“好”牡借,當(dāng)你輸入字母“h”之后,發(fā)現(xiàn)無法再輸入其他拼音字母了袭异。

3.2 為UITextField實例添加監(jiān)聽 UIControlEventEditingChanged钠龙,然后對其進(jìn)一步處理∮澹可以通過在NSNotificationCenter中添加監(jiān)聽碴里,但是還需手動移除,比較繁瑣上真,所以推薦直接使用下面的方法

[textField addTarget:self action:@selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];

- (void)textFieldDidChange:(UITextField *)textField
{
    NSString *toBeString = textField.text;
    
    //獲取高亮部分
    UITextRange *selectedRange = [textField markedTextRange];
    UITextPosition *position = [textField positionFromPosition:selectedRange.start offset:0];
    
    // 沒有高亮選擇的字咬腋,則對已輸入的文字進(jìn)行字?jǐn)?shù)統(tǒng)計和限制
    if (!position)
    {
        if (toBeString.length > kMaxLength)
        {
            NSRange rangeIndex = [toBeString rangeOfComposedCharacterSequenceAtIndex:kMaxLength];
            if (rangeIndex.length == 1)
            {
                textField.text = [toBeString substringToIndex:kMaxLength];
            }
            else
            {
                NSRange rangeRange = [toBeString rangeOfComposedCharacterSequencesForRange:NSMakeRange(0, kMaxLength - 1 )];
                textField.text = [toBeString substringWithRange:rangeRange];
            }
        }
    }
}

該方法是對3.1方法的改善,但是仍舊存在不足睡互。
其一根竿,假如限制輸入5個中文,你可以嘗試在輸入5個中文字符之后就珠,將鍵盤切換為九宮格英文輸入模式寇壳,快速點擊某個字母,看看最后一個中文是否會被替換妻怎?
再者壳炎,當(dāng)輸入達(dá)到限制的字?jǐn)?shù)時,仍舊可以輸入拼音字符逼侦,只是選中詞語之后會被截斷匿辩,這種體驗也不是我們期待的疏日。

4、完美解決方案

對比前面提到的兩種方法撒汉,可以發(fā)現(xiàn)沟优,方法3.1在UITextField文本改變之前調(diào)用,方法3.2在UITextField文本改變之后調(diào)用睬辐,這也導(dǎo)致了它們各自存在一定的缺陷挠阁。
那么,有沒有一個新的方案溯饵,能完美解決這個問題呢?答案是肯定的侵俗,也就是同時實現(xiàn)這兩個方法,達(dá)到兼而有之丰刊。

核心代碼如下

- (void)textFieldDidChange:(UITextField *)textField
{
    if(_maxLength <= 0){
        return;
    }
    
    NSString *text = textField.text;
    UITextRange *selectedRange = [textField markedTextRange];
    UITextPosition *position = [textField positionFromPosition:selectedRange.start offset:0];
    
    //沒有高亮選擇的字隘谣,則對已輸入的文字進(jìn)行字?jǐn)?shù)統(tǒng)計和限制,防止中文/emoj被截斷
    if (!position){
        if (text.length > _maxLength){
            NSRange rangeIndex = [text rangeOfComposedCharacterSequenceAtIndex:_maxLength];
            if (rangeIndex.length == 1){
                textField.text = [text substringToIndex:_maxLength];
            }else{
                if(_maxLength == 1){
                    textField.text = @"";
                }else{
                    NSRange rangeRange = [text rangeOfComposedCharacterSequencesForRange:NSMakeRange(0, _maxLength - 1 )];
                    textField.text = [text substringWithRange:rangeRange];
                }
            }
            
        }
    }
}

#pragma mark -- UITextFieldDelegate
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{  
    if(_maxLength <= 0){
        return YES;
    }
    
    UITextRange *selectedRange = [textField markedTextRange];//高亮選擇的字
    UITextPosition *startPos = [textField positionFromPosition:selectedRange.start offset:0];
    UITextPosition *endPos = [textField positionFromPosition:selectedRange.end offset:0];
    NSInteger markLength = [textField offsetFromPosition:startPos toPosition:endPos];
    
    NSInteger confirmlength =  textField.text.length - markLength - range.length;//已經(jīng)確認(rèn)輸入的字符長度
    if(confirmlength >= _maxLength ){
        return NO;
    }
    
    NSInteger allowMaxMarkLength = [self allowMaxMarkLength:_maxLength - confirmlength];
    if(markLength > allowMaxMarkLength ){// && string.length > 0){
        return NO;
    }
    return YES;
}
/**
 主要是用于中文輸入的場景,可根據(jù)需要自定義
 剩余的允許輸入的字?jǐn)?shù)較少時啄巧,限制拼音字符的輸入寻歧,提升體驗
 */
- (NSInteger)allowMaxMarkLength:(NSInteger)remainLength
{
    NSInteger length = 0;
    if(remainLength > 2){
        length = NSIntegerMax;
    }else if(remainLength > 0){
        length = remainLength * 6;  //一個中文對應(yīng)的拼音一般不超過6個
    }
    
    return length;
}

首先在shouldChangeCharactersInRange方法中計算得到已經(jīng)確認(rèn)輸入的文本長度,該長度不包含高亮或者選中的文本秩仆。同時計算剩余允許輸入的文本長度码泛,通過allowMaxMarkLength來控制允許輸入的拼音字符的長度。
最后澄耍,在textFieldDidChange方法中噪珊,對文本進(jìn)行截斷操作,使得文本長度滿足要求齐莲。

完整demo可以參看這里

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末痢站,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子选酗,更是在濱河造成了極大的恐慌阵难,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件星掰,死亡現(xiàn)場離奇詭異多望,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)氢烘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進(jìn)店門怀偷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人播玖,你說我怎么就攤上這事椎工。” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵维蒙,是天一觀的道長掰吕。 經(jīng)常有香客問我,道長颅痊,這世上最難降的妖魔是什么殖熟? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮斑响,結(jié)果婚禮上菱属,老公的妹妹穿的比我還像新娘。我一直安慰自己舰罚,他們只是感情好纽门,可當(dāng)我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著营罢,像睡著了一般赏陵。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上饲漾,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天蝙搔,我揣著相機(jī)與錄音,去河邊找鬼能颁。 笑死杂瘸,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的伙菊。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼敌土,長吁一口氣:“原來是場噩夢啊……” “哼镜硕!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起返干,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤兴枯,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后矩欠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體财剖,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年癌淮,在試婚紗的時候發(fā)現(xiàn)自己被綠了躺坟。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡乳蓄,死狀恐怖咪橙,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤美侦,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布产舞,位于F島的核電站,受9級特大地震影響菠剩,放射性物質(zhì)發(fā)生泄漏易猫。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一具壮、第九天 我趴在偏房一處隱蔽的房頂上張望擦囊。 院中可真熱鬧,春花似錦嘴办、人聲如沸瞬场。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽贯被。三九已至,卻和暖如春妆艘,著一層夾襖步出監(jiān)牢的瞬間彤灶,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工批旺, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留幌陕,地道東北人。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓汽煮,卻偏偏與公主長得像搏熄,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子暇赤,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容