Reputation: 3633
I'm writing a simple custom photo picker in Swift. Look at the GIF and you'll notice what's wrong with it:
When I click the status bar to quickly
scroll to top, the pictures are all mixed up because cells are being reused and it takes a short amount of time to re-generate the thumbnails, therefore, this happens. I wonder how I could resolve this issue no matter how fast I scroll? My code for this picker is:
func getThumbnail(asset: PHAsset, targetSize: CGSize, onComplete: @escaping (UIImage?, [AnyHashable: Any]?) -> ()) {
let manager = PHImageManager.default()
let option = PHImageRequestOptions()
option.resizeMode = .fast
option.isNetworkAccessAllowed = true
manager.requestImage(for: asset, targetSize: targetSize, contentMode: .aspectFill, options: option) { (thumbnail, info) in
onComplete(thumbnail, info)
}
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = ...
let asset = photos[indexPath.row] // "photos" is an array of all photos in library
// and its type is "[PHAsset]"
let photoSize = collectionView.frame.width / 3
getThumbnail(asset: asset, targetSize: CGSize(width: photoSize, height: photoSize, onComplete: { (image, _) -> Void in
(cell.viewWithTag(1) as! UIImageView).image = image
// A cell has a UIImageView, whose size is equal to cell size,
// whose tag = 1
}))
return cell
}
Upvotes: 0
Views: 721
Reputation: 385600
From your code:
let cell = ...
At this point, you don't have the image yet, so you should either set the image view's image to nil
or to a placeholder image:
let cell = ...
(cell.viewWithTag(1) as! UIImageView).image = placeholderImage
Also from your code:
getThumbnail(asset: asset, targetSize: CGSize(width: photoSize, height: photoSize, onComplete: { (image, _) -> Void in
(cell.viewWithTag(1) as! UIImageView).image = image
At this point, cell
might not be the correct cell for the original index path (and thus for image
) anymore. There might be no cell for the original index path, or there might be a different cell for the index path. You need to ask the collection view for the correct cell:
getThumbnail(asset: asset, targetSize: CGSize(width: photoSize, height: photoSize, onComplete: { (image, _) -> Void in
guard let cell = collectionView.cellForItem(at: indexPath) else { return }
(cell.viewWithTag(1) as! UIImageView).image = image
Upvotes: 0
Reputation: 2470
i was getting the same problem in my UITableView
. when i was using AlamofireImage's
af_setImage(withURL: URL(string: pictureUrl))
then i use AlamofireImage's
af_setImage(withURL: URL(string: pictureUrl)!, placeholderImage: UIImage(named: "YourPlaceholder.png"))
using placeholder image solve the problem.
Upvotes: 0
Reputation: 515
In your cellForItemAt in the first line put
cell.imageView.image = nil
this will prevent a dequeued cell from reusing the image which will reduce your flicker. If the image is in memory it gets replaced immediately, no issues.
Just because you are using caching does not mean that all your images are currently in memory. Flickering occurs when you are reading the image to memory, especially over a network and when you need to resize cells (although yours look the same size).
You may want to consider only updating the view ONCE at the end of scrolling, rather than for each cell.
You can check scrollViewWillBeginDragging to turn off updating and scrollViewDidEndDecelerating to do the update if needed.
Upvotes: 1