Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GCD UITableView asynchronous load images, wrong cells are loaded until new image download

I have a UITableView with custom cells. I load images asynchronously using Grand Central Dispatch. Everything works fine, but when I scroll down, previously loaded images are shown till the new image is downloaded. Here is my code:

if (![[NSFileManager defaultManager] fileExistsAtPath:[path stringByAppendingPathComponent:@"image.png"]])
{
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,  0ul);
    dispatch_async(queue, ^{
        NSString *url=[pat stringByAppendingPathComponent:@"comments.txt"];
        NSString *u=[NSString stringWithContentsOfFile:url encoding:NSUTF8StringEncoding error:nil];
        NSURL *imageURL=[NSURL URLWithString:u];
        NSData *image=[NSData dataWithContentsOfURL:imageURL];
        [image writeToFile:[pat stringByAppendingPathComponent:@"image.png"] atomically:YES];
        dispatch_sync(dispatch_get_main_queue(), ^{
            cell.imageView.image=[UIImage imageWithContentsOfFile:[pat stringByAppendingPathComponent:@"image.png"]];
            [cell setNeedsLayout];
            NSLog(@"Download");
        });
    });
}
else
{
    NSLog(@"cache");
    cell.imageView.image=[UIImage imageWithContentsOfFile:[pat stringByAppendingPathComponent:@"image.png"]];
}

Any suggestions appreciated. P.S. I reuse the cells

like image 412
blackhawk4152 Avatar asked Oct 21 '11 15:10

blackhawk4152


2 Answers

Rather than capturing the cell you need to capture the index path, then get the cell back using:

UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];

That way, if the cell is now off screen you'll get nil back and the image won't be set on the wrong cell.

The other thing you need to add after your dispatch_async() is a cell.imageView.image=somePlaceholderImage.

E.g.:

if (![[NSFileManager defaultManager] fileExistsAtPath:[path stringByAppendingPathComponent:@"image.png"]])
{
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,  0ul);
    dispatch_async(queue, ^{
        NSString *url=[pat stringByAppendingPathComponent:@"comments.txt"];
        NSString *u=[NSString stringWithContentsOfFile:url encoding:NSUTF8StringEncoding error:nil];
        NSURL *imageURL=[NSURL URLWithString:u];
        NSData *image=[NSData dataWithContentsOfURL:imageURL];
        [image writeToFile:[pat stringByAppendingPathComponent:@"image.png"] atomically:YES];
        dispatch_sync(dispatch_get_main_queue(), ^{
            UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
            cell.imageView.image=[UIImage imageWithContentsOfFile:[pat stringByAppendingPathComponent:@"image.png"]];
            [cell setNeedsLayout];
            NSLog(@"Download");
        });
    });
    cell.imageView.image=[UIImage imageNamed:@"placeholder"];
}
else
{
    NSLog(@"cache");
    cell.imageView.image=[UIImage imageWithContentsOfFile:[pat stringByAppendingPathComponent:@"image.png"]];
}
like image 92
hypercrypt Avatar answered Oct 23 '22 04:10

hypercrypt


In your - (void)tableView:(UITableView *)aTableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath { You need to clear out the image, or reset it to your spinner. Since table view rows are reused, this is the behavior you will see.

like image 31
logancautrell Avatar answered Oct 23 '22 03:10

logancautrell