编辑:经过进一步测试,这并不是在 iOS 15.2 上执行更新,而是在 iOS 13.0 和 14.0 上执行更新,因此这似乎是一个 iOS bug!
我正在尝试将批量更新应用于
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
单元格不会更新。使用 updateUsingInsertAndDelete
函数会重新加载所有单元格,但没有像使用 updateUsingReload
时那样的删除动画。
我在完整项目中有这个示例,位于 https://github.com/JosephDuffy/CollectionViewTesting。
您无法使用 reloadItems (或 reconfigureItems)来重新加载已移动的项目。似乎 reloadItems 假设有问题的项目在更新期间保持其位置。看起来无法同时设置动画和更新项目。
您可以在移动项目后更新项目,也可以尝试使用
collectionView.cellForItem(at:)
实现自定义单元格动画