Callout an Annotation
Dropping pins on the map and displaying a callout when the user taps one is an
integral and ubiquitous map feature. With MapKit, this is handled via the
MKMapViewDelegate
protocol.
The Data
MapKit requires a data model that conforms to the MKAnnotation
protocol.
Let's start with that:
@interface LQPortlandAnnotation : NSObject <MKAnnotation>
@end
@implementation LQPortlandAnnotation
@synthesize coordinate, title, subtitle;
- (LQPortlandAnnotation *)init
{
self = [super init];
if (self) {
coordinate = (CLLocationCoordinate2D){ 45.5236, -122.6750 };
title = @"Portland!";
subtitle = @"Yee haw!!!";
}
return self;
}
@end
ArcGIS SDK for iOS has the same ability, and the data model is defined by a
delegate protocol called AGSInfoTemplateDelegate
. We're also going to have
to convert projections, so let's add some functionality to this
LQPortlandAnnotation
class. First, we'll make it implement the
AGSInfoTemplateDelegate
protocol:
@interface LQPortlandAnnotation : NSObject <MKAnnotation, AGSInfoTemplateDelegate>
@end
And add the required methods to its implementation, note that we're just returning the same property data:
- (NSString *)titleForGraphic:(AGSGraphic *)graphic screenPoint:(CGPoint)screen mapPoint:(AGSPoint *)map
{
return self.title;
}
- (NSString *)detailForGraphic:(AGSGraphic *)graphic screenPoint:(CGPoint)screen mapPoint:(AGSPoint *)map
{
return self.subtitle;
}
Finally, one more convenience method to get an AGSPoint
in WebMercator projection:
- (AGSPoint *)webMercatorPoint
{
AGSPoint *p = [AGSPoint pointWithX:self.coordinate.longitude
y:self.coordinate.latitude
spatialReference:[AGSSpatialReference wgs84SpatialReference]];
p = (AGSPoint *)[[AGSGeometryEngine defaultGeometryEngine] projectGeometry:p
toSpatialReference:[AGSSpatialReference webMercatorSpatialReference]];
return p;
}
The Pin
Dropping a pin, which is just a symbol for a location on the map, is done by
adding an MKAnnotation
to the mapView
:
[self.mapView addAnnotation:[LQPortlandAnnotation new]];
Then implementing the delegate method to return the view for it:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
static NSString *annotationId = @"com.geoloqi.MapKitReplacement.annotation";
MKAnnotationView *av = [self.mapView dequeueReusableAnnotationViewWithIdentifier:annotationId];
if (!av) {
MKPinAnnotationView *pav = [[MKPinAnnotationView alloc] initWithAnnotation:annotation
reuseIdentifier:annotationId];
pav.pinColor = MKPinAnnotationColorRed;
pav.animatesDrop = YES;
pav.canShowCallout = YES;
av = pav;
}
return av;
}
Using some of the handy methods we added to LQPortlandAnnotation
above, we
can do the same thing with ArcGIS SDK for iOS just as easily:
// call this method wherever is appropriate in the mapView's lifecycle
- (void)createCallout
{
// pdx is an instance variable
pdx = [LQPortlandAnnotation new];
static NSString *graphicsLayerKey = @"graphicsLayer";
id<AGSLayerView> lv = [self.mapView.mapLayerViews objectForKey:graphicsLayerKey];
AGSGraphicsLayer *graphicsLayer = (AGSGraphicsLayer *)lv.agsLayer;
if (!graphicsLayer) {
graphicsLayer = [AGSGraphicsLayer graphicsLayer];
[self.mapView addMapLayer:graphicsLayer withName:graphicsLayerKey];
}
AGSGraphic *dot = [AGSGraphic graphicWithGeometry:[pdx webMercatorPoint]
symbol:[AGSSimpleMarkerSymbol simpleMarkerSymbolWithColor:[UIColor blueColor]]
attributes:nil
infoTemplateDelegate:pdx];
[graphicsLayer addGraphic:dot];
[graphicsLayer dataChanged];
}
Here are the two screenshots, this time using AGSBingMapLayer
for the tile
service:

