简体   繁体   中英

UITableView with asynchronous loading of image in cells - images keep changing on slow connection

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:

  • Caching the image
  • Setting a placeholder image

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;
}

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM