Advanced navigation app

Add advanced navigation functionality to an iOS app.

AdvancedViewController
/*
This code example is part of the Mapbox Navigation SDK for iOS demo app,
which you can build and run: https://github.com/mapbox/mapbox-navigation-ios-examples
To learn more about each example in this app, including descriptions and links
to documentation, see our docs: https://docs.mapbox.com/ios/navigation/examples/advanced
*/
import UIKit
import MapboxCoreNavigation
import MapboxNavigation
import MapboxDirections
import MapboxMaps
class AdvancedViewController: UIViewController, NavigationMapViewDelegate, NavigationViewControllerDelegate {
var navigationMapView: NavigationMapView!
var navigationRouteOptions: NavigationRouteOptions!
var currentRouteIndex = 0 {
didSet {
showCurrentRoute()
}
}
var currentRoute: Route? {
return routes?[currentRouteIndex]
}
var routes: [Route]? {
return routeResponse?.routes
}
var routeResponse: RouteResponse? {
didSet {
guard currentRoute != nil else {
navigationMapView.removeRoutes()
return
}
currentRouteIndex = 0
}
}
func showCurrentRoute() {
guard let currentRoute = currentRoute else { return }
var routes = [currentRoute]
routes.append(contentsOf: self.routes!.filter {
$0 != currentRoute
})
navigationMapView.show(routes)
navigationMapView.showWaypoints(on: currentRoute)
}
var startButton: UIButton!
// MARK: - UIViewController lifecycle methods
override func viewDidLoad() {
super.viewDidLoad()
navigationMapView = NavigationMapView(frame: view.bounds)
navigationMapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
navigationMapView.delegate = self
navigationMapView.userLocationStyle = .puck2D()
let navigationViewportDataSource = NavigationViewportDataSource(navigationMapView.mapView, viewportDataSourceType: .raw)
navigationViewportDataSource.options.followingCameraOptions.zoomUpdatesAllowed = false
navigationViewportDataSource.followingMobileCamera.zoom = 13.0
navigationMapView.navigationCamera.viewportDataSource = navigationViewportDataSource
let gesture = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPress(_:)))
navigationMapView.addGestureRecognizer(gesture)
view.addSubview(navigationMapView)
startButton = UIButton()
startButton.setTitle("Start Navigation", for: .normal)
startButton.translatesAutoresizingMaskIntoConstraints = false
startButton.backgroundColor = .blue
startButton.contentEdgeInsets = UIEdgeInsets(top: 10, left: 20, bottom: 10, right: 20)
startButton.addTarget(self, action: #selector(tappedButton(sender:)), for: .touchUpInside)
startButton.isHidden = true
view.addSubview(startButton)
startButton.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor, constant: -20).isActive = true
startButton.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
view.setNeedsLayout()
}
// Override layout lifecycle callback to be able to style the start button.
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
startButton.layer.cornerRadius = startButton.bounds.midY
startButton.clipsToBounds = true
startButton.setNeedsDisplay()
}
@objc func tappedButton(sender: UIButton) {
guard let routeResponse = routeResponse, let navigationRouteOptions = navigationRouteOptions else { return }
// For demonstration purposes, simulate locations if the Simulate Navigation option is on.
let navigationService = MapboxNavigationService(routeResponse: routeResponse,
routeIndex: currentRouteIndex,
routeOptions: navigationRouteOptions,
simulating: simulationIsEnabled ? .always : .onPoorGPS)
let navigationOptions = NavigationOptions(navigationService: navigationService)
let navigationViewController = NavigationViewController(for: routeResponse, routeIndex: currentRouteIndex,
routeOptions: navigationRouteOptions,
navigationOptions: navigationOptions)
navigationViewController.delegate = self
present(navigationViewController, animated: true, completion: nil)
}
@objc func handleLongPress(_ gesture: UILongPressGestureRecognizer) {
guard gesture.state == .ended else { return }
let location = navigationMapView.mapView.mapboxMap.coordinate(for: gesture.location(in: navigationMapView.mapView))
requestRoute(destination: location)
}
func requestRoute(destination: CLLocationCoordinate2D) {
guard let userLocation = navigationMapView.mapView.location.latestLocation else { return }
let location = CLLocation(latitude: userLocation.coordinate.latitude,
longitude: userLocation.coordinate.longitude)
let userWaypoint = Waypoint(location: location,
heading: userLocation.heading,
name: "user")
let destinationWaypoint = Waypoint(coordinate: destination)
let navigationRouteOptions = NavigationRouteOptions(waypoints: [userWaypoint, destinationWaypoint])
Directions.shared.calculate(navigationRouteOptions) { [weak self] (_, result) in
switch result {
case .failure(let error):
print(error.localizedDescription)
case .success(let response):
guard let self = self else { return }
self.navigationRouteOptions = navigationRouteOptions
self.routeResponse = response
self.startButton?.isHidden = false
if let routes = self.routes,
let currentRoute = self.currentRoute {
self.navigationMapView.show(routes)
self.navigationMapView.showWaypoints(on: currentRoute)
}
}
}
}
// Delegate method called when the user selects a route
func navigationMapView(_ mapView: NavigationMapView, didSelect route: Route) {
self.currentRouteIndex = self.routes?.firstIndex(of: route) ?? 0
}
func navigationViewControllerDidDismiss(_ navigationViewController: NavigationViewController, byCanceling canceled: Bool) {
dismiss(animated: true, completion: nil)
}
}