UICollectionView-快速后续更新导致“无效更新:第0部分中的项目数无效”

时间:2018-07-29 14:39:15

标签: swift uicollectionview realm

我正在实现一个基于客户端-服务器交互的核心功能的应用程序。为了不让用户等待数据(可能不会更改),我实现了一个“缓存”层作为Realm数据库,在该层中,每当发出请求时,它首先都会从Realm数据库并将其显示给用户。随后,它将请求发送到服务器以获取最新版本的数据并更新collectionView。问题出在collectionView完成之后,collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int)似乎异步地调用collectionView.reloadData()。但是,此调用已经命中了从服务器获取的更新数据(是的,用Go编写的非常快的API-虽然在本地运行),并且似乎在collectionView中不一致地存储了项目数。此外,当实际的服务器请求触发更新并尝试重新加载collectionview中的数据时,collectionview会引发异常:

NSInternalInconsistencyException', reason: 'Invalid update: invalid number of items in section 0.  The number of items contained in an existing section after the update (3) must be equal to the number of items contained in that section before the update (3), plus or minus the number of items inserted or deleted from that section (0 inserted, 1 deleted) and plus or minus the number of items moved into or out of that section (0 moved in, 0 moved out).

以下是与之相关的简短摘要(数据管理器):

token = data.observe { [weak self] (changes) in

            guard let strongSelf = self else { return }

            switch changes {
            case .initial(_):
                strongSelf.delegate?.reloadItemsAll()
                break
            case .update(_, let _deletions, let _insertions, let _modifications):
                print("Pre-update count \(data.count)")
                let transform = { (row: Int) -> IndexPath in
                    return IndexPath(row: row, section: 0)
                }

                let insertions = _insertions.map(transform)
                let deletions = _deletions.map(transform)
                let reloads = _modifications.map(transform)

                strongSelf.delegate?.reloadItems(insertions: insertions, reloads: reloads, deletions: deletions)

                break
            case .error:
                break
            }
        }

CollectionViewController:

 func reloadItemsAll() {
        DispatchQueue.main.async {
            print("Reload ALL started with: \(self.dataManager.getSectionItemCount())")
            self.collectionView?.reloadData()
            print("Reload ALL FINISHED")
        }
    }

func reloadItems(insertions: [IndexPath], reloads: [IndexPath], deletions: [IndexPath]) {
            DispatchQueue.main.async {
                print("About to start reload partial")
                self.collectionView?.performBatchUpdates({
                    print("Reload PARTIAL started with \(self.dataManager.getSectionItemCount())")
                    self.collectionView?.deleteItems(at: deletions)
                    self.collectionView?.insertItems(at: insertions)
                    print("Reload PARTIAL FINISHED")
                }, completion: nil)
                print("Reload partial finished")
            }
        }

override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        let count = dataManager.getSectionItemCount()
        print("Count: \(count)")
        return count
    }

一些有用的输出(请注意第7行的注释):

Count: 4
Initialized
Pre-update count 3
Reload ALL started with: 3
Reload ALL FINISHED
About to start reload partial
Count: 3 <— problem here - the collectionView asked for number of items even though the reloadData() was completely finished
Reload PARTIAL started with 3
Reload PARTIAL FINISHED
Count: 3

编辑: 我忘了提到这种情况并非总是会发生,可能是1/15次(异步)-重复删除/插入同一项目,因此数据集不会有太大变化

0 个答案:

没有答案