I have a UICollectionView, but the same methods should apply to UITableViews. Each of my cells contains an image I load from disk, which is a slow operation. To mitigate this, I use an async dispatch queue. This works fine, but quickly scrolling results in these operations stacking up, such that the cell will change its image from one to another in sequence, until finally stopping at the last call.
In the past, I’ve done a check to see if the cell was still visible, and if not I don’t continue. However, this is not working with UICollectionView, and anyway it is not efficient. I am considering migrating to use NSOperations, which can be cancelled, so that only the last call to modify the cell will go through. I could possibly do this by checking if the operation had finished in the prepareForReuse method, and cancelling it if not. I’m hoping someone has dealt with this issue in the past and can provide some tips or a solution.
Session 211 – Building Concurrent User Interfaces on iOS, from WWDC 2012, discusses the problem of the cell’s image changing as the background tasks catch up (starting at 38m15s).
Here’s how you solve that problem. After you’ve loaded the image on the background queue, use the index path to find the current cell, if any, that’s displaying the item containing that image. If you get a cell, set the cell’s image. If you get nil, there’s no cell currently displaying that item so just discard the image.
Here’s an easy way to reduce the “stacking up” of background tasks loading images that the view no longer needs. Give yourself an
NSMutableSetinstance variable:When you need to load an image, put the index path of the cell in the set and dispatch a task that takes one index path out of the set and loads its image:
If a cell stops being displayed, remove the cell’s index path from the set:
To load an image, get an index path from the set. We have to dispatch back to the main queue for this, to avoid a race condition on the set:
The
bg_loadImageForRowAtIndexPath:method is the same as above. But when we actually set the image in the cell, we also want to remove the cell’s index path from the set: