31

This my code......

 -(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
 {

    location_updated = [locations lastObject];
    NSLog(@"updated coordinate are %@",location_updated);
    latitude1 = location_updated.coordinate.latitude;
    longitude1 = location_updated.coordinate.longitude;

    self.lblLat.text = [NSString stringWithFormat:@"%f",latitude1];
    self.lblLon.text = [NSString stringWithFormat:@"%f",longitude1];

    NSString *str = [NSString stringWithFormat:@"https://maps.googleapis.com/maps/api/geocode/json?latlng=%f,%f&sensor=false",latitude1,longitude1];
    url = [NSURL URLWithString:str];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    connection = [NSURLConnection connectionWithRequest:request delegate:self];
    if (connection)
    {
        webData1 = [[NSMutableData alloc]init];
    }
        GMSMarker *marker = [[GMSMarker alloc] init];
        marker.position = CLLocationCoordinate2DMake(latitude1,longitude1);
        marker.title = formattedAddress;
        marker.icon = [UIImage imageNamed:@"m2.png"];
        marker.map = mapView_;
        marker.draggable = YES;
 }

This method is call multiple times which i don't want.....

mfaani
  • 33,269
  • 19
  • 164
  • 293
  • Then why do you call this method? – rohan-patel Mar 10 '14 at 05:50
  • To update location after every five minutes. –  Mar 10 '14 at 05:53
  • 7
    When you first start location services, you may see it called multiple times with increasingly accurate locations (e.g. decreasing `horizontalAccuracy`). Also set `CLLocationManager` properties `distanceFilter` and `desiredAccuracy` to control how frequently this is called. – Rob Mar 10 '14 at 06:11
  • Swift 3 version of accepted answer http://stackoverflow.com/a/43066729/7250862 – RajeshKumar R Mar 28 '17 at 10:17

12 Answers12

43

While allocating your LocationManager object you can set the distanceFilter property of the LocationManager. Distance filter property is a CLLocationDistance value which can be set to notify the location manager about the distance moved in meters. You can set the distance filter as follows:

LocationManager *locationManger = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.distanceFilter = 100.0; // Will notify the LocationManager every 100 meters
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
Sujith Thankachan
  • 3,508
  • 2
  • 20
  • 25
  • Stops location from updating in the first place where as the selected answer merely stops further handling, so I think this should be the better option – funct7 Feb 04 '16 at 16:05
  • excellent answer. I never get how people think that setting a return sttmnt at the beginning of the function solved something.... – Alon_T May 16 '16 at 02:58
  • 6
    not working for me,, always get 3 updates at the beginning – the Reverend Jan 16 '19 at 20:01
25

The easiest way:

-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations
{
   [manager stopUpdatingLocation];
    manager.delegate = nil;

   //...... do something

}

The manager can't find your didUpdateLocations method without the delegate reference :-D

But don't forget to set it again before using startUpdatingLocation

Maurice Raguse
  • 4,437
  • 2
  • 29
  • 46
  • 1
    this works perfectly as it only updates the location once on a specific instance of the manager.. thanks! – the_legend_27 May 10 '17 at 02:36
  • 2
    why not simply use locationManager.requestLocation() to get location once? stopUpdatingLocation inside didUpdateLocations doesn't make much sense, probably just as a workaround on iOS8 (where .requestLocation() is not available) – Vitalii Aug 22 '17 at 14:19
24

Add some restriction there. For timespan between locations and accuracy

-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
 CLLocation *newLocation = locations.lastObject;

 NSTimeInterval locationAge = -[newLocation.timestamp timeIntervalSinceNow];
 if (locationAge > 5.0) return;

 if (newLocation.horizontalAccuracy < 0) return;

// Needed to filter cached and too old locations
 //NSLog(@"Location updated to = %@", newLocation);
 CLLocation *loc1 = [[CLLocation alloc] initWithLatitude:_currentLocation.coordinate.latitude longitude:_currentLocation.coordinate.longitude];
 CLLocation *loc2 = [[CLLocation alloc] initWithLatitude:newLocation.coordinate.latitude longitude:newLocation.coordinate.longitude];
 double distance = [loc1 distanceFromLocation:loc2];


 if(distance > 20)
 {    
     _currentLocation = newLocation;

     //significant location update

 }

//location updated

}
nerowolfe
  • 4,787
  • 3
  • 20
  • 19
  • It's better to move one line: `if(distance > 20) { _currentLocation = newLocation; //significant location update }` – George Nov 29 '14 at 17:03
  • @nerowolfe , thank you but i have a question for the code : if (newLocation.horizontalAccuracy < 0) return; What would you mean? – LKM Jun 03 '15 at 08:09
  • Was trying to filter incorrect locations (in that case accuracy is wrong for them) – nerowolfe Jun 03 '15 at 09:23
  • How about didFailWithError since there are no updated locations? – Merkurial Feb 01 '16 at 10:33
10

I have similar situation. You can use dispatch_once:

static dispatch_once_t predicate;

- (void)update
{
    if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusNotDetermined &&
        [_locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)]) {
        [_locationManager requestWhenInUseAuthorization];
    }

    _locationManager.delegate = self;
    _locationManager.distanceFilter = kCLDistanceFilterNone;
    _locationManager.desiredAccuracy = kCLLocationAccuracyBest;

    predicate = 0;
    [_locationManager startUpdatingLocation];
}

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
    [manager stopUpdatingLocation];
    manager = nil;

    dispatch_once(&predicate, ^{
        //your code here
    });
}
3

locationManager.startUpdatingLocation() fetch location continuously and didUpdateLocations method calls several times, Just set the value for locationManager.distanceFilter value before calling locationManager.startUpdatingLocation().

As I set 200 meters(you can change as your requirement) working fine

    locationManager = CLLocationManager()
    locationManager.delegate = self
    locationManager.desiredAccuracy = kCLLocationAccuracyBest
    locationManager.distanceFilter = 200
    locationManager.requestWhenInUseAuthorization()
    locationManager.startUpdatingLocation()
Devendra Singh
  • 717
  • 1
  • 13
  • 14
1

You can use a static variable to store the latest location timestamp and then compare it to the newest one, like this:

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
    [manager stopUpdatingLocation];
    static NSDate *previousLocationTimestamp;

    CLLocation *location = [locations lastObject];
    if (previousLocationTimestamp && [location.timestamp timeIntervalSinceDate:previousLocationTimestamp] < 2.0) {
        NSLog(@"didUpdateLocations GIVE UP");
        return;
    }
    previousLocationTimestamp = location.timestamp;

    NSLog(@"didUpdateLocations GOOD");

    // Do your code here
}
marcelosalloum
  • 3,481
  • 4
  • 39
  • 63
  • suspended mode? What exactly are you trying to do? If you'd like to make sure it will work in background, you should start a `UIBackgroundTask`. If you'd like to monitor location updates, you should use `startMonitoringSignificantLocationChanges`. – marcelosalloum Jun 20 '16 at 12:53
  • same thing i have done UIBackgroundTask thanks for the replay. :) – kalpesh Jun 21 '16 at 05:30
1

Swift 5 :

If you are looking for a solution in swift.

I tried the accepted answer but it didn't work for me. I tried the below solution by checking the time duration between locations. if it is less than 10 seconds then it will return and the location handler will not update.

func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        guard let location = locations.last else {
            return
        }
        
        let locationAge = -location.timestamp.timeIntervalSinceNow
        if locationAge > 10.0 { //10 seconds
            return
        }
        if location.horizontalAccuracy < 0 {
            return
        }
        
        self.currentLocation = location
        print("Location :- \(location.coordinate)")
        //location updated
    }
Sourabh Sharma
  • 8,222
  • 5
  • 68
  • 78
0

Write this method when ever you want to stop updating location manager

[locationManager stopUpdatingLocation];
Charan Giri
  • 1,097
  • 1
  • 9
  • 15
  • 2
    This doesn't necessarily stop all updates see: http://stackoverflow.com/questions/14017587/stopupdatinglocation-method-not-working-for-ios5 – Sam Aug 10 '14 at 12:50
0

for the time constraint, i did not understand code from accepted answer, posting a different approach. as Rob points out "When you first start location services, you may see it called multiple times". the code below acts on the first location, and ignores the updated locations for first 120 seconds. it is one way to address orginal question "How to stop multiple times method calling of didUpdateLocations".

in .h file:

@property(strong,nonatomic) CLLocation* firstLocation;

in .m file:

// is this the first location?
    CLLocation* newLocation = locations.lastObject;
    if (self.firstLocation) {
        // app already has a location
        NSTimeInterval locationAge = [newLocation.timestamp timeIntervalSinceDate:self.firstLocation.timestamp];
        NSLog(@"locationAge: %f",locationAge);
        if (locationAge < 120.0) {  // 120 is in seconds or milliseconds?
            return;
        }
    } else {
        self.firstLocation = newLocation;
    }

    // do something with location
tmr
  • 1,500
  • 15
  • 22
  • @kalpesh, i do not know answer to your question, i moved to a different project. – tmr Aug 02 '16 at 20:38
0

You could set a flag (Bool). When you instantiate your locationsManager set flag = true then when locationManager:didUpdateLocations returns inside a code block that you want to run only once set flag = false. This way it will only be run the once.

 if flag == true {
     flag = false
    ...some code probably network call you only want to run the once 
    }

locations manager will be called multiple times but the code you want to execute only once, and I think that is what you are trying to achieve?

RyanTCB
  • 7,400
  • 5
  • 42
  • 62
0

you can write : [manager stopUpdatingLocation]; manager = nil; in didupdatelocation delegate

Pramanshu
  • 71
  • 6
0

A few things worked for me:

  • setting location manager nil while didUpdateLocations after calling manager.stopUpdatingLocation()

I will suggest setting the location manager to nil is not a good approach.because CLLocationManager sometimes give accurate location in 3-4 times.

What I will suggest change accuracy to kilometers that's worked for me

    clLocationManager?.distanceFilter = 1000
    clLocationManager?.desiredAccuracy = kCLLocationAccuracyKilometer
Pawan Kumar
  • 508
  • 3
  • 19