Comment puis-je obtenir une mise à jour de l'emplacement de fond toutes les n minutes dans mon application iOS?

je cherche un moyen d'obtenir une mise à jour de l'emplacement de fond toutes les n minutes dans mon application iOS. J'utilise iOS 4.3 et la solution devrait fonctionner pour les iPhones non jailbroken.

j'ai essayé / envisagé les options suivantes:

  • CLLocationManager startUpdatingLocation/startMonitoringSignificantLocationChanges : cela fonctionne en arrière-plan comme prévu, basé sur les propriétés configurées, mais il ne semble pas possible de le forcer à mettre à jour l'emplacement toutes les n minutes
  • NSTimer : fonctionne quand l'application est en cours d'exécution au premier plan mais ne semble pas être conçu pour les tâches de fond
  • notifications locales: les notifications locales peuvent être programmées toutes les n minutes, mais il n'est pas possible d'exécuter un code pour obtenir l'emplacement actuel (sans que l'utilisateur ait à lancer l'application via la notification). Cette approche ne semble pas non plus être une approche propre, car ce n'est pas pour cela que les notifications devraient être utilisées.
  • UIApplication:beginBackgroundTaskWithExpirationHandler : pour autant que je comprenne, cela devrait être utilisé pour terminer certains travaux en arrière-plan (également limité dans le temps) quand une application est déplacée à l'arrière-plan plutôt que de mettre en œuvre des processus de fond" de longue durée".

Comment puis-je mettre en œuvre ces mises à jour régulières de l'emplacement de fond?

192
demandé sur Ashwini 2011-06-14 21:23:18

15 réponses

j'ai trouvé une solution pour mettre en œuvre cela avec l'aide des forums de développeurs Apple:

  • spécifier location background mode
  • créer un NSTimer en arrière-plan avec UIApplication:beginBackgroundTaskWithExpirationHandler:
  • quand n est plus petit que UIApplication:backgroundTimeRemaining il fonctionnera très bien. Lorsque n est plus grand , le location manager doit être activé (et désactivé) à nouveau avant il ne reste plus de temps pour éviter que la tâche de fond soit tuée.

cela fonctionne parce que l'emplacement est l'un des trois types autorisés d'exécution en arrière-plan .

Note: j'ai perdu du temps en le testant dans le simulateur où il ne fonctionne pas. Cependant, ça marche très bien sur mon téléphone.

109
répondu wjans 2016-07-06 13:53:44

Sur iOS 8/9/10 pour faire en arrière-plan de mise à jour toutes les 5 minutes, procédez de la manière suivante:

  1. Allez dans Projet - > "Capacités" - > arrière-plan Modes -> sélectionner l'Emplacement des mises à jour

  2. aller au projet -> Info -> ajouter une clé NSLocationAlwaysUsageDescription avec valeur vide (ou éventuellement n'importe quel texte)

  3. votre application est à l'arrière-plan et envoyer des coordonnées à web service ou faire quoi que ce soit avec eux toutes les 5 minutes mettre en œuvre comme dans le code ci-dessous.

Je n'utilise aucune tâche de fond ou minuterie. J'ai testé ce code avec mon appareil avec iOS 8.1 qui gisait sur mon bureau pendant quelques heures avec mon application en arrière-plan. L'appareil était verrouillé et le code fonctionnait correctement tout le temps.

@interface LocationManager () <CLLocationManagerDelegate>
@property (strong, nonatomic) CLLocationManager *locationManager;
@property (strong, nonatomic) NSDate *lastTimestamp;

@end

@implementation LocationManager

+ (instancetype)sharedInstance
{
    static id sharedInstance = nil;

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[self alloc] init];
        LocationManager *instance = sharedInstance;
        instance.locationManager = [CLLocationManager new];
        instance.locationManager.delegate = instance;
        instance.locationManager.desiredAccuracy = kCLLocationAccuracyBest; // you can use kCLLocationAccuracyHundredMeters to get better battery life
        instance.locationManager.pausesLocationUpdatesAutomatically = NO; // this is important
    });

    return sharedInstance;
}

- (void)startUpdatingLocation
{
    CLAuthorizationStatus status = [CLLocationManager authorizationStatus];

    if (status == kCLAuthorizationStatusDenied)
    {
        NSLog(@"Location services are disabled in settings.");
    }
    else
    {
        // for iOS 8
        if ([self.locationManager respondsToSelector:@selector(requestAlwaysAuthorization)])
        {
            [self.locationManager requestAlwaysAuthorization];
        }
        // for iOS 9
        if ([self.locationManager respondsToSelector:@selector(setAllowsBackgroundLocationUpdates:)])
        {
            [self.locationManager setAllowsBackgroundLocationUpdates:YES];
        }

        [self.locationManager startUpdatingLocation];
    }
}

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
    CLLocation *mostRecentLocation = locations.lastObject;
    NSLog(@"Current location: %@ %@", @(mostRecentLocation.coordinate.latitude), @(mostRecentLocation.coordinate.longitude));

    NSDate *now = [NSDate date];
    NSTimeInterval interval = self.lastTimestamp ? [now timeIntervalSinceDate:self.lastTimestamp] : 0;

    if (!self.lastTimestamp || interval >= 5 * 60)
    {
        self.lastTimestamp = now;
        NSLog(@"Sending current location to web service.");
    }
}

@end
52
répondu Leszek Szary 2017-06-25 18:42:24

j'ai fait ceci dans une application que je développe. Les minuteries ne fonctionnent pas lorsque l'application est à l'arrière-plan, mais l'application reçoit constamment les mises à jour d'emplacement. J'ai lu quelque part dans la documentation (Je n'arrive pas à la trouver maintenant, je posterai une mise à jour quand je le ferai) qu'une méthode ne peut être appelée que sur une boucle d'exécution active lorsque l'application est à l'arrière-plan. L'app delegate a une boucle d'exécution active, même dans le bg, donc vous n'avez pas besoin de créer votre propre boucle pour faire ce travail. [Je ne sais pas si c'est l'explication correcte mais c'est comment j'ai compris de ce que j'ai lu]

tout d'abord, ajoutez l'objet location pour la clé UIBackgroundModes dans les informations de votre application.plist. Maintenant, ce que vous devez faire est de lancer les mises à jour de localisation n'importe où dans votre application:

    CLLocationManager locationManager = [[CLLocationManager alloc] init];
    locationManager.delegate = self;//or whatever class you have for managing location
    [locationManager startUpdatingLocation];

ensuite, écrivez une méthode pour gérer les mises à jour de localisation, dis -(void)didUpdateToLocation:(CLLocation*)location , dans l'app delegate. Puis mettre en œuvre la méthode locationManager:didUpdateLocation:fromLocation de CLLocationManagerDelegate dans la classe dans laquelle vous avez commencé le gestionnaire d'emplacement (puisque nous avons défini le délégué de gestionnaire d'emplacement à "moi-même"). Dans cette méthode, vous devez vérifier si l'intervalle de temps après lequel vous devez gérer les mises à jour d'emplacement s'est écoulé. Vous pouvez le faire en économisant l'heure actuelle à chaque fois. Si ce temps s'est écoulé, appelez la méthode UpdateLocation de votre délégué app:

NSDate *newLocationTimestamp = newLocation.timestamp;
NSDate *lastLocationUpdateTiemstamp;

int locationUpdateInterval = 300;//5 mins

NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
if (userDefaults) {

        lastLocationUpdateTiemstamp = [userDefaults objectForKey:kLastLocationUpdateTimestamp];

        if (!([newLocationTimestamp timeIntervalSinceDate:lastLocationUpdateTiemstamp] < locationUpdateInterval)) {
            //NSLog(@"New Location: %@", newLocation);
            [(AppDelegate*)[UIApplication sharedApplication].delegate didUpdateToLocation:newLocation];
            [userDefaults setObject:newLocationTimestamp forKey:kLastLocationUpdateTimestamp];
        }
    }
}

cela appellera votre méthode toutes les 5 minutes, même lorsque votre application est en arrière-plan. Imp: cette implémentation draine la batterie, si la précision de vos données de localisation n'est pas critique vous devez utiliser [locationManager startMonitoringSignificantLocationChanges]

avant d'ajouter cette information à votre application, veuillez lire le Location Awareness Programming Guide

35
répondu Bushra Shahid 2013-12-05 10:06:34

maintenant que iOS6 est sur la meilleure façon d'avoir un service de localisation toujours en cours est...

- (void)applicationWillResignActive:(UIApplication *)application
{
/*
 Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
 Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
 */

NSLog(@"to background");

app.isInBackground = TRUE;

UIApplication *app = [UIApplication sharedApplication];

// Request permission to run in the background. Provide an
// expiration handler in case the task runs long.
NSAssert(bgTask == UIBackgroundTaskInvalid, nil);

bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
    // Synchronize the cleanup call on the main thread in case
    // the task actually finishes at around the same time.
    dispatch_async(dispatch_get_main_queue(), ^{

        if (bgTask != UIBackgroundTaskInvalid)
        {
            [app endBackgroundTask:bgTask];
            bgTask = UIBackgroundTaskInvalid;
        }
    });
}];

// Start the long-running task and return immediately.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

    // Do the work associated with the task.

    locationManager.distanceFilter = 100;
    locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters;
    [locationManager startMonitoringSignificantLocationChanges];
    [locationManager startUpdatingLocation];

    NSLog(@"App staus: applicationDidEnterBackground");
    // Synchronize the cleanup call on the main thread in case
    // the expiration handler is fired at the same time.
    dispatch_async(dispatch_get_main_queue(), ^{
        if (bgTask != UIBackgroundTaskInvalid)
        {
            [app endBackgroundTask:bgTask];
            bgTask = UIBackgroundTaskInvalid;
        }
    });
});

NSLog(@"backgroundTimeRemaining: %.0f", [[UIApplication sharedApplication] backgroundTimeRemaining]);

}

vient de le tester comme ça:

j'ai commencé l'application, aller en arrière-plan et de se déplacer dans la voiture de quelques minutes. Puis je rentre à la maison pendant 1 heure et je commence à bouger de nouveau (sans ouvrir à nouveau l'application). Les lieux ont recommencé. Puis elle s'est arrêtée pendant deux heures et a recommencé. Tout est ok de nouveau...

N'oubliez pas D'utiliser le nouveau services de localisation en iOS6

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{   
    CLLocation *loc = [locations lastObject];

    // Lat/Lon
    float latitudeMe = loc.coordinate.latitude;
    float longitudeMe = loc.coordinate.longitude;
}
22
répondu Alejandro Luengo 2012-10-08 18:05:38

à quelqu'un d'autre qui fait un cauchemar. J'ai une solution simple.

  1. regardez cet exemple de raywenderlich.com - > avoir un code échantillon, cela fonctionne parfaitement, mais malheureusement pas de minuterie pendant la localisation de fond. cela permettra de s'exécuter indéfiniment.
  2. ajouter minuterie en utilisant:

    -(void)applicationDidEnterBackground {
    [self.locationManager stopUpdatingLocation];
    
    UIApplication*    app = [UIApplication sharedApplication];
    
    bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
        [app endBackgroundTask:bgTask];
        bgTask = UIBackgroundTaskInvalid;
    }];
    
     self.timer = [NSTimer scheduledTimerWithTimeInterval:intervalBackgroundUpdate
                                                  target:self.locationManager
                                                selector:@selector(startUpdatingLocation)
                                                userInfo:nil
                                                 repeats:YES];
    
    }
    
  3. N'oubliez pas d'ajouter " app registers for emplacement des mises à jour" dans l'info.plist.

13
répondu HelmiB 2013-06-01 15:41:03

Malheureusement, toutes vos suppositions semblent correctes, et je ne pense pas qu'il y ait un moyen de le faire. Afin de sauver la vie de la batterie, les services de localisation de l'iPhone sont basés sur le mouvement. Si le téléphone se trouve à un endroit, il est invisible pour les services de localisation.

le CLLocationManager n'appellera locationManager:didUpdateToLocation:fromLocation: que lorsque le téléphone reçoit une mise à jour de localisation, ce qui n'arrive que si l'un des trois services de localisation (tour cellulaire, gps, wifi) perçoit un changement.

quelques autres choses qui pourraient aider à informer d'autres solutions:

  • "Démarrer et arrêter les services provoque la didUpdateToLocation méthode de délégué à être appelé, mais le newLocation pourrait avoir une vieille estampille de temps.

  • "la surveillance de la région pourrait aider

  • en cours d'exécution en arrière-plan, soyez conscient que cela peut être difficile pour obtenir la prise en charge "complète" des services de localisation approuvée par Apple. De ce que j'ai vu, ils ont spécifiquement conçu startMonitoringSignificantLocationChanges comme une alternative de faible puissance pour les applications qui ont besoin de la localisation de l'arrière-plan, et fortement encourager les développeurs à utiliser ceci à moins que l'application a absolument besoin de lui.

Bonne Chance!

mise à jour: ces réflexions sont peut-être dépassées. On dirait que les gens ont du succès avec la réponse de @wjans, ci-dessus.

5
répondu Chazbot 2012-08-28 08:59:49

j'ai écrit une application en utilisant les services D'emplacement, l'application doit envoyer l'emplacement tous les 10s. Et ça a très bien fonctionné.

il suffit D'utiliser la" allowDeferredLocationUpdatesUntiltraveled:timeout " méthode, en suivant le doc D'Apple.

ce que j'ai fait est:

Requis: Enregistrer le mode arrière-plan pour l'Emplacement de mise à jour.

1. créer LocationManger et startUpdatingLocation , avec accuracy et filteredDistance comme tout ce que vous voulez:

-(void) initLocationManager    
{
    // Create the manager object
    self.locationManager = [[[CLLocationManager alloc] init] autorelease];
    _locationManager.delegate = self;
    // This is the most important property to set for the manager. It ultimately determines how the manager will
    // attempt to acquire location and thus, the amount of power that will be consumed.
    _locationManager.desiredAccuracy = 45;
    _locationManager.distanceFilter = 100;
    // Once configured, the location manager must be "started".
    [_locationManager startUpdatingLocation];
}

2. pour garder l'application en marche pour toujours en utilisant la méthode allowDeferredLocationUpdatesUntilTraveled:timeout en arrière-plan, vous devez redémarrer updatingLocation avec un nouveau paramètre lorsque l'application se déplace en arrière-plan, comme ceci:

- (void)applicationWillResignActive:(UIApplication *)application {
     _isBackgroundMode = YES;

    [_locationManager stopUpdatingLocation];
    [_locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
    [_locationManager setDistanceFilter:kCLDistanceFilterNone];
    _locationManager.pausesLocationUpdatesAutomatically = NO;
    _locationManager.activityType = CLActivityTypeAutomotiveNavigation;
    [_locationManager startUpdatingLocation];
 }

3. Application updatedLocations comme d'habitude avec locationManager:didUpdateLocations: rappel:

-(void) locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
//  store data
    CLLocation *newLocation = [locations lastObject];
    self.userLocation = newLocation;

   //tell the centralManager that you want to deferred this updatedLocation
    if (_isBackgroundMode && !_deferringUpdates)
    {
        _deferringUpdates = YES;
        [self.locationManager allowDeferredLocationUpdatesUntilTraveled:CLLocationDistanceMax timeout:10];
    }
}

4. mais vous devez traiter les données dans alors locationManager:didFinishDeferredUpdatesWithError: callback pour votre but

- (void) locationManager:(CLLocationManager *)manager didFinishDeferredUpdatesWithError:(NSError *)error {

     _deferringUpdates = NO;

     //do something 
}

5. NOTE: je pense que nous devrions Réinitialiser les paramètres de LocationManager chaque fois que l'application commute entre le mode arrière-plan/forground.

5
répondu samthui7 2015-08-12 07:26:57
if ([self.locationManager respondsToSelector:@selector(setAllowsBackgroundLocationUpdates:)]) {
    [self.locationManager setAllowsBackgroundLocationUpdates:YES];
}

Ceci est nécessaire pour le traçage de localisation de fond depuis iOS 9.

5
répondu Nilesh 2016-01-13 06:48:34

voici ce que j'utilise:

import Foundation
import CoreLocation
import UIKit

class BackgroundLocationManager :NSObject, CLLocationManagerDelegate {

    static let instance = BackgroundLocationManager()
    static let BACKGROUND_TIMER = 150.0 // restart location manager every 150 seconds
    static let UPDATE_SERVER_INTERVAL = 60 * 60 // 1 hour - once every 1 hour send location to server

    let locationManager = CLLocationManager()
    var timer:NSTimer?
    var currentBgTaskId : UIBackgroundTaskIdentifier?
    var lastLocationDate : NSDate = NSDate()

    private override init(){
        super.init()
        locationManager.delegate = self
        locationManager.desiredAccuracy = kCLLocationAccuracyKilometer
        locationManager.activityType = .Other;
        locationManager.distanceFilter = kCLDistanceFilterNone;
        if #available(iOS 9, *){
            locationManager.allowsBackgroundLocationUpdates = true
        }

        NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.applicationEnterBackground), name: UIApplicationDidEnterBackgroundNotification, object: nil)
    }

    func applicationEnterBackground(){
        FileLogger.log("applicationEnterBackground")
        start()
    }

    func start(){
        if(CLLocationManager.authorizationStatus() == CLAuthorizationStatus.AuthorizedAlways){
            if #available(iOS 9, *){
                locationManager.requestLocation()
            } else {
                locationManager.startUpdatingLocation()
            }
        } else {
                locationManager.requestAlwaysAuthorization()
        }
    }
    func restart (){
        timer?.invalidate()
        timer = nil
        start()
    }

    func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
        switch status {
        case CLAuthorizationStatus.Restricted:
            //log("Restricted Access to location")
        case CLAuthorizationStatus.Denied:
            //log("User denied access to location")
        case CLAuthorizationStatus.NotDetermined:
            //log("Status not determined")
        default:
            //log("startUpdatintLocation")
            if #available(iOS 9, *){
                locationManager.requestLocation()
            } else {
                locationManager.startUpdatingLocation()
            }
        }
    }
    func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {

        if(timer==nil){
            // The locations array is sorted in chronologically ascending order, so the
            // last element is the most recent
            guard let location = locations.last else {return}

            beginNewBackgroundTask()
            locationManager.stopUpdatingLocation()
            let now = NSDate()
            if(isItTime(now)){
                //TODO: Every n minutes do whatever you want with the new location. Like for example sendLocationToServer(location, now:now)
            }
        }
    }

    func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
        CrashReporter.recordError(error)

        beginNewBackgroundTask()
        locationManager.stopUpdatingLocation()
    }

    func isItTime(now:NSDate) -> Bool {
        let timePast = now.timeIntervalSinceDate(lastLocationDate)
        let intervalExceeded = Int(timePast) > BackgroundLocationManager.UPDATE_SERVER_INTERVAL
        return intervalExceeded;
    }

    func sendLocationToServer(location:CLLocation, now:NSDate){
        //TODO
    }

    func beginNewBackgroundTask(){
        var previousTaskId = currentBgTaskId;
        currentBgTaskId = UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler({
            FileLogger.log("task expired: ")
        })
        if let taskId = previousTaskId{
            UIApplication.sharedApplication().endBackgroundTask(taskId)
            previousTaskId = UIBackgroundTaskInvalid
        }

        timer = NSTimer.scheduledTimerWithTimeInterval(BackgroundLocationManager.BACKGROUND_TIMER, target: self, selector: #selector(self.restart),userInfo: nil, repeats: false)
    }
}

je démarre le tracking dans AppDelegate comme ça:

BackgroundLocationManager.instance.start()
5
répondu hmitkov 2016-07-06 13:36:42

j'ai utilisé la méthode de xs2bush d'obtenir un intervalle (en utilisant timeIntervalSinceDate ) et étendu sur elle un peu. Je voulais m'assurer que j'obtenais la précision dont j'avais besoin et aussi que je ne descendais pas la batterie en laissant la radio gps allumée plus que nécessaire.

je garde de localisation fonctionnant en permanence, avec les paramètres suivants:

locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers;
locationManager.distanceFilter = 5;

c'est un drain relativement faible sur la batterie. Quand je suis prêt à recevoir mon prochain emplacement de lecture, j'ai d'abord vérifier si l'emplacement est dans ma précision désirée, si elle l'est, je puis utiliser l'emplacement. Si ce n'est pas le cas, alors j'augmente la précision avec ceci:

locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters;
locationManager.distanceFilter = 0;

obtenir mon emplacement et puis une fois que j'ai l'emplacement, Je tourne de nouveau la précision vers le bas afin de minimiser la vidange sur la batterie. J'ai écrit un échantillon de travail complet de ceci et aussi j'ai écrit la source pour le code côté serveur pour recueillir les données de localisation, le stocker à une base de données et permettre aux utilisateurs de voir les données gps en temps réel ou de récupérer et de voir les routes précédemment stockées. J'ai des clients pour iOS, android, windows phone et java me. Tous les clients sont écrits nativement et ils fonctionnent tous correctement à l'arrière-plan. Le projet est sous licence du MIT.

le projet iOS est ciblé pour iOS 6 en utilisant un SDK de base de iOS 7. Vous pouvez obtenir le code ici .

s'il vous plaît déposer un numéro sur github si vous voyez un des problèmes avec elle. Grâce.

4
répondu nickfox 2016-08-09 14:52:51

il semble que stopUpdatingLocation est ce qui déclenche la minuterie de watchdog background, donc je l'ai remplacé dans didUpdateLocation par:

     [self.locationManager setDesiredAccuracy:kCLLocationAccuracyThreeKilometers];
     [self.locationManager setDistanceFilter:99999];

qui semble alimenter efficacement le GPS. Le sélecteur pour l'arrière-plan NSTimer devient alors:

- (void) changeAccuracy {
[self.locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
[self.locationManager setDistanceFilter:kCLDistanceFilterNone];
}

tout ce que je fais c'est changer périodiquement la précision pour obtenir une coordonnée de haute précision toutes les quelques minutes et parce que le gestionnaire de localisation n'a pas été arrêté, backgroundTimeRemaining reste à sa valeur maximale. Cela a réduit la consommation de batterie de ~10% par heure (avec kcllocationaccuracybest constant dans l'arrière-plan) à ~2% par heure sur mon appareil

2
répondu Amit Shelgaonkar 2015-06-30 17:52:11

il y a un cocoapod APScheduledLocationManager qui permet d'obtenir des mises à jour de la position de fond chaque n secondes avec la précision de localisation désirée.

let manager = APScheduledLocationManager(delegate: self)
manager.startUpdatingLocation(interval: 170, acceptableLocationAccuracy: 100)

le dépôt contient également un exemple d'application écrit dans Swift 3.

2
répondu sash 2016-10-29 14:40:56

Code De Travail(L'Ensemble De La Progressive Code)

Étape 1

  • Allez dans projet - > "Capacités" - > arrière-plan Modes -> sélectionner l'Emplacement des mises à jour.
  • aller à Project -> Info -> ajouter une clé NSLocationAlwaysUsageDescription avec une chaîne optionnelle.

Étape 2

ajouter ce code à AppDelegate.m

@interface AppDelegate ()<CLLocationManagerDelegate>
@property (strong, nonatomic) CLLocationManager *locationManager;
@property (strong, nonatomic) NSTimer *timer;
@end

l'Étape 3 Ajouter ce Code dans applicationDidEnterBackground méthode dans AppDelegate.m

    - (void)applicationDidEnterBackground:(UIApplication *)application {
        UIApplication *app = [UIApplication sharedApplication];
        __block UIBackgroundTaskIdentifier bgTaskId =
        [app beginBackgroundTaskWithExpirationHandler:^{
            [app endBackgroundTask:bgTaskId];
            bgTaskId = UIBackgroundTaskInvalid;
        }];

        dispatch_async( dispatch_get_main_queue(), ^{
            self.timer = nil;
            [self initTimer];
            [app endBackgroundTask:bgTaskId];
            bgTaskId = UIBackgroundTaskInvalid;
        });
    }

- (void)initTimer {
    if (nil == self.locationManager)
        self.locationManager = [[CLLocationManager alloc] init];

    self.locationManager.delegate = self;
    [self.locationManager requestAlwaysAuthorization];
    [self.locationManager startMonitoringSignificantLocationChanges];
    if (self.timer == nil) {
        self.timer = [NSTimer scheduledTimerWithTimeInterval:0.3
                                                      target:self
                                                    selector:@selector(checkUpdates:)
                                                    userInfo:nil
                                                     repeats:YES];
    }
}

- (void)checkUpdates:(NSTimer *)timer{
    UIApplication *app = [UIApplication sharedApplication];
    double remaining = app.backgroundTimeRemaining;
    if(remaining < 580.0) {
        [self.locationManager startUpdatingLocation];
        [self.locationManager stopUpdatingLocation];
        [self.locationManager startMonitoringSignificantLocationChanges];
    }
}

- (void)locationManager:(CLLocationManager *)manager
    didUpdateToLocation:(CLLocation *)newLocation
           fromLocation:(CLLocation *)oldLocation {
    NSLog(@"Did Update Location = %f / %f", [newLocation coordinate].latitude, [newLocation coordinate].longitude);
    [self updateLocationWithLatitude:[newLocation coordinate].latitude andLongitude:[newLocation coordinate].longitude];
    UIApplication*    app = [UIApplication sharedApplication];
    __block UIBackgroundTaskIdentifier bgTask =
    bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
        [app endBackgroundTask:bgTask];
        bgTask = UIBackgroundTaskInvalid;
    }];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [self initTimer];
    });
}

- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
    [self.locationManager stopUpdatingLocation];
    UIApplication *app = [UIApplication sharedApplication];
    __block UIBackgroundTaskIdentifier bgTask =
    bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
        [app endBackgroundTask:bgTask];
        bgTask = UIBackgroundTaskInvalid;
    }];
    [self initTimer];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // Do the work associated with the task
    });
}

-(void)updateLocationWithLatitude:(CLLocationDegrees)latitude
                     andLongitude:(CLLocationDegrees)longitude{
//Here you can update your web service or back end with new latitude and longitude
}
1
répondu Hari R Krishna 2017-09-06 07:42:25

dans iOS 9 et watchOS 2.0 Il y a une nouvelle méthode sur CLLocationManager qui vous permet de demander l'emplacement actuel: CLLocationManager:requestLocation(). Ceci termine immédiatement et retourne ensuite l'emplacement au délégué de CLLocationManager.

vous pouvez utiliser un NSTimer pour demander un emplacement chaque minute avec cette méthode maintenant et ne doivent pas travailler avec startUpdatingLocation et stopUpdatingLocation méthodes.

Cependant, si vous voulez les emplacements de capture basés sur un changement de X mètres du dernier emplacement, il suffit de définir la propriété distanceFilter de CLLocationManger et de x appeler startUpdatingLocation().

0
répondu Malcolm 2018-01-17 17:31:36

ci-Joint une solution Swift:

Définir App registers for location updates dans l'info.plist

Garder le locationManager de course de tous les temps

Switch kCLLocationAccuracy entre BestForNavigation (pour 5 secondes pour obtenir l'emplacement) et ThreeKilometers pour le reste de la période d'attente pour éviter de drainage de la batterie

cet exemple met à jour l'emplacement toutes les 1 min en avant-plan et toutes les 15 min en arrière-plan.

l'exemple fonctionne très bien avec Xcode 6 Beta 6, tournant dans un appareil iOS 7.

dans L'App Delegate (mapView est un pointage optionnel vers le contrôleur mapView)

func applicationDidBecomeActive(application: UIApplication!) {
    if appLaunched! == false { // Reference to mapView used to limit one location update per timer cycle
        appLaunched = true
        var appDelegate = UIApplication.sharedApplication().delegate as AppDelegate
        var window = appDelegate.window
        var tabBar = window?.rootViewController as UITabBarController
        var navCon = tabBar.viewControllers[0] as UINavigationController
        mapView = navCon.topViewController as? MapViewController
    }
    self.startInitialPeriodWithTimeInterval(60.0)
}

func applicationDidEnterBackground(application: UIApplication!) {
    self.startInitialPeriodWithTimeInterval(15 * 60.0)
}

func startInitialPeriodWithTimeInterval(timeInterval: NSTimeInterval) {
    timer?.invalidate() // reset timer
    locationManager?.desiredAccuracy = kCLLocationAccuracyBestForNavigation
    timer = NSTimer.scheduledTimerWithTimeInterval(5.0, target: self, selector: Selector("getFirstLocationUpdate:"), userInfo: timeInterval, repeats: false)
}

func getFirstLocationUpdate(sender: NSTimer) {
    let timeInterval = sender.userInfo as Double
    timer?.invalidate()
    mapView?.canReportLocation = true
    timer = NSTimer.scheduledTimerWithTimeInterval(timeInterval, target: self, selector: Selector("waitForTimer:"), userInfo: timeInterval, repeats: true)
}

func waitForTimer(sender: NSTimer) {
    let time = sender.userInfo as Double
    locationManager?.desiredAccuracy = kCLLocationAccuracyBestForNavigation
    finalTimer = NSTimer.scheduledTimerWithTimeInterval(5.0, target: self, selector: Selector("getLocationUpdate"), userInfo: nil, repeats: false)
}

func getLocationUpdate() {
    finalTimer?.invalidate()
    mapView?.canReportLocation = true
}

Dans la mapView (locationManager points de l'objet dans l'AppDelegate)

override func viewDidLoad() {
    super.viewDidLoad()
    var appDelegate = UIApplication.sharedApplication().delegate! as AppDelegate
    locationManager = appDelegate.locationManager!
    locationManager.delegate = self
    canReportLocation = true
}

  func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
        if canReportLocation! {
            canReportLocation = false
            locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers
        } else {
            //println("Ignore location update")
        }
    }
-1
répondu eharo2 2014-08-25 22:59:44