Advanced navigation app

In this example, we are going to create an App that allows users to long press on the map to add a destination to request routes and display the routes on the map, this app also enables route selection from alternative routes, lastly, users can start navigation by tapping the “Start” button, then the app will present the NavigationViewController to begin turn-by-turn navigation.

docs-image

For all code examples, refer to Navigation Code Examples

AdvancedNavigationViewController view code

1import UIKit
2import NbmapNavigation
3import NbmapCoreNavigation
4import NbmapDirections
5import Nbmap
6
7class AdvancedNavigationViewController: UIViewController, NGLMapViewDelegate {
8
9 var navigationMapView: NavigationMapView? {
10 didSet {
11 oldValue?.removeFromSuperview()
12 if let navigationMapView = navigationMapView {
13 view.insertSubview(navigationMapView, at: 0)
14 }
15 }
16 }
17
18 var routes : [Route]? {
19 didSet {
20 guard routes != nil else{
21 startButton.isEnabled = false
22 return
23 }
24 startButton.isEnabled = true
25
26 guard let routes = routes,
27 let current = routes.first else { navigationMapView?.removeRoutes(); return }
28
29 navigationMapView?.showRoutes(routes)
30 navigationMapView?.showWaypoints(current)
31 navigationMapView?.showRouteDurationSymbol(routes)
32 }
33 }
34
35 var startButton = UIButton()
36
37 override func viewDidLoad() {
38 super.viewDidLoad()
39
40 self.navigationMapView = NavigationMapView(frame: view.bounds)
41
42 navigationMapView?.userTrackingMode = .followWithHeading
43
44 let singleTap = UILongPressGestureRecognizer(target: self, action: #selector(didLongPress(tap:)))
45 navigationMapView?.gestureRecognizers?.filter({ $0 is UILongPressGestureRecognizer }).forEach(singleTap.require(toFail:))
46 navigationMapView?.addGestureRecognizer(singleTap)
47 navigationMapView?.delegate = self
48 navigationMapView?.navigationMapDelegate = self
49
50 setupStartButton()
51
52 self.view.setNeedsLayout()
53
54 }
55
56 func setupStartButton() {
57 startButton.setTitle("Start", for: .normal)
58 startButton.layer.cornerRadius = 5
59 startButton.contentEdgeInsets = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
60 startButton.backgroundColor = .blue
61
62 startButton.addTarget(self, action: #selector(tappedButton), for: .touchUpInside)
63 view.addSubview(startButton)
64 startButton.translatesAutoresizingMaskIntoConstraints = false
65 startButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -50).isActive = true
66 startButton.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor).isActive = true
67 startButton.titleLabel?.font = UIFont.systemFont(ofSize: 25)
68 }
69
70 @objc func tappedButton(sender: UIButton) {
71 guard let routes = self.routes else {
72 return
73 }
74 let navigationService = NBNavigationService(routes: routes, routeIndex: 0)
75
76 let navigationOptions = NavigationOptions(navigationService: navigationService)
77 let navigationViewController = NavigationViewController(for: routes,navigationOptions: navigationOptions)
78
79 // Set the delegate of navigationViewController to subscribe for NavigationView's events, This is optional
80 // navigationViewController.delegate = weakSelf
81 navigationViewController.modalPresentationStyle = .fullScreen
82
83 present(navigationViewController, animated: true, completion: nil)
84 }
85
86 @objc func didLongPress(tap: UILongPressGestureRecognizer) {
87 guard let navigationMapView = navigationMapView, tap.state == .began else {
88 return
89 }
90 let coordinates = navigationMapView.convert(tap.location(in: navigationMapView), toCoordinateFrom: navigationMapView)
91 let destination = Waypoint(coordinate: coordinates, name: "\(coordinates.latitude),\(coordinates.longitude)")
92 addNewDestinationIcon(coordinates: coordinates)
93
94 guard let currentLocation = navigationMapView.userLocation?.coordinate else { return}
95 let currentWayPoint = Waypoint.init(coordinate: currentLocation, name: "My Location")
96
97 requestRoutes(origin: currentWayPoint, destination: destination)
98 }
99
100
101 func addNewDestinationIcon(coordinates: CLLocationCoordinate2D){
102 guard let mapView = navigationMapView else {
103 return
104 }
105
106 if let annotation = mapView.annotations?.last {
107 mapView.removeAnnotation(annotation)
108 }
109
110 let annotation = NGLPointAnnotation()
111 annotation.coordinate = coordinates
112 mapView.addAnnotation(annotation)
113 }
114
115 func requestRoutes(origin: Waypoint, destination: Waypoint){
116
117 let options = NavigationRouteOptions(origin: origin, destination: destination)
118 /**
119 Sets whether the route contains an alternate route
120 By default, it set to `false`
121 */
122 options.includesAlternativeRoutes = true
123 /**
124 The route classes that the calculated routes will avoid.
125 We can set an array road class to avoid. This property can be set to `.toll`,`.ferry`,`.highway`
126 By default , this property is set to empty
127 */
128 options.roadClassesToAvoid = [.toll,.ferry,.highway]
129 /**
130 Set up the navigation measurement unit
131 This property should be set to `.metric` or `.imperial`
132 By default, this property is set to the unit following with the current system locale.
133 */
134 options.distanceMeasurementSystem = .imperial
135
136 /**
137 Set specifying the primary mode of transportation for the routes.
138 This property should be set to `NBNavigationModeCar`, `NBNavigationModeAuto`, `NBNavigationModeBike`, `NBNavigationMode4W`,`NBNavigationMode2W`,`NBNavigationMode6W`, or `NBNavigationModeEscooter`. The default value of this property is `NBNavigationMode4W`, which specifies driving directions.
139 */
140 options.profileIdentifier = NBNavigationMode.mode4W
141 /**
142 Set the departureTime of the route, By default, it sets the current timestamp since 1970
143 */
144 options.departureTime = Int(Date().timeIntervalSince1970)
145
146 /**
147 Set up the locale in which the route's instructions are written.
148 If you use NbmapDirections.swift with the Nbmap Directions API, this property affects the sentence contained within the `RouteStep.instructions` property, but it does not affect any road names contained in that property or other properties such as `RouteStep.name`.
149 The Navigation API can provide instructions in [a number of languages]. Set this property to `Bundle.main.preferredLocalizations.first` or `Locale.autoupdatingCurrent` to match the application's language or the system language, respectively.
150 By default, this property is set to the current system locale.
151 */
152 options.locale = Locale.autoupdatingCurrent
153
154 /**
155 Set map options, This property may make the ETA more accurate. If set to `NBMapOption.valhala`, shapeFormat needs to be set to `polyline`.
156 By default, the value of this property is `NBMapOption.none`
157 */
158 options.mapOption = NBMapOption.none
159 /**
160 Format of the data from which the shapes of the returned route and its steps are derived.
161
162 This property has no effect on the returned shape objects, although the choice of format can significantly affect the size of the underlying HTTP response.
163
164 The default value of this property is `polyline6`.
165 */
166 options.shapeFormat = .polyline6
167
168 Directions.shared.calculate(options) { [weak self] routes, error in
169 guard let weakSelf = self else {
170 return
171 }
172 guard error == nil else {
173 print(error!)
174 return
175 }
176
177 guard let routes = routes else { return }
178
179
180 // Process or display routes information.For example, display the routes, waypoints and duration symbol on the map
181 weakSelf.navigationMapView?.showRoutes(routes)
182 weakSelf.navigationMapView?.showRouteDurationSymbol(routes)
183
184 guard let current = routes.first else { return }
185 weakSelf.navigationMapView?.showWaypoints(current)
186 weakSelf.routes = routes
187 }
188 }
189}
190
191extension AdvancedNavigationViewController: NavigationMapViewDelegate {
192 // Delegate method called when the user selects a route
193 func navigationMapView(_ mapView: NavigationMapView, didSelect route: Route) {
194 guard let routes = routes else { return }
195 guard let index = routes.firstIndex(where: { $0 == route }) else { return }
196 self.routes!.remove(at: index)
197 self.routes!.insert(route, at: 0)
198 }
199}

The code defines a custom view controller called AdvancedNavigationViewController that inherits from UIViewController and conforms to the NGLMapViewDelegate protocol. The purpose of this custom view controller is to implement an advanced navigation experience using NbmapNavigation, NbmapCoreNavigation, NbmapDirections, and Nbmap libraries.

  • Import statements: The necessary frameworks are imported at the beginning of the file.

  • navigationMapView: A property for the NavigationMapView instance. When its value is set, the old instance is removed from the view hierarchy, and the new one is inserted as a subview.

  • routes: An array of Route objects. When its value is set, the start button's state is updated accordingly, and the route, waypoints, and route duration symbols are shown on the navigation map view.

  • startButton: A UIButton that initiates navigation when tapped.

  • viewDidLoad(): This method sets up the view controller after the view is loaded. It creates a NavigationMapView, adds a long press gesture recognizer, sets up the start button, and calls view.setNeedsLayout() to invalidate the current layout of the receiver and trigger a layout update during the next update cycle.

  • setupStartButton(): This method configures the start button's appearance and adds a target to handle the button tap.

  • tappedButton(sender:): The action method for the start button tap. It initializes a NBNavigationService, sets up navigation options and a NavigationViewController, and then presents the navigation view controller.

  • didLongPress(tap:): The method that handles the long press gesture. It converts the tap location to coordinates and then adds a new destination based on the coordinates. It also makes a request for routes between the user's current location and the destination.

  • addNewDestinationIcon(coordinates:): This method adds a new destination annotation to the navigation map view.

  • requestRoutes(origin:destination:): This method requests routes between the given origin and destination using the specified options.

  • An extension of AdvancedNavigationViewController that conforms to the NavigationMapViewDelegate protocol. It contains the navigationMapView(_:didSelect:) delegate method to handle when the user selects a route from the displayed routes.

ios-sdk-7