質問

I subclassed MKPointAnnotation and added a NSString and a UIImage so I can pass these elements to an MKPinAnnotationView. The string passes into the MKPinAnnotationView fine, but the image attribute is coming through as null. I can't figure out why. I will say that one time running this code on my phone the image appeared on a pin once, so maybe I have a memory leak? The other 1000 times I've compiled the images show as null in the log and do not appear. Any help is appreciated.

Here is my subclass customMapAnnotation.h:

@interface customMapAnnotation : MKPointAnnotation

@property (strong, nonatomic) NSString *restaurantID;

@property (strong, nonatomic) UIImage *restaurantIcon;

@end

Here is where my customMapAnnotation is created. I'm using Parse.com so there is a statement here to convert a PFFile to a UIImage. If I check the log I confirm that foodPoint.restaurantIcon has a value when this is ran.

customMapAnnotation *foodPoint = [[customMapAnnotation alloc] init];
foodPoint.coordinate = spot;
foodPoint.restaurantID = [object objectId];
foodPoint.title = [object objectForKey:@"restaurantName"];
foodPoint.subtitle = [object objectForKey:@"cuisineType"];
leftIcon = [object objectForKey:@"restaurantImage"];

[leftIcon getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
    if (!error) {
        image = [UIImage imageWithData:data];

        // image can now be set on a UIImageView
        foodPoint.restaurantIcon = image;
    }
}];

[self.mainMap addAnnotation:foodPoint];

Now here is all of my code for my MKPinAnnotationView. Notice I run an NSLog to confirm if fp.restaurantIcon has a value, and it is null. I know this use of NSLog is not graceful, but it should return the memory location of the image. It comes back null. Again, my other custom attribute fp.restaurantId is working fine.

- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
    static NSString *identifier = @"RoutePinAnnotation";
    if (annotation == mapView.userLocation)
    {
        return nil;
    }
    else
    {
        MKPinAnnotationView *pinView = [[MKPinAnnotationView alloc]
                                        initWithAnnotation:annotation reuseIdentifier:identifier];
        pinView.animatesDrop=NO;
        pinView.canShowCallout=YES;

        UIButton *rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];

        //cast annotation parameter to our class so compiler understands...
        customMapAnnotation *fp = (customMapAnnotation *)annotation;

        NSLog(fp.restaurantIcon);

        //get restaurantID from the annotation parameter ("fp")...
        [rightButton setTitle:fp.restaurantID forState:UIControlStateNormal];
        [rightButton addTarget:self
                        action:@selector(buttonMethod:)
              forControlEvents:UIControlEventTouchUpInside];

        pinView.rightCalloutAccessoryView = rightButton;

        //NSLog(fp.restaurantIcon);

        UIImageView *profileIconView = [[UIImageView alloc] initWithImage:fp.restaurantIcon];
        profileIconView.frame = CGRectMake(0,0,31,31);

        pinView.leftCalloutAccessoryView = profileIconView;

        return pinView;
    }
}

Here is my didSelectAnnotationView:

 - (void) mainMap:(MKMapView *)mainMap didSelectAnnotationView:(MKAnnotationView *)view
{
    if ([view.annotation isKindOfClass:[chwMapAnnotation class]])
    {
        chwMapAnnotation *fp = (chwMapAnnotation *)view.annotation;
        UIImageView *profileIconView = [[UIImageView alloc] initWithImage:fp.restaurantIcon];
        profileIconView.frame = CGRectMake(0,0,31,31);
        view.leftCalloutAccessoryView = profileIconView;
    }
}
役に立ちましたか?

解決

I agree with the comment by @Vincent that since the images are being loaded in the background asynchronously, they have not yet been loaded when viewForAnnotation is called and so the annotation's callout is stuck with a blank left icon.

When the image loading is finished by getDataInBackgroundWithBlock, there is no automated signal telling the annotation view to refresh its leftCalloutAccessoryView.

A crude workaround in this case would be to manually force the refresh of the leftCalloutAccessoryView when the annotation is selected (which is when the callout will be displayed containing the leftCalloutAccessoryView).

When an annotation is selected, the map view calls the didSelectAnnotationView delegate method. Here, the crude workaround can be applied:

-(void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
{
    if ([view.annotation isKindOfClass:[customMapAnnotation class]])
    {
        customMapAnnotation *fp = (customMapAnnotation *)view.annotation;
        UIImageView *profileIconView = [[UIImageView alloc] initWithImage:fp.restaurantIcon];
        profileIconView.frame = CGRectMake(0,0,31,31);
        view.leftCalloutAccessoryView = profileIconView;
    }
}

Of course, it's possible that the image still hasn't loaded for the annotation that was selected (it's either just taking very long or the image url is invalid, etc).

What you can do is in viewForAnnotation, if fp.restaurantIcon is nil, set the left icon to some generic "loading" icon that you add to the project resources. In didSelectAnnotationView, you can also first check if fp.restaurantIcon is still nil and, if so, do nothing (ie. leave the icon as the generic "loading" icon).

A less crude solution might be to create a custom annotation view class that listens for an "image finished loading" message from its associated annotation and refreshes itself automatically.

他のヒント

I would say don't try to subclass MKPointAnnotation, create your own object that conforms to the MKAnnotation protocol, and use annotation views that are NOT MKPinAnnotationViews.

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