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:

- (void)drawLine {
    // remove polyline if one exists
    [self.mapView removeOverlay:self.polyline];
    // create an array of coordinates from allPins
    CLLocationCoordinate2D coordinates[self.allPins.count];
    int i = 0;
    for (Pin *currentPin in self.allPins) {
        coordinates[i] = currentPin.coordinate;
    // create a polyline with all cooridnates
    MKPolyline *polyline = [MKPolyline polylineWithCoordinates:coordinates count:self.allPins.count];
    [self.mapView addOverlay:polyline];
    self.polyline = polyline;
    // create an MKPolylineView and add it to the map view
    self.lineView = [[MKPolylineView alloc]initWithPolyline:self.polyline];
    self.lineView.strokeColor = [UIColor redColor];
    self.lineView.lineWidth = 5;

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:

self.mapView.delegate = self;

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):

#import <UIKit/UIKit.h>
@import MapKit;

@interface ViewController : UIViewController <MKMapViewDelegate>


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:

- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id<MKOverlay>)overlay {
    return self.lineView;

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:

6 thoughts on “How to draw an MKPolyline on a Map View

  1. I understand and can render out the polylines but when it comes to removing them it *on the simulator* it only seems to remove overlays from the map view when the distance between two coordinates are close. I’m using [self.mapView removeOverlays:self.mapView.overlays]; to remove the overlays that are being drawn which like I said works fine with close distances but when I set my location on the simulator to San Francisco, CA and draw a line to the midwest (Columbia, MO specifically) it will draw the line fine but will never remove it. Any ideas on this? Thanks.

    1. Interesting. Does this happen on a real device too, or only on the simulator? Ive not tested this on iOS 8 yet, I’ll test it later and will let you know.

  2. Nice tutorial it helped me alot, But am unable to drag, zoomIn and ZoomOut the map, how fix this issue pls suggest me…

    1. Hi Satish, that functionality must be enabled in the Map View – it’s on by default. In your storyboard, take a look at the attributes inspector, under View – Interaction. The two tick boxes User Interaction and Muti Touch should be ticked. If they are, and you still can’t interact with the Map View, then something else is covering it – like an invisible button or anything else that will hijack touch gestures.

Leave a Reply