Reputation: 79
Upon creating a custom collection view layout reloadData does not work
Upon creating a custom collection view layout reloadData does not work after I did an infinite scroll which calls a new data from the api
I suspect that it is because of the fixed items called from the collectionview. How will I able to update it?
protocol CustomCollectionViewLayoutDelegate: class {
func collectionView(
collectionView: UICollectionView,
heightForItemAtIndexPath indexPath: IndexPath
) -> CGFloat
}
public class CustomCollectionViewLayout: UICollectionViewLayout {
weak var delegate: CustomCollectionViewLayoutDelegate!
public var numberOfColumns: Int = 1
private var cache: [UICollectionViewLayoutAttributes] = [UICollectionViewLayoutAttributes]()
private var contentHeight: CGFloat = 0
private var contentWidth: CGFloat {
guard let collectionView = collectionView else {
return 0
}
let insets = collectionView.contentInset
return collectionView.bounds.width - (insets.left + insets.right)
}
public override var collectionViewContentSize: CGSize {
return CGSize(
width: self.contentWidth,
height: self.contentHeight
)
}
public override func prepare() {
if cache.isEmpty {
let columnWidth: CGFloat = self.contentWidth / CGFloat(self.numberOfColumns)
var xOffset: [CGFloat] = [CGFloat]()
for column in 0...numberOfColumns{
xOffset.append(CGFloat(column) * columnWidth)
}
var yOffset: [CGFloat] = [CGFloat](repeating: 0, count: self.numberOfColumns)
var column: Int = 0
for item in 0 ..< collectionView!.numberOfItems(inSection: 0) {
// for item in 0 ..< collectionView!.numberOfItems(inSection: 0) {
let indexPath: IndexPath = IndexPath(item: item, section: 0)
let height = delegate.collectionView(
collectionView: collectionView!,
heightForItemAtIndexPath: indexPath
)
let frame: CGRect = CGRect(
x: xOffset[column],
y: yOffset[column],
width: columnWidth,
height: height
)
let attributes: UICollectionViewLayoutAttributes = UICollectionViewLayoutAttributes(
forCellWith: indexPath
)
attributes.frame = frame
self.cache.append(attributes)
self.contentHeight = max(contentHeight, frame.maxY)
yOffset[column] = yOffset[column] + height
column = column < (self.numberOfColumns - 1) ? (column + 1) : 0
}
}
}
public override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
var visibleLayoutAttributes = [UICollectionViewLayoutAttributes]()
// Loop through the cache and look for items in the rect
for attributes in cache {
if attributes.frame.intersects(rect) {
visibleLayoutAttributes.append(attributes)
}
}
return visibleLayoutAttributes
}
public override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
return cache[indexPath.item]
}
}
I just need to be able to use reloadData to update my infinite scroll data
Upvotes: 3
Views: 1179
Reputation: 394
If you remove the cache, your layout will change also for the existing items which will create a bad user experience. When a user scrolls down the view, s/he will notice that the existing items suddenly change.
prepare()
method is triggered when you call .reloadData
you should handle appending new elements in this method. Currently, it does not update the layout because, the cache is not nil. Instead of removing the elements in the cache, you should compare the number of items in the cache and the collectionView.
Have a look at the below answer for the example implementation;
https://stackoverflow.com/a/70154474/13754736
Upvotes: 0
Reputation: 96
I suggest to add this function to your CustomCollectionViewLayout
func reloadData(){
self.cache = [UICollectionViewLayoutAttributes]()
}
and then when you want to reload your data
if let layout = self.collectionView.collectionViewLayout as? CustomCollectionViewLayout {
layout.reloadData()
}
collectionView.reloadData()
deleting if cache.isEmpty may result in reuse of your layout
Upvotes: 5