我有一个使用标准UICollectionViewFlowLayout
的UICollectionView
,estimatedItemSize
设置为 UICollectionViewFlowLayoutAutomaticSize
.
一旦我调用collectionView.reloadData()
视图的滚动位置将重置为CGPoint(x: 0, y: 0)
。我知道.reloadData()
只能用作最后的手段,但在我的场景中是必要的。到目前为止,我发现这可能是由于没有通过collectionView(_:layout:sizeForItemAt:)
返回项目大小引起的。(任何类似于UITableView
tableView:estimatedHeightForRowAtIndexPath:
的解决方案,如这个StackOverflow答案,将不胜感激)。
情况下,我修复了它:在这里,我检测我的单元格是否完全可见,如果不是这样 - 移动到它。(没有"跳跃")
let cellRect = collectionView.convert(cell.frame, to: collectionView.superview)
if !collectionView.frame.contains(cellRect) {
collectionView.selectItem(at: indexPath, animated: true, scrollPosition: .centeredHorizontally)
return collectionView.reloadData()
}
在这里,我持有我的内容偏移量:
let currentContentOffSet = collectionView.contentOffset
if #available(iOS 12.0, *) {
UIView.animate(withDuration: 0) {
collectionView.reloadData()
collectionView.performBatchUpdates(nil, completion: { _ in
collectionView.contentOffset = currentContentOffSet
})
}
} else {
UIView.animate(withDuration: 0) {
collectionView.reloadItems(at: [self.lastSelectedIndexPath, indexPath])
collectionView.contentOffset = currentContentOffSet
}
}
如果单元格最近变得可见(或已经可见),并且我点击单元格,它有助于移动到单元格。也没有"跳转"(不重置内容偏移)
您也可以在collectionViewController中尝试此操作:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
guard let cell = collectionView.cellForItem(at: indexPath) else { return }
if lastSelectedIndexPath != indexPath {
let cellRect = collectionView.convert(cell.frame, to: collectionView.superview)
if !collectionView.frame.contains(cellRect) {
collectionView.selectItem(at: indexPath, animated: true, scrollPosition: .centeredHorizontally)
} else {
collectionView.selectItem(at: indexPath, animated: true, scrollPosition: .init())
}
collectionView.deselectItem(at: lastSelectedIndexPath, animated: false)
lastSelectedIndexPath = indexPath
}
}
这个在集合视图细胞模型:
override var isSelected: Bool {
didSet {
switch self.isSelected {
case false:
case true:
}
}
}
我遇到了同样的问题,并通过子类化集合视图、覆盖重新加载数据以临时存储当前内容偏移量来解决它,然后侦听对 contentOffset 的更改,如果我的临时偏移量变量中存储了任何内容,请将其更改回来
class MyCollectionView : UICollectionView {
deinit {
removeObserver(self, forKeyPath: #keyPath(UIScrollView.contentOffset))
}
override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
super.init(frame: frame, collectionViewLayout: layout)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
private func setup() {
self.addObserver(self, forKeyPath: #keyPath(UIScrollView.contentOffset), options: [.old, .new], context: nil)
}
private var temporaryOffsetOverride : CGPoint?
override func reloadData() {
if let flowLayout = collectionViewLayout as? UICollectionViewFlowLayout, flowLayout.estimatedItemSize == UICollectionViewFlowLayout.automaticSize {
temporaryOffsetOverride = contentOffset
}
super.reloadData()
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == #keyPath(UIScrollView.contentOffset) {
if let offset = temporaryOffsetOverride {
temporaryOffsetOverride = nil
self.setContentOffset(offset, animated: false)
}
}
}
}
UICollectionView 的重新加载部分对我有用。
UIView.performWithoutAnimation {self.collStages.reloadSections([0])}
collStages.scrollToItem(at: IndexPath(row: index, section: 0), at: .centeredHorizontally, animated: true)
reloadData
重新加载UICollectionView
中的所有单元格,因此如果您对单元格有任何引用,它将指向不存在的对象,更重要的是,如果它是强引用,它可能会导致保留周期。所以:
你不应该这样做!
但。。。
很难想象需要reloadData
并且无法通过插入和删除对象来替换的情况。
因此,您可能想要使用的是重新加载单行或插入它在数据模型中更改的项目。您没有发布您的问题的详细信息,但您可以执行前面提到的任何操作,例如:
collectionView.performBatchUpdates({
collectionView.insertItems(at: <#T##[IndexPath]#>)
collectionView.deleteItems(at: <#T##[IndexPath]#>)
collectionView.reloadItems(at: <#T##[IndexPath]#>)
}, completion: { (<#Bool#>) in
<#code#>
})
但请确保先编辑数据模型。所以:
var dataModel: [String] = ["0","1","2"] //Data model for Collection View
collectionView.reloadData() // In that point you will have 3 cells visible on the sceen
.
.
.
dataModel.removeFirst() // now your data model is ["1","2"]
collectionView.performBatchUpdates({
collectionView.deleteItems(at: [IndexPath(row: 0, section:0)])
}, completion: { success in
//some check consistency code
})
这样,您的集合视图将保持不变,除了第一行之外,不会进行任何其他更改。
不确定它是否有效,但您可以尝试一下:
let currentOffset = self.collectionView.contentOffset
self.collectionView.reloadData()
self.collectionView.setContentOffset(currentOffset, animated: false)
我知道吗。谢谢:)