Search code examples
iosswiftuicollectionviewuicollectionviewcellnsindexpath

Why indexPath can change after a URLSessionTask finishes?


I am learning ios coding and reading a book. There is something about the indexPath of a UICollectionViewCell that I don't understand. The UICollectionView displays images that are fetched using the remoteUrl attribute of the Photo Object with URLSessionDataTask. An escaping compeletion handler is passed to the image-fetching function to update the UIImageView inside the cell.

The comment of the code snippet on the book says that "The index path for the photo might have changed between the time the request started and finished". Why does that happen?

func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
    let photo = photoDataSource.photos[indexPath.row]
    
    // Download the image data, which could take some time
    photoStore.fetchImage(photo: photo) { (result) in
        
        // The index path for the photo might have changed between the
        // time the request started and finished, so find the most
        // recent index path
        guard
            let photoIndex = self.photoDataSource.photos.firstIndex(of: photo),
            case let .success(image) = result
        else {
            return
        }
        
        let photoIndexPath = IndexPath(row: photoIndex, section: 0)
        
        // When the request finishes, only update the cell if it's still visible
        if let cell = collectionView.cellForItem(at: photoIndexPath) as? PhotoCollectionViewCell {
            cell.updateImage(image: image)
        }
    }
}

Solution

  • Read about reuse cell for tableView, or for collectionView

    Example: add code In viewDidLoad:

    collectionView.register(UICollectionViewCell.self, forCellReuseIdentifier: "Your Reuse Identifier")
    

    In Extension of your viewController (UICollectionViewDelegateFlowLayout)

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Your Reuse Identifier", for: indexPath)
        cell.imageView.image = photos[indexPath.row]
        return cell
    }