jbeu425
jbeu425

Reputation: 185

MapKit annotation view labels changing position/showing in wrong annotation when dequeued

I'm adding labels just underneath my map pins to show some detail of the pin. I'm doing this by adding a UILabel as a subview of the annotationView and setting it's number of lines to 0 in order to display multiple lines on the same label. I'm having an issue where the map is initially loading the correct annotations and I can see the correct data in the labels for the map pin, but when I navigate to a different screen and then go back to the map, the map pins are all in the right places but the labels beneath them no longer show the correct data in the label, they all get mixed up. The map pins start showing data belonging to other map pins. Also if I move the map around a bit and then go back to the same pin, same thing happens.

I'm adding data to the label in the viewForAnnotation method:

- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation{
// Return the view marker different for editing items ie draggable undragrable.

    if ([annotation isKindOfClass:[PSMapAnnotation class]] == YES) {
        PSMapAnnotation *senderAnnotation = (PSMapAnnotation *)annotation;
        PSMapAnnotationView *annotationView = (PSMapAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:[senderAnnotation getPinKey]];

        if (annotationView == nil) {
            annotationView = [[PSMapAnnotationView alloc]
                              initWithAnnotation:senderAnnotation
                              reuseIdentifier:[senderAnnotation getPinKey]];
            



            // THIS LINE RETURNS A STRING ARRAY CONTAINING LABEL DATA

            NSMutableArray *taggedAnswers = [self _getTaggedAnswersForMarkerLabel:annotationView];
            

            annotationView.enabled = YES;
            annotationView.canShowCallout = NO;
            
            UILabel *annotationLabel = [[UILabel alloc] init];
            
            annotationLabel.text = @"";
            annotationLabel.numberOfLines = 0;
            annotationLabel.font = [UIFont fontWithName:@"HelveticaNeue" size:14.0];
            annotationLabel.textColor = [UIColor blackColor];
            annotationLabel.textAlignment = NSTextAlignmentCenter;

            // LOOP THROUGH STRING ARRAY AND ADD ALL STRINGS TO ANNOTATION LABEL
            
            for (NSString* taggedAnswer in taggedAnswers)
            {
                
                NSString *textToShow = [NSString stringWithFormat: @"%@\n", taggedAnswer];

                annotationLabel.text = [annotationLabel.text stringByAppendingString: textToShow];

            }
            
            [annotationLabel sizeToFit];
            annotationLabel.center = CGPointMake(annotationView.center.x, annotationView.center.y);
            
            
            annotationView.myLabel = annotationLabel;
            [annotationView addSubview:annotationLabel];
            
            
            [annotationView setAnnotation:annotation];
            

        }
        else {
            
            [annotationView setAnnotation:annotation];
            
        }
        return annotationView;

Upvotes: 2

Views: 634

Answers (1)

Rob
Rob

Reputation: 437372

All of your code for configuring the annotation view is inside the if (annotationView == nil) { ... } block. So, if it successfully reused an annotation view (your else block), you’re not setting the label.

You should move the annotationLabel.text = ... code outside of this if statement. E.g.

if (annotationView == nil) {
     // instantiate annotation view and add subview, but don’t set the `text` of the label yet
} else {
     annotationView.annotation = annotation;
}

annotationView.annotationLabel.text = ...

For example

//  CustomAnnotationView.h

@import UIKit;
@import MapKit;

@interface CustomAnnotationView : MKPinAnnotationView
@end

And

//  CustomAnnotationView.m

#import "CustomAnnotationView.h"

@interface CustomAnnotationView ()
@property (nonatomic) UILabel *label;
@end

@implementation CustomAnnotationView

- (instancetype)initWithAnnotation:(id<MKAnnotation>)annotation reuseIdentifier:(NSString *)reuseIdentifier {
    self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier];
    if (self) {
        self.enabled = YES;
        self.canShowCallout = NO;

        self.label = [[UILabel alloc] init];
        self.label.translatesAutoresizingMaskIntoConstraints = false;
        self.label.text = @"";
        self.label.numberOfLines = 0;
        self.label.font = [UIFont fontWithName:@"HelveticaNeue" size:14.0];
        self.label.textColor = [UIColor labelColor];
        self.label.textAlignment = NSTextAlignmentCenter;
        [self addSubview:self.label];

        [NSLayoutConstraint activateConstraints:@[
            [self.label.topAnchor constraintEqualToAnchor:self.bottomAnchor constant:8],
            [self.label.centerXAnchor constraintEqualToAnchor:self.centerXAnchor constant:-self.centerOffset.x]
        ]];

        [self updateForAnnotation:annotation];
    }

    return self;
}

- (void)setAnnotation:(id<MKAnnotation>)annotation {
    [super setAnnotation:annotation];
    [self updateForAnnotation:annotation];
}

- (void)updateForAnnotation:(id<MKAnnotation>)annotation {
    self.label.text = annotation.title;
}

@end

That yields:

enter image description here

Clearly customize this as you see fit, but hopefully it illustrates the idea.

Note the use of constraints to make sure that the label is always centered.

Probably needless to say, here I am using the title property of the annotation, but you could check for your annotation subclass and grab whatever property you want, e.g. your textToShow. But given that there is already a property for associating a text string with an annotation, either title (and optionally subtitle, if you need it), I would use that.


You might consider moving the adding of the label into the PSMapAnnotationView init method, so the annotation view takes care of configuring its subviews. And you can override the setter of the annotation property for PSMapAnnotationView to set the label text for you.

This has the benefit of (a) it simplifies your MKMapViewDelegate code; (b) keeps subview configuration where it belongs; and (c) opens up the door for completely eliminating viewForAnnotation if only supporting iOS 11 and later. E.g. in viewDidLoad add a line that says:

[self.mapView registerClass:[CustomAnnotationView class] forAnnotationViewWithReuseIdentifier:MKMapViewDefaultAnnotationViewReuseIdentifier];

And then you can remove viewForAnnotation entirely (unless you need support for multiple annotation types).

Upvotes: 2

Related Questions