web-dev-qa-db-ja.com

iOSデバイスがロック/ロック解除されているかどうかを確認する方法はありますか?

アプリケーションでGPS位置更新を使用しました。 iOSデバイスがスリープモードになっているかどうかを検出して、GPS位置情報の更新をオフにし、バッテリーの使用を最適化できるようにしたい。 iOS 6でpausesLocationupdatesを試しましたが、期待どおりに機能しません。デバイスがスリープモードになったらすぐにGPS位置情報の更新をオフにしたい。デバイスのロック/ロック解除イベントを検出したい。

この機能を実現する方法はありますか?

これまでのところ、以下のようなダーウィン通知を受け取りました

-(void)registerForall
{
    //Screen lock notifications
    CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), //center
                                    NULL, // observer
                                    displayStatusChanged, // callback
                                    CFSTR("com.Apple.iokit.hid.displayStatus"), // event name
                                    NULL, // object
                                    CFNotificationSuspensionBehaviorDeliverImmediately);


    CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), //center
                                    NULL, // observer
                                    displayStatusChanged, // callback
                                    CFSTR("com.Apple.springboard.lockstate"), // event name
                                    NULL, // object
                                    CFNotificationSuspensionBehaviorDeliverImmediately);

    CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), //center
                                    NULL, // observer
                                    displayStatusChanged, // callback
                                    CFSTR("com.Apple.springboard.hasBlankedScreen"), // event name
                                    NULL, // object
                                    CFNotificationSuspensionBehaviorDeliverImmediately);

    CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), //center
                                    NULL, // observer
                                    displayStatusChanged, // callback
                                    CFSTR("com.Apple.springboard.lockcomplete"), // event name
                                    NULL, // object
                                    CFNotificationSuspensionBehaviorDeliverImmediately);

}
//call back
static void displayStatusChanged(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo)
{
    NSLog(@"IN Display status changed");
    NSLog(@"Darwin notification NAME = %@",name);


}

デバイスがロック/ロック解除されたときにダーウィン通知を受け取ることができますが、本当の問題は、通知がデバイスのロックまたはロック解除のどちらからのものかをどのように識別するかです。コンソールログは次のとおりです。

 LockDetectDemo[2086] <Warning>: IN Display status changed
 LockDetectDemo[2086] <Warning>: Darwin notification NAME = com.Apple.springboard.lockcomplete
 LockDetectDemo[2086] <Warning>: IN Display status changed
 LockDetectDemo[2086] <Warning>: Darwin notification NAME = com.Apple.springboard.lockstate
 LockDetectDemo[2086] <Warning>: IN Display status changed
 LockDetectDemo[2086] <Warning>: Darwin notification NAME = com.Apple.springboard.hasBlankedScreen
 LockDetectDemo[2086] <Warning>: IN Display status changed
 LockDetectDemo[2086] <Warning>: Darwin notification NAME = com.Apple.iokit.hid.displayStatus

プライベートAPIでも十分です。前もって感謝します。

14
Rohit Kashyap

私はそれを次のように解決しました:

//call back
static void displayStatusChanged(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo)
{
    // the "com.Apple.springboard.lockcomplete" notification will always come after the "com.Apple.springboard.lockstate" notification
    CFStringRef nameCFString = (CFStringRef)name;
    NSString *lockState = (NSString*)nameCFString;
    NSLog(@"Darwin notification NAME = %@",name);

    if([lockState isEqualToString:@"com.Apple.springboard.lockcomplete"])
    {
        NSLog(@"DEVICE LOCKED");
        //Logic to disable the GPS
    }
    else
    {
        NSLog(@"LOCK STATUS CHANGED");
        //Logic to enable the GPS
    }
}

-(void)registerforDeviceLockNotif
{
    //Screen lock notifications
    CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), //center
                                    NULL, // observer
                                    displayStatusChanged, // callback
                                    CFSTR("com.Apple.springboard.lockcomplete"), // event name
                                    NULL, // object
                                    CFNotificationSuspensionBehaviorDeliverImmediately);

    CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), //center
                                    NULL, // observer
                                    displayStatusChanged, // callback
                                    CFSTR("com.Apple.springboard.lockstate"), // event name
                                    NULL, // object
                                    CFNotificationSuspensionBehaviorDeliverImmediately);
}

注:「 com.Apple.springboard.lockcomplete 」通知は常に「com.Apple.springboard.lockstate」通知の後に送信されます

更新

IOSの最近のバージョンでは、2つの通知のorderは信頼できなくなりました

17
Rohit Kashyap

現在、アプリはデバイスロック通知をリッスンできません。

私はこれを受け取りました:

親愛なる開発者、

最近の「xxxx」の送信で1つ以上の問題が見つかりました。提出物を処理するには、次の問題を修正する必要があります。

サポートされていない操作-アプリはデバイスロック通知をリッスンできません。

これらの問題が修正されたら、XcodeまたはApplicationLoaderを使用して新しいバイナリをiTunesConnectにアップロードします。 iTunes Connectの[マイアプリ]のアプリの[詳細]ページで新しいバイナリを選択し、[レビューのために送信]をクリックします。

よろしく、

AppStoreチーム
2017年4月26日10:56

8
Jules

/ *ロック状態を検出するためのアプリを登録します* /

 -(void)registerAppforDetectLockState {

     int notify_token;
     notify_register_dispatch("com.Apple.springboard.lockstate",     &notify_token,dispatch_get_main_queue(), ^(int token) {
     uint64_t state = UINT64_MAX;
     notify_get_state(token, &state);
     if(state == 0) {
        NSLog(@"unlock device");
     } else {
        NSLog(@"lock device");
     }

     NSLog(@"com.Apple.springboard.lockstate = %llu", state);
     UILocalNotification *notification = [[UILocalNotification alloc]init];
     notification.repeatInterval = NSDayCalendarUnit;
     [notification setAlertBody:@"Hello world!! I come becoz you lock/unlock your device :)"];
     notification.alertAction = @"View";
     notification.alertAction = @"Yes";
     [notification setFireDate:[NSDate dateWithTimeIntervalSinceNow:1]];
     notification.soundName = UILocalNotificationDefaultSoundName;
     [notification setTimeZone:[NSTimeZone  defaultTimeZone]];

     [[UIApplication sharedApplication] presentLocalNotificationNow:notification];

  });

 }
7
Nits007ak

これがより良い解決策です

#import <notify.h>

#define kNotificationNameDidChangeDisplayStatus                 @"com.Apple.iokit.hid.displayStatus"

@interface YourClass ()
{    
    int _notifyTokenForDidChangeDisplayStatus;
}

@property (nonatomic, assign, getter = isDisplayOn) BOOL displayOn;
@property (nonatomic, assign, getter = isRegisteredForDarwinNotifications) BOOL registeredForDarwinNotifications;

@end

- (void)registerForSomeNotifications
{
    //
    // Display notifications
    //

    __weak YourClass *weakSelf = self;

    uint32_t result = notify_register_dispatch(kNotificationNameDidChangeDisplayStatus.UTF8String,
                                               &_notifyTokenForDidChangeDisplayStatus,
                                               dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0l),
                                               ^(int info) {
                                                   __strong YourClass *strongSelf = weakSelf;

                                                   if (strongSelf)
                                                   {
                                                       uint64_t state;
                                                       notify_get_state(_notifyTokenForDidChangeDisplayStatus, &state);

                                                       strongSelf.displayOn = (BOOL)state;
                                                   }
                                               });
    if (result != NOTIFY_STATUS_OK)
    {
        self.registeredForDarwinNotifications = NO;
        return;
    }

    self.registeredForDarwinNotifications = YES;
}

- (void)unregisterFromSomeNotifications
{
    //
    // Display notifications
    //

    uint32_t result = notify_cancel(_notifyTokenForDidChangeDisplayStatus);
    if (result == NOTIFY_STATUS_OK)
    {
        self.registeredForDarwinNotifications = NO;
    }
}
4
arturgrigor

チェックロックボタンの解決策は、アプリを押すか、バックグラウンドモードにすることでした。

ロックを押してアプリをバックグラウンドモードにするためのアプリサイクル-

ロックを押すと

applicationWillResignActive

applicationDidEnterBackground

プレスのロックを解除するとき

applicationWillEnterForeground

applicationDidBecomeActive

/////////////////////バックグラウンドに置くとき

applicationWillResignActive

applicationDidEnterBackground

挽くとき

applicationWillEnterForeground

applicationDidBecomeActive

両方のシナリオで、同じメソッドが呼び出していることがわかります。この場合、ロックボタンを押したり、アプリをバックグラウンドに置いたりするのは難しい作業です。ロックボタンを押すと小さなハックがあります

applicationWillResignActive

applicationDidEnterBackground

これらのメソッドはすぐに呼び出されますが、アプリをバックグラウンドに置くと、両方のメソッドの間にミリ秒の時間間隔があります。時差を取得して条件を設定できます。お気に入り....

var dateResignActive : Date?
var dateAppDidBack : Date?
func applicationWillResignActive(_ application: UIApplication) {

    dateResignActive = Date()

}
func applicationDidEnterBackground(_ application: UIApplication) {

    dateAppDidBack = Date()

}
func applicationDidBecomeActive(_ application: UIApplication) {

    let el1 = getCurrentMillis(date: dateResignActive!)
    let el2 = getCurrentMillis(date: dateAppDidBack!)
    let diff = el2 - el1

    if diff < 10 { //// device was locked // 10 is aprox
        // device was locked

    }
    else {
        let elapsed = Int(Date().timeIntervalSince(date!))
        if elapsed > 15 { // put app in background
        }
    }

}
func getCurrentMillis(date : Date)->Int64 {
    return Int64(date.timeIntervalSince1970 * 1000)
}

**This code is tested in iPhone X(Notch) and iPhone 6(Home button device). Because notch device and home button device have small difference in above two method calling.**  
1
Vijay Patidar

特定のユースケースでは、画面の明るさを確認すると便利です。

var isScreenLocked: Bool {
    return UIScreen.main.brightness == 0.0
}
1
rockdaswift

ジミーは素晴らしい解決策を提供しましたが、オブザーバーとして(__bridge const void *)(self)を渡す方が安全です。

CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(),
                                (__bridge const void *)(self),
                                displayStatusChanged,
                                CFSTR("com.Apple.springboard.lockcomplete"),
                                NULL,
                                CFNotificationSuspensionBehaviorDeliverImmediately);

これにより、オブザーバーを適切に削除できます。

0
Karim Mourra