从集合视图部分删除 N 个项目可以防止在同一批次中重新加载最后 N 个项目

问题描述 投票:0回答:1

编辑:经过进一步测试,这并不是在 iOS 15.2 上执行更新,而是在 iOS 13.0 和 14.0 上执行更新,因此这似乎是一个 iOS bug!

我正在尝试将批量更新应用于

UICollectionView
,但我发现对于部分中已删除的
N
项目中的每个
N
项目,该部分末尾的项目将忽略任何重新加载。

我有一个例子

UICollectionView

import UIKit

final class CollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
    var data: [[String]] = []

    var requestedIndexPaths: [IndexPath] = []

    override func viewDidLoad() {
        super.viewDidLoad()

        collectionView.backgroundColor = .systemBackground
        collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "UICollectionViewCell")

        applyInitialData(
            [
                ["0, 0", "0, 1", "0, 2", "0, 3", "0, 4", "0, 5", "0, 6", "0, 7"],
            ]
        )

        let refreshControl = UIRefreshControl()
        refreshControl.addTarget(self, action: #selector(reloadAll), for: .valueChanged)
        collectionView.refreshControl = refreshControl
    }

    @objc
    private func reloadAll() {
        collectionView.reloadData()
        collectionView.refreshControl?.endRefreshing()
    }

    func applyInitialData(_ data: [[String]]) {
        self.data = data
        loadViewIfNeeded()
        collectionView.reloadData()
        collectionView.layoutIfNeeded()
        requestedIndexPaths = []
    }

    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        requestedIndexPaths.append(indexPath)
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "UICollectionViewCell", for: indexPath)
        cell.contentView.subviews.forEach { $0.removeFromSuperview() }
        let label = UILabel()
        label.text = data[indexPath.section][indexPath.row]
        cell.contentView.addSubview(label)
        label.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            label.topAnchor.constraint(equalTo: cell.topAnchor),
            label.leadingAnchor.constraint(equalTo: cell.leadingAnchor),
        ])
        return cell
    }

    override func numberOfSections(in collectionView: UICollectionView) -> Int {
        data.count
    }

    override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        data[section].count
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return CGSize(width: collectionView.frame.size.width, height: 40)
    }

    override func collectionView(_ collectionView: UICollectionView, didSelectItemAt: IndexPath) {
        updateUsingReload()
//        updateUsingInsertAndDelete()
    }

    /// With this method 0, 6 will not be updated until pull-to-refresh is used.
    private func updateUsingReload() {
        collectionView.performBatchUpdates({
            data[0][0] = "0, 0 (updated)"
            data[0][1] = "0, 1 (updated)"
            data[0][4] = "0, 4 (updated)"
            data[0][5] = "0, 5 (updated)"
            data[0][6] = "0, 6 (updated)"
            data[0].remove(at: 3)
            data[0].remove(at: 2)
            collectionView.deleteItems(at: [
                IndexPath(item: 2, section: 0),
                IndexPath(item: 3, section: 0),
            ])
            collectionView.reloadItems(at: [
                IndexPath(item: 0, section: 0),
                IndexPath(item: 1, section: 0),
                IndexPath(item: 4, section: 0),
                IndexPath(item: 5, section: 0),
                IndexPath(item: 6, section: 0),
            ])
        })
    }

    /// With this method all cells will be reloaded, but the animation is not as
    /// nice because the cells are not really removed.
    private func updateUsingInsertAndDelete() {
        collectionView.performBatchUpdates({
            data[0][0] = "0, 0 (updated)"
            data[0][1] = "0, 1 (updated)"
            data[0][4] = "0, 4 (updated)"
            data[0][5] = "0, 5 (updated)"
            data[0][6] = "0, 6 (updated)"
            data[0].remove(at: 3)
            data[0].remove(at: 2)
            collectionView.deleteItems(at: [
                IndexPath(item: 2, section: 0),
                IndexPath(item: 3, section: 0),
                IndexPath(item: 4, section: 0),
                IndexPath(item: 5, section: 0),
                IndexPath(item: 6, section: 0),
            ])
            collectionView.insertItems(at: [
                // 2 items have really been deleted, these item use the "after"
                // indexes and are 4, 5, and 6 prior to the update.
                IndexPath(item: 2, section: 0),
                IndexPath(item: 3, section: 0),
                IndexPath(item: 4, section: 0),
            ])
            collectionView.reloadItems(at: [
                IndexPath(item: 0, section: 0),
                IndexPath(item: 1, section: 0),
            ])
        })
    }
}

当我点击某个单元格(以触发更新)时,

0, 6
单元格不会更新。使用
updateUsingInsertAndDelete
函数会重新加载所有单元格,但没有像使用
updateUsingReload
时那样的删除动画。

我在完整项目中有这个示例,位于 https://github.com/JosephDuffy/CollectionViewTesting

ios uicollectionview uikit
1个回答
0
投票

您无法使用 reloadItems (或 reconfigureItems)来重新加载已移动的项目。似乎 reloadItems 假设有问题的项目在更新期间保持其位置。看起来无法同时设置动画和更新项目。

您可以在移动项目后更新项目,也可以尝试使用

collectionView.cellForItem(at:)

实现自定义单元格动画
© www.soinside.com 2019 - 2024. All rights reserved.