mapbox / mapbox/mapbox-navigation-ios
UserCourseView puck is off
Nobody has claimed this yet.
- Dominant language
- Swift
- Stars
- 918
- Forks
- 326
- Avg merge
- 1h 16m
- Merged PRs (30d)
- 3
Description
Hello,
I am writing a custom navigation based on NavigationMapView. Sometimes the puck appears to be off the track after starting navigation. This problem seems to be random, because sometimes it can be reproduced 4 times from 10 launches but sometimes it will show only once or twice per 10 launches. The offset varies by size and by side as well (sometimes it is on right and sometimes on left). It does not happen on simulator.
Please look into this issue as we cannot release our product due to this issue. It is a show stopper for us.
Problem:
After launching navigation by starting NavigationService and centering map the puck will be off by random offset. Sometimes it is off on left side, sometimes on right side. The problem appears exclusively when the NavigationMapView is present on screen and after a while you will start the navigation.
After manually zooming in or out the puck will correct itself.
Steps to reproduce:
- Create a NavigationMapView programmatically with AutoLayout
- Show map and present ViewController with map
- Wait few seconds
- Try to start the navigation
You will need to try it for 5 to 10 times in a row to reproduce it.
I am attaching a slimmed down version of your custom navigation having the issue:
import UIKit
import MapboxCoreNavigation
import MapboxNavigation
import Mapbox
import CoreLocation
import AVFoundation
import MapboxDirections
import Turf
class CustomViewController: UIViewController, MGLMapViewDelegate {
var destination: MGLPointAnnotation!
let directions = Directions.shared
var navigationService: NavigationService!
var simulateLocation = false
var userRoute: Route?
// Start voice instructions
var voiceController: MapboxVoiceController!
var mapView: NavigationMapView = NavigationMapView()
override func viewDidLoad() {
super.viewDidLoad()
let locationManager = simulateLocation ? SimulatedLocationManager(route: userRoute!) : NavigationLocationManager()
navigationService = MapboxNavigationService(route: userRoute!, locationSource: locationManager, simulating: simulateLocation ? .always : .onPoorGPS)
voiceController = MapboxVoiceController(navigationService: navigationService)
self.mapView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(self.mapView)
self.mapView.pinTo(parentView: self.view)
mapView.delegate = self
mapView.compassView.isHidden = true
// Add listeners for progress updates
resumeNotifications()
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
// Start navigation
self.navigationService.start()
// Center map on user
self.mapView.recenterMap()
}
}
func resumeNotifications() {
NotificationCenter.default.addObserver(self, selector: #selector(progressDidChange(_ :)), name: .routeControllerProgressDidChange, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(rerouted(_:)), name: .routeControllerDidReroute, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(updateInstructionsBanner(notification:)), name: .routeControllerDidPassVisualInstructionPoint, object: navigationService.router)
}
func suspendNotifications() {
NotificationCenter.default.removeObserver(self, name: .routeControllerProgressDidChange, object: nil)
NotificationCenter.default.removeObserver(self, name: .routeControllerWillReroute, object: nil)
NotificationCenter.default.removeObserver(self, name: .routeControllerDidPassVisualInstructionPoint, object: nil)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// This applies a default style to the top banner.
DayStyle().apply()
}
deinit {
suspendNotifications()
}
func mapView(_ mapView: MGLMapView, didFinishLoading style: MGLStyle) {
self.mapView.showRoutes([navigationService.route])
}
// Notifications sent on all location updates
@objc func progressDidChange(_ notification: NSNotification) {
let routeProgress = notification.userInfo![RouteControllerNotificationUserInfoKey.routeProgressKey] as! RouteProgress
let location = notification.userInfo![RouteControllerNotificationUserInfoKey.locationKey] as! CLLocation
// Add maneuver arrow
if routeProgress.currentLegProgress.followOnStep != nil {
mapView.addArrow(route: routeProgress.route, legIndex: routeProgress.legIndex, stepIndex: routeProgress.currentLegProgress.stepIndex + 1)
} else {
mapView.removeArrow()
}
// Update the user puck
mapView.updateCourseTracking(location: location, animated: true)
}
@objc func updateInstructionsBanner(notification: NSNotification) {
guard let routeProgress = notification.userInfo?[RouteControllerNotificationUserInfoKey.routeProgressKey] as? RouteProgress else { return }
//instructionsBannerView.update(for: routeProgress.currentLegProgress.currentStepProgress.currentVisualInstruction)
}
// Fired when the user is no longer on the route.
// Update the route on the map.
@objc func rerouted(_ notification: NSNotification) {
self.mapView.showRoutes([navigationService.route])
}
@IBAction func cancelButtonPressed(_ sender: Any) {
self.dismiss(animated: true, completion: nil)
}
@IBAction func recenterMap(_ sender: Any) {
mapView.recenterMap()
}
@IBAction func showFeedback(_ sender: Any) {
//present(feedbackViewController, animated: true, completion: nil)
}
}
How it looks like:
After zooming in:
Please let me know of any workaround.
Mapbox Navigation SDK version:
0.38.0
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the attached CustomViewController and inspect NavigationMapView's recenterMap() and updateCourseTracking(location:animated:) calls. Reproduce the issue on a physical device by starting NavigationService after the map has been visible for several seconds, repeating the launch 5–10 times. Done means the puck remains aligned without requiring manual zooming.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- swift
- Domain
- mobile-dev
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100