点击空白收起键盘时,如何丢弃TextField输入且不触发textFieldDidEndEditing
textFieldDidEndEditing处理逻辑的问题 你的思路其实没问题,view.endEditing(true)确实会让所有可编辑控件调用resignFirstResponder(),而系统默认会在这个时候触发textFieldDidEndEditing代理方法。核心问题是要区分两种结束编辑的场景:一种是用户主动提交输入(比如点击Return键、提交按钮),另一种是用户点击外部单纯收起键盘(不想提交)。
这里提供两种可行的方案,你可以根据自己的需求选择:
方案一:用标记区分收起键盘的触发源
我们可以给UIViewController添加一个标记,用来记录当前是点击外部触发的键盘收起,然后在textFieldDidEndEditing里判断这个标记,决定是否执行处理逻辑。
步骤1:修改UIViewController扩展
添加关联属性来存储标记状态(因为扩展不能直接添加存储属性,所以用关联对象实现):
import UIKit private struct AssociatedKeys { static var isTappingOutsideToDismiss = "isTappingOutsideToDismiss" } extension UIViewController { private var isTappingOutsideToDismiss: Bool { get { return objc_getAssociatedObject(self, &AssociatedKeys.isTappingOutsideToDismiss) as? Bool ?? false } set { objc_setAssociatedObject(self, &AssociatedKeys.isTappingOutsideToDismiss, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } func hideKeyboardWhenTappedAround() { let tap = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard)) tap.cancelsTouchesInView = false view.addGestureRecognizer(tap) } @objc private func dismissKeyboard() { // 标记当前是点击外部收起键盘 isTappingOutsideToDismiss = true view.endEditing(true) // 异步重置标记,避免影响下一次编辑操作 DispatchQueue.main.async { self.isTappingOutsideToDismiss = false } } // 给ViewController提供判断方法 func shouldIgnoreTextFieldEndEditing() -> Bool { return isTappingOutsideToDismiss } }
步骤2:在ViewController的代理方法中判断标记
在你的视图控制器里,实现UITextFieldDelegate的textFieldDidEndEditing方法,加入判断逻辑:
class YourViewController: UIViewController, UITextFieldDelegate { override func viewDidLoad() { super.viewDidLoad() self.hideKeyboardWhenTappedAround() // 记得给TextField设置delegate yourTextField.delegate = self } func textFieldDidEndEditing(_ textField: UITextField) { // 如果是点击外部收起键盘,清空输入并直接返回 if shouldIgnoreTextFieldEndEditing() { textField.text = "" return } // 这里写你原本的输入处理逻辑(比如验证、提交) guard let inputText = textField.text, !inputText.isEmpty else { // 只有用户主动提交时才触发错误提示 showInputError() return } // 处理正确的输入 handleValidInput(inputText) } // 处理Return键的情况(确保主动提交时正常执行逻辑) func textFieldShouldReturn(_ textField: UITextField) -> Bool { textField.resignFirstResponder() return true } // 辅助方法:显示输入错误 private func showInputError() { let alert = UIAlertController(title: "错误", message: "输入内容不能为空", preferredStyle: .alert) alert.addAction(UIAlertAction(title: "确定", style: .default)) present(alert, animated: true) } // 辅助方法:处理有效输入 private func handleValidInput(_ text: String) { // 你的业务逻辑 } }
方案二:直接清空当前活跃TextField并收起键盘
如果你不想用标记,也可以直接找到当前正在编辑的TextField,先清空内容再让它失去第一响应者。这种方式更直接,但需要额外的方法来查找当前的第一响应者:
步骤1:添加UIView扩展查找第一响应者
extension UIView { func findFirstResponder() -> UIView? { if isFirstResponder { return self } for subview in subviews { if let firstResponder = subview.findFirstResponder() { return firstResponder } } return nil } }
步骤2:修改dismissKeyboard方法
extension UIViewController { func hideKeyboardWhenTappedAround() { let tap = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard)) tap.cancelsTouchesInView = false view.addGestureRecognizer(tap) } @objc private func dismissKeyboard() { // 找到当前正在编辑的TextField if let activeTextField = view.findFirstResponder() as? UITextField { // 先清空内容,再收起键盘 activeTextField.text = "" activeTextField.resignFirstResponder() } else { // 没有活跃的TextField时,直接收起键盘 view.endEditing(true) } } }
注意事项
这种方式虽然清空了内容,但textFieldDidEndEditing还是会被触发。如果你的处理逻辑里有针对空输入的错误提示,需要额外判断:只有当用户主动提交(比如点击Return键)时才触发错误,点击外部时忽略。所以建议还是结合方案一的标记逻辑,避免不必要的错误提示。
两种方案都能实现你的需求,方案一更灵活,能清晰区分不同的结束编辑场景;方案二更简洁,适合逻辑简单的场景。你可以根据自己的项目情况选择。
内容的提问来源于stack exchange,提问作者MaX

