0

I found some strange behavior with long click on MKPointAnnotation.

I create my pin like this:

MKPointAnnotation *activeTRPoint = [[MKPointAnnotation alloc] init];
CLLocation *coord = [[CLLocation alloc] initWithLatitude:latitude longitude:longitude];
activeTRPoint.coordinate = coord.coordinate;
activeTRPoint.title = @"Title";
activeTRPoint.subtitle = @"subtitle";
//Added tag
[map addAnnotation:activeTRPoint];
[[map viewForAnnotation:activeTRPoint] setTag:1];
UIImage *im = [UIImage imageNamed:@"pin_icon.png"];
[[map viewForAnnotation:activeTRPoint] setImage:im];

So when I long click on the pin it changes to red pin (default). Do you have any idea why this happens?

UPDATE: Added viewForAnnotation method for further investigation

- (MKAnnotationView *) mapView:(MKMapView *)mapView1 viewForAnnotation:(id <MKAnnotation>) annotation
{
    if(annotation != map.userLocation){
        // This is not the users location indicator (the blue dot)
        MKAnnotationView *view = [map dequeueReusableAnnotationViewWithIdentifier:@"myAnnotationIdentifier"];
        BOOL isCustomPin = [subtitles containsObject:((MKPointAnnotation*)annotation).subtitle];
        if (!view && isCustomPin == NO) {
            // Creating a new annotation view, in this case it still looks like a pin
            MKPinAnnotationView *annView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:@"myAnnotationIdentifier"];
            annView.pinColor = MKPinAnnotationColorGreen;
            view = annView;
            view.canShowCallout = YES; // So that the callout can appear
            UIButton *btnViewVenue = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
            [btnViewVenue addTarget:self action:@selector(pinTouched:) forControlEvents:UIControlEventTouchUpInside];
            view.rightCalloutAccessoryView = btnViewVenue;
            view.enabled = YES;
            view.canShowCallout = YES;
            view.multipleTouchEnabled = NO;
        }else{
            UIImage *im = [UIImage imageNamed:@"pin_icon.png"];
            [view setImage:im];
        }
        return view;
    }else{
        mapView1.userLocation.title = [Lang get:@"USER_CURRENT_LOCATION"];
        mapView1.userLocation.subtitle = @"";
        [mapView1 setTag:999];
        return nil;
    }
}
Streetboy
  • 4,351
  • 12
  • 56
  • 101
  • Have you implemented the viewForAnnotation delegate method? If so, please show that code. If not, that's part of the problem. See http://stackoverflow.com/questions/9275959/annotation-image-is-replaced-by-redpushpin-when-long-press-on-annotation. –  Jul 24 '13 at 12:00
  • In viewForAnnotation, the code creates green MK**Pin**AnnotationViews but after calling addAnnotation, it assigns a custom image instead. Why is this? –  Jul 24 '13 at 12:46
  • Sorry this was was wrong code. I updated my question.In my case isCustomPin = YES; – Streetboy Jul 24 '13 at 12:53

1 Answers1

2

In the viewForAnnotation delegate method shown, the only type of annotation view actually created (alloc+init) is an MKPinAnnotationView.

When isCustomPin is YES, the code as-is never actually creates an MKAnnotationView and so ends up referencing a view that:

  • is either nil because the dequeue didn't return anything, or
  • is an MKPinAnnotationView because that is the only type of view ever created and the dequeue returns a previously-used one of those.

So when isCustomPin is YES:

  • it's returning a nil view which the map view interprets as "show default red pin" (which is basically a red MKPinAnnotationView), or
  • it's returning a green MKPinAnnotationView.

In any case, the code is always returning an MKPinAnnotationView for which the map view will often ignore the image property and show the default pin image according to pinColor.


To solve this, you must separate the creation and re-use of "custom image" pins from "green pins". For example:

BOOL isCustomPin = [subtitles containsObject:((MKPointAnnotation*)annotation).subtitle];

if (isCustomPin)
{
    //Put dequeue and alloc+init of MKAnnotationView here.
    //NOTE: Use a DIFFERENT re-use identifier for "custom image" pins.
    //Eg. Use "myCustomIdentifier" -- not "myAnnotationIdentifier"
}
else
{
    //Put dequeue and alloc+init of MKPinAnnotationView here.
    //NOTE: Use a DIFFERENT re-use identifier from "custom image" pins.
    //That is, use "myAnnotationIdentifier" -- not "myCustomIdentifier".
}


Some separate, probably un-related issues:

  • The logic used to determine isCustomPin looks questionable. It would be better to create a custom annotation class and check if annotation is of that type instead of looking for the annotation in some external array.
  • After an annotation view is dequeued, you should set the view's annotation property to the current annotation otherwise the view will use the previous annotations properties.
  • There should be no need to use tags (I don't recommend it). The user location annotation can be identified by its class MKUserLocation and in the pinTouched: method, you can obtain the selected annotation using the selectedAnnotations property of MKMapView.


By the way, you should also remove the setting of the image after the call to addAnnotation.