iosswiftmapboxuserlocation

Mapbox iOS: User location not showing after turning device location service on


When device location service is off, a user opens my app that is using iOS Mapbox and Mapbox Navigation SDK. The user can't see its device location on the map because location service is off. When the user goes to the device Settings and turns location service on and then comes back to the app, user location is still invisible. When I check the user location coordinate by mapView.userLocation.coordinate, I get an invalid coordinate. Also the below delegate method not be called even when I call locationManager.startUpdatingLocation():

func mapView(_ mapView: MGLMapView, didUpdate userLocation: MGLUserLocation?)

I can solve the issue by removing old map view and set up a new map view. It means when the user comes back to the app after going to Settings and turning location service to on, I have to remove the old map view and set up another one to show user location. But I don't want to do that.

func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
    if status == .authorizedWhenInUse || status == .authorizedAlways {
        isLocationServicesEnabled = true
        if mapView != nil {
           mapView.removeFromSuperview()
           mapView = nil
        }
        resetupMapView()
    } 
}

How can I show the user location on map view after turning location service on? Is there any way to reload map view?

when I try to below code to show user location on map, I get crash with below message:

func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
    if status == .authorizedWhenInUse || status == .authorizedAlways {
        if mapView != nil {
            mapView.showsUserLocation = true
        }
    } 
}

Terminating app due to uncaught exception 'MGLUserLocationAnnotationTypeException', reason: 'User location annotation view must be a kind of MGLUserLocationAnnotationView. I don't know why it is. I also implemented viewForAnnotation delegate method to show a custom image for annotation.

func mapView(_ mapView: MGLMapView, viewFor annotation: MGLAnnotation) -> MGLAnnotationView? {
    let reuseIdentifier = "reusableView"
    var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseIdentifier)

    if annotationView == nil {
        annotationView = MGLAnnotationView(reuseIdentifier: reuseIdentifier)
        let markerImageView = UIImageView(image: UIImage(named: "Orange"))
        markerImageView.frame = CGRect(x: 0, y: 0, width: viewModel.markerImageWidth, height: viewModel.markerImageHeight)
        annotationView?.frame = markerImageView.frame
        annotationView?.center = markerImageView.center
        annotationView?.addSubview(markerImageView)
    }
    return annotationView
}

Solution

  • The mapView has a Bool property called showsUserLocation Even after turning location services on, if this property is set to false then the user's location will not be shown.

    Add mapView.showsUserLocation = true into your didChangeAuthorization method.

    ****** EDIT ******

    viewFor annotation is called when the map is adding each of the annotations, even the userAnnotation. But the userAnnotation needs to be a MGLUserLocationAnnotationView. If you are using custom images for your annotations but DON'T want to add one to the userAnnotation, you need to exclude it inside the viewFor annotation method. The easiest way would be:

        func mapView(_ mapView: MGLMapView, viewFor annotation: MGLAnnotation) -> MGLAnnotationView? {
            // We're not concerned with the user annotation.
            guard annotation is YOUR-ANNOTATION-TYPE else { return nil }
    
            let reuseIdentifier = "reusableView"
            var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseIdentifier)
    
           if annotationView == nil {
               annotationView = MGLAnnotationView(reuseIdentifier: reuseIdentifier)
               let markerImageView = UIImageView(image: UIImage(named: "Orange"))
               markerImageView.frame = CGRect(x: 0, y: 0, width: viewModel.markerImageWidth, height: viewModel.markerImageHeight)
               annotationView?.frame = markerImageView.frame
               annotationView?.center = markerImageView.center
               annotationView?.addSubview(markerImageView)
           }
           return annotationView
        }
    

    If you do want to assign a custom image to your userAnnotation then see this example on Mapbox's website that explains how. Custom User Annotation