问题描述
在iOS 10.0上,UICollectionView默认情况下会预提取单元格.这导致准备在屏幕上显示但已隐藏的单元格.问题很好地描述了它.
On iOS 10.0, UICollectionView pre-fetches cells by default. This leads to cells that are prepared for being shown on screen, but are hidden. This question describes it really well.
以下代码将在其单元格可见或根本不存在时成功取消选择索引路径.如果该单元格存在并被隐藏,则将取消选择索引路径,但是该单元格将停留在选定状态,直到重新使用它.
The following code will successfully deselect an index path when its cell is either visible or does not exist at all. If the cell exists and is hidden, the index path will be deselected, but the cell becomes stuck in the selected state until it is reused.
collectionView!.deselectItem(at: indexPath, animated: false)
在iOS 9上,或者在iOS 10.0上使用 isPrefetchingEnabled = false
禁用预取时,不会出现此问题.
This problem does not exits on iOS 9 or when pre-fetching is disabled with isPrefetchingEnabled = false
on iOS 10.0.
这是UICollectionView中的错误还是我误解了deselectItem应该如何工作?
Is this a bug in UICollectionView or am I misunderstanding how deselectItem is supposed to work?
这是UICollectionViewController子类的完整代码,它通过以下步骤演示了此行为:
Here is the full code of a UICollectionViewController subclass that demonstrates this behaviour with the following steps:
- 点击一个单元格,使其变为选中状态(红色)
- 在屏幕外略微滚动单元格
- 点击取消选择单元格"按钮
- 将单元格滚动回到屏幕上
- 观察它看起来仍然处于选中状态
- 点击另一个单元格
- 观察两个单元格的外观
- 将第一个单元格滚动到屏幕外,然后再次返回
- 观察第一个单元格最终看起来没有被选中
import UIKit
private let reuseIdentifier = "Cell"
class CollectionViewController: UICollectionViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.collectionView!.register(UICollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)
let button = UIButton(frame: CGRect(x: 10, y: 30, width: 360, height: 44))
button.backgroundColor = #colorLiteral(red: 0.9686274529, green: 0.78039217, blue: 0.3450980484, alpha: 1)
button.setTitleColor(#colorLiteral(red: 1, green: 1, blue: 1, alpha: 1), for: .normal)
button.setTitleColor(#colorLiteral(red: 0.05882352963, green: 0.180392161, blue: 0.2470588237, alpha: 1), for: .highlighted)
button.setTitle("Deselect Cell", for: .normal)
button.addTarget(self, action: #selector(CollectionViewController.buttonPress), for: .touchUpInside)
view.addSubview(button)
}
func buttonPress() {
for indexPath in collectionView!.indexPathsForSelectedItems ?? [IndexPath]() {
let cell = collectionView!.cellForItem(at: indexPath)
NSLog("Deselecting indexPath: %@, cell: %@", indexPath.description, cell?.frame.debugDescription ?? "not visible")
collectionView!.deselectItem(at: indexPath, animated: false)
}
}
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 300
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath)
cell.backgroundColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
cell.selectedBackgroundView = UIView(frame: cell.bounds)
cell.selectedBackgroundView!.backgroundColor = #colorLiteral(red: 0.9254902005, green: 0.2352941185, blue: 0.1019607857, alpha: 1)
return cell
}
}
推荐答案
据我所知,这是 UICollectionView
中的错误,我自己打开了Radar.您可能还想这样做,以施加更大的压力.
As far as I can tell this is a bug in UICollectionView
, and I've opened a Radar myself. You might want to do so as well to apply more pressure.
即使在预取之前,集合视图也不会取消选择不可见的单元格.甚至 cellForItemAtIndexPath:
都声明它为不可见的单元格返回 nil
.
Even before prefetching, collection view didn't bother deselecting cells that weren't visible. Even cellForItemAtIndexPath:
states that it returns nil
for cells that are not visible.
但是在预取之前,单元格离开内容视图后,立即将其添加到重用池中,当您向后滚动时,您会得到一个重设了其选定状态的重用单元格.
But before prefetching, as soon as a cell left the content view it was added to the reuse pool, and when you scrolled back you got a reused cell that had its selected state reset.
现在,该单元格将保持加载状态,直到重新使用为止(通过滚动到更远的距离,直到超出预取区域),该单元格都不会重置.
Now, that cell remains loaded and wont reset until it's reused, by scrolling further away, beyond the prefetching area.
要解决此问题,您可以将 prefetchingEnabled
设置为 NO
并失去其优势,或者在单元格出现时更新选择状态-
To fix this you can either set prefetchingEnabled
to NO
and lose its benefits, or update selection state whenever a cell appears -
- (void)collectionView:(UICollectionView *)collectionView
willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath {
if (!cell.isSelected) {
return;
}
if ([[collectionView indexPathsForSelectedItems] containsObject:indexPath]) {
return;
}
cell.selected = NO;
}
这似乎并没有降低性能.
This doesn't seem to diminish performance.
这篇关于启用单元格预取时的UICollectionView deselectItem的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!