UITextField滑动输入、自动填充时.editingChanged不触发如何解决
问题原因
UIControl.Event.editingChanged 的触发逻辑存在系统层面的覆盖不全问题:仅能响应键盘逐字点按输入、手动删除字符的常规交互,对于系统级文本注入场景(包括QuickPath滑动输入、iCloud钥匙串密码/邮箱自动填充、短信验证码自动填充、第三方输入法联想整句上屏等),该事件不会触发,该问题从iOS 13引入滑动输入功能后长期存在。
全场景兼容方案
不要单独依赖.editingChanged做文本变更监听,组合以下监听方式即可覆盖所有输入场景:
- 保留原有
.editingChanged事件绑定,覆盖常规点按输入场景 - 实现
UITextFieldDelegate的文本变更代理,覆盖滑动输入、自动填充场景 - (可选)添加KVO监听text属性变化,覆盖代码修改文本的场景
具体实现代码
首先遵守UITextFieldDelegate协议,设置输入框代理:
// 初始化textField时补充代理设置 textField.delegate = self
实现代理方法:
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { // 代理触发时文本尚未写入输入框,异步派发等系统完成文本赋值后再执行业务逻辑 DispatchQueue.main.async { [weak self] in self?.textFieldDidChange(textField) } return true }
如果需要覆盖代码主动修改textField.text的场景,补充KVO监听:
// 在viewDidLoad中添加监听 textField.addObserver(self, forKeyPath: "text", options: [.new, .old], context: nil) // 实现KVO回调 override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { guard keyPath == "text", let inputField = object as? UITextField, let newText = change?[.newKey] as? String, let oldText = change?[.oldKey] as? String, newText != oldText else { super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context) return } textFieldDidChange(inputField) } // 页面销毁时移除监听 deinit { textField.removeObserver(self, forKeyPath: "text") }
注意事项
- 代理方法中必须通过
DispatchQueue.main.async异步调用文本变更处理方法,否则拿到的textField.text是变更前的旧值 - 需要给业务逻辑加重复触发过滤:滑动输入、自动填充场景下可能同时触发多个监听回调,避免重复执行输入校验、计数、网络请求等逻辑
- 如果项目中多处用到输入框,建议直接封装
UITextField子类,把上述监听逻辑内置在子类中,对外暴露统一的文本变更回调,减少重复代码
内容的提问来源于stack exchange,提问作者Bluebyte
相关产品推荐
相关产品推荐

