这个问题基本有两种解决思路,一是在输入完毕或即将返回时判断文字的长度是否超过限制, 这种方法实现起来简单,只需在委托方法
-(BOOL)textViewShouldEndEditing:(UITextView *)textView 中加以判断即可,但是这种方式体验不好。
另外一种就是通过-(BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text 及
-(void)textViewDidChange:(UITextView *)textView
两个委托方法来在输入过程中进行空间,这种方法实现起来比较复杂,特别是对于中文拼音输入时字符跟字符间夹杂着空格的情况,处理起来比较头疼,稍不注意就会引起crash。
下面讲述第二种思路的实现:
1. 首先,添加一个键盘监听的类KeyboardHandler,监听键盘出现和隐藏以及高度改变的事件,并且向UItextview所在的viewcontroller发出通知,或采用kvo的方式监听键盘高
度。
// Call this method sometwhere in your view controller setup code.
- (void)registerForKeyboardNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardChange:)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardChange:)
name:UIKeyboardWillHideNotification
object:nil];
}
- (void)removeForKeyboardNotifications
{
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIKeyboardWillHideNotification
object:nil];
}
- (void)keyboardChange:(NSNotification *)aNotification
{
NSDictionary *info = (NSDictionary*)[aNotification userInfo];
NSValue *valueBegin = [info objectForKey:UIKeyboardFrameBeginUserInfoKey];
NSValue *valueEnd = [info objectForKey:UIKeyboardFrameEndUserInfoKey];
// Get the size of the keyboard
CGSize keyboardSizeBegin = [valueBegin CGRectValue].size;
CGSize keyboardSizeEnd = [valueEnd CGRectValue].size;
if ([aNotification.name isEqualToString:UIKeyboardWillShowNotification]) {
if (_show) { // 更改输入法
if (keyboardSizeEnd.height != keyboardSizeBegin.height) {
_offset = keyboardSizeBegin.height - keyboardSizeEnd.height;
_accessor.frame = [self updateAccessorFrame:_accessor.frame];
self.keyboardHeight = keyboardSizeEnd.height;
}
return;
}
_offset = self.accessorOffset == 0 ? -keyboardSizeBegin.height : -self.accessorOffset;
[self updateAccessorAnimate:_animating];
self.keyboardHeight = keyboardSizeEnd.height;
_show = YES;
} else if ([aNotification.name isEqualToString:UIKeyboardWillHideNotification])
{
if (!_show) {
return;
}
_offset = self.accessorOffset == 0 ? keyboardSizeEnd.height : self.accessorOffset ;
[self updateAccessorAnimate:_animating];
self.keyboardHeight = keyboardSizeEnd.height;
_show = NO;
}else
{
}
}
2. 在UItextview所在的viewcontroller 增加两个属性
@property(nonatomic,assign) BOOL isTypingChineseCharacter; //是否正在输入中文
@property(nonatomic,strong) NSString* textAfterChange; //上一次改变时文本框内的文字
初始化keyboardHandler 并监听键盘高度的变化,主要是为了对中文输入进行处理
self.keyboardHandler = [QLKeyboardHandler keyboardNotification];
[self.keyboardHandler registerForKeyboardNotifications];
[self.keyboardHandler addObserver:self forKeyPath:QLKEYNAME_KEYBOARD_HEIGHT options:NSKeyValueObservingOptionNew context:nil];
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqualToString:QLKEYNAME_KEYBOARD_HEIGHT]) {
CGFloat newHeight = [[change objectForKey:@"new"]floatValue];
// NSLog(@"newHeight:%f",newHeight);
if (newHeight == kEnglishKeyboardHeight) {
self.isTypingChineseCharacter = NO;
}else if (newHeight == kChineseKeyboardHeight){
self.isTypingChineseCharacter = YES;
}
}
}
3. 实现UITextView的委托方法
//在输入字符后,即将改变textView前
-(BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
BOOL shouldChange =YES;
//对于换行符,直接处理为退出
if ([text isEqualToString:@"\n"]) {
//完成编辑
[textView resignFirstResponder];
return NO;
}
//英文输入过程中,如果文字超出长度限制,后面的直接不让输,但对于中文必须让用户继续输完拼音,选词的时候再截断
NSInteger charactersAfterChange = textView.text.length + text.length - range.length;
if (!_isTypingChineseCharacter && charactersAfterChange > maxTextLength) {
shouldChange = NO;
}
return shouldChange;
}
//在输入了字符(不限中英文)或者选定了中文词语后
-(void)textViewDidChange:(UITextView *)textView
{
NSInteger textCount = textView.text.length;
//如果是中文拼音输入的话,在输入完毕后的选词阶段会调用两次textViewDidChange, 利用这个特性可以记录上一次调用时文本框内的字符,如果跟这一次的字符一样,则说明是中文选词。
BOOL didSelectWord = NO;
if ([textView.text isEqualToString:self.textAfterChange]) {
didSelectWord = YES;
}
//如果是英文输入下,直接截断超出长度限制的文字
if (textCount>maxTextLength && !_isTypingChineseCharacter) {
textView.text = [textView.text substringToIndex:maxTextLength];
}
//如果是中文输入,情况要复杂一点,不能在选词阶段直接截断文字,否则会crash,必须等词选好,即将出现在文本框内的时候再阶段(也就是第二次调用textViewDidChange的时候)
else if (textCount>maxTextLength && _isTypingChineseCharacter && didSelectWord){
textView.text = [textView.text substringToIndex:maxTextLength];
}
_textAfterChange = textView.text;
NSLog(@"textCount = %d",_textAfterChange.length);
_textCountLabel.text = [NSString stringWithFormat:@"%d字",maxTextLength- _textAfterChange.length];
}
这里有个问题,就是当连续输入中文的拼音首字母时,比如输入ttss 之类的文字, 文字的长度并不是输入一个字符就增加一个长度,而是1个字符可能对应1个或2个长度。 在textViewDidChange中打印textView.text,可以看到,text的长度是先增加后减少的,这主要是因为苹果为了区分 不同中文字 而在字符之间加了空格。但是这样就给程序造成了麻烦, 没办法通过textView的文字长度来判断是否该截断。 因为如果在拼音没有输完的情况下截断正在输入的文字会造成程序 crash。经过反复探索实践,我发现在中文输入拼音的时候,如果点了拼音对应的词语,textView会向他的委托调用两次textViewDidChange, 并把词语写到 textView后面,如果发现 两次调用的文字内容不一样,就会抛出异常 。 那么,找到问题之后,就可以对症下药了,只要 增加一个变量,记录上一次文字改变时的内容, 确保在 第二次截断文字,第一次不截断, 就不会报错了。