How to set accuracy and distance filter when using MKMapView

46,968

Solution 1

You can't control the accuracy of the internal MKMapView location manager (the one used to track the user with the blue dot), but you can create your own and use it to recenter the map. Here is a recipe...

To handle core location permissions

In the Core Location Delegate:

- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
    if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusDenied){
        NSLog(@"User has denied location services");
    } else {
        NSLog(@"Location manager did fail with error: %@", error.localizedFailureReason);
    }
}

Right before setting up the location manager:

if (![CLLocationManager locationServicesEnabled]){
    NSLog(@"location services are disabled"];
    return;
}
if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusDenied){
    NSLog(@"location services are blocked by the user");
    return;
}
if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusAuthorized){
    NSLog(@"location services are enabled");
}  
if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusNotDetermined){
    NSLog(@"about to show a dialog requesting permission");
}

To setup core location

self.locationManager = [CLLocationManager new];
self.locationManager.purpose = @"Tracking your movements on the map.";
self.locationManager.delegate = self;

/* Pinpoint our location with the following accuracy:
 *
 *     kCLLocationAccuracyBestForNavigation  highest + sensor data
 *     kCLLocationAccuracyBest               highest     
 *     kCLLocationAccuracyNearestTenMeters   10 meters   
 *     kCLLocationAccuracyHundredMeters      100 meters
 *     kCLLocationAccuracyKilometer          1000 meters 
 *     kCLLocationAccuracyThreeKilometers    3000 meters
 */
self.locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters;

/* Notify changes when device has moved x meters.
 * Default value is kCLDistanceFilterNone: all movements are reported.
 */
self.locationManager.distanceFilter = 10.0f;

/* Notify heading changes when heading is > 5.
 * Default value is kCLHeadingFilterNone: all movements are reported.
 */
self.locationManager.headingFilter = 5;

// update location
if ([CLLocationManager locationServicesEnabled]){
    [self.locationManager startUpdatingLocation];
}

To recenter the map with our location manager

- (void)locationManager:(CLLocationManager *)manager 
    didUpdateToLocation:(CLLocation *)newLocation 
           fromLocation:(CLLocation *)oldLocation 
{
    MKCoordinateRegion region = { { 0.0f, 0.0f }, { 0.0f, 0.0f } };
    region.center = newLocation.coordinate;
    region.span.longitudeDelta = 0.15f; 
    region.span.latitudeDelta = 0.15f;
    [self.mapView setRegion:region animated:YES];
}

Put that on the delegate. MKMapView doesn't have a distance or accuracy filter, only the CLLocationManager does. What MKMapView has is a region span around a point, in the example above 0.15 degrees (0.15*111 Km).

Things that I tried and didn't work

The documentation doesn't tell where MKMapView is getting its updates from. I tried

- (void)locationManager:(CLLocationManager *)manager 
    didUpdateToLocation:(CLLocation *)newLocation 
           fromLocation:(CLLocation *)oldLocation {
    NSLog(@"newLocation %@", newLocation.timestamp);
    NSLog(@"last map location %@", [NSString stringWithFormat:@"%@",[[[self.mapView userLocation] location] timestamp]]);
}

and I'm getting different values on each. It looks as if MKMapView uses its own CLLocationManager, which means you can't set its accuracy. You can't add your delegate for the CLLocationManager of the MKMapView either.

My impression is that the only way to set the accuracy is setting show user position to NO and create a custom annotation with a blue dot, which means recentering the map manually as posted. You can get the blue dot graphic from the SDK with the github project artwork-extractor.

I don't know if I'm missing something or this part of MKMapView just sucks.

Solution 2

For displaying map here is an example code.

First import MKMapKit and CoreLocation framework in your .h file.

#import <MapKit/MapKit.h>
 #import <CoreLocation/CoreLocation.h>

Add MKMapKit and CoreLocation Delegate in .h file

@interface MapViewController : UIViewController <MKMapViewDelegate, CLLocationManagerDelegate>


CGPoint gameMapCenter = CGPointMake([[UIScreen mainScreen] bounds].size.width / 2, [[UIScreen mainScreen] bounds].size.height / 2);
    gameMapView = [[MKMapView alloc] initWithFrame:CGRectMake(0, 0, 640, 620)];
    [gameMapView setCenter:gameMapCenter];
    [gameMapView setMapType:MKMapTypeStandard];
    [gameMapView setDelegate:self];
    [self.view addSubview:gameMapView];
    [gameMapView setShowsUserLocation:YES];

Use CLLocationManager for fetching user location.

Declare an instance of CLLocationManager

CLLocationManager *locationManager;

In ViewDidLoad

locationManager = [[CLLocationManager alloc] init];

[locationManager setDelegate:self];

[locationManager setDesiredAccuracy:kCLLocationAccuracyBest];

[locationManager setDistanceFilter:kCLDistanceFilterNone];

[locationManger startUpdatingLocation];

startUpdatingLocation Method Implementation:

(void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation 
{
      //Your Stuff
}

Solution 3

You can't set the accuracy, however, you can get the accuracy via the userLocation in the MKMapView delegate method.

- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
{
    NSLog(@"%f", userLocation.location.horizontalAccuracy);
}
Share:
46,968
Van Du Tran
Author by

Van Du Tran

I build iPhone apps.

Updated on May 31, 2020

Comments

  • Van Du Tran
    Van Du Tran about 4 years

    When i use setShowsUserLocation with MKMapView to track user location, how do I set the accuracy and distance filter? I am not talking about CLLocationManager.

    Thanks,