1

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?

Pascal
  • 85
  • 8

1 Answers1

2

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];

    });

});

}

ChipK
  • 108
  • 7
  • Brillant! I should have thought about that... thanks a bunch for helping me! I wonder if it would have been possible to do it my way... but nevertheless, I like your approach better! – Pascal Jan 26 '13 at 20:40
  • Are you still following the Fall 2011 course and working on assignment #5? If you want, we could exchange emails (or some contact information) so we could ask each other questions and share code. – Pascal Jan 26 '13 at 20:42
  • Yes, I'm trudging along, also monitoring the new session which started last week to pick up tips on iOS 6 but I'm planning on finishing the projects for the Fall '11 course. I haven't worked with Git for several years but I attempted to post my flickR code on GIt hub this evening. Its at https://github.com/chipk215/FlickrToy. You can get my e-mail there. – ChipK Jan 27 '13 at 03:03
  • Same here, I want to follow the new Winter 2013 lectures... but I want to finish the Fall 2011 lectures before as I invested many hours in them. I have to solve a few bugs and then I'll post my code on Github too. Thanks! – Pascal Jan 27 '13 at 14:38