Draw MKPolyline Fill Color

Sam Spencer picture Sam Spencer · Feb 16, 2013 · Viewed 11.8k times · Source

I am attempting to draw a nice MKPolyline on an MKPolylineView. Everything is going great so far - the polyline draws just as I want it to and when I want it to using the following code:

[[self map] addOverlay:routeLine];

And this method to tell the app how to draw it:

- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay {
    MKOverlayView* overlayView = nil;
    self.routeLineView = [[MKPolylineView alloc] initWithPolyline:[self routeLine]];
    [[self routeLineView] setFillColor:[UIColor colorWithRed:167/255.0f green:210/255.0f blue:244/255.0f alpha:1.0]];
    [[self routeLineView] setStrokeColor:[UIColor colorWithRed:0/255.0f green:136/255.0f blue:255/255.0f alpha:1.0]];
    [[self routeLineView] setLineWidth:15.0];
    [[self routeLineView] setLineCap:kCGLineCapRound];
    overlayView = [self routeLineView];
    return overlayView;
}

As a result I get a line with a solid blue. The blue I see is not however the fill color with the stroke I am expecting - it is the blue of the stroke color. There is no fill color when I use this method. Why doesn't it draw the fill color on the polyline?

After investigating further, I found this tidbit of information in the Quick Help section of Xcode:

The MKPolylineView class provides the visual representation for an MKPolyline annotation object. This view strokes the path represented by the annotation. (This class does not fill the area enclosed by the path.) You can change the color and other drawing attributes of the path by modifying the properties inherited from the MKOverlayPathView class.

That just sounds ridiculous. I have to use this class to set the fill color, but I can't use this class to draw the fill color? That just seems very odd considering it already draws the stroke. The last line in this explanation from the documentation is a little unclear but seems to provide an answer - I'm just having a hard time coding / finding the answer. I don't have an MKOverlayPathView in my project (what is that anyway?) but it seems to be the solution - does anyone know how to use it?

Answer

Rob picture Rob · Feb 16, 2013

If you want a line of one color, and a fill of another, use a MKPolygon instead of a MKPolyline. So modify your original creation of the annotation accordingly. And then you modify your viewForOverlay (or, for iOS 7, rendererForOverlay) to recognize the MKPolygon and do something like the following:

// for iOS7+; see `viewForOverlay` for earlier versions

- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay
{
    if ([overlay isKindOfClass:[MKPolygon class]])
    {
        MKPolygonRenderer *renderer = [[MKPolygonRenderer alloc] initWithPolygon:overlay];

        renderer.fillColor   = [[UIColor cyanColor] colorWithAlphaComponent:0.2];
        renderer.strokeColor = [[UIColor blueColor] colorWithAlphaComponent:0.7];
        renderer.lineWidth   = 3;

        return renderer;
    }

    return nil;
}

// for iOS versions prior to 7; see `rendererForOverlay` for iOS7 and later

- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay
{
    if ([overlay isKindOfClass:[MKPolygon class]])
    {
        MKPolygonView *overlayView = [[MKPolygonView alloc] initWithPolygon:overlay];

        overlayView.fillColor      = [[UIColor cyanColor] colorWithAlphaComponent:0.2];
        overlayView.strokeColor    = [[UIColor blueColor] colorWithAlphaComponent:0.7];
        overlayView.lineWidth      = 3;

        return overlayView;
    }

    return nil;
}

Note, you don't need to reference any of your class properties here, as the overlay is passed to your viewForOverlay. This is a little more flexible in case you ever have multiple overlays added to your map.


Incidentally, these are my standard viewForOverlay (iOS versions prior to 7.0) and rendererForOverlay (iOS 7+), which will handle MKPolygon, MKPolyline, and MKCircle overlays:

// for iOS7+; see `viewForOverlay` for earlier versions

- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay
{
    if ([overlay isKindOfClass:[MKPolygon class]])
    {
        MKPolygonRenderer *renderer = [[MKPolygonRenderer alloc] initWithPolygon:overlay];

        renderer.fillColor   = [[UIColor cyanColor] colorWithAlphaComponent:0.2];
        renderer.strokeColor = [[UIColor blueColor] colorWithAlphaComponent:0.7];
        renderer.lineWidth   = 3;

        return renderer;
    }

    if ([overlay isKindOfClass:[MKCircle class]])
    {
        MKCircleRenderer *renderer = [[MKCircleRenderer alloc] initWithCircle:overlay];

        renderer.fillColor   = [[UIColor cyanColor] colorWithAlphaComponent:0.2];
        renderer.strokeColor = [[UIColor blueColor] colorWithAlphaComponent:0.7];
        renderer.lineWidth   = 3;

        return renderer;
    }

    if ([overlay isKindOfClass:[MKPolyline class]])
    {
        MKPolylineRenderer *renderer = [[MKPolylineRenderer alloc] initWithPolyline:overlay];

        renderer.strokeColor = [[UIColor blueColor] colorWithAlphaComponent:0.7];
        renderer.lineWidth   = 3;

        return renderer;
    }

    return nil;
}

// for iOS versions prior to 7; see `rendererForOverlay` for iOS7 and later

- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay
{
    if ([overlay isKindOfClass:[MKPolygon class]])
    {
        MKPolygonView *overlayView = [[MKPolygonView alloc] initWithPolygon:overlay];

        overlayView.fillColor      = [[UIColor cyanColor] colorWithAlphaComponent:0.2];
        overlayView.strokeColor    = [[UIColor blueColor] colorWithAlphaComponent:0.7];
        overlayView.lineWidth      = 3;

        return overlayView;
    }

    if ([overlay isKindOfClass:[MKCircle class]])
    {
        MKCircleView *overlayView = [[MKCircleView alloc] initWithCircle:overlay];

        overlayView.fillColor     = [[UIColor cyanColor] colorWithAlphaComponent:0.2];
        overlayView.strokeColor   = [[UIColor blueColor] colorWithAlphaComponent:0.7];
        overlayView.lineWidth     = 3;

        return overlayView;
    }

    if ([overlay isKindOfClass:[MKPolyline class]])
    {
        MKPolylineView *overlayView = [[MKPolylineView alloc] initWithPolyline:overlay];

        overlayView.strokeColor     = [[UIColor blueColor] colorWithAlphaComponent:0.7];
        overlayView.lineWidth       = 3;

        return overlayView;
    }

    return nil;
}

This way I can add as many of these three types of overlays to my map, and it can render all of them properly.