Search code examples
cocoa-touchios4mapkitmkannotationmkannotationview

MapKit update annotation image


I'm having a problem finding out the way to update a custom MKAnnotationView image after a asynchronous request completes with information about the status of the annotation. So far, I have this:

- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {

    static NSString *identifier = @"EstacionEB";   
    if ([annotation isKindOfClass:[EstacionEB class]]) {
        EstacionEB *location = (EstacionEB *) annotation;

        CustomPin *annotationView = (CustomPin *) [_mapita dequeueReusableAnnotationViewWithIdentifier:identifier];
        if (annotationView == nil) {
            annotationView = [[CustomPin alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
        } else {
            annotationView.annotation = annotation;
        }

        UIImage * image = [UIImage imageNamed:[NSString stringWithFormat:@"%@.png", [location elStatus]]];

        annotationView.enabled = YES;
        annotationView.canShowCallout = YES;
        annotationView.image = image;

        NSDictionary *temp = [[NSDictionary alloc] 
                              initWithObjects:[NSArray arrayWithObjects:annotationView, location, nil]
                              forKeys:[NSArray arrayWithObjects:@"view", @"annotation", nil]
                              ];
        //This array is synthesized and inited in my controller's viewDidLoad
        [self.markers setObject:temp forKey:location.eid];
        return annotationView;
    }

    return nil;    
}

A little after, I do a request and with the results, an NSDictionary, I'm trying to do the following, which returns null to both elements:

- (void)updateStation:(NSString *)eid withDetails:(NSDictionary *)details
{
    NSInteger free = [details objectForKey:@"free"];
    NSInteger parkings = [details objectForKey:@"parkings"];

    NSDictionary *storedStations = [self.markers objectForKey:eid];

    CustomPin *pin = [storedStations objectForKey:@"view"]; //nil
    EstacionEB *station = [referencia objectForKey:@"annotation"]; //nil as well

    [station setSubtitle:free];

    NSString *status;
    if( free==0 ){
        status = @"empty";
    } else if( (free.intValue>0) && (parkings.intValue<=3)  ){
        status = @"warning";
    } else {
        status = @"available";
    }
    UIImage * image = [UIImage imageNamed:[NSString imageWithFormat:@"%@.png", status]];
    pin.image = image;
}

This brings up no errors (assuming I pasted and traduced everything correctly), but the NSMutableDictionary that should contain both my custom MKAnnotationView and the MKAnnotation, but even when I log them all before the request completes and it appears correctly, when the request completes its as if both the MKAnnotationView and MKAnnotation are just not what I expected, and thus, I can't modify the annotations to change the image or update the annotation view.

Any ideas would be appreciated!


Solution

  • I'm not sure why you're getting nil values from your markers array (especially for annotation). However, I don't recommend storing a reference to the annotation view like that.

    The viewForAnnotation delegate method can get called by the map view any time it thinks it's necessary and the view object could change from one call to the next. Since you are also re-using annotation views using the same re-use identifier for each annotation, there's also the possibility that the same view object could be re-used for another annotation later.

    Instead, in updateStation, I suggest the following:

    • loop through the map view's annotations array
    • if the annotation is of type EstacionEB, then check if its eid matches the one being updated
    • update the annotation's subTitle and elStatus properties (it's important to update elStatus because it's used by the viewForAnnotation delegate method to set the image)
    • get the annotation's current view by calling the map view's viewForAnnotation: instance method (this is not the same as the delegate method mapView:viewForAnnotation:)
    • update the image property of the view

    See this other related question for a similar example.