【问题标题】:cellForItemAt not being called after collectionView.deleteItems在collectionView.deleteItems 之后没有调用cellForItemAt
【发布时间】:2021-08-30 19:21:55
【问题描述】:

我已成功从我的 collectionView 和数据源中删除项目,但在删除后没有调用 cellForItemAt,因此我的索引变得混乱。即使我在删除后立即手动调用collectionView.reloadData(),它也不起作用。

但是,如果我在collectionView.deleteItems(at: [indexPath]) 之前的任何时间调用collectionView.reloadData(),它就会被调用,但显然对我没有任何好处。不知道我做错了什么,请看一下:

class ChipView: UIView {        
    var chips: [ChipModel]?

    lazy var collectionView: UICollectionView = {
         let flowLayout = LeftAlignedCollectionViewFlowLayout()
         flowLayout.scrollDirection = .vertical

         let result = UICollectionView(frame: frame, collectionViewLayout: flowLayout)
         result.translatesAutoresizingMaskIntoConstraints = false
         result.register(Chip.self, forCellWithReuseIdentifier: "Cell")
         result.delegate = self
         result.dataSource = self
         result.backgroundColor = .clear
         result.isAccessibilityElement = false
         return result
     }()

    func deleteItem(_ sender: Chip) {
        guard let chipsArray = chips, !chipsArray.isEmpty, let indexPath = sender.indexPath else { return }

        // 1. remove value from source of truth
        chips?.remove(at: indexPath.row)
        // 2. delete item from collection view
        collectionView.deleteItems(at: [indexPath])
        // TODO: - Figure out why cellForItemAt isn't getting called after collectionView.deleteItems... Needs to update the indexPath
        collectionView.reloadData()
     }

extension ChipView: UICollectionViewDataSource {
     public func collectionView(_: UICollectionView, numberOfItemsInSection _: Int) -> Int {
         return chips?.count ?? 0
}

     public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
         let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! Chip
         cell.indexPath = indexPath

         if let chip = chips?[indexPath.row] {
            cell.setupChip(title: chip.title, icon: chip.icon, chipState: chip.chipState)
         }

         return cell
     }
  }

【问题讨论】:

  • 您的错误是将索引路径分配给单元格。不要那样做。

标签: ios swift uicollectionview reloaddata


【解决方案1】:

为什么我们首先需要 cell.indexPath?

我假设您稍后在单元格内的某个按钮点击上使用 indexPath 值以从 dataSource chips?[indexPath.row] 获取模型值。

如果答案是肯定的,那么我可以想到一种避免这种情况的方法就这么简单。

public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! TruChip
    
    /* Stop doing this
    cell.indexPath = indexPath
    */

    if let chip = chips?[indexPath.row] {
        /* Stop doing this
        cell.setupTruChip(title: chip.title, icon: chip.icon, chipState: chip.chipState)
        */
        cell.setupTruChip(chip)
    }

    return cell
}

/// Inside the cell
class TruChip: UICollectionViewCell {
    var currentChip: TruChipModel?

    func setupTruChip(_ chip: TruChipModel) {
        currentChip = chip
        self.setupTruChip(title: chip.title, icon: chip.icon, chipState: chip.chipState)
    }
}

使用这种方法,您可以完全消除在单元格上记住indexPath 的需要。单元格会记住它在 UI 上表示的当前模型数据。

【讨论】:

  • 谢谢,当我继承这个项目时,我知道 IndexPath 看起来有些不对劲,但它就是这样......我必须进行相当多的重构才能做出这个改变但是一旦我确认它有效,我就会回来。
  • 我重构并从单元格中删除了 indexPath。但是,原来的问题仍然存在,所以我仍在使用 collectionView.performBatchUpdates,它仍然适用于我的用例。
  • 你必须这样做。 为什么?只有 一个 操作(签名暗示更新s,复数),这不会调用itemForRowperformBatchUpdates 用于多个同时 insert/deletemove 操作
【解决方案2】:

在删除之前为我添加批量更新:

    collectionView.performBatchUpdates {
        collectionView.deleteItems(at: [indexPath])
    } completion: { (finished) in
        self.collectionView.reloadData()
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多