UITableView滚动问题:指定行置顶需自动适配底部边界
解决UITableView无动画滚动时的底部空白问题
嘿,这个问题我之前也踩过坑!当你用scrollToRow指定.top位置且关闭动画时,UITableView不会自动处理「内容高度不足以填满屏幕」的情况,所以会强行把目标行置顶,留下底部空白;但开动画时系统会偷偷调整到合理位置(比如最后一行贴底)。要在无动画时实现同样的智能效果,我们可以手动判断后再决定滚动方式。
核心思路
先计算目标行到最后一行的总高度,和TableView的有效可视高度对比:
- 如果总高度小于可视高度:说明置顶目标行后底部会有空隙,直接滚动到最后一行贴底
- 如果总高度大于等于可视高度:正常将目标行置顶
代码实现
func scrollToTargetRowOrBottom(targetRow: Int, section: Int = 0) { let targetIndexPath = IndexPath(item: targetRow, section: section) guard targetRow < tableView.numberOfRows(inSection: section) else { return } // 计算TableView的有效可视高度(减去上下内边距) let effectiveVisibleHeight = tableView.bounds.height - tableView.contentInset.top - tableView.contentInset.bottom // 获取目标行和最后一行的frame guard let targetRowFrame = tableView.rectForRow(at: targetIndexPath), let lastRowIndexPath = tableView.indexPathForLastRow(), let lastRowFrame = tableView.rectForRow(at: lastRowIndexPath) else { return } // 计算从目标行顶部到最后一行底部的总高度 let totalContentHeightFromTarget = lastRowFrame.maxY - targetRowFrame.origin.y if totalContentHeightFromTarget < effectiveVisibleHeight { // 剩余内容不够填满屏幕,滚动到最后一行贴底 tableView.scrollToRow(at: lastRowIndexPath, at: .bottom, animated: false) } else { // 内容足够,将目标行置顶 tableView.scrollToRow(at: targetIndexPath, at: .top, animated: false) } } // 给UITableView加个扩展方便获取最后一行的IndexPath extension UITableView { func indexPathForLastRow() -> IndexPath? { guard numberOfSections > 0 else { return nil } let lastSection = numberOfSections - 1 guard numberOfRows(inSection: lastSection) > 0 else { return nil } let lastRow = numberOfRows(inSection: lastSection) - 1 return IndexPath(item: lastRow, section: lastSection) } }
使用方式
建议在viewDidAppear或者viewDidLayoutSubviews里调用(不要在viewDidLoad里,此时TableView的frame可能还没确定):
override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) scrollToTargetRowOrBottom(targetRow: 18) }
这样就能完美实现你想要的效果:当目标行可以自然置顶时就置顶,否则直接滚到最后一行贴紧底部,全程无动画,用户进入页面时就处于正确的滚动位置。
内容的提问来源于stack exchange,提问作者Sti
相关产品推荐
相关产品推荐

