iOSアプリケーションでn分ごとにバックグラウンドで位置情報を更新するにはどうすればよいですか?


207

iOSアプリケーションでバックグラウンドで位置情報をn分ごとに更新する方法を探しています。私はiOS 4.3を使用していますが、このソリューションはジェイルブレイクされていないiPhoneでも機能するはずです。

私は次のオプションを試しました/検討しました:

  • CLLocationManager startUpdatingLocation/startMonitoringSignificantLocationChanges:これは、構成されたプロパティに基づいて、期待どおりにバックグラウンドで機能しますが、n分ごとに場所を強制的に更新することはできないようです
  • NSTimer:アプリがフォアグラウンドで実行されているときに機能しますが、バックグラウンドタスク用に設計されていないようです
  • ローカル通知:ローカル通知はn分ごとにスケジュールできますが、現在の場所を取得するためにコードを実行することはできません(ユーザーが通知を介してアプリを起動する必要がない場合)。これは通知を使用する必要がある方法ではないため、この方法もクリーンな方法ではないようです。
  • UIApplication:beginBackgroundTaskWithExpirationHandler:私が理解している限り、これは、「長時間」バックグラウンドプロセスを実装するのではなく、アプリがバックグラウンドに移動したときに、バックグラウンド(時間も限られています)で一部の作業を完了するために使用する必要があります。

これらの定期的なバックグラウンドの場所の更新を実装するにはどうすればよいですか?



便利なフォローアップ:stackoverflow.com/questions/10235203/...
ロロ

1
iOS 7で動作させる場合は、こちらのソリューションを試すことができます:stackoverflow.com/questions/18946881/…ご不明な点がございましたら、mobileoop.com
Ricky

1
すべての調査結果は正しいです(4つの箇条書き)。次に、あなたのユースケースと一致しないものを知っている貴重な情報はありますか?そして、はい、サスペンドモードまたは実行していない場合、n分ごとに更新するための究極の方法はありません。
LenArt 2014

回答:


113

私はこれをApple Developer Forumsの助けを借りて実装する解決策を見つけました:

  • 指定する location background mode
  • NSTimerバックグラウンドでを作成しますUIApplication:beginBackgroundTaskWithExpirationHandler:
  • ときにnある小さいよりも、UIApplication:backgroundTimeRemainingそれはうまく動作します。nより大きい場合はlocation manager、バックグラウンドタスクが強制終了されるのを避けるために、残り時間がなくなる前にを再度有効(および無効)にする必要があります。

これは、ロケーションが許可されている3つのタイプのバックグラウンド実行の1つであるため機能します

注:これが機能しないシミュレーターでこれをテストすることにより、時間を失いました。しかし、それは私の電話でうまく動作します。


24
あなたはたまたまフォーラムへのリンクを持っていますか?同じタイプのロケーションマッピングを実装しようとしていますが、機能させることができませんでした。または、いくつかのサンプルコードをいただければ幸いです。
utahwithak 2011

4
ロケーションマネージャを停止して開始しただけで、10分(最大許容時間)後にバックグラウンドタスクが強制終了されない理由を説明していただけますか?それはある種の意図された機能ですか?そのような場合、Apple SDKのバグのように聞こえます。試してみたiOSのバージョンは何ですか?
saurabh

5
@all:はい、私たちのアプリはAppStoreで入手できます。私はすべてのコードを投稿するつもりはありません、言及されたすべての箇条書きは明確に文書化された機能です。特定の問題が発生している場合は、何を試し、何がうまくいかないかを説明する独自の質問を投稿してください。
wjans 2012年

3
@ user836026:はい、それは私がバックグラウンドモードを指定することによって意味します。位置情報の更新を停止した後、アプリが終了しないようにするには、10分以内に再度開始する必要があります。
wjans 2012年

12
ここで説明されている内容を反映した実際のコードを見ることに興味があるすべての人は、stackoverflow.com / questions / 10235203
…を

55

のiOS 8/9/10 5分ごとに次の操作を実行し、バックグラウンド位置更新を作るために:

  1. [プロジェクト]-> [機能]-> [バックグラウンドモード]-> [場所の更新]を選択します。

  2. プロジェクトに移動->情報->空の値(またはオプションで任意のテキスト)を持つキーNSLocationAlwaysUsageDescriptionを追加します。

  3. アプリがバックグラウンドで動作しているときに位置情報を機能させ、座標をWebサービスに送信するか、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

3
これは数時間以上機能しますか?アプリが終了していない場合でも、デバイスは、アプリがバックグラウンドに移動してから約1時間後に位置情報の更新のプッシュを停止するようです。
Myxtic

2
プロジェクトでバックグラウンドフェッチが無効になっています(オフでもオンでもかまいません)。ただし、上記の例を正しく機能させるには、pausesLocationUpdatesAutomaticallyをNOに設定する必要があります。以前にシステムによって一時停止されていた場合、再度移動を開始すると自動的に再開されません。上記の例でこのプロパティをNOに設定したのはそのためです。
Leszek Szary

1
私はあなたが絶対に正しいと思います。この問題に関する詳細情報はこちら:stackoverflow.com/q/17484352/1048331
Myxtic

2
@LeszekSバックグラウンドのiOS 9サポートのコードの下に追加する必要があります if ([self.locationManager respondsToSelector:@selector(setAllowsBackgroundLocationUpdates:)]) { [self.locationManager setAllowsBackgroundLocationUpdates:YES]; }
Fenil

2
これを行うことの便利さは何ですか?常に高い精度を維持しているため、バッテリーを消耗し続けています。しない唯一のことは、5分の間隔に達するまで、実際に受信済みの場所を実際に使用しないことです...
Honey

34

私が開発しているアプリケーションでこれを行いました。アプリがバックグラウンドにある場合、タイマーは機能しませんが、アプリは常に位置情報の更新を受信して​​います。私はドキュメントのどこかを読んでいます(私は今それを見つけることができないようです、私は更新を投稿します)、アプリがバックグラウンドにあるときにアクティブな実行ループでのみ呼び出すことができます。アプリのデリゲートには、bgでもアクティブな実行ループがあるため、これを機能させるために独自のループを作成する必要はありません。[これが正しい説明かどうかはわかりませんが、それが私が読んだ内容からどのように理解したかです]

まず、アプリのinfo.plistにlocationキーのオブジェクトを追加しますUIBackgroundModes。今、あなたがする必要があるのはあなたのアプリのどこかで位置情報の更新を開始することです:

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

次に、位置情報の更新を処理するメソッドを、たとえば-(void)didUpdateToLocation:(CLLocation*)locationアプリデリゲートで記述します。次に、ロケーションマネージャーを開始したクラスにのメソッドlocationManager:didUpdateLocation:fromLocationを実装しCLLocationManagerDelegateます(ロケーションマネージャーデリゲートを 'self'に設定しているため)。このメソッド内では、位置の更新を処理する必要がある時間間隔が経過したかどうかを確認する必要があります。これを行うには、毎回現在時刻を保存します。その時間が経過した場合は、アプリのデリゲートからメソッド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分ごとにメソッドが呼び出されます。インプ:この実装はバッテリーを消耗します。位置データの精度が重要でない場合は、使用する必要があります[locationManager startMonitoringSignificantLocationChanges]

これをアプリに追加する前に、位置認識プログラミングガイドをお読みください


3
位置情報サービスが常に有効になっている(実際にはバッテリーの消耗)ので、私はそうしたくありません。位置情報サービスをn分ごとに有効にし、適切な修正があったらすぐに無効にしたいと思います(質問でそれを明確に説明しなかったことに気づきました)。私が説明したソリューションでこの動作を実現できます。
wjans

3
ロケーションマネージャーの精度を1 kmに設定できます。これにより、バッテリーはほとんどそのままになります。5分後、精度を1mに設定します。満足のいく位置になったら(通常5秒後)、精度を1kmに戻します。
knagode 2013年

knagodeさん、バッテリーの消耗の問題について提案された解決策を試しましたが、N分後に精度を上げても、locationManager:didUpdateLocationsメソッドが再度呼び出されません。精度を増減するのではなく、startUpdatingとstopUpdatingを試しましたが、N分後にlocationManager:didUpdateLocationsメソッドのデリゲートが正常に呼び出されましたが、バックグラウンドモードでは機能しません...
Hardik Darji

ドキュメントのリンクはこちら。ここを参照してください:「デリゲートオブジェクトのメソッドは、対応する位置情報サービスを開始したスレッドから呼び出されます。そのスレッド自体は、アプリケーションのメインスレッドにあるような実行ループを持っている必要があります。」
ハニー、

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時間家に帰り、再び移動を開始します(アプリを再度開くことなく)。場所が再び始まった。その後、2時間停止して再開しました。すべてが再び大丈夫...

iOS6の新しい位置情報サービスの使用を忘れないでください

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

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

1
アプリがクラッシュまたは強制終了された場合、システムは再起動しません。
Ken

1
より読みやすいコードについては、[locations lastObject]を実行できます。[locations objectAtIndex:[locations count]-1]の
代わり

あなたの方法はios6だけですか?
pengwang

これを使用する必要がありますか?クラスのviewDidLoadにロケーションマネージャーのコードがあり、アプリがロケーション更新用に登録するバックグラウンドキーを設定するだけで十分だと思いましたか?これで私を助けてくれませんか!
nithinreddy 2013年

はい、チャームニティンレディのように機能しますが、iOSがその期間の後に長いスレッドを強制終了するため、10分後には機能しなくなります。これらのサービスを永久に開始したい場合は、私のソリューションが最適です。2日前にいくつかのテストを行ったところ、毎時6%のバッテリーが消耗しました。代わりに[locationManager startUpdatingLocation]を使用すると、17%が消費されます
Alejandro Luengo

13

悪夢を持っている他の誰かがこれを理解します。簡単な解決策があります。

  1. raywenderlich.comからこの例を見てください。 ->サンプルコードがあります。これは完全に機能しますが、残念ながらバックグラウンドの位置にタイマーはありません。これは無期限に実行されます。
  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に「位置情報更新用のアプリレジスタ」を追加することを忘れないでください。


これは3分以上場所を取得しますか?
SleepNot

機能->バックグラウンドモードで位置を設定する必要があります。
Sergio Andreotti

動いていない!!?iOS 8とiOS 10を確認しました
Kirti

私が間違っていたら訂正してください。私が読んだ内容に基づいて、locationManagerを1回だけ起動します。その後、すべての間隔は冗長です。それはすでに始まっているので
ハニー

動作していますが、170秒後に停止します。タスクをバックグラウンドで無制限に実行したい
anshuman burmman

8

これが私が使うものです:

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

ありがとう。私たちのプロジェクトは、ユーザーの場所を追跡し、PubNubイベントをバックグラウンドで送信する必要があり、ソリューションは非常にうまく機能しています。
user921509 '19

こんにちはHmitkov、sendLocationToServerメソッドを呼び出してサーバー上のユーザーの場所を送信できる場所
AmanGupta007 2017年

@ AmanGupta007 func locationManager(manager:didUpdateLocations :)でsendLocationToServerを呼び出すことができます。コード内の// TODOコメントに注意してください。
hmitkov

@hmitkovアプリがバックグラウンドにあるときに、これで位置情報サービスを開始および停止することは可能ですか?たとえば、プッシュ通知から位置情報サービスを開始し、緯度/経度を取得し、Webサービスに送信して、位置情報の更新を停止します。'content-available' = 1がプッシュボディに含まれるたびにこれを行います。
DookieMan 2017

1
私はこのコードを試しましたが、iOS11では機能していないようです?(他のバージョンではテストしていません。)
Tom

6

残念ながら、あなたの仮定はすべて正しいように思われ、これを行う方法はないと思います。バッテリー寿命を節約するために、iPhoneの位置情報サービスは動きに基づいています。電話が1つの場所にある場合、位置情報サービスからは見えません。

CLLocationManagerのみ呼び出しますlocationManager:didUpdateToLocation:fromLocation:電話は3つのロケーションサービスの1(セルタワー、GPS、無線LAN)が変更を認識する場合にのみ起こる位置更新を受信したとき。

さらなる解決策を知らせるのに役立つかもしれない他のいくつかのこと:

  • サービスを開始および停止すると、didUpdateToLocationデリゲートメソッドが呼び出されますが、にnewLocationは古いタイムスタンプが含まれている可能性があります。

  • 地域監視が役立つかもしれません

  • バックグラウンドで実行しているときは、Appleによる「完全な」LocationServicesサポートの承認を得るのが難しい場合があることに注意してください。私が見たところ、これらはstartMonitoringSignificantLocationChangesバックグラウンドでの位置情報のサポートを必要とするアプリの低電力の代替として特別に設計されており、アプリが絶対に必要でない限り、これを使用することを開発者に強く勧めています。

幸運を!

更新:これらの考えは今では時代遅れかもしれません。上記の@wjansの回答で人々が成功しているように見えます。


1
AppStoreには、バックグラウンドで位置情報の更新を取得できるようにするアプリ(「My Locus」など)があります。それらは位置情報サービスをアクティブに保ちませんが、定義された間隔に従ってすぐに有効になります。彼らはこれをどのように行いますか?
wjans '06 / 06/15

2
あなたが説明する状況では、アプリはおそらくstartMonitoringSignificantLocationChangesアプローチを使用しています。ここでは、電話が位置情報の更新を受信すると一時的に「ウェイクアップ」しますが、このサービスをバックグラウンドで「ping」するように設定できる間隔はありません。スマートフォンが動く(または携帯電話からGPSまたはWifiに切り替わる)と、アップデートがトリガーされます。トピックのスタンフォード大学のiTunes Uの講義は、私にとって本当に便利だった-うまくいけば、それはあなたが、回避策を見つけることができます:itunes.apple.com/us/itunes-u/iphone-application-development/...を
Chazbot

2
ポインターのThx。しかし、私はまだアプリが何をしているか理解していません。自分の電話が私のデスクにあり、まったく動いていない場合でも、位置情報サービスが10分ごとに(正確に)トリガーされているのがわかります。私が正しく理解していればstartMonitoringSignificantLocationChanges、その場合、更新は行われません。
wjans

1
@wjansお使いの携帯電話のバッテリー消費量はどうですか、おそらくMylocusアプリが原因でそれが急速に消耗していることに気付きましたか?
user836026

6

位置情報サービスを使用してアプリを作成しましたが、アプリは10秒ごとに位置情報を送信する必要があります。そして、それは非常にうまくいきました。

Appleのドキュメントに従って、「allowDeferredLocationUpdatesUntilTraveled:timeout」メソッドを使用するだけです。

私がしたことは:

必須:更新場所のバックグラウンドモードを登録します。

1.作成LocationMangerstartUpdatingLocationし、accuracyそしてfilteredDistanceあなたが好きなよう:

-(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バックグラウンドでメソッドを使用してアプリを永久に実行し続けるには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:コールバックを使用して通常どおりupdatedLocationsを取得します。

-(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. 注:LocationManagerアプリがバックグラウンドモードとフォアグラウンドモードを切り替えるたびに、パラメーターをリセットする必要があると思います。


@Allこのソリューションまたは上記のwjansで説明されているソリューションのうち、デバイスのバッテリーを節約したいのですが、どちらのソリューションでも同じですか?
Yash

2
私はあなたが言及した両方の解決策を試しましたが、@ wjansの解決策はバッテリーを少し節約できることがわかりました。しかし、iOS 8の登場後、そのソリューションは適切に機能しなくなったようです。詳細については、ほとんどの場合、アプリはバックグラウンドモードで長時間実行することはできません。
samthui7 2015

@ samthui7 pausesLocationUpdatesAutomatically = falseを設定するのはなぜですか?
CedricSoubrie 2016

私のアプリの要件は、userLocationを10秒ごとに送信し続けることpausesLocationUpdatesAutomatically = trueですが、ロケーションの変更がないように見える場合は、アップデートを一時停止するようにロケーションマネージャに指示します(Appleのドキュメント)。とにかく、私はlocation manager pauses updatesまだ:Dのケースを明示的にテストしていません。
samthui7 2016

@CedricSoubrie:設定pausesLocationUpdatesAutomatically = trueにより、アプリが位置情報の更新を停止します。
samthui7

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

これは、iOS 9以降のバックグラウンドでの位置追跡に必要です。


1
これは私の日を救った!iOS9デバイスでのiOS8展開ターゲットの使用
Yaro

4

私はxs2bushの間隔を取得する方法を使用しました( timeIntervalSinceDate)し、それを少し拡張しました。必要以上にGPS無線をオンにしておくことで、必要な精度が得られていること、およびバッテリーが消耗していないことを確認したかったのです。

次の設定で位置情報を継続的に実行します。

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

これは、バッテリーの消費が比較的少ないことです。次の定期的な位置測定値を取得する準備ができたら、最初に位置が希望の精度内にあるかどうかを確認し、許容範囲内にある場合は、その位置を使用します。そうでない場合は、次のようにして精度を高めます。

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

現在地を取得し、場所を取得したら、精度を再度下げて、バッテリーの消耗を最小限に抑えます。私はこれの完全な動作サンプルを書きました。また、ロケーションデータを収集してデータベースに保存し、ユーザーがgpsデータをリアルタイムで表示したり、以前に保存したルートを取得して表示したりできるようにするサーバー側コードのソースを書きました。iOS、Android、Windows Phone、Java Meのクライアントがあります。すべてのクライアントはネイティブで作成されており、すべてバックグラウンドで適切に動作します。プロジェクトはMITライセンスです。

iOSプロジェクトは、iOS 7のベースSDKを使用するiOS 6を対象としています。コードはこちらから入手できます。。。

問題がある場合は、githubに問題を報告してください。ありがとう。


私はあなたの解決策を試しましたが、機能しません...アプリがバックグラウンドになると、精度を上げても、didUpdateToLocationメソッドが呼び出されません
Hardik Darji

@HardikDarji重要な質問。引っ越していますか?そうでない場合、位置情報の更新が停止する可能性があります。散歩やドライブにスマートフォンを出してみて、問題が解決するかどうか確認してください。
nickfox 14年

早速の対応、ありがとうございました。しかし、携帯が動いているか動いていないかを気にせずに、2分ごとに位置情報を更新したい。このシナリオでは、didUpdateToLocationメソッドは呼び出されません。私はここを探しています:n分ごとに位置情報を更新するにはどうすればいいですか!!!
Hardik Darji 14年

timeIntervalInSecondsを120に設定して、この行のコメントを外してください。locationManager.pausesLocationUpdatesAutomatically= NO;
nickfox 14年

1
上記のコメントで投稿した解決策はバッテリー寿命を殺しますか、それともまだそのための最適化を行っていますか?
samfr

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は最大値のままです。これにより、バッテリー消費量が1時間あたり最大10%(バックグラウンドでkCLLocationAccuracyBestが一定の場合)からデバイスで1時間あたり最大2%に削減されました


2

cocoapod APScheduledLocationManagerがあり、 バックグラウンドでのロケーション更新をn秒ごとに希望のロケーション精度で取得できます。

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

リポジトリには、Swift 3で記述されたサンプルアプリも含まれています。


目的cにこのようなものはありますか?
Moxarth 2017

1

iOS 9およびwatchOS 2.0では、CLLocationManagerに新しいメソッドがあり、現在の場所をリクエストできます:CLLocationManager:requestLocation()。これはすぐに完了し、CLLocationManagerデリゲートに場所を返します。

NSTimerを使用して、このメソッドで1分ごとに場所をリクエストできます。startUpdatingLocationメソッドとstopUpdatingLocationメソッドを操作する必要はありません。

ただし、最後の場所からのXメートルの変化に基づいて場所をキャプチャする場合は、CLLocationMangerのdistanceFilterプロパティを設定し、XにstartUpdatingLocation()を呼び出します。


-1

添付されているのは、以下に基づくSwiftソリューションです。

定義する App registers for location updatesinfo.plistで

locationManagerを常に実行し続ける

スイッチkCLLocationAccuracyBestForNavigation(5秒間の位置を取得する)と、ThreeKilometers回避バッテリー排水への待機期間の残りの

この例では、フォアグラウンドで1分ごと、バックグラウンドで15分ごとに位置を更新します。

この例は、iOS 7デバイスで実行されているXcode 6 Beta 6で正常に動作します。

アプリデリゲート内(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")
        }
    }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.