How to get access to the last selected UICollectionViewCell after restart of the app?

I have an app where I use the UICollectionView to show images of different cars. Once a user selects a car the picture is animated infinitely until the user selects another car. For this I use the isSelected() method.


After a restart of the app I want the animation to continue on the last selected car.

I already have saved the index of the last selected car and now I want to get access to the specific cell in the viewDidLoad() in order to resume the animation. Is this the correct approach?


if let selectedCarCell = carCollectionView.cellForItem(at: IndexPath(row: 0, section: 0)) as? CarSelectionCollectionViewCell
{
    print("Got access to cell")
 }

Replies

That seems to be OK. But you need to save row and section when you leave the app, and get them back when you reopen.


You can store it in UserDefaults, that's the usual place.

The row is already saved using UserDefaults and the section is always 0. I just used row 0 for testing, because there actually is a cell, however the print statement is never executed.

So, that is a different issue.


Could you show the complete code of the ViewController, in order to understand why carCollectionView.cellForItem(at:) is not called.

Could be that it is not a CarSelectionCollectionViewCell


So, before if let, add a test


let selectedCarCell = carCollectionView.cellForItem(at: IndexPath(row: 0, section: 0))
print(selectedCarCell)
print(selectedCarCell as? CarSelectionCollectionViewCell)

if let selectedCarCell = carCollectionView.cellForItem(at: IndexPath(row: 0, section: 0)) as? CarSelectionCollectionViewCell  {
    print("Got access to cell")
}

You're Doing It Wrong™. 🙂


Trying something like this in viewDidLoad is way too early. The UI is still under construction, and you don't know if all the pieces are in place to allow collection cells to be constructed.


More importantly, the collection view is charge of what cells exist, not you. It's at least possible that the cell that was previously animating is not even visible initially (even after the UI is constructed), and so doesn't exist.


I don't know the safest approach, but I suspect you should put this code inside the cell view itself, and check (in the cell's viewWillAppear) whether that particular cell should restart its own animation. That means the animation won't start unless the cell is really visible, and it won't start if a different cell has been given the animation before the first cell gets around to starting it.

The output of both print statements is nil.


Here is the part in the ViewController responsible for the UICollectionView:


    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return allCars.count + 1 // +1 due to addCar cell, otherwise first car is not shown
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        if indexPath.row == 0 { // makes sure addCar cell is shown
            let addCell = collectionView.dequeueReusableCell(withReuseIdentifier: "addCar", for: indexPath)
           
            return addCell
        }
        
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Car", for: indexPath) as! CarSelectionCollectionViewCell
            
        cell.nameLabel.text = allCars[indexPath.row-1].name // -1 in orter to display all cars. Otherwise first car would not be shown and index out of range
        if let imgData = allCars[indexPath.row-1].photo as Data?
        {
            cell.picImageView.image = UIImage(data: imgData)
        }
            
        return cell
        
    }
    
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        
        let cell = collectionView.cellForItem(at: indexPath) as! CarSelectionCollectionViewCell
        impactGenerator.impactOccurred()
        UIView.animate(withDuration: 0.1, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: [], animations: {
            cell.transform = CGAffineTransform(scaleX: 1.1, y: 1.1)
        },
                       completion: { finished in
                        UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
                            cell.transform = CGAffineTransform(scaleX: 1, y: 1)
                        }, completion: nil)
        })
        
        // EVERYTHING IS UPDATED ACCORDING TO SELECTED CAR
        selectedCar = allCars[indexPath.row-1] // selectedCar is set to newly selected car
        appDelegate.updateSelectedCarRefuelEntries()
        
        //cell.picImageView.layer.add(animation, forKey: "pulsing")
        
        tableView.reloadSections(NSIndexSet(index: 0) as IndexSet, with: .fade) // reloads tableView with animation
        indexOfSelectedCar = indexPath.row-1
        defaults.set(indexOfSelectedCar, forKey: "indexOfSelectedCar")

This makes sense. But in my UITableViewCell class is only the awakeFromNib() function and the willAppear() does not seem to be available. Or am I not getting your approach right?