MK Directions Request - direction line is not shown - ios

I have successfully implemented location of a point of interest and my location. Both is shown. Now, I would like to get calculated the route between two points and a blue line should be shown. Unfortunately, when I am clicking on the button, no line is being shown.
I really appreciate help/hints. Thanks so much.
import UIKit
import MapKit
class MapViewController: UIViewController, MKMapViewDelegate
{
//outlet variable is used for establishing a connection with the
// map view in the storyboard
#IBOutlet var mapView: MKMapView!
var spot = Spot()
let locationManager = CLLocationManager()
var currentPlacemark:CLPlacemark?// it is used to save the selected spot
override func viewDidLoad()
{
super.viewDidLoad()
//request for a user's authorization for lacation services
locationManager.requestWhenInUseAuthorization()
let status = CLLocationManager.authorizationStatus()
if status == CLAuthorizationStatus.authorizedWhenInUse
{
mapView.showsUserLocation = true
}
let geoCoder = CLGeocoder()
geoCoder.geocodeAddressString(spot.location,
completionHandler:
{ placemarks, error in
if let error = error {
print(error)
return
}
if let placemarks = placemarks{
//get the first placemark
let placemark = placemarks[0]
// value of current Placemark
self.currentPlacemark = placemark
// add annotation
let annotation = MKPointAnnotation()
annotation.title = self.spot.name
annotation.subtitle = self.spot.type
if let location = placemark.location{
annotation.coordinate = location.coordinate
//display the annotation
self.mapView.showAnnotations([annotation],animated:true)
self.mapView.selectAnnotation(annotation, animated: true)
}
}
})
mapView.showsCompass = true
mapView.showsTraffic = true
mapView.showsScale = true
// we want to show the users location
mapView.showsUserLocation = true
}
#IBAction func showDirection(sender:AnyObject)
{
// we make sure if current placemark contains a value using a guard statement. Otherwise just
// skip everything
guard let currentPlacemark = currentPlacemark else
{
return
}
// creating an instance of MKDirectionsRequest to request directions
let directionRequest = MKDirectionsRequest()
// set the source(where the user currently is) and destination of the route
directionRequest.source = MKMapItem.forCurrentLocation()// retrieving the current location
let destinationPlacemark = MKPlacemark(placemark:currentPlacemark)
directionRequest.destination = MKMapItem(placemark: destinationPlacemark)
directionRequest.transportType = MKDirectionsTransportType.automobile// later change for transit
// calculate the direction
let directions = MKDirections(request: directionRequest)
// this method initiates an asynchronous request for directions and calls
// your completion handler when the request is conpleted. The MKDirections object
//passes my request to the Apple servers ans asks for route-based directions data
directions.calculate { (routeRepsonse, routeError) -> Void in
guard let routeResponse = routeRepsonse else
{
if let routeError = routeError
{
print("Error:\(routeError)")
}
return
}
let route = routeRepsonse?.routes[0]// provides a container for saving the route information so that the routes are saved in the routes property
// The detailed route geometry is e.g. route.polyline is represented by an MKPolyline object
// the add level method is used to add an MKPolyline object to the existing map view
self.mapView.add((route?.polyline)!,level: MKOverlayLevel.aboveRoads)
}
}
// implementing a mapView method which draws the route
func mapView(_mapView:MKMapView,rendererFor overlay: MKOverlay) -> MKOverlayRenderer
{
let renderer = MKPolylineRenderer(overlay: overlay)
renderer.strokeColor = UIColor.blue
renderer.lineWidth = 3.0
return renderer
}
override func didReceiveMemoryWarning()
{
super.didReceiveMemoryWarning()
// Dispose of any resources hat can be recreated.
}
}

Maybe delegate method name is wrong. Rename
func mapView(_mapView:MKMapView,rendererFor overlay: MKOverlay) -> MKOverlayRenderer
with
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer

Related

Remove Polylines at swift

I'm drawing navigation road at Swift. I'm using current location to another location and made a draw. Afterward, I select another location and redraw it. But even if I write mapView.remove(rotapoly) in my code, it doesnt remove it. How can I solve this?
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
cizim = 1;
let capital = view.annotation as! Station
guard let locValue: CLLocationCoordinate2D = locationManager.location?.coordinate else { return }
let neresi = CLLocationCoordinate2D(latitude: capital.latitude, longitude: capital.longitude)
let nerdeyim = CLLocationCoordinate2D(latitude: locValue.latitude, longitude: locValue.longitude)
let request = MKDirectionsRequest()
request.source = MKMapItem(placemark: MKPlacemark(coordinate: nerdeyim, addressDictionary: nil))
request.destination = MKMapItem(placemark: MKPlacemark(coordinate: neresi, addressDictionary: nil))
request.requestsAlternateRoutes = true
request.transportType = .walking
let directions = MKDirections(request: request)
directions.calculate { [unowned self] response, error in
guard let unwrappedResponse = response else { return }
if (unwrappedResponse.routes.count > 0) {
self.showRoute(response!)
}
}
}
func showRoute(_ response: MKDirectionsResponse) {
mapView.remove(rotapoly)
for route in response.routes {
rotapoly = route.polyline
mapView.add(rotapoly, level: MKOverlayLevel.aboveRoads)
for step in route.steps {
print(step.instructions)
}
}
}
use map view method
self.mapview.removeOverlays(self.mapview.overlays)
this will remove all overlays you have added so you have to do whole process again its like reloading map view
Below is the approach to remove travelled polyline from google map iOS Swift:
var oldPolyLines = [GMSPolyline]() /* Global Array Variable of your Class */
Put below code where you are parsing routes and getting new polyline from direction API.
if self.oldPolyLines.count > 0 {
for polyline in self.oldPolyLines {
polyline.map = nil
}
}
self.oldPolyLines.append(yourNewPolyline)
yourNewPolyLine.map = self.mapView

Draw a path between two coordinates using MapKit Swift

All the answers on SO for this question are using Objective-c, or using Swift 2. Any way I have two points, the coordinates for the first one is (lat, long), and for the second point (coordinates.latitude, coordinates.longitude). This is what I was able to get so far, but still I have two problems:
Value of type 'MKDirectionsRequest' has no member 'setSource'
Value of type 'MKDirectionsRequest' has no member 'setDestination'
Any way I got this code from this page:http://studyswift.blogspot.com/2014/10/mkdirections-draw-route-from-location.html
This is my code so far:
#IBOutlet weak var myMap: MKMapView!
var myRoute : MKRoute?
Inside viewDidLoad I have the following:
let directionsRequest = MKDirectionsRequest()
let markFirstPoint = MKPlacemark(coordinate: CLLocationCoordinate2DMake(lat, long), addressDictionary: nil)
let markSecondPoint = MKPlacemark(coordinate: CLLocationCoordinate2DMake(coordinates.latitude, coordinates.longitude), addressDictionary: nil)
directionsRequest.setSource(MKMapItem(placemark: markSecondPoint))
directionsRequest.setDestination(MKMapItem(placemark: markFirstPoint))
directionsRequest.transportType = MKDirectionsTransportType.automobile
let directions = MKDirections(request: directionsRequest)
directions.calculate { (response:MKDirectionsResponse!, error: Error!) -> Void in
if error == nil {
self.myRoute = response.routes[0] as? MKRoute
self.myMap.add((self.myRoute?.polyline)!)
}
}
after viewDidLoad I have this:
func mapView(mapView: MKMapView!, rendererForOverlay overlay: MKOverlay!) -> MKOverlayRenderer! {
var myLineRenderer = MKPolylineRenderer(polyline: (myRoute?.polyline)!)
myLineRenderer.strokeColor = UIColor.red
myLineRenderer.lineWidth = 3
return myLineRenderer
}
Is there any way I can get this code to work?
Simply look at the documentation for MKDirectionsRequest. There is a source property and a destination property.
The lines:
directionsRequest.setSource(MKMapItem(placemark: markSecondPoint))
directionsRequest.setDestination(MKMapItem(placemark: markFirstPoint))
become:
directionsRequest.source = MKMapItem(placemark: markSecondPoint)
directionsRequest.destination = MKMapItem(placemark: markFirstPoint)

Draw route on map in swift 4

I want draw route between two coordinates in swift4.
and I am using this code,
import UIKit
import MapKit
class ViewController: UIViewController, MKMapViewDelegate {
#IBOutlet weak var myMap: MKMapView!
var myRoute : MKRoute!
override func viewDidLoad() {
super.viewDidLoad()
let point1 = MKPointAnnotation()
let point2 = MKPointAnnotation()
point1.coordinate = CLLocationCoordinate2DMake(25.0305, 121.5360)
point1.title = "Taipei"
point1.subtitle = "Taiwan"
myMap.addAnnotation(point1)
point2.coordinate = CLLocationCoordinate2DMake(24.9511, 121.2358)
point2.title = "Chungli"
point2.subtitle = "Taiwan"
myMap.addAnnotation(point2)
myMap.centerCoordinate = point2.coordinate
myMap.delegate = self
//Span of the map
myMap.setRegion(MKCoordinateRegionMake(point2.coordinate, MKCoordinateSpanMake(0.7,0.7)), animated: true)
let directionsRequest = MKDirectionsRequest()
let markTaipei = MKPlacemark(coordinate: CLLocationCoordinate2DMake(point1.coordinate.latitude, point1.coordinate.longitude), addressDictionary: nil)
let markChungli = MKPlacemark(coordinate: CLLocationCoordinate2DMake(point2.coordinate.latitude, point2.coordinate.longitude), addressDictionary: nil)
directionsRequest.source = MKMapItem(placemark: markChungli)
directionsRequest.destination = MKMapItem(placemark: markTaipei)
directionsRequest.transportType = MKDirectionsTransportType.automobile
let directions = MKDirections(request: directionsRequest)
directions.calculate(completionHandler: {
response, error in
if error == nil {
self.myRoute = response!.routes[0] as MKRoute
self.myMap.add(self.myRoute.polyline)
}
})
}
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) ->MKOverlayRenderer {
let myLineRenderer = MKPolylineRenderer(polyline: myRoute.polyline)
myLineRenderer.strokeColor = UIColor.red
myLineRenderer.lineWidth = 3
return myLineRenderer
}
}
and this code give me right answer
But when I change coordinates then it not show route.
new coordinates are
point1 = 26.9124, 75.7873
point2 = 26.9124, 76.7873
Looks like Apple Maps doesn't yet support India. (quora.com/…) I guess if this is a critical part of your app, you may have to consider Google Maps
Those new coordinates are in India, and it just looks like directions are not available there. I get an error that says "Error Domain=MKErrorDomain Code=4 "Directions Not Available" UserInfo={NSLocalizedDescription=Directions Not Available, MKErrorGEOError=-8, MKErrorGEOErrorUserInfo={ }, MKErrorGEOTransitIncidentKey=<_GEOTransitRoutingIncidentMessage: 0x60800023df20>, MKDirectionsErrorCode=0, NSLocalizedFailureReason=Directions are not available between these locations.}"
Thanx Rob for this answar.

MKPolyLine isn't showing up on iOS app in Swift 2.0

I'm creating a sort of direction/GPS app and so far everything has been sort of easy.
I've figured out how to find the user's location (with their permission of course)
I've managed to allow them to set a destination in a quick easy way
However, I've run into a small issue. What I want is for the user to select their destination on the screen and the app will give them the fastest way to arrive there.
Here's my ViewController:
class ViewController: UIViewController,MKMapViewDelegate,CLLocationManagerDelegate {
#IBOutlet var mapOfMaps: MKMapView!
let locationManager = CLLocationManager()
var center:CLLocationCoordinate2D!
var SourcePM:MKPlacemark!
let sourcePlacemark: MKPlacemark! = nil
override func viewDidLoad() {
super.viewDidLoad()
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.startUpdatingLocation()
self.mapOfMaps.showsUserLocation = true
let sourceAnnotation = MKPointAnnotation()
if let location = locationManager.location {
sourceAnnotation.coordinate = location.coordinate
}
let longPress = UILongPressGestureRecognizer(target: self, action: #selector(CardioViewController.action(_:)))
longPress.minimumPressDuration = 1.0
mapOfMaps.addGestureRecognizer(longPress)
//directionRequest
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations.last
center = CLLocationCoordinate2D(latitude: location!.coordinate.latitude, longitude: location!.coordinate.longitude)
let region = MKCoordinateRegion(center: center, span: MKCoordinateSpan(latitudeDelta: 0.003, longitudeDelta: 0.003))
self.mapOfMaps.setRegion(region, animated: true)
self.locationManager.stopUpdatingLocation()
SourcePM = MKPlacemark(coordinate: center, addressDictionary: nil)
}
func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
print("ERROr " + error.localizedDescription)
}
func action(gestureRecognizer:UIGestureRecognizer) {
let touchPoint = gestureRecognizer.locationInView(self.mapOfMaps)
let newCoord:CLLocationCoordinate2D = mapOfMaps.convertPoint(touchPoint, toCoordinateFromView: self.mapOfMaps)
let newAnotation = MKPointAnnotation()
newAnotation.coordinate = newCoord
newAnotation.title = "Your Destination"
mapOfMaps.addAnnotation(newAnotation)
let anotPM = MKPlacemark(coordinate: newAnotation.coordinate, addressDictionary: nil)
let source = MKMapItem(placemark: SourcePM)
let dstn = MKMapItem(placemark: anotPM)
let directionRequest = MKDirectionsRequest()
directionRequest.source = source
directionRequest.destination = dstn
directionRequest.transportType = .Automobile
// Calculate the direction
let directions = MKDirections(request: directionRequest)
// 8.
directions.calculateDirectionsWithCompletionHandler() {
(response, error) in
if(error == nil && response != nil) {
for route in response!.routes {
var r: MKRoute = route as! MKRoute
self.mapOfMaps.addOverlay(r.polyline, level: MKOverlayLevel.AboveRoads)
}
}
let route = response!.routes[0]
self.mapOfMaps.addOverlay((route.polyline), level: MKOverlayLevel.AboveLabels)
let rect = route.polyline.boundingMapRect
self.mapOfMaps.setRegion(MKCoordinateRegionForMapRect(rect), animated: true)
}
}
func mapView(mapView: MKMapView, rendererForOverlay overlay: MKOverlay) -> MKOverlayRenderer {
let renderer = MKPolylineRenderer(overlay: overlay)
renderer.strokeColor = UIColor.orangeColor()
renderer.alpha = 1
renderer.lineWidth = 4.0
return renderer
}
}
to help walk you through my code. My user will press and hold the destination on the map and the app will add an annotation and it should give the route. However, all that happens is the annotation will be added to the map, and the map will adjust to show both locations(user's location and annotation location) but no route.
This may not be the 'ideal' solution, but it's worth noting that both locationManager functions can't work at the same time. I tried commenting one of the locationManager functions out and low and behold the other worked perfectly

Swift - fatal error: unexpectedly found nil while unwrapping an Optional value on MKCoordinateRegionMakeWithDistance

I'm trying to get my map to show directions to a local searched location from the current user location.
I'm getting an EXC_BAD_INSTRUCTION on the line:
let region = MKCoordinateRegionMakeWithDistance(userLocation.location.coordinate, 2000, 2000)`
And on the line:
else {
self.showRoute(response)
}
I have a feeling the nil it's receiving is from the user location, which I'm not sure why it would be receiving a nil there.
Here is the full code for my view controller if needed:
class RouteViewController: UIViewController, MKMapViewDelegate {
#IBOutlet weak var routeMap: MKMapView!
var destination = MKMapItem?()
override func viewDidLoad() {
super.viewDidLoad()
routeMap.showsUserLocation = true
routeMap.delegate = self
self.getDirections()
}
func getDirections() {
let request = MKDirectionsRequest()
request.setSource(MKMapItem.mapItemForCurrentLocation())
request.setDestination(destination!)
request.requestsAlternateRoutes = false
let directions = MKDirections(request: request)
directions.calculateDirectionsWithCompletionHandler({(response:
MKDirectionsResponse!, error: NSError!) in
if error != nil {
println("Error getting directions")
} else {
self.showRoute(response)
}
})
}
func showRoute(response: MKDirectionsResponse) {
for route in response.routes as! [MKRoute] {
routeMap.addOverlay(route.polyline,
level: MKOverlayLevel.AboveRoads)
for step in route.steps {
println(step.instructions)
}
}
let userLocation = routeMap.userLocation
let region = MKCoordinateRegionMakeWithDistance(
userLocation.location.coordinate, 2000, 2000)
routeMap.setRegion(region, animated: true)
}
func mapView(mapView: MKMapView!, rendererForOverlay
overlay: MKOverlay!) -> MKOverlayRenderer! {
let renderer = MKPolylineRenderer(overlay: overlay)
renderer.strokeColor = UIColor.blueColor()
renderer.lineWidth = 5.0
return renderer
}
}
And the segue code:
override func prepareForSegue(segue: UIStoryboardSegue,
sender: AnyObject?) {
let routeViewController = segue.destinationViewController
as! RouteViewController
let indexPath = self.tableView.indexPathForSelectedRow()
let row = indexPath?.row
routeViewController.destination = mapItems[row!]
}
Any help would be appreciated, thanks!
The problem is this line:
let userLocation = routeMap.userLocation
The result, userLocation might be nil, and its location might be nil, because the map might not have been told to, or succeeded in acquiring, the user's location. You are not taking into account that possibility.
The way to do that is to unwrap the Optionals safely and proceed only if the unwrapping succeeded:
if let userLocation = routeMap.userLocation, loc = userLocation.location {
let region = MKCoordinateRegionMakeWithDistance(
loc.coordinate, 2000, 2000)
routeMap.setRegion(region, animated: true)
}
We don't need the userLocation separately for anything, so we can collapse that into a single test:
if let loc = routeMap.userLocation.location {
let region = MKCoordinateRegionMakeWithDistance(
loc.coordinate, 2000, 2000)
routeMap.setRegion(region, animated: true)
}

Resources