Swift - 限制TextField只能输入中文(附:中文字数的限制)
作者:hangge | 2017-12-29 08:10
1,效果图
这里对界面上的 UITextField 做了输入限制,只能输入中文。不接受除中文外的其他一切字符(包括英文、数字、符号等),即使粘贴进来也不行。

2,实现原理
(1)要对 textField 的输入做限制,无非有两种方法。一种是使用 shouldChangeCharactersIn 这个 textField 的代理方法。该方法会在每次 textfield 有输入的时候被调用,我们只需对每次输入的内容进行判断,看是否符合条件即可。
- 符合条件的:return true,相应的文字会成功输入到 textField 中。
- 不符合条件的:return false,相应的文字无法输入到 textField 中。
但如果我们只允许输入中文的话就会有问题。因为当我们使用系统的拼音输入法输入中文时,首先需要输入拼音字母,这个叫做 marked text(见下图)。

而 marked text 也是会被 shouldChangeCharactersIn 方法强制获取到的。只要打出拼音的第一个英文字母,就会被该方法截取,我们无法判断这个字母是英文字母还是拼音字母。如果禁止英文输入的话,那么中文也就没法打出来了。
(2)所以我们只能使用另一种方法:监听 textField 的 UITextFieldTextDidChangeNotification 通知事件,在事件响应函数里进行如下工作:
- 先判断当前是否有 markedtext,有的话我们不做任何动作。
- 使用正则过滤掉 textField 里非中文的字符,然后再将结果重新赋给 textField。
- 进行上面操作前要记下光标的位置,在赋值后再还原光标位置。否则如果在中间输入文字后,光标会自动跳到末尾。
3,样例代码
import UIKit
class ViewController: UIViewController {
//文本输入框
var textField:UITextField!
override func viewDidLoad() {
super.viewDidLoad()
//初始化文本输入框
textField = UITextField(frame: CGRect(x:20, y:100, width:200, height:30))
textField.borderStyle = .roundedRect
self.view.addSubview(textField)
}
override func viewDidAppear(_ animated: Bool) {
//监听textField内容改变通知
NotificationCenter.default.addObserver(self,
selector: #selector(self.greetingTextFieldChanged),
name:NSNotification.Name(rawValue:"UITextFieldTextDidChangeNotification"),
object: self.textField)
}
//textField内容改变通知响应
@objc func greetingTextFieldChanged(obj: Notification) {
//非markedText才继续往下处理
guard let _: UITextRange = textField.markedTextRange else{
//当前光标的位置(后面会对其做修改)
let cursorPostion = textField.offset(from: textField.endOfDocument,
to: textField.selectedTextRange!.end)
//判断非中文的正则表达式
let pattern = "[^\\u4E00-\\u9FA5]"
//替换后的字符串(过滤调非中文字符)
let str = textField.text!.pregReplace(pattern: pattern, with: "")
textField.text = str
//让光标停留在正确位置
let targetPostion = textField.position(from: textField.endOfDocument,
offset: cursorPostion)!
textField.selectedTextRange = textField.textRange(from: targetPostion,
to: targetPostion)
return
}
}
override func viewDidDisappear(_ animated: Bool) {
//移除textField内容改变通知监听
NotificationCenter.default.removeObserver(self,
name:NSNotification.Name(rawValue: "UITextFieldTextDidChangeNotification"),
object: self.textField)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
extension String {
//使用正则表达式替换
func pregReplace(pattern: String, with: String,
options: NSRegularExpression.Options = []) -> String {
let regex = try! NSRegularExpression(pattern: pattern, options: options)
return regex.stringByReplacingMatches(in: self, options: [],
range: NSMakeRange(0, self.count),
withTemplate: with)
}
}
常用的一些正则表达式:
- 非中文:[^\\u4E00-\\u9FA5]
- 非英文:[^A-Za-z]
- 非数字:[^0-9]
- 非中文或英文:[^A-Za-z\\u4E00-\\u9FA5]
- 非英文或数字:[^A-Za-z0-9]
- 非因为或数字或下划线:[^A-Za-z0-9_]
附:增加中文字数限制
下面代码在前面的基础上再增加个字数限制:textField 不仅只能输入中文,而且最多只能输入 10 个字。 (这个同样是监听 textField 的 UITextFieldTextDidChangeNotification 通知事件来实现,高亮部分表示新增加的代码)//textField内容改变通知响应
@objc func greetingTextFieldChanged(obj: Notification) {
//非markedText才继续往下处理
guard let _: UITextRange = textField.markedTextRange else{
//当前光标的位置(后面会对其做修改)
let cursorPostion = textField.offset(from: textField.endOfDocument,
to: textField.selectedTextRange!.end)
//判断非中文的正则表达式
let pattern = "[^\\u4E00-\\u9FA5]"
//替换后的字符串(过滤调非中文字符)
var str = textField.text!.pregReplace(pattern: pattern, with: "")
//如果长度超过限制则直接截断
if str.count > 10 {
str = String(str.prefix(10))
}
textField.text = str
//让光标停留在正确位置
let targetPostion = textField.position(from: textField.endOfDocument,
offset: cursorPostion)!
textField.selectedTextRange = textField.textRange(from: targetPostion,
to: targetPostion)
return
}
}
全部评论(3)
非中文或英文的不好使啊,还能输入数字,符号呢
站长回复:不是吧,我又测试了下是无法输入数字和符号的。
其实在哪里添加和移除通知一直都是个问题,我看在这段代码里添加和移除是放在willAppear和DidAppear中的,如果不考虑该控制器可能会被循环引用,将移除放在析构函数deinit里更好,至于添加通知就仁者见仁智者见智了
站长回复:你说的有道理,这个还是要根据实际项目,具体情况具体分析。
RxSwift来个呗
站长回复:这个肯定要写的。就是RxSwift东西比较多,需要找个时间好好整整。