34

How can I draw a route between user's current location to a specific location using MapKit in Swift?

I searched a lot, but didn't find any helpful Swift-specific links or tutorials.

CodeShane
  • 6,480
  • 1
  • 18
  • 24
Rawan
  • 1,589
  • 4
  • 23
  • 47
  • Search for MKDirectionsRequest, addOverlay, and rendererForOverlay. There are Swift examples on SO of each. –  Mar 28 '15 at 16:49
  • thanks for your help. But really i didn't find any valuable result for Swift. Do you have any result or example can i start from it? – Rawan Mar 28 '15 at 18:05
  • Use Google to search SO -- the answers are here. Otherwise, look at the documentation for those classes and methods, write some code, try something. –  Mar 28 '15 at 19:54
  • checkout this one http://stackoverflow.com/questions/26563854/how-to-draw-a-route-between-two-pointsannotations-in-swift – Talha Q Apr 01 '15 at 14:20
  • @Anna i followed as you said..but couldnot succeed http://stackoverflow.com/questions/30775154/error-400-while-adding-route-on-mkmapview?noredirect=1#comment49605946_30775154 –  Jun 12 '15 at 05:05
  • I'll just link to a tutorial that shows how to do that in Swift: [Ray Wenderlich: Overlays in MapKit](http://www.raywenderlich.com/87008/overlay-views-mapkit-swift-tutorial) This should cover what you need. – Thomas Krajacic Apr 01 '15 at 14:58

4 Answers4

26

Swift 4

class MapController: UIViewController, MKMapViewDelegate {

// MARK: - showRouteOnMap

func showRouteOnMap(pickupCoordinate: CLLocationCoordinate2D, destinationCoordinate: CLLocationCoordinate2D) {

    let sourcePlacemark = MKPlacemark(coordinate: pickupCoordinate, addressDictionary: nil)
    let destinationPlacemark = MKPlacemark(coordinate: destinationCoordinate, addressDictionary: nil)

    let sourceMapItem = MKMapItem(placemark: sourcePlacemark)
    let destinationMapItem = MKMapItem(placemark: destinationPlacemark)

    let sourceAnnotation = MKPointAnnotation()

    if let location = sourcePlacemark.location {
        sourceAnnotation.coordinate = location.coordinate
    }

    let destinationAnnotation = MKPointAnnotation()

    if let location = destinationPlacemark.location {
        destinationAnnotation.coordinate = location.coordinate
    }

    self.mapView.showAnnotations([sourceAnnotation,destinationAnnotation], animated: true )

    let directionRequest = MKDirectionsRequest()
    directionRequest.source = sourceMapItem
    directionRequest.destination = destinationMapItem
    directionRequest.transportType = .automobile

    // Calculate the direction
    let directions = MKDirections(request: directionRequest)

    directions.calculate {
        (response, error) -> Void in

        guard let response = response else {
            if let error = error {
                print("Error: \(error)")
            }

            return
        }

        let route = response.routes[0]

        self.mapView.add((route.polyline), level: MKOverlayLevel.aboveRoads)

        let rect = route.polyline.boundingMapRect
        self.mapView.setRegion(MKCoordinateRegionForMapRect(rect), animated: true)
    }
}

// MARK: - MKMapViewDelegate

func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {

    let renderer = MKPolylineRenderer(overlay: overlay)

    renderer.strokeColor = UIColor(red: 17.0/255.0, green: 147.0/255.0, blue: 255.0/255.0, alpha: 1)

    renderer.lineWidth = 5.0

    return renderer
}

the image show the How to draw a route between two locations using MapKit in Swift?

  • 1
    Hey , i am using the same code but it will not draw me the path with blue line – Anita Nagori Jul 16 '18 at 09:01
  • please don't forget to use this function // MARK: - MKMapViewDelegate func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer { let renderer = MKPolylineRenderer(overlay: overlay) renderer.strokeColor = UIColor(red: 17.0/255.0, green: 147.0/255.0, blue: 255.0/255.0, alpha: 1) renderer.lineWidth = 5.0 return renderer } – Abdelrahman Mohamed Jul 16 '18 at 09:39
  • you must use MKMapViewDelegate and with func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer you will find renderer.strokeColor = UIColor(red: 17.0/255.0, green: 147.0/255.0, blue: 255.0/255.0, alpha: 1) and make renderer.lineWidth = 5.0 to be visible – Abdelrahman Mohamed Jul 16 '18 at 09:42
  • I am getting error in calculate distance error block – Anita Nagori Jul 16 '18 at 09:45
  • for testing i am calling the function this way : let locationSource = CLLocationCoordinate2D(latitude: 40.7128, longitude: 74.0060) let locationDestination = CLLocationCoordinate2D(latitude: 41.3851, longitude: 2.1734) showRouteOnMap(pickupCoordinate: locationSource, destinationCoordinate: locationDestination) M I doing something wrong ? – Anita Nagori Jul 16 '18 at 09:47
  • I use like this let sourceLocation = CLLocationCoordinate2D(latitude: pickupLatitude as! CLLocationDegrees, longitude: pickupLongitude as! CLLocationDegrees) let destinationLocation = CLLocationCoordinate2D(latitude: destLatitude as! CLLocationDegrees, longitude: destLongitude as! CLLocationDegrees) DispatchQueue.main.async { self.showRouteOnMap(pickupCoordinate: sourceLocation, destinationCoordinate: destinationLocation) } – Abdelrahman Mohamed Jul 16 '18 at 12:01
  • maybe you need to use in DispatchQueue.main.async – Abdelrahman Mohamed Jul 16 '18 at 12:02
  • @Anita: The location drawing won't work in some countries like India. Please have a look at http://www.apple.com/ios/feature-availability/#maps-directions – Anand Jul 24 '18 at 06:31
  • @Anita So I think you can use Google Maps in this part if you work on app on India. – Abdelrahman Mohamed Jul 24 '18 at 12:13
  • 1
    If someone has followed the steps of @AbdelrahmanMohamed and the route doesn't appear, maybe needs to add mapView.delegate = self. btw, why this answer hasn't been marked as the solution yet? – Asneroll Jun 18 '19 at 14:08
  • thanks, @antonio I don't know how to marked as the solution by the way you are right of course you should add `mapView.delegate = self` to use the map delegate – Abdelrahman Mohamed Jun 18 '19 at 14:18
  • @antonio__ I guess you can ask from the question owner to mark my answer as the solution – Abdelrahman Mohamed Jun 18 '19 at 14:25
  • I have 3 pinned locations. CurrentLocation -> Destination1 -> Dest2 -> Dest3. I want to traverse all these 3 locations after dest1 is arrived. From CurrentLocation to Dest1 is arrived route to Dest1->Dest2. After Dest2 is arrived go from Dest2 to Dest3. – Emre Değirmenci Mar 07 '20 at 13:00
  • I believe you can do it but you need to memorize destinations in some value and start using it after that – Abdelrahman Mohamed Mar 10 '20 at 13:24
  • This works like a charm for me. But need 1 help, want to add some padding in left and right side of the overlay so that it looks in the middle of the map. – Pooja Gupta May 20 '20 at 10:40
  • 1
    @PoojaGupta I believe you can reduce the range of zoom – Abdelrahman Mohamed May 22 '20 at 07:17
13
class MapController: UIViewController, MKMapViewDelegate {

func showRouteOnMap() {
    let request = MKDirectionsRequest()
    request.source = MKMapItem(placemark: MKPlacemark(coordinate: annotation1.coordinate, addressDictionary: nil))
    request.destination = MKMapItem(placemark: MKPlacemark(coordinate: annotation2.coordinate, addressDictionary: nil))
    request.requestsAlternateRoutes = true
    request.transportType = .Automobile

    let directions = MKDirections(request: request)

    directions.calculateDirectionsWithCompletionHandler { [unowned self] response, error in
        guard let unwrappedResponse = response else { return }

        if (unwrappedResponse.routes.count > 0) {
            self.mapView.addOverlay(unwrappedResponse.routes[0].polyline)
            self.mapView.setVisibleMapRect(unwrappedResponse.routes[0].polyline.boundingMapRect, animated: true)
        }
    }
}

func mapView(mapView: MKMapView!, rendererForOverlay overlay: MKOverlay!) -> MKOverlayRenderer! {
    if overlay is MKPolyline {
            var polylineRenderer = MKPolylineRenderer(overlay: overlay)
            polylineRenderer.strokeColor = UIColor.blueColor()
        polylineRenderer.lineWidth = 5
        return polylineRenderer
    }
    return nil
}

The return is an array of possible routes, usually we just want to show the first. The annotations are the map annotations.

Renato Probst
  • 5,914
  • 2
  • 42
  • 45
2

Swift 5 + Extension based on bdelrahman Mohamed's answer

extension MKMapView {

  func showRouteOnMap(pickupCoordinate: CLLocationCoordinate2D, destinationCoordinate: CLLocationCoordinate2D) {
    let sourcePlacemark = MKPlacemark(coordinate: pickupCoordinate, addressDictionary: nil)
    let destinationPlacemark = MKPlacemark(coordinate: destinationCoordinate, addressDictionary: nil)
    
    let sourceMapItem = MKMapItem(placemark: sourcePlacemark)
    let destinationMapItem = MKMapItem(placemark: destinationPlacemark)
    
    let sourceAnnotation = MKPointAnnotation()
    
    if let location = sourcePlacemark.location {
        sourceAnnotation.coordinate = location.coordinate
    }
    
    let destinationAnnotation = MKPointAnnotation()
    
    if let location = destinationPlacemark.location {
        destinationAnnotation.coordinate = location.coordinate
    }
    
    self.showAnnotations([sourceAnnotation,destinationAnnotation], animated: true )
    
    let directionRequest = MKDirections.Request()
    directionRequest.source = sourceMapItem
    directionRequest.destination = destinationMapItem
    directionRequest.transportType = .automobile
    
    // Calculate the direction
    let directions = MKDirections(request: directionRequest)
    
    directions.calculate {
        (response, error) -> Void in
        
        guard let response = response else {
            if let error = error {
                print("Error: \(error)")
            }
            
            return
        }
        let route = response.routes[0]
        self.addOverlay((route.polyline), level: MKOverlayLevel.aboveRoads)
        let rect = route.polyline.boundingMapRect
        self.setRegion(MKCoordinateRegion(rect), animated: true)
    }
}}
0

override func viewDidLoad() { super.viewDidLoad()

     mapView.delegate = self
    
    let sourceLocation = CLLocationCoordinate2D(latitude: 22.4649, longitude: 69.0702)
    let destinationLocation = CLLocationCoordinate2D(latitude: 23.0225, longitude: 72.5714)
    
    let span = MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1)
    
    let sourceRegion = MKCoordinateRegion(center: sourceLocation, span: span)
    mapView.setRegion(sourceRegion, animated: true)
    
    let destinationRegion = MKCoordinateRegion(center: destinationLocation, span: span)
    mapView.setRegion(destinationRegion, animated: true)
    
    let sourcePin = MKPointAnnotation()
    sourcePin.coordinate = sourceLocation
    mapView.addAnnotation(sourcePin)
    
    let destinationPin = MKPointAnnotation()
    destinationPin.coordinate = destinationLocation
    mapView.addAnnotation(destinationPin)
    

    let sourcePlacemark = MKPlacemark(coordinate: sourceLocation, addressDictionary: nil)
    let destinationPlacemark = MKPlacemark(coordinate: destinationLocation, addressDictionary: nil)
    
    let directionRequest = MKDirections.Request()
    directionRequest.source = MKMapItem(placemark: sourcePlacemark)
    directionRequest.destination = MKMapItem(placemark: destinationPlacemark)
    directionRequest.transportType = .automobile
    
    let directions = MKDirections(request: directionRequest)
    directions.calculate { (response, error) in
       guard let response = response else {
        if let error = error {
            print("Error: \(error)")
        }
            return
        }
        let route = response.routes[0]
        self.mapView.addOverlay((route.polyline), level: MKOverlayLevel.aboveRoads)
        
        let rect = route.polyline.boundingMapRect
        self.mapView.setRegion(MKCoordinateRegion(rect), animated: true)
    }

func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
     let renderer = MKPolylineRenderer(overlay: overlay)
    renderer.strokeColor = UIColor.red
    renderer.lineWidth = 4.0
    return renderer
}
Hiren
  • 260
  • 3
  • 3