限制文本输入长度

需求

限制字符输入长度是个常见的需求,比如注册的账号或者密码。通常,从用户体验的角度,iOS App中当输入超过制定长度的字符时不需要显式的弹出提示,而只需要让超过的字符不显示即可。这个功能看似简单,但是考虑到一些输入系统例如中文输入法的联想功能,就不那么简单。

一般的实现

以UITextField为例,通常的做法是在delegate中做文章。这样的解决方案很多,stackflow上一个比较全面的解答:Set the maximum character length of a UITextField,大致的思路就是在输入即将要替换的时候,计算最终要显示的字符串。这个方法对于英文输入是没有问题的,但是对于其他语言如中文输入就会存在问题。举例来说中文拼音输入“的”时,用户需要输入de,在未选中“的”时,de以选中的状态在输入框中显示,并作为输入框的内容,如下图:

input1input2

按照中文的输入法,de两个字符并不是最终显示在UITextField控件中,所以背景有选中区域,但此时UITextFiled的属性text的内容包含de。然后,右图显示的是输入“的”后产生的联想,注意此时点击“确”的时候,UITextField的任一delegate都不会响应。所以,要另想办法解决。

解决办法

UITextField有一个属性markedTextRange用于标识如中文拼音输入字母时的情形,那么解决的思路:

  1. UITextFiled的delegate中先检查markedTextRange是否为空,不为空时不做检查允许输入;否则按原先的思路计算字符长度
  2. 注册UITextFieldTextDidChangeNotification消息,用于处理联想输入情形

下面给出代码:

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
    //delete
    if ([string length] == 0 || textField.markedTextRange != nil)
    {
        return YES;
    }

    if (([string length] + [textField.text length] > MAX_LEN ) )
    {
        return NO;
    }
    return YES;
}
- (void)textChange:(NSNotification *)noti
{
    if (self.waterTextField.markedTextRange != nil)
    {
        return;
    }
    NSString *string = self.waterTextField.text;
    if ([string length] > MAX_LEN)
    {
        self.waterTextField.text = [string substringToIndex:MAX_LEN];
    }
}

需要注意的是在UITextFieldTextDidChangeNotification的响应中需要检测markedTextRange,只有在为nil的情况下再进行字符截断处理,否则程序会抛出异常。