You know the Apple Map feature that lets you set a “Dropped Pin” on a Map View. I was wondering how they did it, and after a bit of research I found out:
It’s a clever combination of a long touch touch gesture recogniser, its “view-to-map-coordinate” method and your own class implementing the MKAnnotation protocol. It’s a bit complex so please bear with me.
Create an MKAnnotation Class
Let’s start from the end and work our way backwards: the MKAnnotation is an object that has the CLLocationCoordinate2D point of your map. This is the “red pin” you see on a map. Once you have it, you simply add it to your Map View.
Note that this object does not already exist which is a little weird – so we’ll have to create one first. I’ll call mine Pin and make it a subclass of NSObject. It needs to conform to the MKAnnotaion protocol, and as such it requires us to create three properties. Here’s the Pin.h file:
[emember_protected]
1 2 3 4 5 6 7 8 9 10 11 12 |
#import <Foundation/Foundation.h> @import MapKit; @interface Pin : NSObject <MKAnnotation> @property (nonatomic, readonly) CLLocationCoordinate2D coordinate; @property (nonatomic, copy) NSString *title; @property (nonatomic, copy) NSString *subtitle; - (id)initWithCoordinate:(CLLocationCoordinate2D)newCoordinate; @end |
and here’s the Pin.m file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#import "Pin.h" @implementation Pin - (id)initWithCoordinate:(CLLocationCoordinate2D)newCoordinate { self = [super init]; if (self) { _coordinate = newCoordinate; _title = @"Hello"; _subtitle = @"Are you still there?"; } return self; } @end |
Nothing much happening here, except for a custom initialiser that will on pass the newCoordinate to the necessary property. Feel free to expand this method so that you can replace the title and subtitle values, this is just an example.
We’ll make use of this class when we create our annotation.
Reacting to the Touch Gesture Recognizer
Next we’ll create the method to create that pin and stick it into our Map View. This is called as a selector from the touch gesture which we’re implementing in our Map View afterwards.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
- (void)addPin:(UIGestureRecognizer *)recognizer { if (recognizer.state != UIGestureRecognizerStateBegan) { return; } // convert touched position to map coordinate CGPoint userTouch = [recognizer locationInView:self.mapView]; CLLocationCoordinate2D mapPoint = [self.mapView convertPoint:userTouch toCoordinateFromView:self.mapView]; // and add it to our view Pin *newPin = [[Pin alloc]initWithCoordinate:mapPoint]; [self.mapView addAnnotation:newPin]; } } |
We’re only reacting to “beginning touch states”, hence the first if statement. The magic happens when we take the current user’s finger position (userTouch) and convert it into a Core Location coordinate (mapPoint). We do this via the Map View method convertPoint:toCooridinateFromView.
Now that we have a coordinate, we create a new Pin object and tell it our coordinate. The last thing we need to do is to add our newPin to the Map View. This should display a default red dropped pin.
Creating the Touch Gesture Reconginzer
Our Map View needs to be able to react to touches from our user. Let’s give it a UILongPressGestureRecognizer. We’ll add this in viewDidLoad:
1 2 3 4 5 6 7 8 9 |
- (void)viewDidLoad { [super viewDidLoad]; // add a long press gesture UILongPressGestureRecognizer *recognizer = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(addPin:)]; recognizer.minimumPressDuration = 0.5; [self.mapView addGestureRecognizer:recognizer]; } |
[/emember_protected]
We add our own class as the target and choose the selector we’ve setup in the previous step. We want to wait half a second before touches are converted into pins. The last line adds our touch gesture to the Map View.
And that’s it!
I have a working demo of this on GitHub. It includes several other Map Kit related features – take a look:
Further Reading:
1 thought on “How to let the user set a pin on your MKMapKit Map View”