我们知道 iOS 自带的 UITextField
并没有提供任何有关字数限制的属性,如果想实现这个功能,最简单的办法大家有可能想到使用 delegate
实现 textField(_: , shouldChangeCharactersInRange _: , replacementString _:)
代理方法,在这个方法中我们只需得到最终替换完毕的结果即可对其进行字数或有效性的检查,如果不符合要求就直接返回 false
。
这个代理方法将会在用户修改输入框内文字时触发,传入的 range
是用户已选中的文本区间,string
则是用户输入的文字,而不是最终完整的文本。所以我们要先自己用新文本替换一下原来的文本。
代码如下:
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let replacedString = (textField.text! as NSString).stringByReplacingCharactersInRange(range, withString: string)
if /* Validate replacedString */ {
return false
}
return true
}
注意这里有个小坑,就是如果你使用了 Emoji 表情或者其他西方文字,会出现运行时的异常,这是由于 Swift 和 Objective-C 对于字符串实现不同造成的,所以我并没有使用 Swift 提供的
String
类型,而是将其强制转换成NSString
,然后再进行字符串替换处理,以解决上述的问题。
但是问题并没有解决,当我们使用中文键盘的时候,连续按两下空格会输入句号,而且上面的代理方法并不能拦截这个句号的输入。所以我们只能曲线救国,由于 iOS 还提供了一个 Notification 叫做 UITextFieldTextDidChangeNotification
,监听这个通知,然后在文本改变之后再进行一次检验。
我采用的方法是先修改上面的代理方法为:
func validate(string: String) -> Bool {
if string.isEmpty {
return true
}
if let regex = try? NSRegularExpression(pattern: "^[0-9]{0,4}.{0,1}[0-9]{0,2}$", options: .CaseInsensitive) {
return regex.matchesInString(string, options: .WithoutAnchoringBounds, range: NSMakeRange(0, string.characters.count)).count > 0
}
return false
}
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let replacedString = (textField.text! as NSString).stringByReplacingCharactersInRange(range, withString: string)
if !self.validate(replacedString) {
return false
} else {
self.lastValidText = replacedString
}
return true
}
可以看到我使用了一个validate(_:)
函数对文本进行检验,这样需求有变更时我们只需修改这个函数即可,本例子中我使用了一个正则表达式来检验用户的输入,当然实际使用时大家也可以去检验字数等。
然后我会保存每次合法的输入,当输入完毕时,下面方法将会调用:
func textFieldDidChangeText() {
if !self.validate(self.textField?.text ?? "") {
self.textField?.text = self.lastValidText
}
}
判断文本修改完毕后的值是否合法,如果不合法就替换回上一次合法的输入,这样我们就完美的实现了字数、输入限制了。
我们还可以对这些方法进行封装:
然后使用时就可以这样:
以上。