UICollectionView单元格仅在第二次调用reloadData

时间:2017-03-19 20:04:57

标签: ios swift uicollectionview

我可能做了一件非常愚蠢的事,但我无法弄清楚我的生活是怎样的......

我试图做的很简单。我有UICollectionView依赖于从服务器加载的数据。从服务器获取并设置数据后,我在集合视图上调用reloadData()。当我执行此操作时,会使用正确的计数调用numberOfItemsInSection,但cellForItemAt永远不会被调用,除非我再次调用reloadData()(甚至不需要延迟)。

这是一般性的想法(虽然我意识到它可能与其他地方的某些竞争有关,但也许这足以启动对话):

class MyViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {

    @IBOutlet weak var collectionView: UICollectionView!

    var data = [DataModel]()

    override func viewDidLoad() {
        collectionView.delegate = self
        collectionView.dataSource = self
        collectionView.register(UINib(nibName: "MyCell", bundle: nil), forCellWithReuseIdentifier: "MyCell")
        getData()
    }

    func getData() {
        someAsyncMethodToGetData() { (data) in
            // Using CloudKit so I think this is necessary, but tried it with and without
            DispatchQueue.main.async {
                self.data = data
                self.collectionView.reloadData()
                // This is the only way to get cells to render and for cellForItemAt to be called after data is loaded
                self.collectionView.reloadData()
            }
        }

    // Gets called initially AND after reloadData is called in the data completion closure. First time is 0, next time is number of DataModel objects
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return data.count
    }

    // Only gets called initially (unless I call reloadData() twice)
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        guard let cell = feedCollectionView.dequeueReusableCell(withReuseIdentifier: "MyCell", for: indexPath) as? MyCell else {
            return UICollectionViewCell()
        }

        // Configure the cell with data...

        return cell
    }
}

所以这里有一些我已经尝试过的事情:

  • 在主线程上调用双重确认reloadData()(添加符号断点并调用Thread.isMainThread进行验证)
  • reloadData()移至按钮操作以消除任何线程问题的可能性 - 仍然必须单击按钮两次才能显示单元格
  • 根据我遇到的其他一些SO解决方案将更新添加到performBatchUpdates
  • 添加了setNeedsLayoutsetNeedsDisplay(不知道为什么在这种情况下需要这样做,但我确实是出于绝望而尝试。

有什么建议吗?

更新1

好的,我想我发现了这个问题,但我想了解最好的解决方法。通过调用reloadData显示,集合视图将仅重新加载已经可见的单元格。我在上面修改了一些代码,在调用getData()之前返回1个单元格。当getData()然后调用reloadData()时,1个单元格会更新(但如果数据数组中有10个项目,它仍然只会[重新]显示1个单元格)。我认为这是有道理的,因为reloadData()仅重新加载可见单元格,但当数组的长度发生变化并且屏幕上仍有空间显示时,这必然是一个常见问题。另外,为什么调用reloadData()两次解决问题并显示从未见过的单元格?

Here是我上面代码的更简单版本,从服务器中删除整个"获取数据"想法并以简单的延迟取代。

2 个答案:

答案 0 :(得分:10)

事情是在更改项目数后,UICollectionViewFlowLayout未正确无效。所以collectionView.contentSize仍旧。你需要做一些像

这样的事情
self.collectionView.reloadData()
self.collectionView.collectionViewLayout.invalidateLayout()

这将告诉布局重新计算新数据源。

答案 1 :(得分:1)

首先请确保委托和数据源是否正确添加到collectionView。

第二次确认数据是填充断点还是简单打印

第三添加:

    DispatchQueque.main.async{
     self.collection.reloadData()
     self.collection.collectionViewLayout.invalidateLayout()
    }