user978064
user978064

Reputation: 79

Custom CollectionView Layout Reload Data not working

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

Answers (3)

musakokcen
musakokcen

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

Amin Fadul
Amin Fadul

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

Bruno Henrique
Bruno Henrique

Reputation: 141

just remove if cache.isEmpty you should be fine

Upvotes: 2

Related Questions