Today, I spent roughly 5 hours, off and on, trying to figure out why the following custom MKOverlayRenderer wasn’t rendering a line:

final class MyOverlay: NSObject, MKOverlay {
    var startCoordinate: CLLocationCoordinate2D
    var endCoordinate: CLLocationCoordinate2D

    init(start: CLLocationCoordinate2D, end: CLLocationCoordinate2D) {
        self.startCoordinate = start
        self.endCoordinate = end
        super.init()
    }
}

final class MyOverlayRenderer: MKOverlayRenderer {
    var strokeColor: Color = .clear
    var lineWidth: CGFloat = 2

    var myOverlay: MyOverlay { return self.overlay as! MyOverlay }

    override func draw(_ mapRect: MKMapRect, zoomScale: MKZoomScale, in context: CGContext) {
        guard self.startPoint != CGPoint.zero && self.endPoint != CGPoint.zero else {
            return
        }
        context.saveGState()

        let path = CGMutablePath()
        path.addLines(between: [self.startPoint, self.endPoint])

        context.setStrokeColor(self.strokeColor.cgColor)
        context.setLineWidth(self.lineWidth)

        context.addPath(path)
        context.drawPath(using: .stroke)

        context.restoreGState()
    }

    private var startPoint: CGPoint { return self.point(for: MKMapPoint(self.myOverlay.startCoordinate)) }
    private var endPoint: CGPoint { return self.point(for: MKMapPoint(self.myOverlay.endCoordinate)) }
}

Which was rendering as below (circles are where the two ends of the line segments should be):

I spent hours trying to figure out why these two points weren’t rendering.

Eventually, looking through the docs, I noticed the MKRoadWidthAtZoomScale(_:) function, and wondered if I should use that as a scaling factor for my line.

So, I did that. I changed the setLineWidth line to be: context.setLineWidth(MKRoadWidthAtZoomScale(zoomScale) * max(self.lineWidth, 1)), and ran it.

Behold, it works:

But, hey, in the future. If you’re looking at MKMapView, with a custom MKOverlayRenderer, and are trying to draw line segments, remember: MULTIPLY YOUR LINE WIDTH BY MKRoadWidthAtZoomScale(_:).

For reference, the full code is below:

final class MyOverlay: NSObject, MKOverlay {
    var startCoordinate: CLLocationCoordinate2D
    var endCoordinate: CLLocationCoordinate2D

    init(start: CLLocationCoordinate2D, end: CLLocationCoordinate2D) {
        self.startCoordinate = start
        self.endCoordinate = end
        super.init()
    }
}

final class MyOverlayRenderer: MKOverlayRenderer {
    var strokeColor: Color = .clear
    var lineWidth: CGFloat = 2

    var myOverlay: MyOverlay { return self.overlay as! MyOverlay }

    override func draw(_ mapRect: MKMapRect, zoomScale: MKZoomScale, in context: CGContext) {
        guard self.startPoint != CGPoint.zero && self.endPoint != CGPoint.zero else {
            return
        }
        context.saveGState()

        let path = CGMutablePath()
        path.addLines(between: [self.startPoint, self.endPoint])

        context.setStrokeColor(self.strokeColor.cgColor)
        let width = MKRoadWidthAtZoomScale(zoomScale) * max(1, self.lineWidth)
        context.setLineWidth(width)

        context.addPath(path)
        context.drawPath(using: .stroke)

        context.restoreGState()
    }

    private var startPoint: CGPoint { return self.point(for: MKMapPoint(self.myOverlay.startCoordinate)) }
    private var endPoint: CGPoint { return self.point(for: MKMapPoint(self.myOverlay.endCoordinate)) }
}