Tag Archives: Map Kit

How to draw an MKPolyline on a Map View

Screen Shot 2014-04-01 at 12.56.53Following on from my previous article in which we’ve discussed how to set Pin Annotations to a Map View, let’s discuss how we can connect those pins to draw a user definable route.

In MKMapView speak, this is a polyline which is created from a series of points or coordinates. Once the polyline is created, we need to add it to the Map View. Weirdly, this does not draw the line yet.

Much like with Pin Annotations, we also need to create an overlay view (MKPolylineView) in which we can specify our line colour and thickness, and return this object via a delegate method.

All this is a LOT more complicated than it really needs to be. When I researched how to do this over the last week all I could find were sketchy and scary code snippets from 4 years ago, and – surprise surprise – nothing in the Apple documentation.

Let’s see how to do this as of 2014. Note that as of iOS 7 both MKPolyline and MKPolylineView are deprecated – but this approach still works fine if your app needs to support iOS 6 and lower.

Creating the Polyline and Polyline View

To create our polyline we either need a collection of points or CLLocation Coordinates. I will use the latter, as we get them from positions the user can define. Check out my previous article on how to do this.

Mine are saved as Pin objects (annotation markers) which contain the coordinates. Because a CLLocationCoordinate2D object is a construct it is not possible to pass those directly to the polyline. To make the polyline init method understand this we need to create an array of those first.

Here’s a rather complex method which will do all this:

Let’s go through this step by step, and don’t worry if this doesn’t make too much sense at first glance

We want to call this method whenever the user adds a new Pin to the Map. So first we’ll remove a previous overlay, otherwise we’ll keep adding incremental polylines to our map view.

Next we’ll loop through our array of Pins and extract only the coordinate objects from them. We’ll add those to a new array called coordinates.

Then we create an MKPolyline object with our coordinates and add it to the map view. Note that we also have to specify how many coordinates are in that array.

And finally we create an MKPolylineView object which will be responsible for displaying our line. You can specify line thickness and colour, as well as a few other funky parameters here.

The Map View Delegate

Our Map View must implement the MKMapViewDelegate protocol for this line to show up. You can either connect the map view in interface builder and make our class the delegate, or simply add it in code with something like this:

We also need to declare that we’re implementing this protocol in our header file (and import MapKit of course… sometimes we forget the little things):

Now we need to implement the following method from said protocol. This one is called by the map view and basically asks “Hey, do we have an overlay for this map? If so, give it to me”. We return nil if there isn’t one, or – as in our case – return the overlay which we’ve created earlier:

Note that this method – like MKPolyline and MKPolylineView – is deprecated in iOS 7. They would like us to use the MKRenderer equivalents from now on. Because I do care about my iOS 5 and 6 users, I’ll stick to these terribly deprecated methods though – which work perfectly fine for this exercise.

Where and when to call that method

Every time a user sets a new Pin on their map, we can call the above drawLine method and the line is extended to the next Pin.

There is one snag – and I have no idea why this happens: our line only gets drawn every second time we call it. So to make sure the line actually shows up on the map, we need to call it TWICE. I know it’s a hack, but life is too short – and there is no reason why this happens.

If you know an explanation, drop me a line and I’ll give you a free lifetime membership to this site 😉

Full Project on GitHub

I have a fully working project on GitHub which lets users set Pins as described and draws lines in between each point. I’ve even implemented an “undo last point” routine. Check it out:

How to let the user set a pin on your MKMapKit Map View

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.

Screen Shot 2014-03-20 at 15.56.03

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:

This content is for members only.

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:

How to feed a location into an MKMapView

mapsNow that we know how to read a location from our device, here’s how to display that location in a Map View. All we need is a referenced MKMapView in your storyboard, and to import the MapKit framework into our project / class.

For the map view to display our coordinates, we need to create an MKCoordinateRegion and give it those coordinates. We can optionally pass Delta values for longitude and latitude which will specify how zoomed in the map view will appear. Smaller values are closer, larger values are further away from our region.

This method takes a CLLocation from our previous step and sets them in our map view:

Setting a Pin on the centre

Notice how the above displays the map correctly, but no red centre pin. To do that we need to create an MKPlacemark with the same longitude and latitude as above:

This content is for members only.

This method is called asynchronously and hence uses a block which is called upon completion. In it we’ll access the place marks array and grab the last object, then access its many exciting properties. Note that sometimes that information is not available, hence I’m querying the first one before updating a label (which would show NULL if no information is present).

I’ve created a Demo Project on GitHub called Locations – feel free to fork and examine how it works.