Force MKMapView viewForAnnotation to update
Asked Answered
N

5

18

So I have a MKMapView with all my pins added, and the colour of the pin is dependent on whether a value is set for that pin. When I first load the app, viewForAnnotation is called and the colours are set accordingly. However, when I update the pin's details (such as location, title, etc...) I also update the pinColour to find it doesn't update. It looks like viewForAnnotation isn't called again after the initial add.

I have read many questions similar to this and I can confirm that mapView.delegate = self;

Here is my viewForAnnotation code:

- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(MapAnnotation *)annotation
{
    if([annotation class] == MKUserLocation.class)
        return nil;

    NSString *pinIdentifier = annotation.identifier; // This is a unique string for each pin and is getting populated every time!

    MKPinAnnotationView *annotationView = (MKPinAnnotationView *) [mapView dequeueReusableAnnotationViewWithIdentifier:pinIdentifier];

    if(annotationView == nil)
        annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:pinIdentifier];
    else
        annotationView.annotation = annotation; // Never had this line fire...

    annotationView.canShowCallout = YES;
    annotationView.animatesDrop = NO;
    annotationView.enabled = YES;
    annotationView.tag = annotation.counter;

    if(annotation.pinColour == Stopped) // from enum
        annotationView.pinColor = MKPinAnnotationColorRed;
    else
        annotationView.pinColor = MKPinAnnotationColorGreen;

    UIButton *infoButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
    [infoButton addTarget:self action:@selector(mapCalloutButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
    infoButton.tag = annotation.counter;
    annotationView.rightCalloutAccessoryView = infoButton;

    return annotationView;
}

Here is the code where I add the pin:

CLLocationCoordinate2D annotationCoord;
annotationCoord.latitude = latestPosition.latitude;
annotationCoord.longitude = latestPosition.longitude;

MapAnnotation *annotation = [[MapAnnotation alloc] init];
annotation.coordinate = annotationCoord;
annotation.identifier = theIdentifier;
annotation.title = theTitle;
annotation.subtitle = theSubtitle
annotation.pinColour = [self getPinColour];
annotation.counter = theCounter;

[theMapView addAnnotation:annotation];

Here is the code where I update the pin (different method to add):

updatePin = true;
pinCounter = mapPin.counter;

CLLocationCoordinate2D annotationCoord;
annotationCoord.latitude = latestPosition.latitude;
annotationCoord.longitude = latestPosition.longitude;
[mapPin setCoordinate:annotationCoord];

mapPin.identifier = theIdentifier;
mapPin.subtitle = theSubtitle;
mapPin.pinColour = [self getPinColour];

I'm not sure what I'm missing. viewForAnnotation is obviously working, it's just not ever called after the initial add! If it were to call this function I'm 100% sure it would work as it does the colour change if I restart the app!

EDIT: Oh and I really don't want to start removing annotations and re-adding them. It's what I'm doing in the short term anyway!

Nw answered 27/5, 2012 at 12:31 Comment(0)
C
17

Due to the way the map view caches its annotations, you NEED to remove and re-add the annotation if you need to make changes to its appearance. A simple remove & add is the way to go. There is no cache invalidating mechanism but this.

Congress answered 27/5, 2012 at 16:24 Comment(2)
Ah thanks. I didn't really want to do it that way but I've managed to retain the callout view so now you can't tell anyway! I will mark this as the answer as I didn't know that this was the only way.Nw
This answer works https://mcmap.net/q/673149/-swift-how-to-update-data-in-custom-mkannotation-callout. You create a custom annotation and add kvo to the properties to make changes to annotation when the properties changeNyberg
B
46

Actually, I dont' know if this worked for you but this is how I did it.

I didn't need to delete the annotation from map. All I need to do is tell the map to give me the annotation view for a parameter annotation. The map will return the correct annotation. From there, I have a property for my custom annotation to identify whether it is an active item, if yes, show the normal pin image, else show full pin image.

-(void)updateAnnotationImage:(CustomAnnotation *)paramAnnotation
{
    MKAnnotationView *av = [geoMap viewForAnnotation:paramAnnotation];

    if (paramAnnotation.active)
    {
        av.image = [UIImage imageNamed:@"PinNormal.png"];
    }
    else
    {
        av.image = [UIImage imageNamed:@"PinFull.png"];
    }
}

Bit late but hopefully it helps others who came across this problem.

Baxley answered 25/7, 2012 at 6:31 Comment(2)
This is possible and this is the correct way.. +1 for the answer.Backsword
This should really be the accepted answer. Thanks!Selassie
C
17

Due to the way the map view caches its annotations, you NEED to remove and re-add the annotation if you need to make changes to its appearance. A simple remove & add is the way to go. There is no cache invalidating mechanism but this.

Congress answered 27/5, 2012 at 16:24 Comment(2)
Ah thanks. I didn't really want to do it that way but I've managed to retain the callout view so now you can't tell anyway! I will mark this as the answer as I didn't know that this was the only way.Nw
This answer works https://mcmap.net/q/673149/-swift-how-to-update-data-in-custom-mkannotation-callout. You create a custom annotation and add kvo to the properties to make changes to annotation when the properties changeNyberg
W
2

I also found this answer helpful: In which case that mapView:viewForAnnotation: will be called?

Whenever you call addAnnotation method

- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id < MKAnnotation >)annotation gets called. 
Weisburgh answered 14/3, 2014 at 0:25 Comment(0)
M
1

Swift 2.1:

I had the same issue, and found a quick solution, trigger this when needed, also sending it to the main thread would be wise:

var annotationsArray = mapView.annotations
mapView.removeAnnotations(mapView.annotations)
mapView.addAnnotations(arrayIncs)
arrayIncs.removeAll()
Musgrove answered 4/2, 2016 at 19:20 Comment(0)
S
0

Just spent a couple of hours to get this to work on Xamarin; this is a warning for other Xamarin developers. Make sure you use the ViewForAnnotation method and not the GetViewForAnnotation delegate. I was using the wrong method which returned new annotation views instead of the existing ones... of course it wasn't working!

Sentimentalize answered 6/9, 2018 at 20:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.