I am trying to add annotations to a MKMapView
I have created a class CustomMapPin
which conforms to the MKAnnotation
protocol
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
@interface CustomMapPin : NSObject <MKAnnotation> {
CLLocationCoordinate2D coordinate;
NSString *title;
NSString *subtitle;
}
@property(nonatomic, assign) CLLocationCoordinate2D coordinate;
@property(nonatomic, copy) NSString *title;
@property(nonatomic, copy) NSString *subtitle;
@property(nonatomic, strong) NSString *type; // this is to differentiate between the different annotations on the map
@end
I have created a class CustomMapAnnotationView
which is a subclass of MKAnnotationView
CustomMapAnnotationView.h
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
@interface CustomMapAnnotationView : MKAnnotationView
@property (nonatomic, strong) UIImageView *annotationImage;
@end
CustomMapAnnotationView.m
#import "CustomMapAnnotationView.h"
@implementation CustomMapAnnotationView
-(id) initWithAnnotation:(id<MKAnnotation>)annotation reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier];
if (self) {
self.frame = CGRectMake(0, 0, 28, 40);
self.backgroundColor = [UIColor clearColor];
self.annotationImage = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 28, 40)];
self.annotationImage.contentMode = UIViewContentModeScaleAspectFit;
[self addSubview:self.annotationImage];
}
return self;
}
@end
I am adding the custom pins inside FindMechanicViewController
which is a CLLocationManagerDelegate
and MKMapViewDelegate
The code snippet is:
-(void) viewWillAppear:(BOOL)animated {
[self.locationManager startUpdatingLocation];
self.currentLocation = [self.locationManager location];
// Set the region of the map
MKCoordinateSpan mapViewSpan = MKCoordinateSpanMake(0.01, 0.01);
MKCoordinateRegion mapRegion = MKCoordinateRegionMake(self.currentLocation.coordinate, mapViewSpan);
[self.mapView setRegion:mapRegion];
// Add custom pin showing user location
CustomMapPin *annotation = [[CustomMapPin alloc] init];
annotation.title = @"Current Location";
annotation.coordinate = self.currentLocation.coordinate;
annotation.type = @"user location";
[self.mapView addAnnotation:annotation];
}
And the delegate method
-(MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
static NSString *reuseId = @"CustomMapPin";
CustomMapAnnotationView *annotationView = (CustomMapAnnotationView *) [self.mapView dequeueReusableAnnotationViewWithIdentifier:reuseId];
if ([annotation isKindOfClass:[CustomMapPin class]]) {
CustomMapPin *customAnnotation = (CustomMapPin *)annotation;
if ([customAnnotation.type isEqualToString:@"user location"]) {
[annotationView setAnnotation:customAnnotation];
[annotationView setImage:[UIImage imageNamed:@"pin_user"]];
[annotationView setCanShowCallout:YES];
}
}
return annotationView;
}
This does not show anything on the map. How do I fix this ?
In viewForAnnotation
, CustomMapAnnotationView
is never actually alloc+inited (the dequeueReusableAnnotationViewWithIdentifier
does not do this).
If viewForAnnotation
is even getting called, it must be returning nil
which means the map view must be putting a red pin somewhere (and if the delegate method isn't getting called, then again the map view will default to a red pin). Log the coordinates where the annotation is being added and look there.
A corrected viewForAnnotation
might look like this:
-(MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
if ([annotation isKindOfClass:[CustomMapPin class]]) {
static NSString *reuseId = @"CustomMapPin";
CustomMapAnnotationView *annotationView = (CustomMapAnnotationView *) [self.mapView dequeueReusableAnnotationViewWithIdentifier:reuseId];
if (!annotationView) {
annotationView = [[CustomMapAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:reuseId];
[annotationView setCanShowCallout:YES];
}
CustomMapPin *customAnnotation = (CustomMapPin *)annotation;
//view's annotation should be set regardless of "type"
[annotationView setAnnotation:customAnnotation];
if ([customAnnotation.type isEqualToString:@"user location"]) {
[annotationView setImage:[UIImage imageNamed:@"pin_user"]];
}
else {
//If it's not a "user location", then set image
//to something else otherwise image will either be nil
//or it will show some other annotation's image...
[annotationView setImage:[UIImage imageNamed:@"pin_other"]];
}
return annotationView;
}
return nil;
}
Some other issues with the code:
- In
viewWillAppear
, code is retrieving the location immediately after callingstartUpdatingLocation
. This is not guaranteed to work every time and even if it does "work", you will most likely be getting an old, cached location. When it doesn't work, the annotation will either end up at 0,0 (Atlantic Ocean) or app will crash due to invalid coordinates. It's much better to read the location in thedidUpdateLocations
delegate method. InviewWillAppear
, callstartUpdatingLocation
and then indidUpdateLocations
, if the accuracy and age of the location is adequate for you, callstopUpdatingLocation
and then use the location (create and add the annotation, etc). - In
CustomMapAnnotationView
,annotationImage
object is created and added but itsimage
property is never set. TheannotationImage
is actually never really used for anything. - The whole
CustomMapAnnotationView
class is unnecessary for your purpose . InviewForAnnotation
, code is setting theimage
property on theCustomMapAnnotationView
(instead of usingannotationImage
). Thisimage
property is inherited fromMKAnnotationView
. The built-in, basicMKAnnotationView
class is all you need for what you're doing. It already has animage
property and it automatically displays it for you (you don't need to create your ownUIImageView
).
来源:https://stackoverflow.com/questions/28703347/adding-a-custom-annotation-to-a-mkmapview