MKMapView's user location is wrong on startup or resume

前端 未结 7 1978
臣服心动
臣服心动 2021-01-17 19:15

When I start my application fresh, or resume after a long time, MKMapView\'s notion of the userLocation is wrong and shows me in the middle of the sea.

I am using th

相关标签:
7条回答
  • 2021-01-17 19:48

    When using CLLocationManager directly, you normally get a cached location in the first callback. It's normally a good location, although old. After that you quickly get additional callbacks giving better locations using wifi, cell tower (if available). If you have asked for < 1000m accuracy you will (after more seconds) get GPS triangulation.

    None of those should be inaccurate enough to be in the middle of the ocean. I suspect that the this line of code:

    self.mapView.centerCoordinate = self.mapView.userLocation.location.coordinate;
    

    is accessing the coordinate while userLocation or location is nil. If userLocation or location is nil, this will return 0 coordinates. The location of lat=0, lon=0 is in the Atlantic Ocean, off the coast of Africa. You could add a check of location to make sure it is not nil before getting the coordinate from it, ie:

    if (self.mapView.userLocation.location) {
        self.mapView.centerCoordinate = self.mapView.userLocation.location.coordinate;
        [mapView setCenterCoordinate:self.mapView.userLocation.location.coordinate zoomLevel:ZOOM_LEVEL animated:YES];           
    }
    

    You will also want to wait for callbacks to the MKMapViewDelegate mapView:didUpdateUserLocation: to know when there is a valid location available. Your implementation of didUpdateUserLocation: should discard any location that has a horizontalAccuracy < 0 which indicates an invalid location.

    -(void)mapView:(MKMapView*)mapView didUpdateUserLocation:(MKUserLocation*)userLocation
    {
        if (userLocation.location.horizontalAccuracy > 0) {
            [mapView setCenterCoordinate:self.mapView.userLocation.location.coordinate zoomLevel:ZOOM_LEVEL animated:YES];           
        }
    }
    
    0 讨论(0)
  • 2021-01-17 19:48

    What I do here is-

    1. start location updates
    2. on location update, check the age of location, if it's too old, I wait
    3. on receiving new update I update it on the map

    If you need very accurate location, then put a check on accuracy as well and a timeout beyond which you can't tolerate waiting and use the less accurate fix.

    0 讨论(0)
  • 2021-01-17 19:50

    This is unfortunately a function of the GPS chip. It's not always on, so the data will usually be wrong for the first couple of moments. Your best bet would probably be to store the last position recorded by the app in NSUserDefaults, then wait for the precision to be where you want it to be before switching to live data, or else hide the MkMapView until the precision is where you want it to be, then display it at that point (you could show a loading screen in the interim)

    0 讨论(0)
  • 2021-01-17 19:54

    That's the expected behavior : the user location isn't always tracked by the iPhone using GPS (it would consume to much battery). So as soon as the map is displayed, the MKMapView instance shows the last 'best' user position it knows and then, improves the accuracy by activating the tracking (this is a seamless process, you don't have to care about it) .

    You can monitor when the MKMapView updates the user location on the map by implementing the MKMapViewDelegate protocol. Just implement :

    - (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation      {
        CLLocationAccuracy accuracy = userLocation.location.horizontalAccuracy;
        if (accuracy ......) {
        } 
    }
    

    (More info from the apple documentation here )

    The code above in my example checks the accuracy of the position currently being displayed by the mapView and reacts accordingly.

    [EDIT]
    If showing the user location in the middle of the sea at first really bother you, you can you hide the user location until you get a location that is accurate/fresh enough.
    To do so, set the showsUserLocation property of the MKMapView to NO at first until you get an accurate enough location (thanks to the previous delegate callback) and then set it to YES.
    By doing you, you will avoid displaying a location that is not accurate or too old to be diplayed (there is a timestamp property in the CLLocation to check wether it's an old location or not)

    N.B:You don't have to create a CLLocationManager instance on your side, the MKMapView creates one internally and publish locations it receives via this delegate selector.

    0 讨论(0)
  • 2021-01-17 19:55

    In my apps I used these methods:

    First - it's good to have a default position (for example - application is about a city - then zoom in to show whole city) (on first - first app opening, when no data downloaded)

    Second - if user has already used map - can store his coordinates, and show those (+ span(zoom) level) in case there are no other data available.

    Third - in case of data:

    1.) If database has already some coordinates - on first map opening (when application has been closed) - zoom out/in to show all pins on map.

    2.) When still in map and just arrived updates (new locations or changed something), there are two possibilities :

    2.1.) If user has not zoomed in to any location - map zooms in/out to show all locations.

    2.2.) if user has zoomed in - locations are updated, but map doesnt zoom out

    Ofcourse - then there are also some filter buttons (for example : search, user location, and some more - which filters locations, and zooms out/in to show filtered results)

    0 讨论(0)
  • 2021-01-17 19:57

    Just as Jeremy Massel wrote you have to sort out the first bad positions on app start / continue. Found a great blog post a couple of months ago:

    http://troybrant.net/blog/2010/02/detecting-bad-corelocation-data/

    0 讨论(0)
提交回复
热议问题