Help needed now. I can draw lines with MKPolyline and MKPolylineView, but how to draw an arc or curve lines between two coordinates on the MKMapView? Great thanks.
Before answering the question it is important to mention that MKOverlayView is deprecated and from iOS7 and later we should use MKOverlayRenderer:
In iOS 7 and later, use the MKOverlayRenderer class to display overlays instead.
We can now break it down on how to implement the arc/curve line:
let polyline = MKPolyline(coordinates: coordinates, count: coordinates.count)
mapView.addOverlay(polyline)
MKMapView will want us to provide a proper MKOverlayRenderer in corresponding to the MKPolyline we've created at section 1. The method we need is:mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer
Which basically:
Asks the delegate for a renderer object to use when drawing the specified overlay.
MKOverlayPathRenderer which obviously inherits from MKOverlayRenderer and as documentation states:Use this renderer when your overlay's shape is defined by a CGPath object. By default, this renderer fills the overlay's shape and represents the strokes of the path using its current attributes.
So if we will use our newly subclass object as is, we will get an out-of-the-box solution which is a solid line from the 1st coordinate to the 2nd one we've defined in section 1, but since we want a curved line we will have to override a method for that:
You can use this class as-is or subclass to define additional drawing behaviors. If you subclass, override the createPath() method and use that method to build the appropriate path object. To change the path, invalidate it and recreate the path using whatever new data your subclass has obtained.
It means that inside our CustomObject: MKOverlayPathRenderer we will override createPath:
override func createPath() {
// Getting the coordinates from the polyline
let points = polyline.points()
// Taking the center of the polyline (between the 2 coordiantes) and converting to CGPoint
let centerMapPoint = MKMapPoint(polyline.coordinate)
// Converting coordinates to CGPoint corresponding to the specified point on the map
let startPoint = point(for: points[0])
let endPoint = point(for: points[1])
let centerPoint = point(for: centerMapPoint)
// I would like to thank a co-worker of mine for the controlPoint formula :)
let controlPoint = CGPoint(x: centerPoint.x + (startPoint.y - endPoint.y) / 3,
y: centerPoint.y + (endPoint.x - startPoint.x) / 3)
// Defining our new curved path using Bezier path
let myPath = UIBezierPath()
myPath.move(to: startPoint)
myPath.addQuadCurve(to: endPoint,
controlPoint: controlPoint)
// Mutates the solid line with our curved one
path = myPath.cgPath
}
We are basically done. You might want to consider adding width/stroke/color etc so you could see the curved line.
4.1. After overriding override func draw(_ mapRect: MKMapRect, zoomScale: MKZoomScale, in context: CGContext), just before adding the gradient, you will have to add the same code from section 3 but instead of changing the inner path you will have to add it to the provided context:
context.move(to: startPoint)
context.addQuadCurve(to: endPoint,
control: controlPoint)
This basically gives us a curved line we need regarding the gradient coloring.
4.2. Instead of using path.boundingBoxOfPath we will need to use path.boundingBox because:
... including control points for Bézier and quadratic curves.
Unlike boundingBoxOfPath:
... not including control points for Bézier and quadratic curves.
Hope that helps :)