I'm having trouble getting the MKAnnotationViews show up on the map in MapKit. I'm using iOS 7, and have now searched the forums and the web for many hours, trying different samples and setups.
Below I have the most basic setup possible (I think) to make it work. The app contains a single ViewController with a toolbar with a button on top, and the rest is the MapView. The button triggers the method zoomToUser:(UIBarButtonItem*)sender. Right-clicking and checking my outlets and delegates seem to be correct. I have some NSLog-statements being triggered to output some debug info.
First the VC:
#import "ViewController.h"
#import "Data.h"
@interface ViewController () <MKMapViewDelegate>
@property (weak, nonatomic) IBOutlet MKMapView *mapView;
- (IBAction)zoomToUser:(UIBarButtonItem *)sender;
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.mapView.showsUserLocation = YES;
Data *ann = [[Data alloc] init];
[self.mapView addAnnotation:ann];
}
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self zoomToUser:nil];
}
-(IBAction)zoomToUser:(UIBarButtonItem *)sender
{
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(self.mapView.userLocation.coordinate, 2000, 2000);
NSLog(@"region: %f x %f",self.mapView.userLocation.coordinate.latitude,self.mapView.userLocation.coordinate.longitude);
[self.mapView setRegion:region animated:YES ];
NSArray *arr = self.mapView.annotations;
for(int i = 0, max = arr.count; i < max; i++)
{
id<MKAnnotation> annotation = arr[i];
NSLog(@"%d of %d: %@ (%@)",i+1,max,annotation.title, [annotation class]);
}
}
- (MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>) annotation
{
NSString *reuseId = @"Test";
if([annotation isKindOfClass:[MKUserLocation class]])
{
return nil;
}
else if( [annotation isKindOfClass:[Data class]])
{
MKAnnotationView *annov = [mapView dequeueReusableAnnotationViewWithIdentifier:reuseId];
if(!annov)
{
annov = [[MKAnnotationView alloc]initWithAnnotation:annotation
reuseIdentifier:reuseId];
}
else
{
annov.annotation = annotation;
}
annov.canShowCallout = YES;
NSLog(@"Title: %@",annotation.title);
return annov;
}
else
{
return nil;
}
}
@end
And the Data
class:
#import "Data.h"
@implementation Data
-(NSString *)title
{
return @"The title";
}
-(NSString *)subtitle
{
return @"A subtitle";
}
-(CLLocationCoordinate2D)coordinate
{
CLLocationCoordinate2D coordinate;
coordinate.latitude = 60.123456;
coordinate.longitude = 10.123456;
return coordinate;
}
@end
The Data.h:
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
@interface Data : NSObject <MKAnnotation>
@end
And the ViewController.h:
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
@interface ViewController : UIViewController
@end
As you can see the viewForAnnotation
-method prints the annotations title in console when it's requested, and each time the map pans the area, it prints.
Pressing the BarButton and triggering the zoomToUser:
-method logs to console that the map indeed has two annotations, the MKUserLocation
and the one I added.
How come my MapView tells me it has the annotation, it asks for the view, it gets the view, but it does not show it?
In viewForAnnotation
, the code is creating and returning an MKAnnotationView
for annotations of type Data
.
The MKAnnotationView
class, by default, creates an empty view.
That is, its image
property is nil
and the view basically contains no content.
So, as-is, the annotation views are being created and added but they are invisible.
You can either:
Set the image
property on the MKAnnotationView
:
annov.image = [UIImage imageNamed:@"something"];
Or, simpler, create an MKPinAnnotationView
instead which is a convenient subclass of MKAnnotationView
that displays a pin image for you:
MKPinAnnotationView *annov = (MKPinAnnotationView *)[mapView dequeue...
if(!annov)
{
annov = [[MKPinAnnotationView alloc]initWithAnnotation:annotation
reuseIdentifier:reuseId];
annov.pinColor = MKPinAnnotationColorRed; //or Green or Purple
}
else
...
Unrelated:
You've created a custom annotation class Data
which is fine but if all you need are the three properties title
, subtitle
, and coordinate
, then you can just use the pre-defined MKPointAnnotation
class which lets you set all the properties per-instance:
MKPointAnnotation *ann = [[MKPointAnnotation alloc] init];
ann.title = @"The title";
ann.subtitle = @"A subtitle";
ann.coordinate = CLLocationCoordinate2DMake (60.123456, 10.123456);
[self.mapView addAnnotation:ann];