Swift 4中基于Block的KVO监听UITextField文本实时变更问题
这个问题我之前也碰到过!核心原因是UITextField的text属性在实时输入时并不会触发标准的KVO通知——UIKit内部在用户逐字符输入时,并没有通过KVO兼容的方式(比如调用setText:或者触发willChangeValue(forKey:)/didChangeValue(forKey:))更新text属性,只有当编辑结束(比如文本框失焦、按下回车)时才会触发KVO,这是UIKit的内部实现特性。
下面给你两种符合需求的解决方案,都能实时获取新旧值,且不需要手动维护旧值:
方案一:自定义属性+KVO配合editingChanged事件
我们可以在控制器里封装一个自己的属性,通过editingChanged事件同步UITextField的文本,然后监听这个自定义属性的KVO,这样就能完美实现实时触发+自动获取新旧值:
class YourViewController: UIViewController { @IBOutlet weak var textField: UITextField! private var textObservation: NSKeyValueObservation? // 自定义属性,用来同步textField的文本 private var currentTextFieldText: String? override func viewDidLoad() { super.viewDidLoad() // 监听自定义属性的KVO textObservation = observe(\.currentTextFieldText, options: [.new, .old]) { _, change in print("新值: \(change.newValue ?? "空")") print("旧值: \(change.oldValue ?? "空")") } // 监听textField的实时编辑事件 textField.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged) } @objc private func textFieldDidChange(_ textField: UITextField) { // 同步文本到自定义属性,触发KVO currentTextFieldText = textField.text } }
这个方案的好处是完全遵循KVO的逻辑,不需要手动处理新旧值的存储,KVO会自动帮你记录。
方案二:手动触发text属性的KVO通知
如果你不想额外定义属性,也可以在editingChanged事件里手动触发text属性的KVO变更通知,让原来的KVO监听实时生效:
class YourViewController: UIViewController { @IBOutlet weak var textField: UITextField! private var textObservation: NSKeyValueObservation? override func viewDidLoad() { super.viewDidLoad() // 原来的KVO监听代码 textObservation = textField.observe(\.text, options: [.new, .old]) { textField, change in print("新值: \(change.newValue ?? "空")") print("旧值: \(change.oldValue ?? "空")") } // 添加实时编辑监听,手动触发KVO textField.addTarget(self, action: #selector(triggerTextKVO(_:)), for: .editingChanged) } @objc private func triggerTextKVO(_ textField: UITextField) { // 手动触发KVO的变更通知 textField.willChangeValue(forKey: "text") textField.didChangeValue(forKey: "text") } }
这里需要注意:手动触发时,KVO会使用当前text属性的最新值作为newValue,上一次的text值作为oldValue,完全符合你的需求。
补充说明
如果你愿意跳出KVO的限制,UITextFieldDelegate的textField(_:shouldChangeCharactersIn:replacementString:)方法也能计算新旧值,但需要你自己通过范围和替换字符串推导,不如上面的KVO方案省心,所以优先推荐前面两种。
内容的提问来源于stack exchange,提问作者鸡肉味嘎嘣脆

