Smooth tracking of user location in MKMapView

I would like to follow the blue dot of the user in the same way that Google Maps does. This means that when the place changes, the map (or its center) should smoothly follow it. But when I use the standard method with a delegate:

- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation { [map setCenterCoordinate:map.userLocation.coordinate animated:YES]; } 

or an observer with key signs on mapview.userLocation , then moving the map is pretty "intermittent", it quickly "jumps" to a new location, even if I use the animated parameter.

In addition, I think that the blue dot itself does not move as smoothly as in its own Google Maps application, and often jumps to a new location, rather than moving there.

Thanks for any help.

+6
objective-c cocoa-touch mkmapview
source share
3 answers

'hi jak

I also tried to solve this problem, but it seems that MKMapView can use some fancy things between updates to predict the next position and make the movement smoother.

If you look at how they handle the compass, they do a lot of filtering because the raw compass data is not very stable, so they make it really smooth and smooth.

Also note that when tracking, you can scale, but not pan. I could not repeat this function in my application, even if scrolling is disabled, it still scrolls when you zoom in, if you roll your fingers.

I think they do not disclose everything to keep us ahead :) D

+3
source share

I used the CLLocationManager delegate update methods to get the current location about every second, keeping the previous location and interpolating between them. This is a bit more effort, but t makes tracking a lot smoother. Here is my code:

 #define zoomSpan MKCoordinateSpanMake(0.001f, 0.001f) @property (strong, nonatomic) CLLocation *lastLocation; @property (strong, nonatomic) MKMapView *mapView; @property (strong, nonatomic) CLLocationManager *locationManager; ... - (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations { CLLocation *currentLocation = locations[locations.count - 1]; // Set to last location, for faster setting to correct position [self.mapView setRegion:MKCoordinateRegionMake(self.lastLocation.coordinate, zoomSpan) animated:NO]; // Locations in-between coordinates for smooth tracking CLLocationDegrees deltaLat = currentLocation.coordinate.latitude - self.lastLocation.coordinate.latitude; CLLocationDegrees deltaLon = currentLocation.coordinate.longitude - self.lastLocation.coordinate.longitude; NSArray *locationsArray = @[[[CLLocation alloc] initWithLatitude:(self.lastLocation.coordinate.latitude + deltaLat * 0.2) longitude:(self.lastLocation.coordinate.longitude + deltaLon * 0.2)], [[CLLocation alloc] initWithLatitude:(self.lastLocation.coordinate.latitude + deltaLat * 0.4) longitude:(self.lastLocation.coordinate.longitude + deltaLon * 0.4)], [[CLLocation alloc] initWithLatitude:(self.lastLocation.coordinate.latitude + deltaLat * 0.6) longitude:(self.lastLocation.coordinate.longitude + deltaLon * 0.6)], [[CLLocation alloc] initWithLatitude:(self.lastLocation.coordinate.latitude + deltaLat * 0.8) longitude:(self.lastLocation.coordinate.longitude + deltaLon * 0.8)], currentLocation]; float timer = 0; for (CLLocation *nextLocation in locationsArray) { [self performSelector:@selector(updateMapCenterLocation:) withObject:nextLocation afterDelay:timer]; timer += 0.2; } self.lastLocation = currentLocation; } - (void)updateMapCenterLocation:(CLLocation *)location { [self.mapView setRegion:MKCoordinateRegionMake(location.coordinate, zoomSpan) animated:YES]; } 

Make sure that the class in which you use it (usually a view controller that contains the map view) is set to the CLLocationManager delegate and implements the CLLocationManagerDelegate protocol.

Some additional comments:

  • The behavior of this code is similar to MKUserTracking , but it does not support custom zoom levels. There seems to be a bug that returns the zoom level when starting tracking, so this is a workaround.
  • zoomSpan is currently fixed, but you can also use the range of the current area with self.mapView.region.span .
+1
source share

You can also try the following:

 - (void)adjustRegion:(CLLocation*)location { MKCoordinateRegion viewRegion = MKCoordinateRegionMakeWithDistance(location.coordinate, 150.0, 150.0); MKCoordinateRegion adjustedRegion = [self.mapView regionThatFits:viewRegion]; [self.mapView setRegion:adjustedRegion animated:YES]; } 
0
source share

All Articles