美文网首页UIKitios 进阶iOS控件封装
iOS中UITextField的字数限制

iOS中UITextField的字数限制

作者: 小失 | 来源:发表于2015-08-28 15:12 被阅读35909次

    在开发中, 有些时候会碰到这样的需求: 希望输入框有最大字数限制. 比如, 用户昵称长度限制, 评论最大字数限制.

    刚开始的时候, 采用的是shouldChangeCharactersInRange

    http://stackoverflow.com/questions/433337/set-the-maximum-character-length-of-a-uitextfield

    这样在输入全部是英文的情况下是可以的. 但是当输入是中文时, 由于shouldChangeCharactersInRange判断的是当前键盘的字符数, 会出现这样的问题: 比如你还剩下2个字可以打, 你想输入"张三", "张"的拼音是Zhang, 于是你在输入Zh的时候就无法输入了. 显然, 这样的结果不是我们想要的.

    而且, shouldChangeCharactersInRange也没有响应最后拼音到汉字的过程.

    然后在这里找到了基本可行的解决方案:

    http://blog.sina.com.cn/s/blog_60f977e70101g4gj.html#cmt_3529521

    在viewDidLoad中注册<UITextFieldTextDidChangeNotification>通知.

    [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(textFieldEditChanged:) 
              name:@"UITextFieldTextDidChangeNotification" object:myTextField];
    

    然后实现监听方法:

    -(void)textFieldEditChanged:(NSNotification *)obj{   
       UITextField *textField = (UITextField *)obj.object;     
       NSString *toBeString = textField.text;  
       NSString *lang = [[UITextInputMode currentInputMode] primaryLanguage]; // 键盘输入模式  
       if ([lang isEqualToString:@"zh-Hans"]) { // 简体中文输入,包括简体拼音,健体五笔,简体手写       
          UITextRange *selectedRange = [textField markedTextRange];       //获取高亮部分      
          UITextPosition *position = [textFieldpositionFromPosition:selectedRange.start offset:0];       
          // 没有高亮选择的字,则对已输入的文字进行字数统计和限制       
          if (!position) {
               if (toBeString.length > kMaxLength) {
                   textField.text = [toBeString substringToIndex:kMaxLength];
               }
           }       // 有高亮选择的字符串,则暂不对文字进行统计和限制
           else{                
            }   
          }   // 中文输入法以外的直接对其统计限制即可,不考虑其他语种情况   else{
           if (toBeString.length > kMaxLength) {
               textField.text = [toBeString substringToIndex:kMaxLength];
           }
       }}
    

    一切看起来, 似乎还不错. 通过截取字符来达到目的.然后导师告诉我, 碰到emoji就挂了. 假设限制输入15个字符, 第十五个字符如果输入是emoji, 则emoji不能正常显示. 因为emoji是两个字符大小.

    于是, 在这里找到防止这种粗暴截断方法的思路.

    http://stackoverflow.com/questions/15775294/truncate-string-containing-emoji-or-unicode-characters-at-word-or-character-boun

    使用rangeOfComposedCharacterSequencesForRange, 防止在range范围内整词被截断.
    但是iOS貌似不能正确识别中文的composed character sequences , 只要是两个中文字都会被识别成composed character sequences. 恰好, 输入emoji时currentInputMode也不是zh-Hans. 因此, 在判断当前输入Mode是中文时, 可以继续使用substringToIndex, 进行截断. 在非中文Mode时, 加以判断.
    代码如下:

    #pragma mark - Notification Method
    -(void)textFieldEditChanged:(NSNotification *)obj
    {
        UITextField *textField = (UITextField *)obj.object;
        NSString *toBeString = textField.text;
        NSString *lang = [textField.textInputMode primaryLanguage];
        if ([lang isEqualToString:@"zh-Hans"])// 简体中文输入
        {
            //获取高亮部分
            UITextRange *selectedRange = [textField markedTextRange];
            UITextPosition *position = [textField positionFromPosition:selectedRange.start offset:0];
            
            // 没有高亮选择的字,则对已输入的文字进行字数统计和限制
            if (!position)
            {
                if (toBeString.length > MAX_STARWORDS_LENGTH)
                {
                    textField.text = [toBeString substringToIndex:MAX_STARWORDS_LENGTH];
                }
            }
    
        }
        // 中文输入法以外的直接对其统计限制即可,不考虑其他语种情况
        else
        {
            if (toBeString.length > MAX_STARWORDS_LENGTH)
            {
                NSRange rangeIndex = [toBeString rangeOfComposedCharacterSequenceAtIndex:MAX_STARWORDS_LENGTH];
                if (rangeIndex.length == 1)
                {
                    textField.text = [toBeString substringToIndex:MAX_STARWORDS_LENGTH];
                }
                else
                {
                    NSRange rangeRange = [toBeString rangeOfComposedCharacterSequencesForRange:NSMakeRange(0, MAX_STARWORDS_LENGTH)];
                    textField.text = [toBeString substringWithRange:rangeRange];
                }
            }
        }
    }
    

    看了一下微信,QQ,知乎的修改昵称.
    微信是将英文字符算一个长度, 中文算两个长度,emoji算四个长度 总长度是32. 当你在输入中文字符超过规定长度时, 则强制将当前的键盘输入变成英文. 如果剩下的字符数小于等于3, 则不可以输入emoji.
    QQ也是将英文字符和中文分开计算长度, 但是当只剩下一个长度时, 键盘无法输入完整的汉语拼音. 即上面讲的<张三>的例子.
    不过在修改昵称这里长度设长一些无所谓. 如果是一些需要写评论的地方则还是有体验优化的余地.
    知乎二货居然没有长度限制, 不过修改昵称居然要审核...

    10月14日更新
    后来发现第三方输入法(如搜狗,百度输入法)会出现错误, 发现只需要这样就行了.

        UITextField *textField = (UITextField *)obj.object;
        NSString *toBeString = textField.text;
        
        //获取高亮部分
        UITextRange *selectedRange = [textField markedTextRange];
        UITextPosition *position = [textField positionFromPosition:selectedRange.start offset:0];
        
        // 没有高亮选择的字,则对已输入的文字进行字数统计和限制
        if (!position)
        {
            if (toBeString.length > MAX_STARWORDS_LENGTH)
            {
                NSRange rangeIndex = [toBeString rangeOfComposedCharacterSequenceAtIndex:MAX_STARWORDS_LENGTH];
                if (rangeIndex.length == 1)
                {
                    textField.text = [toBeString substringToIndex:MAX_STARWORDS_LENGTH];
                }
                else
                {
                    NSRange rangeRange = [toBeString rangeOfComposedCharacterSequencesForRange:NSMakeRange(0, MAX_STARWORDS_LENGTH)];
                    textField.text = [toBeString substringWithRange:rangeRange];
                }
            }
        }
    

    update2: 监听变化可以直接 [self addTarget:self action:@selector(textFieldDidChange) forControlEvents:UIControlEventEditingChanged];
    iOS9的shouldChangeCharactersInRange函数有bug,在中文输入下, 推荐的字不调用shouldChangeCharactersInRange. 所以不要用其来判断中文字长度.

    相关文章

      网友评论

      • ZhangCc_:中英文混合在一起的时候怎么截取?我的会崩溃
      • macroC:小伙子 ,据我亲测,三方输入法的emoji表情输入,也属于中文输入。所以仅仅在中文输入的时候判断末尾字符是不够的。
        macroC:@小失 看过啊
        小失:你没有看完我的文章
      • 秋醴泉:字数达到限制之后,还允许用户输入拼音字符,感觉体验不好。我也研究了这个问题,改进了这个不足,可以参看
        http://www.jianshu.com/p/90dce094b082
      • 毛大虎:demo地址呢 呱呱
      • felix9:但是iOS貌似不能正确识别中文的composed character sequences , 只要是两个中文字都会被识别成composed character sequences.

        实测并没有这个问题。不知道博主能否提供被错误识别的中文范本?
      • 郑明明:真的很棒棒哦
      • NateLam:写的很好, 转载一下做个笔记, 已注明出处, 感谢:+1:
      • 国王or乞丐:三个文本框怎么搞啊?
        国王or乞丐:@小失 额,搞不来:joy:,不知道怎么去封装这个组间,我还得代理在开出去呢
        小失:自己封一个组件监听变化
      • 昨夜雨轻栏:如果一个控制器里有多个textFiled不就死翘翘了吗。。。
        多LV信源:@JuiceLv 你说的是对的
        Juice007:代理方法可以判断当前是哪一个textField的,再进行具体的操作
        小失:自己封一个组件监听变化
      • 方同学哈:直接addTaget不就可以了吗?
        小失:是可以的
      • 向晨宇:这个有一个问题,服务器限制5个字符,那么允许3个Emoj就是6个字符。

        欢迎查看 :http://www.iosxxx.com/blog/2016-11-27-UITextField%E6%9C%80%E5%A4%A7%E5%AD%97%E7%AC%A6%E6%95%B0%E5%92%8C%E6%9C%80%E5%A4%A7%E5%AD%97%E8%8A%82%E6%95%B0%E7%9A%84%E9%99%90%E5%88%B6.html

        解决了字符和字节的对齐问题
      • 欢欢1206:通知有点不好的地方是,当你超过maxLength的时候,继续输入然后删除,你会发现textfiled没动静
        向晨宇:@欢欢1206 用XXTextfield,完美解决。http://www.iosxxx.com/blog/2016-11-27-UITextField%E6%9C%80%E5%A4%A7%E5%AD%97%E7%AC%A6%E6%95%B0%E5%92%8C%E6%9C%80%E5%A4%A7%E5%AD%97%E8%8A%82%E6%95%B0%E7%9A%84%E9%99%90%E5%88%B6.html
      • fa8683e22908:这是目前我能找到的最好的解决方案
      • oriyum:rangeOfComposedCharacterSequenceAtIndex这个方法不错。
      • oriyum:很不错,uitextview判断类似。
      • plantseeds:请问这个 判断是否有 选中的高亮部分 有什么作用?
      • angelen:有的 Emoji 不是两个字符大小,比如这些:👩‍👩‍👦,诶呀,竟然书写不出来啊,我打一个 Emoji 表情,竟然输入了 3 个 Emoji...
      • df57d5e6a14d:实测需要增加一个条件

        if (!position1 || !selectedRange)
        {
        if (toBeString.length > maxLength)
        {
        NSRange rangeIndex = [toBeString rangeOfComposedCharacterSequenceAtIndex:maxLength];
        if (rangeIndex.length == 1)
        {
        textField.text = [toBeString substringToIndex:maxLength];
        }
        else
        {
        NSRange rangeRange = [toBeString rangeOfComposedCharacterSequencesForRange:NSMakeRange(0, maxLength)];
        textField.text = [toBeString substringWithRange:rangeRange];
        }
        }
        }
        7ff9b6c07336:实测你的条件完美
      • df57d5e6a14d:在iOS7下position总是不为空导致无法使用啊
      • fa67205f6add:您好,我请教一个问题 当我复制粘贴一长段文字(大于textfield限制输入的最大长度)后, 通过iPhone摇一摇 弹出撤销弹框 点击撤销崩溃 错误原因如下:-[NSBigMutableString substringWithRange:]: Range {0, 43} out of bounds; string length 8 请问有什么好的解决方案吗?
        fa67205f6add:@码者__行也 通过这种方法 摇一摇之后不会弹出撤销的弹出视图,谢谢,但是总感觉有些暴力 :stuck_out_tongue_winking_eye:
        fa67205f6add:@小失 好的 我去瞅瞅
        小失:@码者__行也 看看这个?http://stackoverflow.com/questions/20751548/strange-crash-in-uitextview-undo-on-ios-7
      • 5f2b9e16bcb4:大神,我的中文在未完成状态下的,也就是输入框显示英文时,获取到的字符不是想要的英文字符,似乎是乱码,请问如何获得正确的英文字符?
        小失:@legendaryHu 试了一下, 的确是这样. 如果使用
        - searchBar:shouldChangeTextInRange:replacementText: 这个函数, 则获取到的text是对应在九宫格的➒这样的东西. 具体不清楚为什么apple要这么做.
        但是如果要实现这个需求的话, 可以使用
        - (void)searchBar:(UISearchBar *)searchBar
        textDidChange:(NSString *)searchText 这个函数, 在系统九宫格中文输入下, 还在高亮状态(即中文字没有显示在searchBar上)时, 没有searchText; 只有当中文打上去的时候, 有searchText. 所以在这个时候判断searchText的长度, 进行range的截断就行了.
        5f2b9e16bcb4:@小失 iOS 8.4 iPhone6 . 我仔细地测了下. 使用系统九宫格的拼音输入法 在searchbar显示拼音状态下在 searchBar shouldChangeTextInRange 获取到的不是英文字母,类似这种"➒"奇怪的东西,而全键盘的拼音输入没有问题.改使用textfield获取也没有问题.应该searchbar比较特殊,目前没想到好方法处理searchbar的情况.
        小失:@legendaryHu 用的是什么输入法? 操作系统是哪个版本

      本文标题:iOS中UITextField的字数限制

      本文链接:https://www.haomeiwen.com/subject/xmhkcttx.html