我有UICollectionView
使用标准UICollectionViewFlowLayout
且estimatedItemSize
设置为UICollectionViewFlowLayoutAutomaticSize
。
我致电collectionView.reloadData()
后,视图的滚动位置会重置为CGPoint(x: 0, y: 0)
。我知道.reloadData()
应该只作为最后的手段使用,但在我的场景中这是必要的。到目前为止我发现它可能是因为没有通过collectionView(_:layout:sizeForItemAt:)
返回项目大小。 (任何与UITableView
tableView:estimatedHeightForRowAtIndexPath:
in this StackOverflow answer类似的解决方案都会受到高度赞赏。
答案 0 :(得分:2)
我遇到了同样的问题,并通过对集合视图进行子类化,覆盖reloadData来临时存储当前contentOffset,然后侦听对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)
}
}
}
}
答案 1 :(得分:1)
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()
}
在这里,我保持我的contentOffset:
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
}
}
如果单元最近(或已经可见)可见并且我点击该单元,则有助于移动到单元。也没有“跳转”(无需重置contentOffset)
您还可以在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
}
}
,这是collectionViewCellModel中的:
override var isSelected: Bool {
didSet {
switch self.isSelected {
case false:
case true:
}
}
}
答案 2 :(得分:0)
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
})
由此,您的收集视图将保持不变,除第一行外不会进行任何其他更改。
答案 3 :(得分:0)
重新加载 UICollectionView 的部分对我有用。
UIView.performWithoutAnimation {self.collStages.reloadSections([0])}
collStages.scrollToItem(at: IndexPath(row: index, section: 0), at: .centeredHorizontally, animated: true)
答案 4 :(得分:-1)
不确定它是否可行,但您可以尝试一下:
let currentOffset = self.collectionView.contentOffset
self.collectionView.reloadData()
self.collectionView.setContentOffset(currentOffset, animated: false)
知道吗?谢谢:))