【发布时间】:2017-03-19 20:04:57
【问题描述】:
我可能正在做一些非常愚蠢的事情,但我一生都无法弄清楚发生了什么......
我想要做的很简单。我有一个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中 - 添加了
setNeedsLayout和setNeedsDisplay(不知道为什么在这种情况下需要这样做,但出于绝望我确实尝试了。
有什么建议吗? ????
更新 1
好的,我想我找到了问题,但我想了解解决问题的最佳方法。它通过调用reloadData 出现,集合视图只会重新加载已经可见的单元格。在调用 getData() 之前,我稍微修改了上面的代码以返回 1 个单元格。当getData() 然后调用reloadData() 时,1 个单元格得到更新(但如果数据数组中有 10 个项目,它仍然只会 [重新] 显示 1 个单元格)。我想这是有道理的,因为reloadData() 只重新加载可见单元格,但是当数组的长度发生变化并且屏幕上仍有显示空间时,这必须是一个常见问题。另外,为什么调用reloadData() 两次可以解决问题并显示从不可见的单元格? ????
Here 是我上面的代码的一个更简单的版本,删除了整个“从服务器获取数据”的想法,并用一个简单的延迟代替。
【问题讨论】:
-
仅仅因为代码在主线程上执行,并不意味着你的代码在主队列上执行。请使用
Dispatch.main.async {}来确定。你在哪里打电话给getData? -
我明白了。我在调用完成闭包之前在我的数据层中调用
Dispatch.main.async {},但我也将它放在闭包中以确保它。我打电话给getDataviewDidLoad()
标签: ios swift uicollectionview