Swift iOS:ScrollView键盘适配异常,首次点击TextView无效
问题解决:首次点击TextView时ScrollView未自动调整位置的修复
问题根源分析
你的代码存在几个关键问题导致首次点击不生效:
- 使用
UIKeyboardFrameBeginUserInfoKey获取键盘尺寸,该Key对应键盘开始显示时的初始位置,而非最终显示完成的位置,导致计算的键盘高度不准确。 scrollRectToVisible使用的commentInput.frame是相对于父视图的坐标,未转换为ScrollView内部坐标系,导致滚动目标位置错误。- 在
keyboardWillHide中禁用了ScrollView的滚动功能,首次点击TextView触发keyboardWillShow时,ScrollView滚动功能的启用时机滞后于滚动操作。
修复步骤及代码调整
1. 替换键盘尺寸获取的Key
将UIKeyboardFrameBeginUserInfoKey替换为UIKeyboardFrameEndUserInfoKey,确保获取的是键盘完全显示后的准确尺寸:
let keyboardSize = (info[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue.size
2. 转换滚动目标的坐标系
把commentInput的frame转换为ScrollView内部的坐标,确保滚动到正确位置:
let targetRect = scrollView.convert(commentInput.frame, from: commentInput.superview) scrollView.scrollRectToVisible(targetRect, animated: true)
3. 调整ScrollView滚动启用时机
移除keyboardWillHide中scrollView.isScrollEnabled = false的设置,或者在TextView成为第一响应者时提前启用滚动(比如在TextView的didBeginEditing代理方法中设置)。如果必须在键盘隐藏时禁用滚动,可确保在keyboardWillShow中先启用滚动再执行滚动操作,同时延迟滚动操作确保设置生效:
@objc open override func keyboardWillShow(notification: NSNotification) { keyboardTapGesture.isEnabled = true scrollView.isScrollEnabled = true let info = notification.userInfo! let keyboardSize = (info[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue.size let contentInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: keyboardSize?.height ?? 0.0, right: 0.0) scrollView.contentInset = contentInsets scrollView.scrollIndicatorInsets = contentInsets // 延迟执行滚动确保ScrollView状态已更新 DispatchQueue.main.async { let targetRect = self.scrollView.convert(self.commentInput.frame, from: self.commentInput.superview) self.scrollView.scrollRectToVisible(targetRect, animated: true) } } @objc open override func keyboardWillHide(notification: NSNotification) { keyboardTapGesture.isEnabled = false scrollView.contentInset = .zero scrollView.scrollIndicatorInsets = .zero view.endEditing(true) // 可选:如果需要在键盘隐藏后禁用滚动,确保下次点击TextView前重新启用 // 可在TextView的didBeginEditing中设置scrollView.isScrollEnabled = true // scrollView.isScrollEnabled = false }
额外建议
- 确保已正确注册键盘通知,在
viewDidLoad中添加:
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil)
- 在
deinit中移除通知,避免内存泄漏:
deinit { NotificationCenter.default.removeObserver(self) }
内容的提问来源于stack exchange,提问作者Filip Z
相关产品推荐
相关产品推荐

