DidUpdateUserLocation is not called when view for userLocation is custom

I have an MKMapView that needs to track a user's location using a custom view (rather than a blue dot). To replace this view with a blue dot, I return it as follows:

 - (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation { if (annotation == [mapView userLocation]) { return userLocationView; } } 

To initiate tracking, I call

 [mapView setShowsUserLocation: YES]; [mapView setUserTrackingMode: MKUserTrackingModeFollow animated: NO]; [mapView setDelegate: self]; 

As expected, -mapView:didUpdateUserLocation: is called once when the application loads. Unfortunately, it was never called again unless I changed -mapView:viewForAnnotation: to the following implementation:

 - (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation { if (annotation == [mapView userLocation]) { return nil; } } 

With these changes, the map loads a blue dot as an indicator of the user's location, and -mapView:didUpdateUserLocation: often called often, as you would expect.

Is there some kind of mutual exclusivity to track user locations and user location? How can I do both events?

A source

This project demonstrates this problem. https://dl.dropbox.com/u/2338382/MapKitFuckery.zip

Mistake

This is most likely a mistake that I filed as a radar. In the meantime, an acceptable answer should be sufficient. However, it should be noted that I had to completely abandon [mapView userLocation] and [mapView showsUserLocation] in favor of just user annotation and CLLocationManager .

+7
source share
2 answers

Instead of relying on updates to the map display location, run CLLocationManager , set its delegate and wait for -locationManager:didUpdateToLocation:fromLocation: (on iOS 5 and below) or -locationManager:didUpdateLocations: (iOS 6). You will receive much more reliable and abundant information than using the delegation methods of the map view. You probably know how 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 looked at the delegate calls that go into the mapView delegate and returns anything other than nil , stops the calls to -mapView:didUpdateUserLocation: as you said. Here are the calls in the order in which 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 MKMapView delegate with update calls. If you check the status of showsUserLocation and mapView.userLocation , they look fine:

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

returns 1 and a non-nil object ( 1 <MKUserLocation: 0x1e02e580> ). Perhaps mapView requests its userLocation object to get the current location, and then sends it to the delegate. If this object disappears, it will not work.

This is a bit strange, but as I said, you will get the best updates from CLLocationManager updates.

+11
source

An old question that I would like to answer in my own way. I had a question similar to a question. I just needed to make a web service call, passing the user location as a GET parameter, when my MapView ViewController / Screen was loaded and the user location obtained by mapView. Then it made sense to call the web service as part of the didUpdateUserLocation delegation method. At first, I didn’t notice the wrong behavior, because everything seemed to work correctly, but for some reason, sometimes when the mapView screen opens, the user's blue dot is displayed, but the map will not “zoom in” and none of them were called by UpdateUserLocation, nor mine internal web service explicitly. After a few seconds, looking at the screen, the map will "zoom in" and the didUpdateUserLocation / web service is called. Some small glitch that I thought didn’t matter. Now my detail-oriented developer was still disappointed, and after several weeks of thinking about it, I decided to take action on this. Stackoverflow did not give me an answer right away, but nonetheless pointed me in the right direction. And there was a criminal: The sequence of calls! Madening, but it made sense as soon as I understood everything. I knew that in order to see the blue dot and get the location of the user, I had to tell mapView to do this. So, as a lover of Interface Builder, I correctly configured my mapView in IB, that is, I checked the field: "User location", easily. Then, after carefully reading the mapView documentation, I realized that my ViewController must comply with MKMapViewDelegate, made a deal. As I said, everything seemed to work fine, the blue dot will be displayed immediately, but sometimes the "enlargement" of the card will take several seconds ... well, my iPhone is already 3 years old, everything is slow, I "I'm dealing with slowness. .. Then I read this post ( https://stackoverflow.com/a/3/985616/ ... ) and everything became clear. In my case, using IB, there was a sequence of calls:

  • User flag set to IB
  • In viewDidLoad, call: mapView.delegate = self

While the sequence of calls should be:

  • User location flag is NOT set to IB (this is important)
  • mapView.delegate = self
  • mapView.showsUserLocation = true

And that changes EVERYTHING. Instead of sometimes scaling a mapView, and sometimes after a few seconds, mapView now scales immediately when the screen opens and the doUpdateUserLocation / web service is called.

Now a little more about why, it still "sometimes" worked. This is due to the fact that the location of my iPhone was sometimes updated immediately after loading the map screen, and sometimes not :-). I have to say that in addition to the stackoverflow message mentioned above, what helped me also tested my application in Simulator, since I needed to use a gpx file with an obviously static location coordinate, the unpredictable behavior that I described was then systematic.

So as much as I like Interface Builder, I may have to rethink how unconditional this love is.

PS: I know that this post is old and my answer is not entirely related to the @Patrick Perini problem, but I want him to help others, and that he could respond to @Patrick Perini's conclusion that the error was to blame, when she is not. Thanks for reading this blog post :-)

0
source

All Articles