You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

UICollectionView输入UITextView时如何避免滚动重置并定位光标位置

解决方案

核心问题出在你调用了全局的布局失效方法,会触发整个UICollectionView重新计算所有单元格布局,导致原有滚动偏移被重置。按以下步骤修改即可同时满足两个需求:


核心修改步骤

1. 替换全局布局失效为单单元格失效

不要直接调用无参数的invalidateLayout(),而是创建自定义失效上下文,仅指定需要刷新的当前单元格,避免全局布局重算:

func textViewDidChange(_ checklistCell: ChecklistCell) {
    // 先同步数据、获取当前单元格索引
    guard let indexPath = collectionView.indexPath(for: checklistCell),
          let textView = checklistCell.textView else { return }
    let item = indexPath.item
    self.checklists[item].text = textView.text

    // 仅失效当前变化的单元格,不触动其他单元格布局缓存
    let invalidContext = UICollectionViewLayoutInvalidationContext()
    invalidContext.invalidateItems(at: [indexPath])
    collectionView.collectionViewLayout.invalidateLayout(with: invalidContext)

    // 布局更新后滚动到光标位置
    DispatchQueue.main.async { [weak self] in
        guard let self = self else { return }
        // 计算光标在collectionView坐标系中的位置
        if let cursorRange = textView.selectedTextRange {
            let cursorRect = textView.convert(textView.caretRect(for: cursorRange.start), to: self.collectionView)
            // 滚动到光标可见,添加少量上下内边距避免贴边
            self.collectionView.scrollRectToVisible(cursorRect.insetBy(dx: 0, dy: -2), animated: false)
        }
    }
}

2. 确认基础配置无误

提前检查以下配置,避免底层逻辑出错:

  • UITextView的isScrollEnabled必须设为false
  • UICollectionViewCompositionalLayout的组高度要设置为.estimated(44),不要用固定高度
  • 单元格内UITextView的上下左右四条边必须完整约束到单元格的contentView,保证单元格高度能被文本内容撑起来

原理解释

全局调用invalidateLayout()时,UICollectionView会清空所有布局缓存,重新计算所有单元格的尺寸和位置,原有的滚动偏移是基于旧的估算高度计算的,重算后就会出现偏移跳变。仅失效单个单元格时,其他单元格的布局缓存会保留,仅重算当前单元格的高度,不会触发全局滚动偏移调整,自然就不会出现滚动位置重置的问题。
该方案和Diffable Data Source没有冲突,不需要调用performBatchUpdates或者重新应用快照,性能开销更低。


内容的提问来源于stack exchange,提问作者Cheok Yan Cheng

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 21:18:05