14

我在我的应用程序中使用了 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);


}

当设备被锁定/解锁时,我能够获得 darwin 通知,但真正的问题是如何识别通知是来自锁定还是设备解锁。控制台日志是:

 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 也足够了。提前致谢。

4

8 回答 8

18

我是这样解决的:

//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 开始,不再依赖两个通知的顺序

于 2013-01-11T04:01:50.553 回答
18

现在不允许应用程序收听设备锁定通知!

我收到了这个:

亲爱的开发者,

我们发现您最近提交的“xxxx”存在一个或多个问题。要处理您的提交,必须更正以下问题:

不支持的操作 - 不允许应用收听设备锁定通知。

纠正这些问题后,使用 Xcode 或 Application Loader 将新的二进制文件上传到 iTunes Connect。在 iTunes Connect 上的我的应用程序的应用程序详细信息页面上选择新的二进制文件,然后单击提交以供审核。

问候,

App Store 团队
2017 年 4 月 26 日 10:56

于 2017-04-26T03:47:31.193 回答
7

/* 注册应用程序以检测锁定状态 */

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

  });

 }
于 2014-07-24T07:26:27.457 回答
4

这是一个更好的解决方案

#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;
    }
}
于 2013-09-05T12:15:03.990 回答
3

对于您的特定用例,检查屏幕亮度可能很有用。

var isScreenLocked: Bool {
    return UIScreen.main.brightness == 0.0
}
于 2018-03-01T11:18:41.377 回答
1

我得到了检查锁定按钮被按下或将应用程序置于后台模式的解决方案。

用于锁定按下的应用程序周期并将应用程序置于后台模式 -

当锁定按下

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.**  
于 2019-03-04T13:09:05.950 回答
0

Jimmy 提供了一个很好的解决方案,但(__bridge const void *)(self)作为观察者传入会更安全。

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

这允许您正确删除观察者。

于 2016-03-01T23:37:18.887 回答
0

在使用内容保护的设备上,受保护的文件以加密形式存储,并且仅在特定时间可用,通常是在设备解锁时。此通知让您的应用程序知道设备即将被锁定,并且它当前正在访问的任何受保护文件可能很快就会变得不可用。

您可以订阅applicationProtectedDataWillBecomeUnavailable用户刚刚锁定 iPhone 时很可能触发的通知。

我自己从来没有做过,但这只是一个替代解决方案......

于 2020-10-27T04:29:22.553 回答