Preventing MKMapView from continually re-zooming and re-centering to user location
Asked Answered
I

3

11

I seem to have the opposite problem from many of those posted here about MKMapView. Rather than not being able to get it to zoom to and display the current location, I can't seem to get it to stop doing so. Here's what happens:

  • I launch the app
  • The MKMapView shows my location with a blue dot
  • I zoom out and away on the map using my fingers
  • After a few seconds, the MKMapView suddenly zooms back in to center on my current location again

I've tried telling my CLLocationManager to stopUpdatingLocation (no effect, since an MKMapView knows how to use CoreLocation), and I've tried telling the MKMapView to setShowsUserLocation:NO (no blue dot displayed at all, which is not what I want). I even tried eliminating my CLLocationManager (no effect). What is causing this and how do I stop it?


Yes, I do set the location manager's accuracy and distance in -loadView.

I don't implement -mapViewDidFinishLoadingMap:. Here is my implementation of

-locationManager:didUpdateToLocation:fromLocation:
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
    // How many seconds ago was this new location created?
    NSTimeInterval t = [[newLocation timestamp] timeIntervalSinceNow];

    // CLLocationManagers will return the last found location of the device first,
    // you don't want that data in this case. If this location was made more than 3
    // minutes ago, ignore it.
    if (t < -180)
    {
        // This is cached data, you don't want it, keep looking
        return;
    }

    [locationManager stopUpdatingLocation];
}

I think this is the centering you're asking about, @Anna Karenina:

- (void)mapView:(MKMapView *)mv didUpdateUserLocation:(MKUserLocation *)u
{
    CLLocationCoordinate2D loc = [u coordinate];
    // Display the region 500 meters square around the current location
    MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(loc, 500, 500);
    [mv setRegion:region animated:YES];
}
Intercession answered 23/9, 2011 at 2:8 Comment(8)
Where are you doing the centering? In mapView:didUpdateUserLocation:? Can you post that code?Cockup
The didUpdateUserLocation method will get called every time the user location changes or gets a new reading. You can either add a bool ivar that you can check/set in the method so the centering is done only once OR you can conditionally center based on u.location.horizontalAccuracy or u.location.timestamp.Cockup
Ok, that makes sense, but... I now have -mapView:didUpdateUserLocation: conditionally centering if u.location.horizontalAccuracy is > locationManager.desiredAccuracy and it's still happening. If I reverse the condition, I don't get an initial zoom.Intercession
Log the horizontalAccuracy and desiredAccuracy values to see what you're getting and set the condition accordingly. It may take a few seconds for the first user location to come up.Cockup
I shouldn't have compared u.location.horizontalAccuracy to locationManager.desiredAccuracy because it's always -1. Now I'm comparing to 50.0 instead. The initial display is good, but I'm still getting the problem behavior.Intercession
Maybe you still need a bool flag so it doesn't center once you've got the accuracy you like. Also, look at the comment by syntaxrigger to this answer. It sounds similar to what you're seeing.Cockup
I read the problem and answer you suggested; looks like a similar problem, but his "solution" of changing the distanceFilter didn't work for me. I've now removed everything to do with a CLLocationManager. It looks like setting a boolean flag the first time the accuracy is within 50 meters works. However I'm concerned about when the user is out driving around with the app running and checking the map again. Ideally, the blue current location dot would move, but the map wouldn't keep re-zooming in on it. I don't think this solution provides that, does it?Intercession
Answering my own question: yes, this solution does provide for the current location dot to keep moving! I tested it on the road today. Thank you, Anna Karenina. Problem solved by he boolean flag. Hm, apparently I can't mark this as "solved" since your comments weren't in a separate answer....Intercession
P
4

Dont implement - (void)mapView:(MKMapView *)mv didUpdateUserLocation:(MKUserLocation *)u method.

Instead use locationmanager didUpdateToLocation.

didUpdateUserLocation is called multiple times without any reason.where didUpdateToLocation is called when any location changes occurred.

Then u can manually set the mkmapview region in the didUpdateToLocation method.

Pevzner answered 9/7, 2012 at 5:53 Comment(0)
D
0

First, and I dont know that this will effect things much, but do you set your location managers accuracy and distance filter at all in viewDidLoad (or wherever you are implementing your locationManager):

[locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
[locationManager setDistanceFilter:kCLDistanceFilterNone];

Second, check these 2 functions in your source (ignore the content of the actual functions I have listed here, it is just for illustration and to show usage) and if possible, provide your code for these functions so we can better assist:

- (void)mapViewDidFinishLoadingMap:(MKMapView *)mapView
{
    [activityIndicator stopAnimating];
}

- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
    NSDate* eventDate = newLocation.timestamp;
    NSTimeInterval howRecent = [eventDate timeIntervalSinceNow];
    if (abs(howRecent) < 15.0)
    {
        NSLog(@"New Latitude %+.6f, Longitude %+.6f", newLocation.coordinate.latitude, newLocation.coordinate.longitude);
    }
}
Degenerate answered 23/9, 2011 at 2:21 Comment(0)
U
0

I use dispatch_once in MKMapViewDelegate method to zoom only once at the beginning. Here is the code.

- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation  {    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        CLLocationCoordinate2D newCoordinate = CLLocationCoordinate2DMake(mapView.userLocation.coordinate.latitude, mapView.userLocation.coordinate.longitude);

        MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(newCoordinate, 500, 500);
        [mapView setRegion:region animated:YES];
    });
}
Unreasonable answered 30/6, 2017 at 18:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.