问题
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 ?
回答1:
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 thedidUpdateLocationsdelegate method. InviewWillAppear, callstartUpdatingLocationand then indidUpdateLocations, if the accuracy and age of the location is adequate for you, callstopUpdatingLocationand then use the location (create and add the annotation, etc). - In
CustomMapAnnotationView,annotationImageobject is created and added but itsimageproperty is never set. TheannotationImageis actually never really used for anything. - The whole
CustomMapAnnotationViewclass is unnecessary for your purpose . InviewForAnnotation, code is setting theimageproperty on theCustomMapAnnotationView(instead of usingannotationImage). Thisimageproperty is inherited fromMKAnnotationView. The built-in, basicMKAnnotationViewclass is all you need for what you're doing. It already has animageproperty 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