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.

documentation image

For all code examples, refer to Navigation Code Examples

AdvancedNavigationViewController view code

1
import UIKit
2
import NbmapNavigation
3
import NbmapCoreNavigation
4
import NbmapDirections
5
import Nbmap
6
7
class 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
191
extension 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

© 2024 NextBillion.ai all rights reserved.