如何在手指触达前收起iOS键盘
实现UIScrollView键盘提前40像素交互式收起
我之前也碰到过类似的需求,默认的interactive键盘收起必须要手指碰到键盘才会触发,要让它提前40像素就响应,我们可以通过监听键盘状态、拦截拖拽手势,再手动控制键盘收起的交互过程来实现,具体步骤如下:
1. 监听键盘高度,记录关键位置
首先我们需要获取键盘的实时高度,这样才能计算出“提前40像素”的触发区域:
class YourViewController: UIViewController { @IBOutlet weak var scrollView: UIScrollView! private var keyboardHeight: CGFloat = 0 private let triggerOffset: CGFloat = 40 // 提前触发的距离 override func viewDidLoad() { super.viewDidLoad() scrollView.keyboardDismissMode = .interactive // 注册键盘通知,获取键盘高度 NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: UIResponder.keyboardWillShowNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(_:)), name: UIResponder.keyboardWillHideNotification, object: nil) } @objc private func keyboardWillShow(_ notification: Notification) { guard let userInfo = notification.userInfo, let keyboardFrame = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else { return } // 转换为当前视图坐标系下的键盘高度 keyboardHeight = view.convert(keyboardFrame, from: nil).height } @objc private func keyboardWillHide(_ notification: Notification) { keyboardHeight = 0 } deinit { NotificationCenter.default.removeObserver(self) } }
2. 自定义ScrollView拦截拖拽手势
我们需要自定义UIScrollView子类,拦截它的拖拽手势,判断手指位置是否进入提前触发区域,一旦进入就手动启动键盘的交互式收起:
class PreDismissScrollView: UIScrollView { var keyboardHeight: CGFloat = 0 let triggerOffset: CGFloat = 40 private var isTriggeringDismiss = false override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { // 仅处理拖拽手势,且键盘处于显示状态 guard let panGesture = gestureRecognizer as? UIPanGestureRecognizer, keyboardHeight > 0 else { return super.gestureRecognizerShouldBegin(gestureRecognizer) } let touchLocation = panGesture.location(in: self) // 计算当前视图中键盘顶部的Y坐标 let keyboardTopY = bounds.height - keyboardHeight // 判断手指是否处于键盘上方40像素范围内 let isInTriggerZone = touchLocation.y >= (keyboardTopY - triggerOffset) && touchLocation.y <= keyboardTopY if isInTriggerZone { isTriggeringDismiss = true // 让第一响应者 resign,启动键盘收起的交互流程 UIResponder.currentFirstResponder()?.resignFirstResponder() // 同步scrollView的拖拽偏移,让视觉上更连贯 let translation = panGesture.translation(in: self) contentOffset.y += translation.y panGesture.setTranslation(.zero, in: self) } return super.gestureRecognizerShouldBegin(gestureRecognizer) } override func scrollViewDidScroll(_ scrollView: UIScrollView) { if isTriggeringDismiss { // 当拖拽过程中,根据scrollView的偏移量调整键盘收起的进度 let dismissProgress = (contentOffset.y - (contentSize.height - bounds.height + keyboardHeight - triggerOffset)) / triggerOffset // 这里可以配合UIPercentDrivenInteractiveTransition来更精准控制动画 // 如果不需要精细控制,系统默认的interactive dismiss会自动跟进 } } } // 扩展UIResponder,方便获取当前第一响应者 extension UIResponder { static func currentFirstResponder() -> UIResponder? { _currentFirstResponder = nil UIApplication.shared.sendAction(#selector(findFirstResponder(_:)), to: nil, from: nil, for: nil) return _currentFirstResponder } private static weak var _currentFirstResponder: UIResponder? @objc private func findFirstResponder(_ sender: Any) { UIResponder._currentFirstResponder = self } }
3. 适配ViewController中的ScrollView
把你原有的UIScrollView替换成自定义的PreDismissScrollView,并在ViewController中传递键盘高度:
// 在keyboardWillShow方法中添加 if let customScrollView = scrollView as? PreDismissScrollView { customScrollView.keyboardHeight = keyboardHeight }
关键说明
- 我们通过判断手指在scrollView中的位置,提前触发
resignFirstResponder(),启动键盘的交互式收起流程; - 同步scrollView的拖拽偏移是为了让手指移动和键盘收起的视觉效果更连贯,避免突兀;
- 如果需要更精细的动画控制,可以结合
UIPercentDrivenInteractiveTransition类,手动控制键盘收起的进度百分比。
内容的提问来源于stack exchange,提问作者Laurent Crivello
相关产品推荐
相关产品推荐

