6

I've created a test application with only one view containing an MKMapView and a controller which acts as the MapView's delegate.

When I do a fresh build (removed from the device completely before re-installing) and log the callbacks, I can see that mapView:didUpdateUserLocation is called twice before the user has indicated whether they wish to show their current location or not.

The MKUserLocation objects passed to the callback are invalid:

2012-03-13 08:20:17.518 MapTest[3325:707] Did update user location: 0.000000,0.000000
2012-03-13 08:20:17.581 MapTest[3325:707] Did update user location: 0.000000,0.000000

Is this the expected behaviour for MKMapKit or a bug?

Update

I'm running this on my iPhone 4, not a simulator. Here's the controller code:

#import "ViewController.h"

@implementation ViewController

@synthesize mapView;

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.mapView.showsUserLocation = YES;
    self.mapView.delegate = self;
}

- (void)viewDidUnload
{
    [super viewDidUnload];
    // Release any retained subviews of the main view.
}

-(IBAction)trackButtonTapped:(id)sender
{
    self.mapView.showsUserLocation = !self.mapView.showsUserLocation;
}

#pragma mark - MKMapKitDelegate

-(void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
{
    NSLog(@"Did update user location: %f,%f", userLocation.coordinate.latitude, userLocation.coordinate.longitude);
}

-(void)mapViewWillStartLoadingMap:(MKMapView *)mapView
{
    NSLog(@"Will start loading map");
}

-(void)mapViewDidFinishLoadingMap:(MKMapView *)mapView
{
    NSLog(@"Did finish loading map");    
}

-(void)mapViewWillStartLocatingUser:(MKMapView *)mapView
{
    NSLog(@"Will start locating user");
}

-(void)mapViewDidStopLocatingUser:(MKMapView *)mapView
{
    NSLog(@"Did stop locating user");
}

-(void)mapViewDidFailLoadingMap:(MKMapView *)mapView withError:(NSError *)error
{
    NSLog(@"Did fail loading map");
}

-(void)mapView:(MKMapView *)mapView didFailToLocateUserWithError:(NSError *)error
{
    if (error.code == kCLErrorDenied){
        NSLog(@"User refused location services");
    } else {
        NSLog(@"Did fail to locate user with error: %@", error.description);    
    }    
}

@end
bodacious
  • 6,608
  • 9
  • 45
  • 74
  • didUpdateLocation is called by CLLocationManager and showsUserLocation merely tells map whether or not to show blue dot on map as current location. So I think this behavior is expected. – chatur Mar 13 '12 at 09:52
  • regardless of where it's called - should it be called at all before the user has stated whether they wish to allow location services or not? – bodacious Mar 13 '12 at 09:55
  • didUpdateLocation will get called only after you say [locationManager startUpdatingLocation]; and secondly showsUserLocation property does not decide whether location cervices are allowed to locate user. Again, showsUserLocation merely tells map whether or not to show blue dot on map as current location – chatur Mar 13 '12 at 10:37
  • My opinion is that it's a bug (how can the map view say user location has been updated when the user hasn't even granted permission?). See a workaround in [this answer](http://stackoverflow.com/questions/9543571/how-to-check-validity-of-cllocation-in-ios). –  Mar 13 '12 at 13:01
  • there is no call to `[locationManager startUpdatingLocation];` in this app – bodacious Mar 13 '12 at 13:31
  • @AnnaKarenina If this is a bug - where is the best place to open a ticket with apple? – bodacious Mar 13 '12 at 13:31
  • That answer is referring to a CLLocation object (not CLLocationManager) but the workaround is the same (check if userLocation.location is nil). To file bugs, see [this answer](http://stackoverflow.com/questions/2729187/file-bug-for-iphone-sdk). –  Mar 13 '12 at 13:38
  • Thanks @AnnaKarenina - would you like to repost your comments as an answer to this question so I can close it? – bodacious Mar 14 '12 at 08:35

4 Answers4

10

My opinion is that it's a bug.

How can the map view say user location has been updated when the user hasn't even granted permission?

The workaround I use is to check if userLocation.location is nil:

- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
{
    if (userLocation.location == nil)
        return;

    //do something with userLocation...
}
5

instead checking for nil userlocation (where 0,0 coordinates can actually be valid) you should use:

if([CLLocationManager authorizationStatus] == kCLAuthorizationStatusAuthorized) {
  // do something with the coords
} else {
  // the coords are invalid
}
Gianluca P.
  • 1,536
  • 15
  • 15
  • If the device coordinates actually were 0,0 then the location object would _not_ be nil. Also, being authorized for location doesn't necessarily mean you actually have a location. –  Apr 21 '15 at 11:24
0

Maybe this helps someone. For me the "problem" was that I had set show user's location trough interface builder too. Removing it here and setting it through code made the delegate methods get work as suspected.

Cedrick
  • 566
  • 3
  • 13
0

With "self.mapView.showsUserLocation = YES;" in viewDidLoad it will start trying to get the user's location straight away. If you take that out it'll wait until the user has pressed a button.

Craig
  • 8,093
  • 8
  • 42
  • 74
  • Yes, but my point is: the map shouldn't start trying to fetch the user's location until the User has decided whether or not to allow the app to access their location info. – bodacious Mar 14 '12 at 08:34
  • Oh, so there is that Location Services alert on screen and the user hasn't responded yet? – Craig Mar 14 '12 at 19:08
  • Yes - that's the issue. The user hasn't decided whether or not to allow location services yet, but the call backs for updating the user location are being called – bodacious Mar 14 '12 at 19:25