UITableViewController嵌套容器视图时滚动异常问题求助
解决UITableViewController容器视图内表单键盘滚动问题
这个问题我之前也碰到过!UITableViewController自带的键盘适配确实省心,但一旦嵌套了容器视图,层级关系变复杂后,默认的适配就失效了——因为外层的tableView根本感知不到容器里的文本框位置,自然没法正确滚动到可见区域。给你两个靠谱的解决方案,你可以根据自己的场景选:
方案一:让容器VC主动通知外层TableVC滚动
这个思路是让容器里的表单VC在文本框开始编辑时,把文本框的位置信息传递给外层的UITableViewController,由外层来处理滚动逻辑,精准度很高。
步骤1:给容器VC添加回调闭包
在你的表单容器ViewController里,定义一个回调闭包,用来传递当前编辑的文本框位置:
class FormContainerVC: UIViewController { // 定义回调,传递文本框在容器视图内的frame var textFieldDidBeginEditing: ((CGRect) -> Void)? override func viewDidLoad() { super.viewDidLoad() // 给所有文本框绑定编辑开始的监听 for textField in view.subviews.compactMap({ $0 as? UITextField }) { textField.addTarget(self, action: #selector(textFieldStartedEditing(_:)), for: .editingDidBegin) } } @objc private func textFieldStartedEditing(_ textField: UITextField) { // 把文本框的frame转换到容器VC的view坐标系,再传递出去 let frameInContainer = textField.convert(textField.bounds, to: self.view) textFieldDidBeginEditing?(frameInContainer) } }
步骤2:外层TableVC接收回调并处理滚动
在你的UITableViewController里,拿到容器VC的实例,设置回调逻辑,把容器内的位置转换到tableView坐标系,然后滚动到可见区域:
class MainTableVC: UITableViewController { private var formContainerVC: FormContainerVC? override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if let containerVC = segue.destination as? FormContainerVC { formContainerVC = containerVC // 设置回调,处理滚动 containerVC.textFieldDidBeginEditing = { [weak self] frameInContainer in guard let self = self else { return } // 找到容器所在的tableView单元格 guard let containerCell = self.tableView.cellForRow(at: IndexPath(row: 1, section: 0)) else { return } // 把容器内的frame转换到tableView坐标系 let frameInTableView = containerCell.contentView.convert(frameInContainer, to: self.tableView) // 调整滚动区域,留出键盘上方的间距(可以根据需求改20这个值) var scrollRect = frameInTableView scrollRect.origin.y -= 20 // 平滑滚动到目标区域 self.tableView.scrollRectToVisible(scrollRect, animated: true) } } } }
方案二:全局监听键盘通知,手动处理滚动
如果你的表单比较复杂,或者不想依赖容器VC的回调,可以禁用UITableViewController的默认适配,自己监听键盘弹出/收起事件,手动计算滚动位置。
步骤1:禁用默认适配并添加键盘监听
class MainTableVC: UITableViewController { override func viewDidLoad() { super.viewDidLoad() // 禁用UITableViewController默认的键盘适配 tableView.contentInsetAdjustmentBehavior = .never // 添加键盘通知监听 NotificationCenter.default.addObserver( self, selector: #selector(keyboardWillShow(_:)), name: UIResponder.keyboardWillShowNotification, object: nil ) NotificationCenter.default.addObserver( self, selector: #selector(keyboardWillHide(_:)), name: UIResponder.keyboardWillHideNotification, object: nil ) } // 记得移除通知,避免内存泄漏 deinit { NotificationCenter.default.removeObserver(self) } }
步骤2:实现键盘事件处理逻辑
我们需要先找到当前正在编辑的文本框,然后计算它在tableView里的位置,判断是否被键盘挡住,再调整滚动偏移:
extension MainTableVC { @objc private func keyboardWillShow(_ notification: Notification) { guard let keyboardFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else { return } let keyboardHeight = keyboardFrame.height // 找到当前第一响应者(正在编辑的文本框) guard let activeTextField = UIResponder.currentFirstResponder() as? UITextField else { return } // 把文本框的frame转换到tableView坐标系 let frameInTableView = activeTextField.convert(activeTextField.bounds, to: tableView) // 计算文本框底部到tableView底部的距离 let bottomGap = tableView.bounds.height - frameInTableView.maxY // 如果间距小于键盘高度,说明文本框被挡住了,需要滚动 if bottomGap < keyboardHeight { let neededOffset = keyboardHeight - bottomGap + 20 // 加20的缓冲间距 tableView.setContentOffset( CGPoint(x: 0, y: tableView.contentOffset.y + neededOffset), animated: true ) } } @objc private func keyboardWillHide(_: Notification) { // 键盘收起时恢复滚动位置 tableView.setContentOffset(CGPoint(x: 0, y: 0), animated: true) } } // 给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 } }
注意事项
- 方案一更适合你的场景,因为容器固定在第二个单元格,逻辑更直接,不会有额外的判断
- 两种方案都要注意用
weak self避免内存泄漏 - 滚动的缓冲间距(代码里的20)可以根据你的UI设计调整,让文本框和键盘之间有足够的留白
内容的提问来源于stack exchange,提问作者Roberto Sampaio
相关产品推荐
相关产品推荐

