Refreshing UITableViewCell after fetching image data by dispatch_async

2.1k views Asked by At

I'm writing app that shows TableView with entries that contains images. I'm trying to fetch image by executing this line of code inside cellForRowAtIndexPath method:

cell.detailTextLabel.text =  [artistData objectForKey:generesKey];
dispatch_async(backgroundQueue, ^{
         NSURL *url_img = [NSURL URLWithString:[artistData objectForKey:pictureKey]];
        NSData* data = [NSData dataWithContentsOfURL:
                         url_img];
        cell.imageView.image = [UIImage imageWithData:data];
        [self performSelectorOnMainThread:@selector(refreshCell:) withObject:cell waitUntilDone:YES];
    });

After setting image I perform selector that contains:

-(void)refreshCell:(UITableViewCell*)cell{
    [cell setNeedsDisplay];
    [self.view setNeedsDisplay];
    [self.tableViewOutlet setNeedsDisplay];
}

And image is not shown but when I click on cell or scroll entire list, images are shown. Why my View is not refreshing? Did I missed something?

3

There are 3 answers

2
aahrens On BEST ANSWER

You'll could always reload the cell by calling [self.tableView reloadRowsAtIndexPaths@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];

In order to prevent the infinite loop once you successfully download an image you'll want to cache the results. How long you cache is up to you.

 NSCache *imageCache = [[NSCache alloc] init];
 imageCache.name = @"My Image Cache";
 UIImage *image = [imageCache objectForKey:url_img];
 if (image) {
    cell.imageView.image = image;
 } else {
    // Do your dispatch async to fetch the image.

    // Once you get the image do
    [imageCache setObject:[UIImage imageWithData:data] forKey:url_img];
}

You'll want the imageCache to be a property at the ViewController level. Don't create one each time incellForRowAtIndexPath

1
Mark On

It might be related to interacting with the UI from a background queue. Try this:

dispatch_async(backgroundQueue, ^{
    NSURL *url_img = [NSURL URLWithString:[artistData objectForKey:pictureKey]];
    NSData* data = [NSData dataWithContentsOfURL:url_img];
    dispatch_async(dispatch_get_main_queue(), ^{
        cell.imageView.image = [UIImage imageWithData:data];
    });
});
0
scope On

It should be enough to call setNeedsLayout() on a cell.

In swift 4 it looks like this:

DispatchQueue.global().async {
    let data = try? Data(contentsOf: URL(string: imageUrl)!)
    DispatchQueue.main.async {
        cell.imageView?.image = UIImage(data: data!)
        cell.setNeedsLayout()
    }
}