Route alerts
Display route alerts.
/*This code example is part of the Mapbox Navigation SDK for iOS demo app,which you can build and run: https://github.com/mapbox/mapbox-navigation-ios-examplesTo learn more about each example in this app, including descriptions and linksto documentation, see our docs: https://docs.mapbox.com/ios/navigation/examples/route-alerts*/ import UIKitimport MapboxCoreNavigationimport MapboxNavigationimport MapboxDirectionsimport MapboxNavigationNative class RouteAlertsViewController: UIViewController {override func viewDidLoad() {super.viewDidLoad() let origin = CLLocationCoordinate2DMake(37.789811651648456, -122.47075850058)let destination = CLLocationCoordinate2DMake(37.79727245401114, -122.46951395567203)let options = NavigationRouteOptions(coordinates: [origin, destination]) Directions.shared.calculate(options) { [weak self] (_, result) inswitch result {case .failure(let error):print(error.localizedDescription)case .success(let response):guard let strongSelf = self else {return} // For demonstration purposes, simulate locations if the Simulate Navigation option is on. let indexedRouteResponse = IndexedRouteResponse(routeResponse: response, routeIndex: 0)let navigationService = MapboxNavigationService(indexedRouteResponse: indexedRouteResponse,customRoutingProvider: NavigationSettings.shared.directions,credentials: NavigationSettings.shared.directions.credentials,simulating: simulationIsEnabled ? .always : .onPoorGPS) // Define a customized `topBanner` to display route alerts during turn-by-turn navigation, and pass it to `NavigationOptions`.let topAlertsBannerViewController = TopAlertsBarViewController()let navigationOptions = NavigationOptions(navigationService: navigationService,topBanner: topAlertsBannerViewController)let navigationViewController = NavigationViewController(for: indexedRouteResponse,navigationOptions: navigationOptions) let parentSafeArea = navigationViewController.view.safeAreaLayoutGuidetopAlertsBannerViewController.view.topAnchor.constraint(equalTo: parentSafeArea.topAnchor).isActive = true navigationViewController.modalPresentationStyle = .fullScreen strongSelf.present(navigationViewController, animated: true)}}}} // MARK: - TopAlertsBarViewControllerclass TopAlertsBarViewController: ContainerViewController { lazy var topAlertsBannerView: InstructionsBannerView = {let banner = InstructionsBannerView()banner.translatesAutoresizingMaskIntoConstraints = falsebanner.layer.cornerRadius = 25banner.layer.opacity = 0.8return banner}() override func viewDidLoad() {view.addSubview(topAlertsBannerView)setupConstraints()} override func viewWillAppear(_ animated: Bool) {super.viewWillAppear(animated)setupConstraints()} private func setupConstraints() { // To change top banner size and position change layout constraints directly.let topAlertsBannerViewConstraints: [NSLayoutConstraint] = [topAlertsBannerView.topAnchor.constraint(equalTo: view.topAnchor, constant: 10),topAlertsBannerView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 60),topAlertsBannerView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -60),topAlertsBannerView.heightAnchor.constraint(equalToConstant: 100.0),topAlertsBannerView.centerXAnchor.constraint(equalTo: view.centerXAnchor)]NSLayoutConstraint.activate(topAlertsBannerViewConstraints)} open override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {super.traitCollectionDidChange(previousTraitCollection)setupConstraints()} public func updateAlerts(alerts: [String]) { // Change the property of`primaryLabel: InstructionLabel`.let text = alerts.joined(separator: "\n")topAlertsBannerView.primaryLabel.text = texttopAlertsBannerView.primaryLabel.numberOfLines = 0topAlertsBannerView.primaryLabel.lineBreakMode = NSLineBreakMode.byWordWrapping} // MARK: - NavigationServiceDelegate implementation public func navigationService(_ service: NavigationService, didPassVisualInstructionPoint instruction: VisualInstructionBanner, routeProgress: RouteProgress) {topAlertsBannerView.update(for: instruction)} public func navigationService(_ service: NavigationService, didRerouteAlong route: Route, at location: CLLocation?, proactive: Bool) {topAlertsBannerView.updateDistance(for: service.routeProgress.currentLegProgress.currentStepProgress)} public func navigationService(_ service: NavigationService, didUpdate progress: RouteProgress, with location: CLLocation, rawLocation: CLLocation) {topAlertsBannerView.updateDistance(for: service.routeProgress.currentLegProgress.currentStepProgress)let allAlerts = progress.upcomingRouteAlerts.filter({ !$0.description.isEmpty }).map({ $0.description })if !allAlerts.isEmpty {updateAlerts(alerts: allAlerts)} else {// If there's no usable route alerts in the route progress, displaying `currentVisualInstruction` instead.let instruction = progress.currentLegProgress.currentStepProgress.currentVisualInstructiontopAlertsBannerView.primaryLabel.lineBreakMode = NSLineBreakMode.byTruncatingTailtopAlertsBannerView.update(for: instruction)}}} // MARK: - MapboxCoreNavigation.RouteAlert to String implementationextension MapboxDirections.Incident: CustomStringConvertible { public var alertDescription: String {guard let kind = self.kind else { return self.description }if let impact = self.impact, let lanesBlocked = self.lanesBlocked {return "A \(impact) \(kind) ahead blocking \(lanesBlocked)"} else if let impact = self.impact {return "A \(impact) \(kind) ahead"} else {return "A \(kind) ahead blocking \(self.lanesBlocked!)"}}} extension MapboxCoreNavigation.RouteAlert: CustomStringConvertible { public var description: String {let distance = Int64(self.distanceToStart)guard distance > 0 && distance < 500 else { return "" } switch roadObject.kind {case .incident(let incident?):return "\(incident.alertDescription) in \(distance)m."case .tunnel(let alert?):if let alertName = alert.name {return "Tunnel \(alertName) in \(distance)m."} else {return "A tunnel in \(distance)m."}case .borderCrossing(let alert?):return "Crossing border from \(alert.from) to \(alert.to) in \(distance)m."case .serviceArea(let alert?):switch alert.type {case .restArea:return "Rest area in \(distance)m."case .serviceArea:return "Service area in \(distance)m."}case .tollCollection(let alert?):switch alert.type {case .booth:return "Toll booth in \(distance)m."case .gantry:return "Toll gantry in \(distance)m."}default:return ""}}}
Was this example helpful?