Search code examples
iosswiftfirebaseuicollectionviewuicollectionviewcell

How to keep CollectionView cells on return to view


I have a CollectionView that have cells corresponding to children in Firebase database. My problem is that when I segue to another view, then segue back, the CollectionView cells are gone.

I have a cellForItemAt function:

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "playerItem", for: indexPath) as! PlayerCollectionViewCell
    cell.setupViews(parentSize: Int(self.playerCollectionView.frame.width), hostUID: self.currentUID, tempUserName: playerArrayList[indexPath.item])
    cell.layer.cornerRadius = 6
    cell.layer.borderWidth = 2
    return cell
}

I also have another code snippit:

for i in 0..<playerArrayList.count{
    print(playerCollectionView.numberOfItems(inSection: 0))
    //playerCollectionView.insertItems(at: [IndexPath(item: i, section: 0)])
    let cell = playerCollectionView.cellForItem(at: IndexPath(item: i, section: 0)) as! PlayerCollectionViewCell
    cell.tempUserName = playerArrayList[i]
    cell.setupViews(parentSize: Int(self.playerCollectionView.frame.width), hostUID: self.currentUID, tempUserName: playerArrayList[i])
}

I still learning how the CollectionView creates cells, and what the program does when a View is asleep, but I believe my problem is that the cellForItemAt function creates all of the cells the first time the view is created and doesn't happen again after. I am sure of this because when the view is created for the first time, there are three cells(which are exactly how many children there are in the database) however when segued back to the same view, there are no cells. I have tried to fix this by deleting all the cells then remaking them. In the code snippet, you can see the commented line:

playerCollectionView.insertItems(at: [IndexPath(item: i, section: 0)])

however this gives me the error:

Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of items in section 0. The number of items contained in an existing section after the update (3) must be equal to the number of items contained in that section before the update (3), plus or minus the number of items inserted or deleted from that section (0 inserted, 1 deleted) and plus or minus the number of items moved into or out of that section (0 moved in, 0 moved out).'

I have also tried using this to create cells:

let cell = playerCollectionView.dequeueReusableCell(withReuseIdentifier: "playerItem", for: IndexPath(item: i, section: 0)) as! PlayerCollectionViewCell

But I get the same error. How can I recreate the cells on return to the View?


Solution

  • Check that you are implementing methods collectionView(_:numberOfItemsInSection:) and collectionView(_:cellForItemAt:) in your UICollectionViewDataSource object.

    When accessing cell with cellForItem you should check if you actually get a cell. That method only returns visible cells.

    if let cell = self.playerCollectionView.cellForItem(at: indexPath) as? PlayerCollectionViewCell {
        cell.tempUserName = playerArrayList[i]
        cell.setupViews(parentSize: Int(self.playerCollectionView.frame.width), hostUID: self.currentUID, tempUserName: playerArrayList[i])
    }
    

    Instead of inserting cells again, you should have a delegate for collection view that tells how many items there are to show

    override function viewDidLoad() {
        super.viewDidLoad()
        playerCollectionView.dataSource = self
    }
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return playerArrayList.count
    }
    

    insertItemsAt method requires that you update return value from numberOfItemsInSection to match the new number of items.

    If playerArrayList object needs reloading you can do that life cycle method

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        // reload
    }