UITableView中可编辑UITextView问题:末尾空行无法触发滚动
解决UITableViewCell中空TextView行无法触发滚动的问题
我之前也碰到过一模一样的场景,这个问题的核心是添加空行时,单元格高度更新和滚动操作的时机不匹配——你调用scrollToCursor的时候,单元格的新高度还没完全生效,导致滚动逻辑基于旧的布局计算,自然不会触发预期的滚动行为。
下面是具体的修复方案,分步骤来解决:
1. 修正TextView的高度计算准确性
默认的UITextView带有内置内边距(textContainerInset和lineFragmentPadding),这会导致空行时intrinsicContentSize的高度计算出现偏差。在awakeFromNib里添加以下代码,确保高度计算精准:
override func awakeFromNib() { super.awakeFromNib() // 移除默认内边距,避免空行高度计算误差 textView.textContainerInset = UIEdgeInsets.zero textView.textContainer.lineFragmentPadding = 0 textViewHeight = textView.intrinsicContentSize.height textView.becomeFirstResponder() }
2. 让滚动操作在单元格高度更新完成后执行
tableView.beginUpdates()和tableView.endUpdates()的布局更新是异步的,直接在之后调用scrollToCursor会拿到旧的布局信息。我们可以借助CATransaction的完成回调,确保滚动在布局更新彻底完成后触发:
修改selfUpdate方法,并调整textViewDidChangeSelection的逻辑:
func textViewDidChangeSelection(_ textView: UITextView) { guard let tableView = tableView else { return } selfUpdate(in: tableView) // 这里不再直接调用scrollToCursor,交给selfUpdate的完成回调处理 } func selfUpdate(in tableView: UITableView) { let newHeight = textView.intrinsicContentSize.height guard textViewHeight != newHeight else { return } textViewHeight = newHeight UIView.setAnimationsEnabled(false) CATransaction.begin() // 布局更新完成后再执行滚动逻辑 CATransaction.setCompletionBlock { [weak self] in self?.scrollToCursor() } tableView.beginUpdates() tableView.endUpdates() CATransaction.commit() UIView.setAnimationsEnabled(true) }
额外优化:强制TextView重新计算高度(可选)
如果偶尔还是出现空行高度不更新的情况,可以在textViewDidChange方法里(别忘了给textView设置delegate)强制让TextView重新计算内在高度:
func textViewDidChange(_ textView: UITextView) { textView.invalidateIntrinsicContentSize() }
这样修改后,无论是添加空行还是输入内容,UITableView都会准确滚动到光标位置了。
内容的提问来源于stack exchange,提问作者Alexey Chekanov
相关产品推荐
相关产品推荐

