The issue is that when scrolling fast, the cell might be dequeued and reused for another cell by the time the asynchronous request finishes, thereby updating the wrong cell. So, in your completion block, you should make sure the cell is still visible:
[self downloadFromURL:fileURL to:filePath completionBlock:^(BOOL succeeded, UIImage *image) {
if (succeeded) {
BrowseCollectionViewCell *updateCell = (id)[collectionView cellForItemAtIndexPath:indexPath];
if (updateCell) { // if the cell is still visible ...
updateCell.imageView.image = image; // ... then update its image
}
}
}];
Note, this UICollectionView
method cellForItemAtIndexPath
should not be confused with the similarly named UICollectionViewDataSource
method collectionView:cellForItemAtIndexPath:
. The UICollectionView
method cellForItemAtIndexPath
returns a UICollectionViewCell
if the cell is still visible, and returns nil
if it is not.
By the way, the above assumes that the NSIndexPath
for this cell cannot change (i.e. that it's not possible that additional rows could have been inserted above this row while the image was being asynchronously retrieved). That sometimes is not a valid assumption. So, if you wanted to be careful, what you really should do is go back to the model and recalculate what the NSIndexPath
for this model object, and use that when determining the appropriate updateCell
reference.
Ideally, once you address the above, there are a few optimizations you could make to this process:
If the cell is reused while the previous request is still running, you might want to cancel that prior request. If you don't and you scroll quickly past, say, 200 cells, now showing, say, cells 201 through 221, the requests for those 20 currently visible cells will be queued up and won't display until the prior 200 requests finish.
To be able to cancel prior requests, though, you can't use
sendAsynchronousRequest
. You'd have to use a delegate-basedNSURLConnection
orNSURLSessionTask
, which are cancelable.You probably should be caching your images. If your currently visible cells all have their images and you then scroll them off and back on, you appear to be re-requesting them. You should first see if you've already retrieved the image, and if so, use that, and only if you don't have one in your cache, re-issue the request.
Yes, I know you're using the version of the file in persistent storage to cache, but you might want to cache to RAM, too. Typically people use
NSCache
for this purpose.
This is a lot to change. If you want help doing this, let us know. But far easier is to use the UIImageView
category in SDWebImage or AFNetworking, which does all of this for you.