didUpdateUserLocation not called when view for userLocation is custom

geolocation, ios, mapkit, mkmapview, mkmapviewdelegate

Solution

Instead of relying on the map view's location updates, start a `CLLocationManager`, set its `delegate` and wait for `-locationManager:didUpdateToLocation:fromLocation:` (in iOS 5 and lower) or `-locationManager:didUpdateLocations:` (iOS 6). You will get much more reliable and plentiful information than using the map view's delegate methods. You probably know the way to do this, but here it is:

#import <CoreLocation/CoreLocation.h>

- (void)viewWillAppear:(BOOL)animated
{
    self.locationManager = [[CLLocationManager alloc] init];
    self.locationManager.delegate = self;
    [self.locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
    [self.locationManager startUpdatingLocation];
}

// Deprecated in iOS 6
- (void)locationManager:(CLLocationManager *)manager
    didUpdateToLocation:(CLLocation *)newLocation
           fromLocation:(CLLocation *)oldLocation
{

    // Check the age of the newLocation isn't too long ago using newLocation.timestamp

    // Set the map dot using newLocation.coordinate

    // Set an MKCircle to represent accuracy using newLocation.horizontalAccuracy
}

I had a look at the delegate calls that come in to the mapView's delegate, and returning anything other than `nil` stops calls to `-mapView:didUpdateUserLocation:`, like you said. Here are the calls in the order they arrive:

 - (void)mapViewWillStartLocatingUser:(MKMapView *)mapView
 - (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
 - (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id < MKAnnotation >)annotation
 - (void)mapViewWillStartLoadingMap:(MKMapView *)mapView
 - (void)mapView:(MKMapView *)mapView didFailToLocateUserWithError:(NSError *)error

Presumably the `MKUserLocation` object, not the `MKMapView` is the object responsible for calling the delegate with update calls. If you check the status of `showsUserLocation` and `mapView.userLocation`, they both look fine:

NSLog(@"%d %@", mapView.showsUserLocation, mapView.userLocation);

returns `1` and a non-nil object (`1 <MKUserLocation: 0x1e02e580>`). Maybe the `mapView` queries its `userLocation` object to get the current location, then sends it to the delegate. If that object has gone, it won't work.

It's a bit strange, but like I said, you'll get better updates from a `CLLocationManager`'s updates.

Problem

I have an `MKMapView` that's supposed to track the user's location using a custom view (not the blue dot). In order to substitute this view for the blue dot, I return it thusly: ``` - (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation { if (annotation == [mapView userLocation]) { return userLocationView; } } ``` To initialize tracking, I call ``` [mapView setShowsUserLocation: YES]; [mapView setUserTrackingMode: MKUserTrackingModeFollow animated: NO]; [mapView setDelegate: self]; ``` As would be expected, `-mapView:didUpdateUserLocation:` gets called once when the app loads. Unfortunately, it's never called again unless I change `-mapView:viewForAnnotation:` to have the following implementation: ``` - (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation { if (annotation == [mapView userLocation]) { return nil; } } ``` With these changes, the map loads the blue dot as the indicator of the user's location, and `-mapView:didUpdateUserLocation:` gets called frequently, as would be expected. Is there some sort of mutual exclusivity for tracking users' locations and have a custom user location view? How can I make both happen? Source This project demonstrates this issue. https://dl.dropbox.com/u/2338382/MapKitFuckery.zip Bug This is most likely a bug, which I've filed as a radar. In the interim, the accepted answer should prove sufficient. However, it bears noting that I had to give up entirely on `[mapView userLocation]` and `[mapView showsUserLocation]`, in favor of simply a custom annotation and the `CLLocationManager`.

Original source