MKMapview annotation dynamic pin image changes after zooming
Asked Answered
S

3

5

I am working on a little project that shows 7 different types of annotations on the map. My annotations are taken from a url result in array and I parse it using JSON. I have lots of annotations and everything seems to look good once the map loads. After zooming in and zooming out, the the pin images changes for some reason to the wrong pin image (a specific image, no clue why).

I am sure I am missing something here...may you please help :) ?

Here's one part of my code, let me know if you need anymore of it:

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

static NSString *identifier;

if(_mapView.tag==1){identifier = @"TurbulencePin";}
if(_mapView.tag==2){identifier = @"IcingPin";}
if(_mapView.tag==3){identifier = @"WindsPin";}
if(_mapView.tag==4){identifier = @"TemperaturePin";}
if(_mapView.tag==5){identifier = @"CloudsPin";}
if(_mapView.tag==6){identifier = @"VisibilityPin";}
if(_mapView.tag==7){identifier = @"MultiplePin";}


if ([annotation isKindOfClass:[MKUserLocation class]])
    return nil;

if ([annotation isKindOfClass:[Annotation class]]) {

    CustomAnnotationView* annotationView = (CustomAnnotationView*)[mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
    annotationView = nil;

    if (annotationView == nil) {

        annotationView = [[CustomAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
        annotationView.enabled = YES;
        annotationView.canShowCallout = YES;


        UIImage *img = [UIImage imageNamed:[NSString stringWithFormat:@"%@.png",identifier]];
        annotationView.image = img;

    }
else
    {

        annotationView.annotation = annotation;

    }


    return annotationView;

}
return nil;

}

Update:

Based upon feedback of others, I've modified the code for the image setting to be as follows:

 Annotation *myCustomAnn = (Annotation *)annotation;
 NSString *imgName = myCustomAnn.imageName;
 UIImage *img = [UIImage imageNamed:[NSString stringWithFormat:@"%@Pin.png",imgName]];
 annotationView.image = img;

 return annotationView;

Plus, I removed the annotationView = nil;

However, I cannot set the image name in the annotation.m as a hardcoded value because I need to display a different pin image for each annotation. I'm sure that there is an explanation but the only value that I can get from the annotation.m under the mapView:viewForAnnotation: is the annotation coordinates (myCustomAnn.coordinate.latitude and myCustomAnn.coordinate.longitude), I have no clue how to get other properties from the annotation.m

The other properties, such as title, imgname etc comes back as null

Slow answered 10/5, 2013 at 22:48 Comment(8)
This annotation type should be a property of the annotation, not using the map's tag property for this. (As an aside, your identifier doesn't need to be static if you're setting it programmatically.) But the only thing that I could imagine would cause the pin images to change is if the tag is changing somehow. I'd suggest logging that and see what's going on (but, even better, abandon tag and use a custom property for your annotation).Pecoraro
By the way, I hope you don't have seven maps open at the same time. And if you do, don't use _mapView (which is some ivar) but rather use mapView, the parameter to this method.Pecoraro
You said "cannot set the image name in annotation.m ...". Correct. You shouldn't do it in annotation.m. In your annotation.h you should simply define a new property for imageName. Then in your code that creates the annotation (your view controller?) after creating the annotation, that's where you set the imageName property for your annotation. Regarding your other properties being null, the question is whether you set those properties in your VC where you did the alloc/init of the annotation object, but before you do addAnnotation call.Pecoraro
I feed the annotations properties by parsing an array of many annotations using JSON. The array includes the coordinates, title etc. of each annotation. So basically, the coordinates and the rest of the properties are being set at the same time and the only property that I can pass from annotation.m to the mapView:viewForAnnotation: is the coordinates... :/ What am I missing And Rob, for your question, no, I use only one mapSlow
Roy, you say you "feed the annotation properties" from the object created by JSON. Is there not a field in that JSON that dictates whether this is to be wind annotation or a cloud or whatever? Perhaps you can update your question telling us precisely (a) what these coordinates you're parsing physically represent; and (b) what these seven annotation view images represent. Clearly we're all assuming that the annotation view's image is a function of the type of annotation. Is that not the case?Pecoraro
Yes, I did not explained myself properly. I get an array from a url result, them using JSON I parse the array into variables, then I create an annotation and set it's properties starting like this: Annotation *ann = [[Annotation alloc] initWithLocation:coordinate]; [_mapView addAnnotation:ann]; ann.title = ReportType; ann.ReportTime = ReportTime; And so on, so basically, the only property that is being fed initially when the annotation is created, is the coordinations... anyway to set more properties ont he initial part of creating the annotation?Slow
You should not add the annotation to your map until you're done setting all of the properties for that annotation. The addAnnotation should be the last step. Or alternatively, sometimes I'll add the annotations to a NSMutableArray, tweak them to my heart's content, and then, at the very end, I'll use addAnnotations (note the s), passing it my array.Pecoraro
Rob, you just solved my question. I moved the "addAnnotations" to the end, after setting up all properties and it solved everything! Great point and thank you for your prompt help! Well done!Slow
P
0

One issue is that the viewForAnnotation is determining the correct image to show based upon a class instance variable. Generally the identifier for the annotation's image would be a property of the custom annotation itself, not some external instance variable.

On top of that, it appeared that the annotation was being added to the map before all of the annotation's properties were being set. One should defer the addAnnotation until all of the annotation's properties are set.

Alternatively, you can add the annotations to a NSMutableArray, tweak them as you see fit, and only add the annotations at the very end using the addAnnotations (note the s), passing it the array.

Pecoraro answered 11/5, 2013 at 16:4 Comment(0)
B
5

The main problem is that the code in viewForAnnotation is relying on the outside variable _mapView.tag to determine the annotation view.

It is unsafe to assume when and how frequently the viewForAnnotation delegate method will be called by the map.

If an annotation's view depends on certain values, it is generally best to embed those values directly in the annotation object itself. This way, in the viewForAnnotation delegate method, you can reference those annotation-specific values through the annotation parameter that is passed to the method. Those annotation-specific values should be set when creating the annotation (before calling addAnnotation).

For some more details and examples, see:

A separate issue is the code is setting annotationView to nil after calling dequeueReusableAnnotationViewWithIdentifier which defeats the dequeue.


At least in viewForAnnotation, the corrected code where it handles the Annotation class might look like this (not the whole method -- just the part inside the second if):

static NSString *identifier = @"ann";

CustomAnnotationView* annotationView = (CustomAnnotationView*)[mapView 
    dequeueReusableAnnotationViewWithIdentifier:identifier];

//annotationView = nil;  // <-- remove this line

if (annotationView == nil) 
{
    annotationView = [[CustomAnnotationView alloc] 
                         initWithAnnotation:annotation 
                         reuseIdentifier:identifier];
    annotationView.enabled = YES;
    annotationView.canShowCallout = YES;
}
else
{
    annotationView.annotation = annotation;
}

//MOVE the setting of the image to AFTER the dequeue/create 
//because the image property of the annotation view 
//is based on the annotation and we can update image in one place
//after both the dequeue and create are done...

//Get the image name from the annotation ITSELF 
//from a custom property (that you add/set)
Annotation *myCustomAnn = (Annotation *)annotation;

NSString *imgName = myCustomAnn.imageName;  
//imageName is the custom property you added/set

UIImage *img = [UIImage imageNamed:
                   [NSString stringWithFormat:@"%@.png",imgName]];

annotationView.image = img;

return annotationView;
Bayberry answered 11/5, 2013 at 13:14 Comment(3)
I agree regarding the use of annotation properties. I'd vote for a enum annotation type parameter rather than the name of the string, tho. And I personally don't mind the OP's use of a custom annotation identifier for each type of annotation view (rather than your resetting the image property regardless of whether you have to or not). Also, I presume that the OP's setting of annotationView = nil was not a bug on his part, but rather a deliberate diagnostic process to temporarily take the dequeue... logic out of the loop, thereby simplifying the problem.Pecoraro
@Rob, I agree with all your points. My main intent was to point out the misuse of the external variable.Bayberry
Agreed. It was especially curious to reference the _mapView ivar when the actual mapView was passed to the method. In retrospect, the use of the tag is worrisome, not only for the reasons you correctly point out, but it might also imply that he has seven mapviews open at the same time (which strikes me as an extravagant use of system resources). +1Pecoraro
S
0

SOLVED by Rob

The problem was, that I could not retrive the annotation properties which I set at my VC. The reason was that I used the [_mapView addAnnotation:ann]; command, before I set all the annotation's properties. That said, by moving this line to the end part of the annotation initial properties setup, solved the problem.

Thank you all and big thanks to Rob! I'm up to my next challenge.

Slow answered 11/5, 2013 at 15:53 Comment(0)
P
0

One issue is that the viewForAnnotation is determining the correct image to show based upon a class instance variable. Generally the identifier for the annotation's image would be a property of the custom annotation itself, not some external instance variable.

On top of that, it appeared that the annotation was being added to the map before all of the annotation's properties were being set. One should defer the addAnnotation until all of the annotation's properties are set.

Alternatively, you can add the annotations to a NSMutableArray, tweak them as you see fit, and only add the annotations at the very end using the addAnnotations (note the s), passing it the array.

Pecoraro answered 11/5, 2013 at 16:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.