Max
Max

Reputation: 774

Photos framework and reusable UICollectionViewCell

I have a UICollectionView to display photos from a device's album with the Photos framework. The photos are correctly displayed, but if I scroll fast (like when you tape at the top of the screen to go to the top of the collectionView), I have some photos which are not at the good indexPath. I just need to scroll a bit to put the bad photo out of the screen, and everything go back in place.

I clean the cell during prepareForReuse by canceling the current request.

I presume it's a problem with the asynchronous request of PHImageManager, but I don't know how to avoid this problem.

Here some code :

View Controller

extension AlbumDetailViewController : UICollectionViewDataSource {
    func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
        return 1
    }

    func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return photoList.count
    }

    func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {

        let cell = collectionView.dequeueReusableCellWithReuseIdentifier("PhotoCell", forIndexPath: indexPath) as! PhotoCollectionCell

        cell.setImage(photoList.objectAtIndex(indexPath.row) as! PHAsset)

        return cell
    }
}

Custom CollectionViewCell

class PhotoCollectionCell: UICollectionViewCell {

    @IBOutlet weak var imageView: UIImageView!

    var requestId: PHImageRequestID!
    let manager = PHImageManager.defaultManager()

    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
    }

    override func prepareForReuse() {
        self.imageView.image = nil

        manager.cancelImageRequest(self.requestId)
    }

    func setImage(asset: PHAsset) {

        let option = PHImageRequestOptions()

        option.resizeMode = .Fast
        option.deliveryMode = .HighQualityFormat

        self.requestId = manager.requestImageForAsset(asset, targetSize: CGSize(width: self.frame.size.width * UIScreen.mainScreen().scale, height: self.frame.size.height * UIScreen.mainScreen().scale), contentMode: PHImageContentMode.Default, options: option, resultHandler: {(result, info)->Void in
            self.imageView.image = result
        })
    }
}

Thank you

Upvotes: 4

Views: 732

Answers (1)

Maxim Vingalov
Maxim Vingalov

Reputation: 21

Before calling requestImageForAsset method, set cell.tag equal indexPath.item, and after when getting image in callback, check indexPath.item and cell.tag, if this is matched do that
self.imageView.image = result. For example

        cell.tag = indexPath.item

        if let asset = assets?[indexPath.item] {

            let option = PHImageRequestOptions()
            option.isSynchronous = false
            option.resizeMode = .exact
            option.isNetworkAccessAllowed = true

            DispatchQueue.global(qos: .userInitiated).async {
                manager.requestImage(for: asset, targetSize: Constant.targetSize, contentMode: .aspectFit, options: option, resultHandler: {(result, info)->Void in

                    DispatchQueue.main.async {

                        if cell.tag == indexPath.item {
                            cell.imageView.image = result
                        }
                    }
                })
            }
        }

Hope, it helps you

Upvotes: 2

Related Questions