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

发布于 2025-02-08 01:05:47 字数 5546 浏览 2 评论 0原文

编辑:进一步测试后,这不是在iOS 15.2上执行更新,而是在iOS 13.0和14.0上进行更新,因此这似乎是iOS错误!

我正在尝试将批处理更新应用于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单元格不会更新。使用UpdateSertInSertAndDelete函数的功能确实重新加载了所有单元格,但是没有删除动画,例如使用Update> UpdateRusingReload

我在一个完整的项目中有一个示例,网址为 https://github.com/josephub.com/josephduffy/collectionviewTeviewTestEttestingTestinging

Edit: Upon further testing this is not performing the update on iOS 15.2, but does update on iOS 13.0 and 14.0, so this appears to be an iOS bug!

I'm trying to apply batch updates to a UICollectionView but I'm finding that for every N items in a section that are deleted N items at the end of the section will ignore any reloads.

I have an example 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),
            ])
        })
    }
}

When I tap a cell (to trigger the updates) the 0, 6 cell does not update. Using the updateUsingInsertAndDelete function instead does reload all the cells, but there's no delete animation like there is when using updateUsingReload.

I have this example in a full project at https://github.com/JosephDuffy/CollectionViewTesting.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

慢慢从新开始 2025-02-15 01:05:47

您不能将重新启动(或重新配置)用于移动的重新加载项目。好像ReloAditems认为所讨论的项目在更新过程中保持其位置。看来没有办法同时进行动画和更新项目。

您可以在移动后更新项目,也可以尝试使用CollectionView.cellforitem(at:)来实现自定义单元动画

You can't use reloadItems (or reconfigureItems) to reload items which was moved. Seems like reloadItems assume the items in question are keeping their positions during update. It looks like there is no way to animate and update item at the same time.

You can either update item after it was moved or try to implement custom cell animation using collectionView.cellForItem(at:)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文