CollectionViewCell 中来自标签的数据有时会在重新加载时刷新,而有时则不会
Data From Label in CollectionViewCell Sometimes Refreshes on Reload other times it Doesn't
首先让我说这似乎是 SO 上的一个常见问题,我已经通读了从 Swift 到 Obj-C 的所有 post。我在过去 9 小时内尝试了很多不同的方法,但我的问题仍然存在。
我有一个 vc (vc1),里面有一个 collectionView。在 collectionView 中,我有一个自定义单元格,里面有一个标签和一个 imageView。在 cellForItem 内部,我有一个 属性,它也在自定义单元格内,当从 datasource[indePath.item]
设置 属性 时,单元格内有一个 属性 观察器,用于设置数据标签和 imageView。
vc1 中有一个按钮可以按下 vc2,如果用户从 vc2 中选择某项,它会通过以下方式传回 vc1一个代表。 vc2 被弹出。
总是传回正确的数据(我在调试器中检查了多次)。
问题是如果 vc1 中有一个现有的单元格,当新数据添加到数据源时,在我重新加载 collectionView 之后,第一个单元格的标签数据现在显示在新单元格中的标签,新单元格中的数据现在显示在旧单元格中的标签上。
我已经尝试了从 prepareToReuse 到删除标签的所有方法,但由于某种原因,只有单元格的标签数据变得混乱。奇怪的是 有时标签会正确更新,有时却不会? imageView 总是显示正确的图像,即使标签数据不正确,我也从来没有遇到过任何问题。数据源内的 2 个模型对象始终位于其正确的索引位置并具有正确的信息。
可能是什么问题?
vc1: UIViewController, CollectionViewDataSource & Delegate {
var datasource = [MyModel]() // has 1 item in it from viewDidLoad
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: customCell, for: indexPath) as! CustomCell
cell.priceLabel.text = ""
cell.cleanUpElements()
cell.myModel = dataSource[indexPath.item]
return cell
}
// delegate method from vc2
func appendNewDataFromVC2(myModel: MyModel) {
// show spinner
datasource.append(myModel) // now has 2 items in it
// now that new data is added I have to make a dip to fb for some additional information
firebaseRef.observeSingleEvent(of: .value, with: { (snapshot) in
if let dict = snapshot.value as? [String: Any] else { }
for myModel in self.datasource {
myModel.someValue = dict["someValue"] as? String
}
// I added the gcd timer just to give the loop time to finish just to see if it made a difference
DispatchQueue.main.asyncAfter(deadline: .now() + 2, execute: {
self.datasource.sort { return [=10=].postDate > .postDate } // Even though this sorts correctly I also tried commenting this out but no difference
self.collectionView.reloadData()
// I also tried to update the layout
self.collectionView.layoutIfNeeded()
// remove spinner
}
})
}
}
下面的自定义单元格。这是 myModel 属性 观察器内部的简化得多的版本。标签中显示的数据取决于其他数据,并且有一些条件决定它。将所有这些添加到 cellForItem 中会创建一堆代码,这就是为什么我没有在其中更新数据(或在此处添加)而是选择在单元格中进行更新的原因。但正如我之前所说,当我检查数据时,它总是 100% 正确。 属性 观察器始终正常工作。
CustomCell: UICollectionViewCell {
let imageView: UIImageView = {
let iv = UIImageView()
iv.translatesAutoresizingMaskIntoConstraints = false
return iv
}()
let priceLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
var someBoolProperty = false
var myModel: MyModel? {
didSet {
someBoolProperty = true
// I read an answer that said try to update the label on the main thread but no difference. I tried with and without the DispatchQueue
DispatchQueue.main.async { [weak self] in
self?.priceLabel.text = myModel.price!
self?.priceLabel.layoutIfNeeded() // tried with and without this
}
let url = URL(string: myModel.urlStr!)
imageView.sd_setImage(with: url!, placeholderImage: UIImage(named: "placeholder"))
// set imageView and priceLabel anchors
addSubview(imageView)
addSubview(priceLabel)
self.layoutIfNeeded() // tried with and without this
}
}
override func prepareForReuse() {
super.prepareForReuse()
// even though Apple recommends not to clean up ui elements in here, I still tried it to no success
priceLabel.text = ""
priceLabel.layoutIfNeeded() // tried with and without this
self.layoutIfNeeded() // tried with and without this
// I also tried removing the label with and without the 3 lines above
for view in self.subviews {
if view.isKind(of: UILabel.self) {
view.removeFromSuperview()
}
}
}
func cleanUpElements() {
priceLabel.text = ""
imageView.image = nil
}
}
我为我添加的所有地方添加了 1 个断点 priceLabel.text = ""
(总共 3 个),一旦 collectionView 重新加载,断点总是被命中 6 次(数据源中的 2 个对象为 3 次)。第一次在 prepareForReuse
,第 2 次在 cellForItem
,第 3 次在 cleanUpElements()
原来我不得不在单元格中重置 属性。即使单元格被重复使用并且 priceLabel.text 被清除,属性 仍然保持它的旧 bool
值。一旦我通过 cellForItem 重置它,问题就消失了。
10 小时,smh
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: customCell, for: indexPath) as! CustomCell
cell.someBoolProperty = false
cell.priceLabel.text = ""
cell.cleanUpElements()
cell.myModel = dataSource[indexPath.item]
return cell
}
首先让我说这似乎是 SO 上的一个常见问题,我已经通读了从 Swift 到 Obj-C 的所有 post。我在过去 9 小时内尝试了很多不同的方法,但我的问题仍然存在。
我有一个 vc (vc1),里面有一个 collectionView。在 collectionView 中,我有一个自定义单元格,里面有一个标签和一个 imageView。在 cellForItem 内部,我有一个 属性,它也在自定义单元格内,当从 datasource[indePath.item]
设置 属性 时,单元格内有一个 属性 观察器,用于设置数据标签和 imageView。
vc1 中有一个按钮可以按下 vc2,如果用户从 vc2 中选择某项,它会通过以下方式传回 vc1一个代表。 vc2 被弹出。
总是传回正确的数据(我在调试器中检查了多次)。
问题是如果 vc1 中有一个现有的单元格,当新数据添加到数据源时,在我重新加载 collectionView 之后,第一个单元格的标签数据现在显示在新单元格中的标签,新单元格中的数据现在显示在旧单元格中的标签上。
我已经尝试了从 prepareToReuse 到删除标签的所有方法,但由于某种原因,只有单元格的标签数据变得混乱。奇怪的是 有时标签会正确更新,有时却不会? imageView 总是显示正确的图像,即使标签数据不正确,我也从来没有遇到过任何问题。数据源内的 2 个模型对象始终位于其正确的索引位置并具有正确的信息。
可能是什么问题?
vc1: UIViewController, CollectionViewDataSource & Delegate {
var datasource = [MyModel]() // has 1 item in it from viewDidLoad
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: customCell, for: indexPath) as! CustomCell
cell.priceLabel.text = ""
cell.cleanUpElements()
cell.myModel = dataSource[indexPath.item]
return cell
}
// delegate method from vc2
func appendNewDataFromVC2(myModel: MyModel) {
// show spinner
datasource.append(myModel) // now has 2 items in it
// now that new data is added I have to make a dip to fb for some additional information
firebaseRef.observeSingleEvent(of: .value, with: { (snapshot) in
if let dict = snapshot.value as? [String: Any] else { }
for myModel in self.datasource {
myModel.someValue = dict["someValue"] as? String
}
// I added the gcd timer just to give the loop time to finish just to see if it made a difference
DispatchQueue.main.asyncAfter(deadline: .now() + 2, execute: {
self.datasource.sort { return [=10=].postDate > .postDate } // Even though this sorts correctly I also tried commenting this out but no difference
self.collectionView.reloadData()
// I also tried to update the layout
self.collectionView.layoutIfNeeded()
// remove spinner
}
})
}
}
下面的自定义单元格。这是 myModel 属性 观察器内部的简化得多的版本。标签中显示的数据取决于其他数据,并且有一些条件决定它。将所有这些添加到 cellForItem 中会创建一堆代码,这就是为什么我没有在其中更新数据(或在此处添加)而是选择在单元格中进行更新的原因。但正如我之前所说,当我检查数据时,它总是 100% 正确。 属性 观察器始终正常工作。
CustomCell: UICollectionViewCell {
let imageView: UIImageView = {
let iv = UIImageView()
iv.translatesAutoresizingMaskIntoConstraints = false
return iv
}()
let priceLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
var someBoolProperty = false
var myModel: MyModel? {
didSet {
someBoolProperty = true
// I read an answer that said try to update the label on the main thread but no difference. I tried with and without the DispatchQueue
DispatchQueue.main.async { [weak self] in
self?.priceLabel.text = myModel.price!
self?.priceLabel.layoutIfNeeded() // tried with and without this
}
let url = URL(string: myModel.urlStr!)
imageView.sd_setImage(with: url!, placeholderImage: UIImage(named: "placeholder"))
// set imageView and priceLabel anchors
addSubview(imageView)
addSubview(priceLabel)
self.layoutIfNeeded() // tried with and without this
}
}
override func prepareForReuse() {
super.prepareForReuse()
// even though Apple recommends not to clean up ui elements in here, I still tried it to no success
priceLabel.text = ""
priceLabel.layoutIfNeeded() // tried with and without this
self.layoutIfNeeded() // tried with and without this
// I also tried removing the label with and without the 3 lines above
for view in self.subviews {
if view.isKind(of: UILabel.self) {
view.removeFromSuperview()
}
}
}
func cleanUpElements() {
priceLabel.text = ""
imageView.image = nil
}
}
我为我添加的所有地方添加了 1 个断点 priceLabel.text = ""
(总共 3 个),一旦 collectionView 重新加载,断点总是被命中 6 次(数据源中的 2 个对象为 3 次)。第一次在 prepareForReuse
,第 2 次在 cellForItem
,第 3 次在 cleanUpElements()
原来我不得不在单元格中重置 属性。即使单元格被重复使用并且 priceLabel.text 被清除,属性 仍然保持它的旧 bool
值。一旦我通过 cellForItem 重置它,问题就消失了。
10 小时,smh
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: customCell, for: indexPath) as! CustomCell
cell.someBoolProperty = false
cell.priceLabel.text = ""
cell.cleanUpElements()
cell.myModel = dataSource[indexPath.item]
return cell
}