zoukankan      html  css  js  c++  java
  • UITextFiled,UITextView长度限制

    长度限制用到的地方很多,但是需求都不一样.有的要求全部字符按一个处理,有的要求英文字母按一个,中文按两个,emoji按四个.这样就会遇到各种各样奇怪的问题,再被虐了无数次后,终于解决掉了.下面就来写写遇到的各种坑.

    Delegate

    首先想到的方法肯定是delegate:

    #define kMaxLength 10
    - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{
        if (textField.text.length > kMaxLength) {
            return NO;
        }
        return YES;
    }
    

    结果运行下来有问题,输到第10为的时候连删除也没法接收了,这样肯定不行.于是想到了每次都让它输进去,之后截取到第10位.

    #define kMaxLength 10
    - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{
        NSString *toString = [textField.text stringByReplacingCharactersInRange:range withString:string];
        if (toString.length > kMaxLength) {
            textField.text = [toString substringToIndex:kMaxLength];
            return NO;
        }
        return YES;
    }  
    

    简单地测试了下发现没什么问题,不过稍微细致点就发现了两个问题:

    • 输入结束后,点击输入框上面的候选汉字,不会进入委托,可以无限的长.
    • 当使用拼音输入法时,输入的汉字默认两个字符长度,当你输入到上方候选汉字有6位时,实际上还没超过长度,但是已经无法输入,框里也变成了输入的字母,十分不方便.

    之后网上查了很多,有在delegate里实现,感觉很复杂.还是用UITextInputCurrentInputModeDidChangeNotification来做更方便点.

    Notification
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textField_textDidChange:) name:UITextFieldTextDidChangeNotification object:textF];
    
    #define kMaxLength 10
    - (void)textField_textDidChange:(NSNotification *)notification {
        UITextField *textField = (UITextField *)notification.object;
        
        NSString *toBeString = textField.text;
        UITextRange *selectedRange = [textField markedTextRange];
        //获取高亮部分
        UITextPosition *position = [textField positionFromPosition:selectedRange.start offset:0];
        // 没有高亮选择的字,则对已输入的文字进行字数统计和限制
        if (!position) {
            if (toBeString.length > kMaxLength) {
                textField.text = [toBeString substringToIndex:kMaxLength];
            }
        }
        // 有高亮选择的字符串,则暂不对文字进行统计和限制
        else{
            
        }
    }
    
    - (void)dealloc {
        [[NSNotificationCenter defaultCenter] removeObserver:self];
    }  
    

    输入框上面的高亮部分可以无限输,只是长度变化的时候截取.只是有个瑕疵,就是高亮部分可以无限输入.

    emoji表情截取

    本来以为万事大吉了,但是测试还是挑出了bug,当前面输入的是字母,最后一个是表情时,表情会被截取,成为一个很奇怪的符号.
    原来问题出在了substringToIndex这个方法上.怎么得出这个结论的呢:

    -(unichar)characterAtIndex:(NSUInteger)index  
    typedef unsigned short unichar;  
    

    这个方法的返回值unichar是个16位的无符号整型.那么所有对NSString的index位置操作,都是以unichar为单位的.
    查阅字符编码可以发现:

    例如这个emoji表情,字符编码为:
    Unicode: U+1F601 (U+D83D U+DE01)
    发现了问题所在了,emoji表情有20位啊,16位的unichar根本存不下!原来Unicode编码最初是被设计为16位的,后来为了编码一些冷门的中文日文,Unicode编码扩展到了21位(从U+0000到 U+10FFFF).
    原因是找到了,怎么解决呢?

    NSString与Unicode,这篇文章把我所有的困惑都解决了,并且附上了解决办法.真要感谢下objc中国,不然让我看原版英文,估计够呛,英文还是不能丢啊!

    //通知的方法
    #define kMaxLength 8
    - (void)textField_textDidChange:(NSNotification *)notification {
        UITextField *textField = (UITextField *)notification.object;
        
        NSString *toBeString = textField.text;
        UITextRange *selectedRange = [textField markedTextRange];
        //获取高亮部分
        UITextPosition *position = [textField positionFromPosition:selectedRange.start offset:0];
        // 没有高亮选择的字,则对已输入的文字进行字数统计和限制
        if (!position) {
            if (toBeString.length > kMaxLength) {
                UITextRange *textRange = textField.selectedTextRange;
                textField.text = [toBeString subStringWithMaxLength:kMaxLength];
                textField.selectedTextRange = textRange;
            }
        }
        // 有高亮选择的字符串,则暂不对文字进行统计和限制
        else{
            
        }
    }
    
    @implementation NSString (Add)
    - (NSString *)subStringWithMaxLength:(NSInteger)maxLength {
        __block NSString *aString = @"";
        __block int length = 0;
        [self enumerateSubstringsInRange:NSMakeRange(0, self.length) options:NSStringEnumerationByComposedCharacterSequences usingBlock:^(NSString * _Nullable substring, NSRange substringRange, NSRange enclosingRange, BOOL * _Nonnull stop) {
            char *p = (char *)[substring cStringUsingEncoding:NSUnicodeStringEncoding];
            for (int i = 0; i < [substring lengthOfBytesUsingEncoding:NSUnicodeStringEncoding]; i++) {
                if (*p && p != '') {
                    length++;
                }
                p++;
            }
            if (length <= maxLength) {
                aString = [aString stringByAppendingString:substring];
            }
        }];
        
        return aString;
    }
    @end
    

    重写个NSString截取方法,以后每次截取都用这个方法,就可以解决最后一个表情被截的问题了.

    总结

    相信很多人都被产品经理虐过,例如textView,两边文字内的间距调整,增加placeholder,设置placeholder的字体颜色,或者上文讲的文字不超过多少等等.
    被虐过千百回,大多数情况也都遇到过了,特地封装了两个category,是textField和textView,基本上解决了大多数状况,只需要设置属性值就行了:

        tv.maxLength = 20;
        tv.placeholder = @"我是textView";
        tv.placeholderFont = [UIFont systemFontOfSize:15];
        tv.placeholderColor = [UIColor redColor];
    

    是不是很方便,github地址,欢迎大家交流,提出产品经理的要求,继续完善.

  • 相关阅读:
    Oracle中TO_DATE格式
    实现带查询功能的Combox控件
    Combox和DropDownList控件的区别
    C# 获取字符串中的数字
    C# try catch finally 执行
    树形DP codevs 1814 最长链
    codevs 2822 爱在心中
    匈牙利算法 cojs.tk 搭配飞行员
    匈牙利算法 codevs 2776 寻找代表元
    2016-6-19 动态规划,贪心算法练习
  • 原文地址:https://www.cnblogs.com/stevenfukua/p/5625639.html
Copyright © 2011-2022 走看看