一行代码解决UITextFiled和UITextView限制字数和输入特殊字符

一.textFiled/textView限制字符长度

一开始我在做UITextFiled和UITextView限制字数
1.TextFiled限制字数
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(textFiledChange:) name:UITextFieldTextDidChangeNotification object:nil];
或者
[textField addTarget:self action:@selector(textFiledChange:) forControlEvents:UIControlEventEditingChanged];
然后在

-(void)textFiledChange:(UITextFiled *)textFiled{ 

     if(textFiled.text.length > maxNumber)
     {
        textField.text= [textFiled.text substringToIndex:maxNumber];
     }
 
   }

也可以在textField的代理中做相应的处理

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {} //具体实现和上面方法一样就不多说了

2.TextView限制字数
在TextView的代理中

- (void)textViewDidChange:(UITextView *)textView{
   if(textView.text.length > maxNumber) 
   {
      textView.text= [textView.text substringToIndex:maxNumber];
   }
}

以上方法是最简单的处理,如果输入英文字符完全没有问题,但是输入中文时,如果使用第三方键盘也是没有问题的,但就是在系统自带键盘输入拼音时,你就会发现有很严重的问题😂,当你输入一个拼音时,你还没有选中你要选的文字时,已经被键盘当做字母输入到textFiled/textView中了,这并不是你想要的结果...比如你用系统键盘输入"你好",它会把n i h a o显示在textFiled/textView中, 不但没有输入汉字,还每个字母占两个字符长度...

分析一下当你输入拼音时,此时你输入的拼音还是处于高亮状态,这时已经走截取字符的方法,截取完又赋值给textFiled/textView,所以就会出现以上的问题,处理方法就是监听系统键盘输入拼音处于高亮状态时不截取字符

/**
 textFiled限制字数
 */
- (void)textFiledChange:(UITextField *)textField{
    NSInteger maxNumber = 15;
    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 > maxNumber) {
                textField.text = [toBeString substring index:maxNumber];
            }
        } else{ //有高亮选择的字符串,则暂不对文字进行统计和限制
        
        }
    }
    else{ //中文输入法以外的直接对其统计限制即可,不考虑其他语种情况
        MyLog(@"haha ");
        
        if(toBeString.length > maxNumber) {
          textField.text= [toBeString substringToIndex:maxNumber];
//此方法防止emoji表情被截断 
// 3月19日更新为
       textField.text=  [[self class] subStringWith:toBeString ToIndex:maxNumber];
        }
    }
}
//textView同上处理

完美解决使用系统键盘输入汉字出现的截取拼音字母显示在textFiled/textView

以上处理一般textFiled/textView限制字数长度

二.textFiled/textView限制输入特殊字符

/**
 限制输入emoji表情
 */
+ (NSString *)disable_emoji:(NSString *)text{
    NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"[^\\u0020-\\u007E\\u00A0-\\u00BE\\u2E80-\\uA4CF\\uF900-\\uFAFF\\uFE30-\\uFE4F\\uFF00-\\uFFEF\\u0080-\\u009F\\u2000-\\u201f\r\n]"options:NSRegularExpressionCaseInsensitive error:nil];
    NSString *modifiedString = [regex stringByReplacingMatchesInString:text
                                                               options:0
                                                                 range:NSMakeRange(0, [text length])
                                                          withTemplate:@""];
    return modifiedString;
}

/**
 只能输入汉字,数字,英文,括号,下划线,横杠,空格
 */
+(NSString *)filterCharactors:(NSString *)string{
    
    NSString *regular = @"[^a-zA-Z0-9()_\u4E00-\u9FA5\\s-]"; //根据你不同的需求填写你自己的正则
    NSString *str = [[self class] filterCharactor:string withRegex:[NSString stringWithFormat:@"%@",regular]];
    return str;
}

原理是当字符串判断是否输入你要限制的字符,把要限制的字符截掉
但是当你的textFiled/textView既要限制字数,又要限制输入特殊字符,把上面两个方法调完,你会发现,第一个问题又出来了,而且这次是第一次用系统键盘输入汉字时正常,如果你要继续在输入时依然会把拼音当成字符输入到textFiled/textView里.所以对上面的方法再次进行优化

三.textFiled/textView限制字符长度并且限制输入特殊字符

/**
 判断NSString中是否有表情
 */
+ (BOOL)isContainsEmoji:(NSString *)string {
    __block BOOL isEomji = NO;
    [string enumerateSubstringsInRange:NSMakeRange(0, [string length]) options:NSStringEnumerationByComposedCharacterSequences usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
        const unichar hs = [substring characterAtIndex:0];
        // surrogate pair
        if (0xd800 <= hs && hs <= 0xdbff) {
            if (substring.length > 1) {
                const unichar ls = [substring characterAtIndex:1];
                const int uc = ((hs - 0xd800) * 0x400) + (ls - 0xdc00) + 0x10000;
                if (0x1d000 <= uc && uc <= 0x1f77f) {
                    isEomji = YES;
                }
            }
        } else {
            // non surrogate
            if (0x2100 <= hs && hs <= 0x27ff && hs != 0x263b) {
                if (!(9312 <= hs && hs <= 9327)) { // 9312代表①   表示①至⑯
                    isEomji = YES;
                }
            } else if (0x2B05 <= hs && hs <= 0x2b07) {
                isEomji = YES;
            } else if (0x2934 <= hs && hs <= 0x2935) {
                isEomji = YES;
            } else if (0x3297 <= hs && hs <= 0x3299) {
                isEomji = YES;
            } else if (hs == 0xa9 || hs == 0xae || hs == 0x303d || hs == 0x3030 || hs == 0x2b55 || hs == 0x2b1c || hs == 0x2b1b || hs == 0x2b50|| hs == 0x231a ) {
                isEomji = YES;
            }
            if (!isEomji && substring.length > 1) {
                const unichar ls = [substring characterAtIndex:1];
                if (ls == 0x20e3) {
                    isEomji = YES;
                }
            }
        }
    }];
    return isEomji;
}
/**
 判断是否存在特殊字符 只能输入汉字,数字,英文,括号,下划线,横杠,空格
 */
+ (BOOL)isContainsSpecialCharacters:(NSString *)searchText
{
/*此方法也可以理论上可以,但实测还是会有问题*/
//    NSString *regex = @"[^a-zA-Z0-9()_\u4E00-\u9FA5\\s-]";
//    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regex];
//    BOOL isValid = [predicate evaluateWithObject:string];
//    return isValid; 
    
    NSError *error = NULL;
    NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"[^a-zA-Z0-9()_\u4E00-\u9FA5\\s-]"  options:NSRegularExpressionCaseInsensitive error:&error];
    NSTextCheckingResult *result = [regex firstMatchInString:searchText options:0 range:NSMakeRange(0, [searchText length])];
    if (result) {
        return YES;
    }else {
        return NO;
    }
    
}

/**
 textFiled限制字数
 */
+ (void)restrictionInputTextField:(UITextField *)textField maxNumber:(NSInteger)maxNumber{
    
    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 > maxNumber) {
                textField.text = [toBeString substringToIndex:maxNumber];
            }
        } else{ //有高亮选择的字符串,则暂不对文字进行统计和限制
        
        }
    }
    else{ //中文输入法以外的直接对其统计限制即可,不考虑其他语种情况
        MyLog(@"haha ");
        
        if(toBeString.length > maxNumber) {
//            textField.text= [toBeString substringToIndex:maxNumber];
//此方法防止emoji表情被截断 
// 3月19日更新为
  textField.text=  [[self class] subStringWith:toBeString index:maxNumber];
        }
    }
    
}

最终方法

/**
 除去特殊字符并限制字数的textFiled
 */
+ (void)restrictionInputTextFieldMaskSpecialCharacter:(UITextField *)textField maxNumber:(NSInteger)maxNumber{
    
    if ([[self class]isContainsEmoji:textField.text]){
        textField.text = [[self class]disable_emoji:textField.text];
        return;
    }
    if ([[self class]isContainsSpecialCharacters:textField.text]){
        textField.text = [[self class]filterCharactors:textField.text];
        return;
    }
    [[self class]restrictionInputTextField:textField maxNumber:maxNumber];
}

UITextView同上方法

最最后......

最近测试妹子给测出一个问题,就是我的一个输入框是填写电话的,我的 keyboardTypeUIKeyboardTypePhonePad.理论上只能输入数字了,于是我也就偷懒没做任何判断屏蔽特殊字符,但是依然给我测出可以输入emoji表情和一些特殊字符,🤣,然后我就想应该是在其他地方复制粘贴到我的textFiled里吧,于是我就想到很粗鲁的方法,就是把textFiled的粘贴给禁掉🤔 哈哈,以下方法不建议采用,还是老老实实的吧限制特殊字符吧,但是你非要和我一样懒😂,那我也管不着🙄

//屏蔽textFiled粘贴
//Initialzation code
        UILongPressGestureRecognizer *longRecognizer = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(addGestureRecognizer:)];
        longRecognizer.allowableMovement = 100.0f;
        longRecognizer.minimumPressDuration = 1.0;
        [self addGestureRecognizer:longRecognizer];

//重写        
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
    UIMenuController *menuController = [UIMenuController sharedMenuController];
    if (menuController) {
        //设置为不可用
        [UIMenuController sharedMenuController].menuVisible = NO;
    }
    return NO;
}


- (void)addGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
{
    //Prevent zooming but not panning
    if ([gestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]])
    {
        gestureRecognizer.enabled = NO;
    }
    [super addGestureRecognizer:gestureRecognizer];
}

如果你还有其他好的方法欢迎提出,共同学习,peace!

参考这篇文章

详细代码请查看GitHub:SDTextLimit
我的微博:slowdony

3月19更新

//防止原生emoji表情被截断
+ (NSString *)subStringWith:(NSString *)string index:(NSInteger)index{

    NSString *result = string;
    if (result.length > index) {
        NSRange rangeIndex = [result rangeOfComposedCharacterSequenceAtIndex:index];
        result = [result substringToIndex:(rangeIndex.location)];
    }

    return result;
}

推荐阅读更多精彩内容