What We Did In The First Part
We created a simple mapView application with default pins / annotations. We created a default callout view and a simple annotation class.
Now, we are going to a little advance level and we will create a custom callout view and custom pins/annotations. So Lets get started.
The Final Project – Advanced Mapkit Tutorial
Prerequisites
- basic iOS programming
- basic mapkit development (refer to first part of the tutorial)
Resources And Previous Project Files.
If you did not do the first part of the tutorial, then you can download the
complete project for the first part here.
We are going to continue this tutorial from the project created in first part.
Our class name is MVViewController and our class for storing annotation objects is myAnnotation.h. All we did in the first part was created myAnnotation.h, added 3 places with titles and coordinates and used default MKPinAnnotationView to show pins.
Creating Our Custom Callout View
- Right click on MapView folder in the navigator panel and click on new file. Select ObjectiveC class and name it CalloutView and subclass as UIView.
- Again, right click on MapView folder in navaigation panel and click on new file. Select View underUser Interface tab. Click Next and make sure iPhone is selected. Click Next and save it as callOutView.
- Now select calloutView.xib and select the screen. Go to attributes inspector and set the following properties
- Size – Freeform
- Status Bar – None
- Click on size inspector and set the frame size as Width -222 , Height – 50 .
- Click on identity inspector and set class as CalloutView
- Unzip and drag the downloaded resources to your project on Xcode and make sure copy items to destination folder is selected before saving.
Next, drag an imageView to your calloutView and set the following properties
- Width – 222, Height – 50
- image – callout.png
Finally drag a UILabel and set its frame as X – 40, Y -14, width – 127 , height – 15.
- Make an outlet for UIlabel you just created onto CalloutView.h and name it calloutLabel.
Your calloutView.xib should look like this
Let’s Code
Replace the entire method in MVViewController.m
1
2
|
//6
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id)annotation { ...}
|
with
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
//6
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id)annotation {
if([annotation isKindOfClass:[MKUserLocation class]]) {
return nil;
}
//7
static NSString *identifier = @"myAnnotation";
MKAnnotationView * annotationView = (MKAnnotationView *)[self.mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if (!annotationView)
{
annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
//10
annotationView.image = [UIImage imageNamed:@"pin.png"];
}else {
annotationView.annotation = annotation;
}
return annotationView;
}
|
6) This is a delegate method for mapView which asks us to return the View( visual representation) of the pin to be shown and passes us the annotation object for which the view will be created by us.
7) check if the annotation passed is of kind MKUserLocation class, that is it is not the annotation created by us but the default annotation ( a single blue point marker that shows our current location) and we do not want to show the pin there. So we return nil if this is the case.
10)We set the pin image as pin.png (in resources folder) and return the annotationView. This was not possible with MKPinAnnotation.
Next add the following import statement at the top pf MVViewController.m
1
|
#import "CalloutView.h"
|
Finally add the following two methods in MVViewController.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
//11
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view {
if(![view.annotation isKindOfClass:[MKUserLocation class]]) {
CalloutView *calloutView = (CalloutView *)[[[NSBundle mainBundle] loadNibNamed:@"calloutView" owner:self options:nil] objectAtIndex:0];
CGRect calloutViewFrame = calloutView.frame;
calloutViewFrame.origin = CGPointMake(-calloutViewFrame.size.width/2 + 15, -calloutViewFrame.size.height);
calloutView.frame = calloutViewFrame;
[calloutView.calloutLabel setText:[(myAnnotation*)[view annotation] title]];
[view addSubview:calloutView];
}
}
//12
-(void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)view {
for (UIView *subview in view.subviews ){
[subview removeFromSuperview];
}
}
|
11)This is map delegate method which is called when a user selects a pin to show the callout View. We load the calloutView.xib from Bundle as shown in the code above and set it’s frame. The default frame is that the left edge of calloutView will be on top of the pin so set the frame accordingly. We get the title of the annotation from view parameter as view.annotation. Finally, we add our calloutView as subview of the view parameter.
12) This map delegate is called when a pin is deselected. We remove all the subviews from the view.
Run the app (Cmd+R) and you should be able to see the map with three custom pins and when you click on the pin , you should see a custom callout view.
Note: In order to see your current location do the following steps.
- Select the simulator and in the top, select (Debug->Location->Custom Location) and set the latitude as 40.740848 and longitude as -73.991134. Re- Run the app and you should be able to see a sude custom location.
That’s it! The series ends here. I hope you enjoyed coding with us.
Source Code
You can download the complete source code here
Feedback and Doubts
Please use the comments section of this post and I will gladly help you resolve your doubts.
What Next
You can further create custom buttons on the calloutView and see if you can handle them. If you face any problems use the comments section.