Custom navigation camera

Optimize navigation experience by adjusting MapView camera based on requested route, ensuring clear visibility and focus on route.

This example shows:

  • How to fit the mapview camera according to the requested route info
docs-image

For all code examples, refer to Navigation Code Examples

CustomCameraController view source

1
import UIKit
2
import NbmapNavigation
3
import NbmapCoreNavigation
4
import NbmapDirections
5
import Nbmap
6
7
class CustomCameraController: UIViewController, NGLMapViewDelegate, NavigationViewControllerDelegate {
8
9
var mapView: NavigationMapView? {
10
didSet {
11
oldValue?.removeFromSuperview()
12
if let mapView = mapView {
13
view.insertSubview(mapView, at: 0)
14
}
15
}
16
}
17
18
var routes : [Route]? {
19
didSet {
20
guard let routes = routes,
21
let current = routes.first
22
else {
23
mapView?.removeRoutes()
24
mapView?.removeRouteDurationSymbol()
25
if let annotation = mapView?.annotations?.last {
26
mapView?.removeAnnotation(annotation)
27
}
28
return
29
30
}
31
32
mapView?.showRoutes(routes)
33
mapView?.showWaypoints(current)
34
mapView?.showRouteDurationSymbol(routes)
35
36
fitCameraWithRoutes(routes: routes)
37
}
38
}
39
40
var startButton = UIButton()
41
42
var trackingImage = UIImageView()
43
44
override func viewDidLoad() {
45
super.viewDidLoad()
46
47
self.mapView = NavigationMapView(frame: view.bounds)
48
49
mapView?.userTrackingMode = .followWithHeading
50
51
let singleTap = UILongPressGestureRecognizer(target: self, action: #selector(didLongPress(tap:)))
52
mapView?.gestureRecognizers?.filter({ $0 is UILongPressGestureRecognizer }).forEach(singleTap.require(toFail:))
53
mapView?.addGestureRecognizer(singleTap)
54
mapView?.delegate = self
55
56
setupStartButton()
57
setupLocationTrackingButton()
58
}
59
60
func setupStartButton() {
61
startButton.setTitle("Start", for: .normal)
62
startButton.layer.cornerRadius = 5
63
startButton.contentEdgeInsets = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
64
startButton.backgroundColor = .blue
65
66
startButton.addTarget(self, action: #selector(tappedButton), for: .touchUpInside)
67
view.addSubview(startButton)
68
startButton.translatesAutoresizingMaskIntoConstraints = false
69
startButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -50).isActive = true
70
startButton.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor).isActive = true
71
startButton.titleLabel?.font = UIFont.systemFont(ofSize: 25)
72
}
73
74
func setupLocationTrackingButton(){
75
trackingImage.isUserInteractionEnabled = true
76
trackingImage.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(trackingClick)))
77
trackingImage.image = UIImage(named: "my_location")
78
view.addSubview(trackingImage)
79
startButton.translatesAutoresizingMaskIntoConstraints = false
80
startButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -50).isActive = true
81
startButton.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor,constant: 16).isActive = true
82
}
83
84
@objc func didLongPress(tap: UILongPressGestureRecognizer) {
85
guard let mapView = mapView, tap.state == .began else {
86
return
87
}
88
let coordinates = mapView.convert(tap.location(in: mapView), toCoordinateFrom: mapView)
89
let destination = Waypoint(coordinate: coordinates, name: "\(coordinates.latitude),\(coordinates.longitude)")
90
91
guard let currentLocation = mapView.userLocation?.coordinate else { return}
92
let currentWayPoint = Waypoint.init(coordinate: currentLocation, name: "My Location")
93
94
requestRoutes(origin: currentWayPoint, destination: destination)
95
}
96
97
98
@objc func tappedButton(sender: UIButton) {
99
guard let routes = self.routes else {
100
return
101
}
102
var engineConfig = NavigationEngineConfig()
103
// Config mininum and maxnum camera zoom
104
engineConfig.minNavigationCameraZoom = 16
105
engineConfig.maxNavigationCameraZoom = 18
106
let navigationService = NBNavigationService(routes: routes, routeIndex: 0,navigationEngConfig: engineConfig)
107
108
let navigationOptions = NavigationOptions(navigationService: navigationService)
109
let navigationViewController = NavigationViewController(for: routes,navigationOptions: navigationOptions)
110
navigationViewController.modalPresentationStyle = .fullScreen
111
112
navigationViewController.delegate = self
113
114
// Start navigation
115
present(navigationViewController, animated: true, completion: nil)
116
}
117
118
@objc func trackingClick() {
119
guard let location = mapView?.userLocation else {
120
return
121
}
122
guard let newCamera = mapView?.camera else {
123
return
124
}
125
newCamera.centerCoordinate = location.coordinate
126
newCamera.viewingDistance = 1000
127
mapView?.setCamera(newCamera, animated: true)
128
}
129
130
func fitCameraWithRoutes(routes: [Route]) {
131
guard let mapView = mapView else {
132
return
133
}
134
135
var coordinates: [CLLocationCoordinate2D] = []
136
routes.forEach({route in
137
coordinates.append(contentsOf: route.coordinates!)
138
})
139
let polyLine = NGLPolyline(coordinates: coordinates, count: UInt(coordinates.count))
140
let camera = mapView.cameraThatFitsShape(polyLine, direction: mapView.camera.heading, edgePadding: UIEdgeInsets(top: view.safeAreaInsets.top, left: view.safeAreaInsets.left, bottom: view.safeAreaInsets.bottom, right: view.safeAreaInsets.right))
141
mapView.setCamera(camera, animated: true)
142
}
143
144
func requestRoutes(origin: Waypoint, destination: Waypoint){
145
let options = NavigationRouteOptions(origin: origin, destination: destination)
146
147
Directions.shared.calculate(options) { [weak self] routes, error in
148
guard let weakSelf = self else {
149
return
150
}
151
guard error == nil else {
152
print(error!)
153
return
154
}
155
156
guard let routes = routes else { return }
157
weakSelf.routes = routes
158
}
159
}
160
}

This class is a view controller that allows users to add a custom destination marker to a map and then navigate to that destination. The class has the following properties:

  • mapView: A NavigationMapView object that displays the map.

  • routes: An array of Route objects that represent the routes between the user's current location and the custom destination marker.

  • startButton: A UIButton object that is used to start navigation.

  • trackingImage: A UIImageView object that is used to show the current location of the user.

The class has the following methods:

  • viewDidLoad(): This method is called when the view controller is loaded. In this method, the map view is created and configured.

  • didLongPress(): This method is called when the user long-presses on the map. In this method, a custom destination marker is added to the map at the location of the long press.

  • tappedButton(): This method is called when the user taps the "Start Navigation" button. In this method, a navigation controller is created and presented to the user. The navigation controller uses the routes property to display the route between the user's current location and the custom destination marker.

  • trackingClick(): This method is called when the user taps the "My Location" button. In this method, the map view is centered on the user's current location.

  • fitCameraWithRoutes(): This method fits the camera to the routes on the map.

  • requestRoutes(): This method requests routes between the user's current location and the custom destination marker.

The code you provided also has two extensions:

  • CustomCameraController: NGLMapViewDelegate

  • CustomCameraController: NavigationViewControllerDelegate

These extensions conform the CustomCameraController class to the NGLMapViewDelegate and NavigationViewControllerDelegate protocols. These protocols allow the CustomCameraController class to respond to events from the map view and the navigation controller.

ios-sdk-7

© 2024 NextBillion.ai all rights reserved.