【问题标题】:UICollectionView deselectItem when cell pre-fetching is enabled启用单元格预取时 UICollectionView 取消选择项
【发布时间】:2017-03-04 17:55:31
【问题描述】:

在 iOS 10.0 上,UICollectionView 默认预取单元格。这导致准备在屏幕上显示但被隐藏的单元格。这个question 描述得非常好。

以下代码将在其单元格可见或根本不存在时成功取消选择索引路径。如果单元格存在且被隐藏,则索引路径将被取消选中,但单元格会一直停留在选中状态,直到被重用。

collectionView!.deselectItem(at: indexPath, animated: false)

在 iOS 9 上或在 iOS 10.0 上使用 isPrefetchingEnabled = false 禁用预取时,此问题不会出现。

这是 UICollectionView 中的错误还是我误解了 deselectItem 应该如何工作?


这是 UICollectionViewController 子类的完整代码,它通过以下步骤演示了此行为:

  • 点击一个单元格,使其被选中(红色)
  • 将单元格滚动到屏幕外
  • 点击“取消选择单元格”按钮
  • 在屏幕上滚动单元格
  • 观察它看起来仍然被选中
  • 点击另一个单元格
  • 观察两个单元格看起来如何被选中
  • 将第一个单元格滚动到屏幕外并再次返回
  • 观察第一个单元格最终没有被选中


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 swift3 xcode8 ios10


    【解决方案1】:

    据我所知,这是UICollectionView 中的一个错误,我自己打开了一个雷达。您可能也想这样做以施加更大的压力。

    即使在预取之前,集合视图也不会取消选择不可见的单元格。甚至cellForItemAtIndexPath: 声明它为不可见的单元格返回nil

    但在预取之前,一旦一个单元格离开内容视图,它就会被添加到重用池中,当您向后滚动时,您会得到一个重用单元格,它的选定状态已重置。

    现在,该单元格保持加载状态并且不会重置,直到它被重新使用,通过滚动更远,超出预取区域。

    要解决此问题,您可以将 prefetchingEnabled 设置为 NO 并失去它的好处,或者在单元格出现时更新选择状态 -

    - (void)collectionView:(UICollectionView *)collectionView
           willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath {
      if (!cell.isSelected) {
        return;
      }
    
      if ([[collectionView indexPathsForSelectedItems] containsObject:indexPath]) {
        return;
      }
    
      cell.selected = NO;
    }
    

    这似乎并没有降低性能。

    【讨论】:

    • 此代码将解决单元格在不应该被选中时显示为选中状态的问题,但请注意还有另一种可能性,即在应该选中单元格时显示为取消选中。
    • @user3246268 在这种特殊情况下,这不是问题。问题是预取区域中的单元格 - 但被选中的单元格将保持选中状态,因为系统不会为这些单元格调用 prepareForReuse。出于所有目的,它们是仍然可见的单元格,除了如果您在不可见时以编程方式取消选择单元格,则不会取消选择这些单元格 - 在这种情况下,系统错误地认为没有必要这样做,因为它们会在出现之前无论如何都要preparedForReuse
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多