iOS - UITextField限制长度 emoji表情

捣鼓了半天 终于把自己的博客搭建好了,发一篇文章试试手。公司里的服务器比较low,不能支持emoji表情,本意是解决这个问题,自定义一个UITextField的控件。后来索性把长度校验也做了进去,基本满足了正常的需求。

我的博客 http://www.linit.space

一 限制文本长度


  • 目前textfield的输入大概就2种:

  • 通过点击键盘按键输入的

  • 通过点击键盘联想输入的 [同时有高亮字符(maskText)占位]

首先参考了一些文章:

主要我看这篇文章比较全

http://www.jianshu.com/p/2d1c06f2dfa4

网上能搜索到的主要在这篇文章里都有体现了,但是也是有问题的。

先说说实现的方法
  • 对应上述3种情况限制长度的主要方法

  • 点击键盘输入的不管中英文点击键盘就能唤起UITextFieldDelegate

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string所以通过这个方法可以达到过滤掉键盘输入的文字超长的效果。

  • 中文输入等 都是通过点击键盘后选取键盘联想也就是上述第二种情况,在上面的代理中无法监听,不过UITextField身为UIControl 可以通过[self addTarget:<#(nullable id)#> action:<#(nonnull SEL)#> forControlEvents:<#(UIControlEvents)#>] 通过监听UIControlEventEditingChanged 来实现效果

==注意:在需要高亮字符占位的输入法(不仅只有中文输入法),在点击键盘按键输入时(拼音,笔画等)还是能被第一种情况的代理监听,只有当选取联想出来的文字的时候才不能。==

反例分析

现在网上搜索一大堆限制文字长度的方法大概是这样(我就用上述连接上的代码了反正都类似)


-(void)textFiledEditChanged:(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];

}

}}

  • 首先看代码就觉得这个是用通知的形势来发送UIControlEventEditingChanged的事件,这明显不可取,同一个页面可能有多个textField这玩意还要做个校验通知是谁发出来的。

  • 然后他写死限制了中文输入法,这不是很可取,日语,韩语输入法的形式和中文差不多,还有特意过滤的高亮状态,对高亮状态的字不进行字数统计,这明显是错的。高亮状态的时候,可以不停的输入在还没有结束输入之前完全可能超过字数限制,这时候直接取textField.text的长度就超过了要求。

解决方案
  1. 针对多个控件发通知的问题,使用[self addTarget:<#(nullable id)#> action:<#(nonnull SEL)#> forControlEvents:<#(UIControlEvents)#>] 通过监听UIControlEventEditingChanged 明显更加合理。

  2. 没必要特别排除高亮状态,高亮状态仍然应该属于校验范围内。

  3. 还有一点比较隐蔽,就是这时候就算字数超过被截去,用户仍然可以不停的按键盘,这时候,自动联想还能继续,导致自动联想出来的字数越来越多,交互很不合理,用户体验不佳。这时候要同时实现- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string限制用户点击的键盘输入。这里可能有人会说 ==这样在输入全部是英文的情况下是可以的. 但是当输入是中文时, 由于shouldChangeCharactersInRange判断的是当前键盘的字符数, 会出现这样的问题: 比如你还剩下2个字可以打, 你想输入"张三", "张"的拼音是Zhang, 于是你在输入Zh的时候就无法输入了. 显然, 这样的结果不是我们想要的.== 我再强调下但是这必须要这样。因为如果你不限制高亮字符让其出现在文本框内,使用控件的同学直接取textField.text这时候的文本自然包含高亮文本,导致长度就超过了要求(如 要求是5个字不进行限制,输入xxx后再输入张三的拼音得到 xxx==zhangsan==这时候张三还在高亮状态,然后用户直接点击保存,这时候如果使用控件的人不先进行 endEditing操作,然后直接取textField.text 取得的文本肯定是 xxxzhangsan 超过了5个字的要求,控件限制文本长度的功能不能依赖与控件使用者,所以不可取)

Demo

//self.helpObject 就是一个 UITextField

//[self addTarget:self.helper action:@selector(textFiledEditChanged) forControlEvents:UIControlEventEditingChanged];

- (void)textFiledEditChanged

{

if (self.helpObject.maxLength != 0) {

NSInteger kmaxLength = self.helpObject.maxLength;

NSString *toBeString = self.helpObject.text;

//截取长度

if (toBeString.length > kmaxLength) {

self.helpObject.text = [toBeString substringToIndex:kmaxLength];

}

}

}

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string

{

//长度校验 string.length > 0 排除删除按

if (self.helpObject.maxLength != 0 && string.length != 0) {

NSString *toBeString = [textField.text stringByReplacingCharactersInRange:range withString:string];

NSInteger kmaxLength = self.helpObject.maxLength;

if (toBeString.length > kmaxLength){

return NO;

}

}

return YES;

}

二 限制emoji表情


还是网上一搜索 搜到的都不合理只好自己捣鼓。直接上反例。


- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string

{

//检查 string  string 包含emoji表情则return NO;

}

  • 上诉代码解决不了通过文字联想出来的emoji表情输入,因为这个方法更本监听不到。

  • 其次上述代码非常隐晦的屏蔽了九宫格输入法。因为点击九宫格按键得到的string就是emoji表情符号文字 ①②③④⑤⑥⑦⑧⑨⑩

解决方案
  1. 想到用UIControlEventEditingChanged事件,但是不可取,因为这个事件并不知道emoji表情添加到哪个位置上,如果每次在此方法中做个遍历,去掉emoji太耗性能。所以采用- (void)setMarkedText:(NSString *)markedText selectedRange:(NSRange)selectedRange这个方法,通过联想得到的emoji表情,选取后必然调用这个,我们只要重写此方法,就能做到过滤。

  2. - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string方法中加入校验,当前是emoji表情键盘的时候才进行验证,==此处有坑,网上找的方法全都是采用if ([[[UITextInputMode currentInputMode ]primaryLanguage] isEqualToString:@"emoji"]) currentInputMode 这明明都是IOS7就废弃的方法,好吧那就采用 textField.textInputMode.primaryLanguage 结果选择emoji表情的时候得到的是nil 直接判断nil为moeji表情键盘太不合理了==,网上找了半天没找到,然后自己瞎捣鼓了下发现了[UITextInputMode activeInputModes]这里有然后就写了个方法 不过得到的数组实际都是iOS的私有类,不过根据泛型参数估且认为他是NSString *类型然后通过KVC可以取得我们想要的结果。

Demo

- (BOOL)nowIsEmojiKeyBorad

{

for (NSString *keyboardInputMode in [UITextInputMode activeInputModes]) {

if ([[keyboardInputMode valueForKey:PrimaryLanguage] isEqualToString:EmojiprimaryLanguage]) {
//这里ios7会崩溃的建议增加判断 本文不做展开 具体参见博客
NSNumber *isDisplayed = [keyboardInputMode valueForKey:IsDisplayed];

if ([isDisplayed boolValue] == YES) {

return YES;

}

}

}

return false;

}

- (void)setMarkedText:(NSString *)markedText selectedRange:(NSRange)selectedRange

{

//将含有emoji表情的文字替换为@""

NSString *helpMarkedText = [self.helper setMarkedText:markedText selectedRange:selectedRange];

[super setMarkedText:helpMarkedText selectedRange:selectedRange];

}

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string

{

//判断是否是 emoji键盘 并且 string.length > 0 (排除删除按)

//检查 string  string 包含emoji表情则return NO;

}

==写到这里,具体控件实现,设计思路放下一篇博客里讲==

你可能感兴趣的:(iOS - UITextField限制长度 emoji表情)