When you dequeue a cell object, most of the time you'll get a reused cell i.e. a cell that has been configured by tableView:cellForRowAtIndexPath:
once or more times before.
To visualise what's happening in your case, consider one likely sequence of events for a single cell as you perform a long, quick scroll:
- The cell is created and an image load is spun off in the background
- The cell is scrolled off screen, so added to the table view's cache, ready for dequeuing. The image loading is not canceled at this point
- The cell is dequeued and an image load is spun off in the background
- Steps 2 and 3 are repeated a few times
- The cell is visible, but the several image loading tasks are now completing and each is updating the cell's
imageView
with the loaded image. This will indeed look like the images are chaotically changing as each loading operation finishes.
(What's more, with a concurrent queue, there's no guarantee that the image loads will complete in the order that they're started - you may not end up with the correct final image!)
So what do we do about it? Now that we understand the problem, there are lots of different solutions. A very simple solution (that I don't really recommend) is to check that the cell's label text matches the value for that indexPath, when you come to set the image:
if ([labelText.text isEqualToString:[[self.listOfPlaceDetails objectAtIndex:indexPath.row] objectForKey:@"name"]]) {
cell.imageView.image = [UIImage imageWithData:image];
}
Obviously, this assumes that all the place details have unique names.
A better solution might be to create an object that handles the image download, and is something that you can register/unregister cells against to handle download completion. This object could enforce the condition that a cell cannot be waiting for more than one image load. As @Leena pointed out, caching is a good idea and this object could be responsible for that too.
As for the blank images, calling [cell setNeedsLayout]
after setting the image should sort that out.