Search code examples
iosnavigationannotationsmkmapview

Navigation from user location to annotation


so I want to set up a map with navigation from the users location to a annotation that the user selected. I Have all the annotations loaded from a .Plist . someone help me out I'm just a beginner. this is my code for the map:

import UIKit
import MapKit
import CoreLocation

class MapViewController: UIViewController, CLLocationManagerDelegate, UISearchBarDelegate {

    @IBOutlet weak var mapView: MKMapView!

    @IBAction func searchButton(_ sender: Any) {
        let searchController = UISearchController(searchResultsController: nil)
        searchController.searchBar.delegate = self
        present(searchController, animated: true, completion: nil)
    }

    let locationManager = CLLocationManager()

    // Vraag om locatie van gebruiker
    func requestLocationAccess()
    {
        let status = CLLocationManager.authorizationStatus()

        switch status
        {
        case .authorizedAlways, .authorizedWhenInUse:
            return

        case .denied, .restricted:
            print("location access denied")

        default:
            locationManager.requestWhenInUseAuthorization()
        }
    }

    class func createViewAnnotationForMap(mapView:MKMapView, annotation:MKAnnotation)->MKAnnotationView
    {
        if let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "PinAnnotation"){
            return annotationView
        }
        else
        {
            let returnedAnnotationView:MKPinAnnotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier:"PinAnnotation")
            returnedAnnotationView.animatesDrop = false
            returnedAnnotationView.canShowCallout = true
            return returnedAnnotationView

        }
    }

    func searchBarSearchButtonClicked(_ searchBar: UISearchBar)
    {
        //Ignoring user
        UIApplication.shared.beginIgnoringInteractionEvents()

        //Activity Indicator
        let activityIndicator = UIActivityIndicatorView()
        activityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.gray
        activityIndicator.center = self.view.center
        activityIndicator.hidesWhenStopped = true
        activityIndicator.startAnimating()

        self.view.addSubview(activityIndicator)

        //Hide search bar
        searchBar.resignFirstResponder()
        dismiss(animated: true, completion: nil)

        //Create the search request
        let searchRequest = MKLocalSearchRequest()
        searchRequest.naturalLanguageQuery = searchBar.text

        let activeSearch = MKLocalSearch(request: searchRequest)

        activeSearch.start { (response, error) in

            activityIndicator.stopAnimating()
            UIApplication.shared.endIgnoringInteractionEvents()

            if response == nil
            {
                print("ERROR")
            }
            else
            {
                //Getting data
                let latitude = response?.boundingRegion.center.latitude
                let longitude = response?.boundingRegion.center.longitude

                //Create annotation
                let annotation = MKPointAnnotation()
                annotation.title = searchBar.text
                annotation.coordinate = CLLocationCoordinate2DMake(latitude!, longitude!)
                self.mapView.addAnnotation(annotation)

                //Zooming in on annotation
                let coordinate:CLLocationCoordinate2D = CLLocationCoordinate2DMake(latitude!, longitude!)
                let span = MKCoordinateSpanMake(0.1, 0.1)
                let region = MKCoordinateRegionMake(coordinate, span)
                self.mapView.setRegion(region, animated: true)
            }

        }
    }

    func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
        let renderer = MKPolylineRenderer(polyline: overlay as! MKPolyline)
        renderer.strokeColor = UIColor.blue
        return renderer
    }

    override func viewDidLoad()
    {
        super.viewDidLoad()
        requestLocationAccess()
        mapView.isZoomEnabled = true
        mapView.isScrollEnabled = true
        mapView.showsScale = true
        mapView.isRotateEnabled = true
        mapView.showsUserLocation = true
        mapView.showsCompass = true
        mapView.delegate = self

        //Regio instellen
        var newRegion:MKCoordinateRegion = MKCoordinateRegion()
        newRegion.center.latitude = -25.106303;
        newRegion.center.longitude = 133.587542;
        newRegion.span.latitudeDelta = 22;
        newRegion.span.longitudeDelta = 22;

        self.mapView.setRegion(newRegion, animated: true)

        if let cityDetailsPath = Bundle.main.path(forResource: "places", ofType: "plist") {
            guard let cityDetails = NSArray(contentsOfFile: cityDetailsPath) as? [[String: String]] else {return}

            for city in cityDetails
            {
                guard let latStr = city["latitude"] else { continue }
                guard let lonStr = city["longitude"] else { continue }
                guard let titleStr = city["title"] else { continue }
                guard let subtitleStr = city["subTitle"] else { continue }

                if let lat = Double(latStr) {
                    if let lon = Double(lonStr) {
                        let annotation = MKPointAnnotation()
                        annotation.coordinate = CLLocationCoordinate2DMake(lat,lon)
                        annotation.title = titleStr
                        annotation.subtitle = subtitleStr
                        mapView.addAnnotation(annotation)
                    }
                }
            }
        }
    }

    override func didReceiveMemoryWarning()
    {
        super.didReceiveMemoryWarning()

    }
}

extension MapViewController: MKMapViewDelegate {
    func createViewAnnotationForMap(mapView:MKMapView, annotation:MKAnnotation)->MKAnnotationView {
        let annoannotationId = "PinAnnotation"
        if let annotationView = self.mapView.dequeueReusableAnnotationView(withIdentifier: annoannotationId) {
            return annotationView
        }
        else
        {
            let returnedAnnotationView:MKPinAnnotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: annoannotationId)
            returnedAnnotationView.animatesDrop = false
            returnedAnnotationView.canShowCallout = true
            return returnedAnnotationView
        }

    }
}

Solution

  • Use didSelect delegate method.

    e.g.

    func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
        guard let annotation = view.annotation else {
            return
        }
    
        let directionRequest = MKDirectionsRequest()
        directionRequest.source = MKMapItem.forCurrentLocation()
        directionRequest.destination = MKMapItem(placemark: MKPlacemark(coordinate: annotation.coordinate))
        directionRequest.transportType = .automobile
        let directions = MKDirections(request: directionRequest)
    
        directions.calculate {
            (response, error) -> Void in
            guard let response = response else {
                if let error = error {
                    print("Error: \(error)")
                }
                return
            }
    
            if !response.routes.isEmpty {
                let route = response.routes[0]
                DispatchQueue.main.async { [weak self] in
                    self?.mapView.add(route.polyline)
                }
            }
        }
    }
    

    Don't forget guard to prevent crash.

    func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) ->
        MKOverlayRenderer {
            guard overlay is MKPolyline else {
                return MKPolylineRenderer()
            }
        ...
    }