Custom Waypoint Styling

Easily tailor the waypoint styling to suit your preferences and create a visually appealing and intuitive navigation experience.

This example shows:

  • How to customize the UI of waypoints displayed in a route

  • The waypoint styling is customized in CustomWaypointStylingViewController

documentation image

For all code examples, refer to Navigation Code Examples

CustomWaypointStylingViewController view source

1
import UIKit
2
import NbmapNavigation
3
import NbmapCoreNavigation
4
import NbmapDirections
5
import Nbmap
6
7
class CustomWaypointStylingViewController: UIViewController {
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
if oldValue != nil || routes == nil || routes?.first == nil {
21
navigationMapView?.removeRoutes()
22
navigationMapView?.removeRouteDurationSymbol()
23
if let annotation = navigationMapView?.annotations?.last {
24
navigationMapView?.removeAnnotation(annotation)
25
}
26
return
27
}
28
29
guard let routes = routes,
30
let current = routes.first
31
else {
32
return
33
}
34
35
navigationMapView?.showRoutes(routes)
36
navigationMapView?.showWaypoints(current)
37
navigationMapView?.showRouteDurationSymbol(routes)
38
}
39
}
40
41
private let startButton = UIButton()
42
43
override func viewDidLoad() {
44
super.viewDidLoad()
45
46
self.navigationMapView = NavigationMapView(frame: view.bounds)
47
48
navigationMapView?.userTrackingMode = .follow
49
navigationMapView?.navigationMapDelegate = self
50
setupStartButton()
51
requestRoutes()
52
53
}
54
55
func setupStartButton() {
56
startButton.setTitle("Start", for: .normal)
57
startButton.layer.cornerRadius = 5
58
startButton.contentEdgeInsets = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
59
startButton.backgroundColor = .blue
60
61
startButton.addTarget(self, action: #selector(performAction), for: .touchUpInside)
62
view.addSubview(startButton)
63
startButton.translatesAutoresizingMaskIntoConstraints = false
64
startButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -50).isActive = true
65
startButton.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor).isActive = true
66
startButton.titleLabel?.font = UIFont.systemFont(ofSize: 25)
67
}
68
69
@objc func performAction(_ sender: Any) {
70
guard let routes = self.routes else {
71
return
72
}
73
let navigationService = NBNavigationService(routes: routes, routeIndex: 0)
74
let styles = [DayStyle(), NightStyle()]
75
let navigationOptions = NavigationOptions(styles:styles,navigationService: navigationService)
76
77
let navigationViewController = NavigationViewController(for: routes,navigationOptions: navigationOptions)
78
navigationViewController.modalPresentationStyle = .fullScreen
79
80
navigationViewController.delegate = self
81
82
present(navigationViewController, animated: true, completion: nil)
83
}
84
85
func requestRoutes(){
86
let origin = CLLocation(latitude: 37.775252, longitude: -122.416082)
87
let firstWaypoint = CLLocation(latitude: 37.779196, longitude: -122.410833)
88
let secondWaypoint = CLLocation(latitude: 37.777342, longitude: -122.404094)
89
90
let options = NavigationRouteOptions(locations: [origin,firstWaypoint,secondWaypoint])
91
Directions.shared.calculate(options) { [weak self] routes, error in
92
guard let weakSelf = self else {
93
return
94
}
95
guard error == nil else {
96
print(error!)
97
return
98
}
99
100
guard let routes = routes else { return }
101
weakSelf.routes = routes
102
}
103
}
104
105
106
func customShape(for waypoints: [Waypoint], legIndex: Int) -> NGLShape? {
107
var features = [NGLPointFeature]()
108
109
for (waypointIndex, waypoint) in waypoints.enumerated() {
110
let feature = NGLPointFeature()
111
feature.coordinate = waypoint.coordinate
112
feature.attributes = [
113
"waypointCompleted": waypointIndex < legIndex,
114
"name": waypointIndex + 1
115
]
116
features.append(feature)
117
}
118
119
return NGLShapeCollectionFeature(shapes: features)
120
}
121
122
func customWaypointCircleStyleLayer(identifier: String, source: NGLSource) -> NGLStyleLayer {
123
let circles = NGLCircleStyleLayer(identifier: identifier, source: source)
124
let opacity = NSExpression(forConditional: NSPredicate(format: "waypointCompleted == true"), trueExpression: NSExpression(forConstantValue: 0.5), falseExpression: NSExpression(forConstantValue: 1))
125
126
circles.circleColor = NSExpression(forConstantValue: UIColor(red:0.9, green:0.9, blue:0.9, alpha:1.0))
127
circles.circleOpacity = opacity
128
circles.circleRadius = NSExpression(forConstantValue: 10)
129
circles.circleStrokeColor = NSExpression(forConstantValue: UIColor.green)
130
circles.circleStrokeWidth = NSExpression(forConstantValue: 1)
131
circles.circleStrokeOpacity = opacity
132
133
return circles
134
}
135
136
func customWaypointSymbolStyleLayer(identifier: String, source: NGLSource) -> NGLStyleLayer {
137
let symbol = NGLSymbolStyleLayer(identifier: identifier, source: source)
138
139
symbol.text = NSExpression(format: "CAST(name, 'NSString')")
140
symbol.textOpacity = NSExpression(forConditional: NSPredicate(format: "waypointCompleted == true"), trueExpression: NSExpression(forConstantValue: 0.5), falseExpression: NSExpression(forConstantValue: 1))
141
symbol.textFontSize = NSExpression(forConstantValue: 10)
142
symbol.textHaloWidth = NSExpression(forConstantValue: 0.25)
143
symbol.textHaloColor = NSExpression(forConstantValue: UIColor.blue)
144
145
return symbol
146
}
147
148
}
149
150
extension CustomWaypointStylingViewController: NavigationMapViewDelegate {
151
152
func navigationMapView(_ mapView: NavigationMapView, waypointStyleLayerWithIdentifier identifier: String, source: NGLSource) -> NGLStyleLayer? {
153
return customWaypointCircleStyleLayer(identifier: identifier, source: source)
154
}
155
156
func navigationMapView(_ mapView: NavigationMapView, waypointSymbolStyleLayerWithIdentifier identifier: String, source: NGLSource) -> NGLStyleLayer? {
157
return customWaypointSymbolStyleLayer(identifier: identifier, source: source)
158
}
159
160
func navigationMapView(_ mapView: NavigationMapView, shapeFor waypoints: [Waypoint], legIndex: Int) -> NGLShape? {
161
return customShape(for: waypoints, legIndex: legIndex)
162
}
163
}
164
165
extension CustomWaypointStylingViewController: NavigationViewControllerDelegate {
166
func navigationViewController(_ navigationViewController: NavigationViewController, waypointStyleLayerWithIdentifier identifier: String, source: NGLSource) -> NGLStyleLayer? {
167
return customWaypointCircleStyleLayer(identifier: identifier, source: source)
168
}
169
170
func navigationViewController(_ navigationViewController: NavigationViewController, waypointSymbolStyleLayerWithIdentifier identifier: String, source: NGLSource) -> NGLStyleLayer? {
171
return customWaypointSymbolStyleLayer(identifier: identifier, source: source)
172
}
173
174
func navigationViewController(_ navigationViewController: NavigationViewController, shapeFor waypoints: [Waypoint], legIndex: Int) -> NGLShape? {
175
return customShape(for: waypoints, legIndex: legIndex)
176
}
177
178
}

The code example is for a CustomWaypointStylingViewController class. This class is used to customize the UI of waypoints displayed in a route. The class does this by overriding the navigationMapView(_:waypointStyleLayerWithIdentifier:source:), navigationMapView**(:waypointSymbolStyleLayerWithIdentifier:source:)**, and **navigationMapView(:shapeFor:legIndex:)** methods. The navigationMapView(_:waypointStyleLayerWithIdentifier:source:) method is used to customize the style of the waypoint circle. The method takes three parameters:

  • mapView: The NavigationMapView instance that is displaying the route.
  • identifier: The identifier of the style layer.
  • source: The source of the style layer.

The method returns a NGLStyleLayer instance that represents the custom style of the waypoint circle. The NGLStyleLayer instance can be used to specify the color, opacity, radius, and stroke of the waypoint circle.

The navigationMapView(_:waypointSymbolStyleLayerWithIdentifier:source:) method is used to customize the style of the waypoint symbol. The method takes three parameters:

  • mapView: The NavigationMapView instance that is displaying the route.

  • identifier: The identifier of the style layer.

  • source: The source of the style layer.

The method returns a NGLStyleLayer instance that represents the custom style of the waypoint symbol. The NGLStyleLayer instance can be used to specify the text, opacity, font size, halo width, and halo color of the waypoint symbol.

The navigationMapView(_:shapeFor:legIndex:) method is used to customize the shape of the waypoints. The method takes three parameters:

  • mapView: The NavigationMapView instance that is displaying the route.

  • waypoints: The waypoints that are being displayed.

  • egIndex: The index of the current leg.

The method returns a NGLShape instance that represents the custom shape of the waypoints. The NGLShape instance can be used to specify the coordinates of the waypoints.

By overriding these methods, you can customize the UI of waypoints displayed in a route. Here are some examples of how you can use the CustomWaypointStylingViewController class to customize the UI of waypoints:

  • You can change the color of the waypoint circle.

  • You can change the opacity of the waypoint circle.

  • You can change the radius of the waypoint circle.

  • You can change the stroke of the waypoint circle.

  • You can change the text of the waypoint symbol.

  • You can change the opacity of the waypoint symbol.

  • You can change the font size of the waypoint symbol.

  • You can change the halo width of the waypoint symbol.

  • You can change the halo color of the waypoint symbol.

  • You can change the coordinates of the waypoints.

© 2024 NextBillion.ai all rights reserved.