Reputation: 31863
I'm loading images asynchronously in my table view. On slow connections, the images in the cell keep flickering/changing (I assume it's from reusing the cells). I've followed the advice on some of the other related questions including:
It works fine on normal connection, but on EDGE and slow networks, the problem still occurs. I wonder if it is the global queue that is getting backed up with network requests? If so, how do I solve this?
Here is my code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// get the data for this photo post
NSDictionary *data = [self.streamPhotosData objectAtIndex:indexPath.row];
static NSString *CellIdentifier = @"StreamPhotoTableViewCell";
__block StreamPhotoTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"StreamPhotoTableViewCell"];
if (cell == nil) {
cell = [[StreamPhotoTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
cell.delegate = self;
// check if image in cache, else cache it
if ([[[GlobalCaches getCurrentInstance] photoCache] objectForKey:[data objectForKey:@"photoGuid"]] != nil) {
// cache hit
cell.photoImageView.image = [[[GlobalCaches getCurrentInstance] photoCache] objectForKey:[data objectForKey:@"photoGuid"]];
} else {
// set a placeholder image
cell.photoImageView.image = [UIImage imageNamed:@"placeholder.png"];
// download the image asynchronously
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
UIImage *downloadedImage = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[data objectForKey:@"url"]]]];
if (downloadedImage) {
dispatch_async(dispatch_get_main_queue(), ^{
cell.photoImageView.image = downloadedImage;
// update cache
[[[GlobalCaches getCurrentInstance] photoCache] setObject:downloadedImage forKey:[data objectForKey:@"photoGuid"]];
});
}
});
}
return cell;
}
Upvotes: 0
Views: 320
Reputation: 3199
I had a similar issue once.
Why does it happen? Let's say you're in cellForRow for row number 1, this image is not in the cache, so you're downloading it. Meantime, you scroll the tableView, and let's say that now you're in cellForRow for row number 8, which is the same cell instance. Let's say that now you have this image in cache, so you're just setting the image with no time offset, and after the cell is with the right image, the image from row number 1 is ready, and remember that it is the same cell instance, thus the image is getting replaced. Of course this is only one example.
You can try to 'remember' for which row you started to download the image, set a 'beforeTag' before you start the download, and after is, check if it is the same indexPath.row
NSInteger beforeTag = indexPath.row;
// download the image asynchronously
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
UIImage *downloadedImage = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[data objectForKey:@"url"]]]];
if (downloadedImage) {
dispatch_async(dispatch_get_main_queue(), ^{
if (indexPath.row == beforeTag) {
cell.photoImageView.image = downloadedImage;
}
// update cache
[[[GlobalCaches getCurrentInstance] photoCache] setObject:downloadedImage forKey:[data objectForKey:@"photoGuid"]];
});
}
});
Also, I would put this line in prepareForReuse in your custom cell:
cell.photoImageView.image = [UIImage imageNamed:@"placeholder.png"];
This method is called every time a UITableViewCell is getting reused, it is called just after you 'dequeueReusableCellWithIdentifier', if the cell is already exist. And it's okay to always put a placeHolder image, if the image is in cache, you wont notice this placeholder.
Hope it helps
Upvotes: 1