I'm following the excellent CS193P lecture on iOS 5.0 development:
http://www.stanford.edu/class/cs193p/cgi-bin/drupal/
I'm now at assignment #5, required task #5b. I need to display a callout with a thumbnail in it when I click on an annotation on a map.
I managed to do this without any problem with the following code (using Flickr).
MapViewController.h:
@class MapViewController;
@protocol MapViewControllerDelegate <NSObject>
- (UIImage *)mapViewController:(MapViewController *)sender imageForAnnotation:(id <MKAnnotation>)annotation;
@end
@interface MapViewController : UIViewController
@property (nonatomic, strong) NSArray *annotations; // of id <MKAnnotation>
@property (nonatomic, weak) id <MapViewControllerDelegate> delegate;
@end
MapViewController.m:
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)annotationView
{
if ([annotationView.leftCalloutAccessoryView isKindOfClass:[UIImageView class]]) {
UIImage *image = [self.delegate mapViewController:self imageForAnnotation:annotationView.annotation];
[(UIImageView *)annotationView.leftCalloutAccessoryView setImage:image];
}
}
PhotoListTableViewController.m:
@interface PhotoListTableViewController() <MapViewControllerDelegate>
- (UIImage *)mapViewController:(MapViewController *)sender imageForAnnotation:(id <MKAnnotation>)annotation
{
UIImage *image = nil;
if ([annotation isKindOfClass:[FlickrAnnotation class]]) { // make sure the annotation is a FlickrAnnotation
FlickrAnnotation *flickrAnnotation = (FlickrAnnotation *)annotation;
NSURL *photoUrl = [FlickrFetcher urlForPhoto:flickrAnnotation.photo format:FlickrPhotoFormatSquare];
image = [UIImage imageWithData:[NSData dataWithContentsOfURL:photoUrl]];
}
return image;
}
@end
I use a delegate in the mapView:didSelectAnnotationView: method of the MapViewController to retrieve the image from another controller, PhotoListTableViewController (to make sure I have a generic MapViewController).
It's working fine... but I need to modify this code to call Flickr in another thread. So I modified the mapViewController:imageForAnnotation: method for this:
- (UIImage *)mapViewController:(MapViewController *)sender imageForAnnotation:(id <MKAnnotation>)annotation
{
__block UIImage *image = nil;
if ([annotation isKindOfClass:[FlickrAnnotation class]]) { // make sure the annotation is a FlickrAnnotation
FlickrAnnotation *flickrAnnotation = (FlickrAnnotation *)annotation;
dispatch_queue_t downloadQueue = dispatch_queue_create("ThumbnailDownloader", NULL);
dispatch_async(downloadQueue, ^{
NSURL *photoUrl = [FlickrFetcher urlForPhoto:flickrAnnotation.photo format:FlickrPhotoFormatSquare];
dispatch_async(dispatch_get_main_queue(), ^{ // execute on the main thread
image = [UIImage imageWithData:[NSData dataWithContentsOfURL:photoUrl]];
});
});
dispatch_release(downloadQueue);
}
return image;
}
However, with this code, no thumbnail is displayed. I know it is because when the method "return image", the image is still nil cause the downloadQueue thread hasn't completed. I tried to "return image" inside the block like this:
return [UIImage imageWithData:[NSData dataWithContentsOfURL:photoUrl]];
but the compiler doesn't like that:
Incompatible block pointer types passing 'UIImage *(^)(void)' to parameter of type 'disptach_block_t' (aka 'void (^)(void)')
I know that ^{ at the beginning of the block means void, but can it be changed to return an UIImage? Or am I doing it the wrong way from the beginning?
I just don't know how to return the image from the PhotoListTableViewController back to the MapViewController. How should this be done?
I'm working on the same assignment. I switched threads when the tableview controller delegate was invoked, rather than waiting until the table view controller fetched the image and this works for me.
In code:
-(void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view { // NSLog(@"Annotation selected, getting thumbnail");
dispatch_queue_t downloadQueue = dispatch_queue_create("flickr download", NULL);
dispatch_async(downloadQueue, ^{
UIImage *image = [self.delegate thumbNailImageForAnnotation:view.annotation sender: self];
dispatch_async(dispatch_get_main_queue(), ^{
[(UIImageView *)view.leftCalloutAccessoryView setImage:image];
});
});
}