Search code examples
iosmapkitmapkitannotation

iOS MapKit - Is it possible to change a map pin based on map type?


I cannot find an exact question to this.

I have some custom pins that look ok on a Standard map. I want to use other pins if the map changes to Satellite or Hybrid.

Is this possible?

I've tried this so far:

    annotationImageName = @"blackPin.png";

    if (segment == 1) {
        NSLog(@"segment 1");
        annotationImageName = @"whitePin.png";
    }
    else if (segment == 2) {
        NSLog(@"segment 2");
        annotationImageName = @"greyPin.png";
    }


}

......

MKAnnotationView *annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:@"annotationPin"];

annotationView.image = [UIImage imageNamed:annotationImageName];

Solution

  • You can make your own annotation view class that observes a custom notification that you will post when the mapType property of the map view changes:

    @interface MyAnnotationView : MKAnnotationView
    @property (nonatomic, strong) id<NSObject> observer;
    @end
    
    static NSString *kMapTypeChangeNotificationKey = @"com.domain.app.maptypechange";
    
    @implementation MyAnnotationView
    
    - (void)dealloc {
        [[NSNotificationCenter defaultCenter] removeObserver:self.observer];
    }
    
    - (instancetype)initWithAnnotation:(id<MKAnnotation>)annotation reuseIdentifier:(NSString *)reuseIdentifier mapType:(MKMapType)mapType {
        self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier];
    
        if (self) {
            [self updateImageBasedUponMapType:mapType];
    
            typeof(self) __weak weakSelf = self;
            self.observer = [[NSNotificationCenter defaultCenter] addObserverForName:kMapTypeChangeNotificationKey object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
                MKMapType mapType = [note.userInfo[@"mapType"] unsignedIntegerValue];
                [weakSelf updateImageBasedUponMapType:mapType];
            }];
        }
    
        return self;
    }
    
    - (void)updateImageBasedUponMapType:(MKMapType)mapType {
        if (mapType == MKMapTypeStandard) {
            self.image = [UIImage imageNamed:@"whitePin.png"];
        } else if (mapType == MKMapTypeSatellite) {
            self.image = [UIImage imageNamed:@"greyPin.png"];
        } else {
            NSLog(@"Unexpected mapType %lu", (unsigned long)mapType);
        }
    }
    
    @end
    

    Clearly, this means that when you instantiate it, you have to pass it a reference to the map type:

    - (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
        if ([annotation isKindOfClass:[MKUserLocation class]]) { return nil; }
    
        static NSString *reuseIdentifier = @"MyCustomAnnotation";
    
        MyAnnotationView *annotationView = (id)[mapView dequeueReusableAnnotationViewWithIdentifier:reuseIdentifier];
        if (annotationView) {
            annotationView.annotation = annotation;
        } else {
            annotationView = [[MyAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:reuseIdentifier mapType:mapView.mapType];
            annotationView.canShowCallout = true;
        }
    
        return annotationView;
    }
    

    Now, when you update the mapType of the map, also post this custom annotation:

    - (IBAction)changedValueSegmentControl:(UISegmentedControl *)sender {
        if (sender.selectedSegmentIndex == 0) {
            self.mapView.mapType = MKMapTypeStandard;
        } else if (sender.selectedSegmentIndex == 1) {
            self.mapView.mapType = MKMapTypeSatellite;
        }
    
        [[NSNotificationCenter defaultCenter] postNotificationName:kMapTypeChangeNotificationKey object:self userInfo:@{@"mapType" : @(self.mapView.mapType)}];
    }