在 UICollectionView 中执行 UITextView 输入时防止滚动位置重置

Prevent scroll position from resetting when perform UITextView typing in UICollectionView

目前,我们尝试在 UICollectionView 中放置多个 UITextView

为了保证UICollectionView的单元格高度,会根据UITextView的动态内容进行调整,这就是我们所做的。

但是,调用 collectionView.collectionViewLayout.invalidateLayout() 确实会带来副作用。

UICollectionView 的当前滚动位置将在调用 collectionView.collectionViewLayout.invalidateLayout() 后重置。

有谁知道我怎样才能

  1. 防止滚动位置自动重置?
  2. UICollectionView 将自动滚动到当前光标位置,以便用户可以看到当前键入的内容?

演示此问题的代码如下 - https://github.com/yccheok/checklist-demo

这是代码片段,说明了在输入过程中发生的事情

func textViewDidChange(_ checklistCell: ChecklistCell) {
    //
    // Critical code to ensure cell will resize based on cell content.
    //
    // (But comes with a side effect which will reset scroll position.)
    self.collectionView.collectionViewLayout.invalidateLayout()
    
    //
    // Ensure our checklists data structure in sync with UI state.
    //
    guard let indexPath = collectionView.indexPath(for: checklistCell) else { return }
    let item = indexPath.item
    let text = checklistCell.textView.text
    self.checklists[item].text = text
}

旁注

请注意,我们遇到的最接近的解决方案发布在 https://medium.com/@georgetsifrikas/embedding-uitextview-inside-uitableviewcell-9a28794daf01

UITableViewController中,在文本更改期间,作者正在使用

DispatchQueue.main.async {
    tableView?.beginUpdates()
    tableView?.endUpdates()
}

效果很好。但是,UICollectionView 的等效解决方案是什么?

我们无法尝试使用 self.collectionView.performBatchUpdates,因为我们的解决方案是围绕 Diffable Data Source 构建的。

我试过了

DispatchQueue.main.async {
    self.collectionView.collectionViewLayout.invalidateLayout()
}

这也没有解决问题。

除非您正在利用 UICollectionViewCompositionalLayout 的其他布局功能,否则我认为使用 table 视图会更好。

但是,如果您对 textViewDidChange(...) 函数进行这 2 处更改,您可能会得到想要的结果:

func textViewDidChange(_ checklistCell: ChecklistCell) {
    //
    // Critical code to ensure cell will resize based on cell content.
    //
    
    // 1. DO NOT call this here...
    //self.collectionView.collectionViewLayout.invalidateLayout()
    
    //
    // Ensure our checklists data structure in sync with UI state.
    //
    guard let indexPath = collectionView.indexPath(for: checklistCell) else { return }
    let item = indexPath.item
    let text = checklistCell.textView.text
    self.checklists[item].text = text
    
    // 2. update your diffable data source here
    applySnapshot(true)
}