iOS (cs193p): Issue using a delegate in a MapViewController to get an image using a different thread from another controller

StackOverflow https://stackoverflow.com/questions/14531299

  •  05-03-2022
  •  | 
  •  

質問

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

    });

});

}

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top