1

I have a collection view and I am loading 20 cells in every call (Fetching) from firebase in it. So I have two sections where the first section is the count number of my source array and the second section is just showing the loading cell when a bool value fetch more is true.

This is the collection view code:

extension ProductViewController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 2
    }

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        if section == 0 {
            return self.objectArray.count
        } else if section == 1 {
            return fetchingMore ? 1 : 0
        }
        return 0
    }

    func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
        cellHeights[indexPath] = cell.frame.size.height
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
        return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
        return 0
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        var minimumLineSpacingForSectionAt: CGFloat
        if section == 0 {
            minimumLineSpacingForSectionAt = -50
        } else {
            minimumLineSpacingForSectionAt = 0
        }
        return minimumLineSpacingForSectionAt
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        if indexPath.section == 0 {
            return CGSize(width: productCollectionView.bounds.width, height: CommonService.heightForRowAtIndexPathForiPhones(cellType: .itemCell))
        } else {
            return CGSize(width: productCollectionView.bounds.width, height: 70)
        }
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        if indexPath.section == 0 {
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ItemAlbumCollectionViewCell", for: indexPath) as! ItemAlbumCollectionViewCell
            cell.setUpCell(product: objectArray[indexPath.row]!)
            return cell
        } else {
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "LoadingCollectionViewCell", for: indexPath) as! LoadingCollectionViewCell
            cell.activityIndicator.startAnimating()
            return cell
        }
    }

    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        let productCategoryViewController = Storyboards.ProductCategoryVC.controller as! ProductCategoryViewController
        productCategoryViewController.products = objectArray[indexPath.row]!
        pushViewController(T: productCategoryViewController)
    }

    func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {

        switch kind {
        case UICollectionView.elementKindSectionHeader:
            guard let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "StreachyCollectionHeaderReusableView", for: indexPath) as? StreachyCollectionHeaderReusableView else { fatalError("Invalid view type") }
            return headerView
        default:
            assert(false, "Invalid element type")
        }
    }
}

And this the function which is called for fetching data:

func pagination(forDivisionCollection: String) {
    fetchProduct(forDivisionCollection: forDivisionCollection, completion: { newData in
        self.productCollectionView.reloadSections(IndexSet(integer: 1))
        self.objectArray.append(contentsOf: newData)
        self.endReached = newData.count == 0
        self.fetchingMore = false

        UIView.performWithoutAnimation {
            self.productCollectionView.reloadData()
        }
    })
}

And it is working perfectly.

Now I have added a top menu section with a couple of options.

enter image description here

If a user taps on that section the function func pagination(forDivisionCollection: String) will be called with a parameter and it will filter data source with that parameter and populate data source according to the top menu option like this:

func pagination(isForNewDivision: Bool, forDivisionCollection: String) {
    self.fetchingMore = true
        if isForNewDivision == true {
            self.objectArray.removeAll()
        }
    fetchProduct(forDivisionCollection: forDivisionCollection, completion: { newData in
        self.productCollectionView.reloadSections(IndexSet(integer: 1))
        self.objectArray.append(contentsOf: newData)
        self.endReached = newData.count == 0
        self.fetchingMore = false

        UIView.performWithoutAnimation {
            self.productCollectionView.reloadData()
        }
    })
}

But I am getting 'Invalid update: invalid number of items in section 0. warning on Xcode when running it in the simulator and it is getting crashed on the physical device. I have researched a bit and come up with the solution of using performBatchUpdates but none of them are working. So my question is in this kind of situation what would be the perfect way to fetch data and load in collection view? Thanks a lot in advance.

Tulon
  • 4,011
  • 6
  • 36
  • 56
  • 1
    Are you needing to deploy to less than ios13 ? Investigate NSCollectionViewDiffableDataSource. Your approach of calling both reloadSections and reloadData is problematic and will be a source of never ending issues for a multi section collection. What you’re doing is possible but is brittle even when done well. See also https://developer.apple.com/wwdc19/220 – Warren Burton Dec 08 '19 at 09:18
  • @WarrenBurton Thanks a lot for your suggestion. Yes, my deployment target is below iOS 13. Ok, I am looking into it. – Tulon Dec 08 '19 at 13:33
  • 1
    You can use - https://github.com/onmyway133/DeepDiff to grab the changes between your objects and then perform batch updates on it. It has an example project on there. – mn1 Dec 08 '19 at 15:37
  • Thanks for the link. OK, I am looking into it. – Tulon Dec 08 '19 at 15:45

0 Answers0