• Optimization
  • Navigation
  • Tracking
  • Maps
  • Places

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

For all code examples, refer to Navigation Code Examples

CustomCameraController view source

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
import UIKit
import NbmapNavigation
import NbmapCoreNavigation
import NbmapDirections
import Nbmap

class CustomCameraController: UIViewController, NGLMapViewDelegate, NavigationViewControllerDelegate {
  
   var mapView: NavigationMapView? {
       didSet {
           oldValue?.removeFromSuperview()
           if let mapView = mapView {
               view.insertSubview(mapView, at: 0)
           }
       }
   }
  
   var routes : [Route]? {
       didSet {
           guard let routes = routes,
                 let current = routes.first
           else {
               mapView?.removeRoutes()
               mapView?.removeRouteDurationSymbol()
               if let annotation = mapView?.annotations?.last {
                   mapView?.removeAnnotation(annotation)
               }
               return
              
           }
          
           mapView?.showRoutes(routes)
           mapView?.showWaypoints(current)
           mapView?.showRouteDurationSymbol(routes)
          
           fitCameraWithRoutes(routes: routes)
       }
   }
  
   var startButton = UIButton()
  
   var trackingImage = UIImageView()
  
   override func viewDidLoad() {
       super.viewDidLoad()
      
       self.mapView = NavigationMapView(frame: view.bounds)
      
       mapView?.userTrackingMode = .followWithHeading
      
       let singleTap = UILongPressGestureRecognizer(target: self, action: #selector(didLongPress(tap:)))
       mapView?.gestureRecognizers?.filter({ $0 is UILongPressGestureRecognizer }).forEach(singleTap.require(toFail:))
       mapView?.addGestureRecognizer(singleTap)
       mapView?.delegate = self
      
       setupStartButton()
       setupLocationTrackingButton()
   }
  
   func setupStartButton() {
       startButton.setTitle("Start", for: .normal)
       startButton.layer.cornerRadius = 5
       startButton.contentEdgeInsets = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
       startButton.backgroundColor = .blue
      
       startButton.addTarget(self, action: #selector(tappedButton), for: .touchUpInside)
       view.addSubview(startButton)
       startButton.translatesAutoresizingMaskIntoConstraints = false
       startButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -50).isActive = true
       startButton.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor).isActive = true
       startButton.titleLabel?.font = UIFont.systemFont(ofSize: 25)
   }
  
   func setupLocationTrackingButton(){
       trackingImage.isUserInteractionEnabled = true
       trackingImage.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(trackingClick)))
       trackingImage.image = UIImage(named: "my_location")
       view.addSubview(trackingImage)
       startButton.translatesAutoresizingMaskIntoConstraints = false
       startButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -50).isActive = true
       startButton.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor,constant: 16).isActive = true
   }
  
   @objc func didLongPress(tap: UILongPressGestureRecognizer) {
       guard let mapView = mapView, tap.state == .began else {
           return
       }
       let coordinates = mapView.convert(tap.location(in: mapView), toCoordinateFrom: mapView)
       let destination = Waypoint(coordinate: coordinates, name: "\(coordinates.latitude),\(coordinates.longitude)")
      
       guard let currentLocation =  mapView.userLocation?.coordinate else { return}
       let currentWayPoint = Waypoint.init(coordinate: currentLocation, name: "My Location")
      
       requestRoutes(origin: currentWayPoint, destination: destination)
   }
  
  
   @objc func tappedButton(sender: UIButton) {
       guard let routes = self.routes else {
           return
       }
       var engineConfig = NavigationEngineConfig()
       // Config mininum and maxnum camera zoom
       engineConfig.minNavigationCameraZoom = 16
       engineConfig.maxNavigationCameraZoom = 18
       let navigationService = NBNavigationService(routes: routes, routeIndex: 0,navigationEngConfig: engineConfig)
      
       let navigationOptions = NavigationOptions(navigationService: navigationService)
       let navigationViewController = NavigationViewController(for: routes,navigationOptions: navigationOptions)
       navigationViewController.modalPresentationStyle = .fullScreen
      
       navigationViewController.delegate = self
      
       // Start navigation
       present(navigationViewController, animated: true, completion: nil)
   }
  
   @objc func trackingClick() {
       guard let location = mapView?.userLocation else {
           return
       }
       guard let newCamera = mapView?.camera else {
           return
       }
       newCamera.centerCoordinate = location.coordinate
       newCamera.viewingDistance = 1000
       mapView?.setCamera(newCamera, animated: true)
   }
  
   func fitCameraWithRoutes(routes: [Route]) {
       guard let mapView = mapView else {
           return
       }
      
       var coordinates: [CLLocationCoordinate2D] = []
       routes.forEach({route in
           coordinates.append(contentsOf: route.coordinates!)
       })
       let polyLine = NGLPolyline(coordinates: coordinates, count: UInt(coordinates.count))
       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))
       mapView.setCamera(camera, animated: true)
   }
  
   func requestRoutes(origin: Waypoint, destination: Waypoint){
       let options = NavigationRouteOptions(origin: origin, destination: destination)
      
       Directions.shared.calculate(options) { [weak self] routes, error in
           guard let weakSelf = self else {
               return
           }
           guard error == nil else {
               print(error!)
               return
           }
          
           guard let routes = routes else { return }
           weakSelf.routes = routes
       }
   }  
}

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