如何在iOS应用程序中每n分钟更新一次后台位置?

时间:2011-06-14 17:23:19

标签: ios objective-c core-location background-process

我正在寻找一种在iOS应用程序中每n分钟更新一次后台位置的方法。我正在使用iOS 4.3,该解决方案适用于非越狱的iPhone。

我尝试/考虑了以下选项:

  • CLLocationManager startUpdatingLocation/startMonitoringSignificantLocationChanges:根据配置的属性,它在后台按预期工作,但似乎无法强制它每n分钟更新一次位置
  • NSTimer:当应用程序在前台运行但似乎不是为后台任务设计时有效吗
  • 本地通知:本地通知可以每n分钟安排一次,但是无法执行某些代码来获取当前位置(用户无需通过通知启动应用程序)。这种方法似乎也不是一种干净的方法,因为这不是应该使用的通知。
  • UIApplication:beginBackgroundTaskWithExpirationHandler:据我所知,当应用程序移动到后台而不是实现“长时间运行”的后台进程时,这应该用于在后台完成一些工作(也限制在时间上)

如何实施这些常规后台位置更新?

15 个答案:

答案 0 :(得分:112)

我找到了一个在Apple Developer Forums的帮助下实现这个目标的解决方案:

  • 指定location background mode
  • 使用NSTimer
  • 在后台创建UIApplication:beginBackgroundTaskWithExpirationHandler:
  • n 小于 <{1}}时,它会正常工作。当UIApplication:backgroundTimeRemaining 更大时,应该在没有剩余时间之前再次启用(和禁用)n以避免后台任务被终止。

这是有效的,因为location是三种允许的后台执行类型之一

注意:我在模拟器中测试它时失去了一些时间。但是,它在我的手机上运行良好。

答案 1 :(得分:53)

iOS 8/9/10 上每隔5分钟更新一次背景位置,请执行以下操作:

  1. 转到项目 - &gt;能力 - &gt;背景模式 - &gt;选择位置更新

  2. 转到项目 - &gt;信息 - &gt;添加一个带有空值(或任选的任何文本)

  3. 的密钥NSLocationAlwaysUsageDescription
  4. 当您的应用在后台并将坐标发送到网络服务或每隔5分钟对其执行任何操作时,为了使位置正常工作,请按照下面的代码实现它。

  5. 我没有使用任何后台任务或计时器。我已经使用iOS 8.1设备测试了这段代码,我的应用程序在后台运行时,它躺在我的桌面上几个小时。设备已被锁定,代码一直在正常运行。

    @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
    

答案 2 :(得分:35)

我在我正在开发的应用程序中这样做了。当应用程序在后台但应用程序不断收到位置更新时,计时器不起作用。我在文档中的某处读过(我现在似乎无法找到它,当我这样做时会发布更新),当应用程序在后台时,只能在活动的运行循环上调用方法。即使在bg中,app委托也有一个活跃的运行循环,所以你不需要创建自己的循环来使这个工作。 [我不确定这是否是正确的解释,但这就是我从我所读到的内容中理解的]

首先,在应用的info.plist中为密钥location添加UIBackgroundModes对象。现在,您需要做的是在应用中的任何位置启动位置更新:

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

接下来,编写一个方法来处理位置更新, 在app委托中说-(void)didUpdateToLocation:(CLLocation*)location。然后在您启动位置管理器的类中实现locationManager:didUpdateLocation:fromLocation的方法CLLocationManagerDelegate(因为我们将位置管理器委托设置为'self')。在此方法中,您需要检查是否已经过了必须处理位置更新的时间间隔。您可以通过每次保存当前时间来完成此操作。如果该时间已过,请从您的app delegate调用方法UpdateLocation:

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];
        }
    }
}

即使您的应用处于后台,这也会每隔5分钟调用您的方法。 Imp:此实施方案耗尽电池,如果您的位置数据的准确性不重要,则应使用[locationManager startMonitoringSignificantLocationChanges]

在将此添加到您的应用之前,请阅读Location Awareness Programming Guide

答案 3 :(得分:24)

现在,iOS6已经成为永久运行位置服务的最佳方式......

- (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]);

}

刚刚测试过它:

我启动了应用程序,去了背景并在车内移动了几分钟。然后我回家1小时再开始移动(不再打开应用程序)。地点再次开始。然后停了两个小时又重新开始了。一切都好了......

请勿忘记使用iOS6中的新位置服务

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

    // Lat/Lon
    float latitudeMe = loc.coordinate.latitude;
    float longitudeMe = loc.coordinate.longitude;
}

答案 4 :(得分:13)

对于有噩梦的人来说,找出这个。我有一个简单的解决方案。

  1. raywenderlich.com - &gt;中查看此示例有示例代码,这很好用,但遗憾的是在后台位置没有计时器。这将无限期地运行。
  2. 使用以下方式添加计时器:

    -(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. 请不要忘记在info.plist中添加“应用程序寄存器以进行位置更新”。

答案 5 :(得分:7)

以下是我使用的内容:

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)
    }
}

我在AppDelegate中开始跟踪:

BackgroundLocationManager.instance.start()

答案 6 :(得分:6)

不幸的是,你所有的假设似乎都是正确的,我认为没有办法做到这一点。为了节省电池寿命,iPhone的定位服务基于移动。如果手机位于一个位置,则位置服务不可见。

CLLocationManager只会在手机收到位置更新时拨打locationManager:didUpdateToLocation:fromLocation:,只有在三个位置服务(手机信号塔,gps,wifi)中的一个感知到更改时才会发生。

其他一些可能有助于进一步解决方案的事项:

  • 开始&amp;停止服务会导致调用didUpdateToLocation委托方法,但newLocation可能有旧的时间戳。

  • Region Monitoring might help

  • 在后台运行时,请注意可能很难获得Apple批准的“完整”LocationServices支持。从我所看到的,他们专门设计startMonitoringSignificantLocationChanges作为需要后台位置支持的应用程序的低功耗替代品,并强烈鼓励开发人员使用它,除非应用程序绝对需要它。

祝你好运!

更新:这些想法可能已经过时了。看起来人们在上面的@wjans回答中取得了成功。

答案 7 :(得分:5)

我确实使用位置服务编写了应用,应用必须每10秒发送一次位置。 而且效果很好。

只需使用&#34; allowDeferredLocationUpdatesUntilTraveled:timeout&#34;方法,遵循Apple的文档。

我做的是:

必填:注册后台模式以获取更新位置。

1。创建LocationMangerstartUpdatingLocationaccuracyfilteredDistance,如您所愿:

-(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。要在后台使用allowDeferredLocationUpdatesUntilTraveled:timeout方法让应用永久运行,当app移至后台时,您必须使用新参数重新启动updatingLocation,如下所示:

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

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

3。应用locationManager:didUpdateLocations:回调正常更新地点:

-(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. 但是你应该为了你的目的处理locationManager:didFinishDeferredUpdatesWithError:回调中的数据

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

     _deferringUpdates = NO;

     //do something 
}

5. 注意: 我认为我们应该在每次app切换背景/ forground模式时重置LocationManager的参数。

答案 8 :(得分:5)

if ([self.locationManager respondsToSelector:@selector(setAllowsBackgroundLocationUpdates:)]) {
    [self.locationManager setAllowsBackgroundLocationUpdates:YES];
}

这是iOS 9以来的背景位置跟踪所必需的。

答案 9 :(得分:4)

我使用了xs2bush获取间隔的方法(使用timeIntervalSinceDate)并对其进行了扩展。我想确保我获得了所需的准确度,并且我没有通过保持gps无线电超过必要的电量而耗尽电池电量。

我使用以下设置持续运行位置:

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

这对电池的消耗相对较低。当我准备好让我的下一个周期性位置读数时,我首先检查位置是否在我想要的精度范围内,如果是,我然后使用该位置。如果不是,那么我用这个来提高准确度:

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

获取我的位置,然后一旦我有位置,我再次将精度调低,以最大限度地减少电池消耗。我已经编写了完整的工作样本,并且我已经编写了服务器端代码的源代码来收集位置数据,将其存储到数据库并允许用户实时查看gps数据或检索和查看以前存储的路径。我有iOS,Android,Windows Phone和java me的客户端。所有客户都是本地编写的,并且它们都在后台正常工作。该项目获得麻省理工学院许可。

iOS项目针对iOS 6使用iOS 7的基础SDK。您可以获取代码here

如果您发现任何问题,请在github上提交问题。感谢。

答案 10 :(得分:2)

似乎stopUpdatingLocation是触发后台监视程序计时器的东西,因此我在didUpdateLocation中将其替换为:

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

似乎可以有效地关闭GPS。然后,背景NSTimer的选择器变为:

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

我正在做的是定期切换精度以每隔几分钟获得一个高精度坐标,并且因为locationManager尚未停止,所以backgroundTimeRemaining保持其最大值。这样可以减少电池消耗,从每小时约10%(背景中的常量kCLLocationAccuracyBest)到我的设备每小时约2%

答案 11 :(得分:2)

cocoapod APScheduledLocationManager允许每隔 n 秒获取背景位置更新,并具有所需的位置准确性。

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

存储库还包含一个用Swift 3编写的示例应用程序。

答案 12 :(得分:1)

工作代码(整个逐步代码)

第1步

  • 转到项目 - &gt;能力 - &gt;背景模式 - &gt;选择位置更新。
  • 转到项目 - &gt;信息 - &gt;使用可选字符串添加密钥NSLocationAlwaysUsageDescription。

第2步

将此代码添加到AppDelegate.m

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

第3步 将此代码添加到 AppDelegate.m 中的 applicationDidEnterBackground 方法

    - (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
}

答案 13 :(得分:0)

在iOS 9和watchOS 2.0中,CLLocationManager上有一个新方法,允许您请求当前位置:CLLocationManager:requestLocation()。这立即完成,然后将位置返回给CLLocationManager委托。

您现在可以使用NSTimer每分钟使用此方法请求一个位置,而不必使用startUpdatingLocation和stopUpdatingLocation方法。

但是,如果您想根据最后一个位置的X米更改来捕获位置,只需设置CLLocationManger的distanceFilter属性并调用X调用startUpdatingLocation()。

答案 14 :(得分:-1)

附件是基于以下内容的Swift解决方案:

在info.plist中定义App registers for location updates

始终使locationManager保持运行

kCLLocationAccuracy之间切换BestForNavigation(获取位置需要5秒),在剩余的等待时间内切换ThreeKilometers以避免电池耗尽

此示例在前景中每1分钟更新一次位置,在背景中每15分钟更新一次。

该示例适用于在iOS 7设备上运行的Xcode 6 Beta 6。

在App Delegate中(mapView是一个指向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
}

在mapView中(locationManager指向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")
        }
    }