在 UICollectionView 中执行 UITextView 输入时防止滚动位置重置
Prevent scroll position from resetting when perform UITextView typing in UICollectionView
目前,我们尝试在 UICollectionView
中放置多个 UITextView
。
为了保证UICollectionView
的单元格高度,会根据UITextView
的动态内容进行调整,这就是我们所做的。
- 在
UITextView
中禁用滚动。
- 对
UICollectionViewCompositionalLayout
使用.estimated(CGFloat(44))
- 每当有文本更改时,调用
collectionView.collectionViewLayout.invalidateLayout()
。这是确保单元格高度相应调整的关键步骤。
但是,调用 collectionView.collectionViewLayout.invalidateLayout()
确实会带来副作用。
UICollectionView
的当前滚动位置将在调用 collectionView.collectionViewLayout.invalidateLayout()
后重置。
有谁知道我怎样才能
- 防止滚动位置自动重置?
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)
}
目前,我们尝试在 UICollectionView
中放置多个 UITextView
。
为了保证UICollectionView
的单元格高度,会根据UITextView
的动态内容进行调整,这就是我们所做的。
- 在
UITextView
中禁用滚动。 - 对
UICollectionViewCompositionalLayout
使用.estimated(CGFloat(44))
- 每当有文本更改时,调用
collectionView.collectionViewLayout.invalidateLayout()
。这是确保单元格高度相应调整的关键步骤。
但是,调用 collectionView.collectionViewLayout.invalidateLayout()
确实会带来副作用。
UICollectionView
的当前滚动位置将在调用 collectionView.collectionViewLayout.invalidateLayout()
后重置。
有谁知道我怎样才能
- 防止滚动位置自动重置?
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)
}