I have a tableview that is populated with the results of a search term. Many of the results have images that need to load from URLs, but not all. I originally had it grabbing the image from the URL in the cellForRowAtIndexPath
method in the main thread, which worked perfectly, but it made the scrolling of the tableview choppy as it "stuck" on each image momentarily.
So, I decided to try loading the images in a background thread. Here is my cellForRowAtIndexPath
method. The URLs are stored in the resultsArray, with the indices corresponding to the row of the cell.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
[sBar resignFirstResponder];
if (indexPath.row != ([resultsArray count]))
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ResultCell"];
UIImageView * bookImage = (UIImageView *)[cell viewWithTag:102];
//set blank immediately so repeats are not shown
bookImage.image = NULL;
//get a dispatch queue
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//this will start the image loading in bg
dispatch_async(concurrentQueue, ^{
NSData *image = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:[[resultsArray objectAtIndex:indexPath.row] thumbnailURL]]];
//this will set the image when loading is finished
dispatch_async(dispatch_get_main_queue(), ^{
bookImage.image = [UIImage imageWithData:image];
if(bookImage.image == nil)
{
bookImage.image = [UIImage imageNamed:@"no_image.jpg"];
}
});
});
return cell;
}
// This is for the last cell, which loads 10 additional items when touched
// Not relevant for this question, I think, but I'll leave it here anyway
else{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"More"];
UILabel *detailLabel = (UILabel *)[cell viewWithTag:101];
detailLabel.text = [NSString stringWithFormat:@"Showing %d of %d results", [resultsArray count], [total intValue]];
if ([UIApplication sharedApplication].networkActivityIndicatorVisible == NO) {
cell.userInteractionEnabled = YES;
}
return cell;
}
}
When the tableview scrolls slowly, the images all load in their appropriate cells, which I can confirm because the images match the titles of the cells, but I left out the setting of everything but the image to shorten it for this question. However when I scroll faster, especially when I simulate a slow internet connection, images start loading in the wrong cells. I think it has something to do with reusing the cells, because, if I a scrolling toward the top quickly, the image of the cell just leaving the view often ends up in the cell just entering. I thought the bookImage.image = NULL; line would ensure that didn't happen, but I guess not. I guess I don't understand background threading very well, when the image is finally loaded from the URL, has it lost track of which cell it was intended for? Do you know what may be happening? Thanks for any feedback!