Please note, this blog entry is from a previous course. You might want to check out the current one.
Add a photo’s thumbnail image (FlickrFetcherPhotoFormatSquare) to every row in any table view that shows a list of photos. Don’t ask Flickr for the image data of thumbnails that never appear on screen (i.e. fetch thumbnail image data only on demand). There’s no need to cache these images (they are very small). Obviously when a row first appears, it will not have the thumbnail image, but, sometime later, when the thumbnail data comes back from Flickr (and if the row is still on screen), it should appear. Beware Required Task #1. And consider the hint above about not calling UI methods from outside the main thread and also remember that table view cells are reused as the scroll on and off screen.
Basically the procedure is the same as for the annotation views. Check if the table item is photo, download its photo asynchronously and put it on screen back in the main queue. The only difference is that it is necessary to put an placeholder image into the cell otherwise it is not possible to update the image once it has been loaded:
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath { NSDictionary *photo = [[self.data objectAtIndex:indexPath.row] copy]; NSNumber *photoID = [photo objectForKey:FLICKR_PHOTO_ID]; if (!photoID) return; cell.imageView.image = [UIImage imageNamed:@"Placeholder"]; dispatch_queue_t queue = dispatch_queue_create("Flickr Thumbnails", NULL); dispatch_async(queue, ^{ FlickrCache *cache = [FlickrCache cacheFor:@"photos"]; NSURL *url = [cache urlForCachedPhoto:photo format:FlickrPhotoFormatSquare]; if (!url) url = [FlickrFetcher urlForPhoto:photo format:FlickrPhotoFormatSquare]; NSData *data = [NSData dataWithContentsOfURL:url]; [cache cacheData:data ofPhoto:photo format:FlickrPhotoFormatSquare]; if ([[[self.data objectAtIndex:indexPath.row] objectForKey:FLICKR_PHOTO_ID] isEqualToString:[photo objectForKey:FLICKR_PHOTO_ID]]) dispatch_async(dispatch_get_main_queue(), ^{ UIImage *image = [UIImage imageWithData:data]; cell.imageView.image = image; [cell setNeedsDisplay]; }); }); dispatch_release(queue); }
The complete code for this task is available at github.
I got my code like this:
– (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @”Photo”;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
// Configure the cell…
FlickrPhoto *flickrPhoto = [self.flickrPhotos objectAtIndex:indexPath.row];
NSArray *flickrPhotoTitleAndSubtitle = flickrPhoto.titleAndSubtitle;
cell.textLabel.text = [flickrPhotoTitleAndSubtitle objectAtIndex:0];
cell.detailTextLabel.text = [flickrPhotoTitleAndSubtitle objectAtIndex:1];
cell.imageView.image = nil;
dispatch_queue_t cellImageQueue = dispatch_queue_create(“Cell Image Queue”, NULL);
dispatch_async(cellImageQueue, ^{
NSURL *photoURL = [FlickrFetcher urlForPhoto:flickrPhoto.dictionary format:FlickrPhotoFormatSquare];
NSData *photoData = [NSData dataWithContentsOfURL:photoURL];
UIImage *image = [UIImage imageWithData:photoData];
dispatch_async(dispatch_get_main_queue(), ^{
if ([flickrPhoto isEqual:[self.flickrPhotos objectAtIndex:indexPath.row]]) {
cell.imageView.image = image;
}
});
});
return cell;
}
But it only loads the images when I click on a cell.
Any ideas why?
Ps.: it does work if I remove all the thread stuff and use the same code.
Thank you.
Is there a particular reason you use cellForRowAtIndexPath instead of willDisplayCell ?