有没有办法检查 iOS 设备是不是被锁定/解锁?

Posted

技术标签:

【中文标题】有没有办法检查 iOS 设备是不是被锁定/解锁?【英文标题】:Is there a way to check if the iOS device is locked/unlocked?有没有办法检查 iOS 设备是否被锁定/解锁? 【发布时间】:2012-12-23 04:32:58 【问题描述】:

我在我的应用程序中使用了 GPS 位置更新。我想检测 ios 设备是否处于睡眠模式,以便我可以关闭 GPS 位置更新并优化电池使用。我已经在 iOS 6 中尝试过 pausesLocationupdates,但它不能按预期工作。 我想在设备进入睡眠模式后立即关闭 GPS 位置更新。 我想检测设备中的锁定/解锁事件。

有没有办法实现这个功能?

到目前为止,我收到了如下所示的 darwin 通知

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

【问题讨论】:

您可以尝试在UIApplication.shared.isProtectedDataAvailable 上使用观察者,设备解锁后将返回true 这是一个简单而伟大的答案。 【参考方案1】:

我是这样解决的:

//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

【讨论】:

这是,根据事件,我想暂停/恢复应用程序中的位置更新,以防止电池耗尽。 现在没有必要讨论了,更新是各自的用例,我的应用程序甚至在后台使用位置更新,我有一个处理程序,当用户锁定设备时(后台应用程序) ,我想暂停位置更新。 来自 Eskimo@apple 此处:devforums.apple.com/message/912689#912689:这些通知密钥未记录在案,因此被视为私有 API。此外,从兼容性的角度来看,依赖它们并不是对前面描述的技术的显着改进。顺便说一句,被视为公共 API 的通知键列在 中,但需要注意的是,并非所有这些都在 iOS 上有意义。 有没有办法区分按下电源按钮和设备收到通知之间的显示状态变化? @Jimmy 当应用处于后台状态时我无法检测到事件?【参考方案2】:

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

我收到了这个:

尊敬的开发者,

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

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

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

问候,

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

【讨论】:

这似乎不是一个答案,而是对问题的更多限制。【参考方案3】:

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

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

  );

 

【讨论】:

我认为你可以使用这种方法..它可以帮助njoy..!! 在使用此代码之前只需导入#import notify.h 否则会出现编译错误 @VanditMehta Vandit - 我看到您已将有关背景检测的问题发布到多个 SO 问题中。你有没有找到在后台检测锁定状态的解决方案?【参考方案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;
    

【讨论】:

感谢您发布此信息!据我所知,另一种方法实际上并没有明确告诉您屏幕或锁定状态正在更改为什么(userInfo 字典始终为零),并且根据顺序进行操作是有问题的,尤其是因为我的顺序获取这些通知实际上与帖子声称的内容相反——可能是 iOS 7 中的一种新行为。【参考方案5】:

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

var isScreenLocked: Bool 
    return UIScreen.main.brightness == 0.0

【讨论】:

这是查找屏幕是否锁定的简单方法。谢谢@rockdaswift 我无法让它工作。它总是返回假。 iOS 13.4 谢谢@rockdaswift,是的,它正在工作,但是如果设备被锁定并且用户触摸屏检查让说时钟这个条件将返回错误,则有一个案例可以避免这个检查【参考方案6】:

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

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

当锁定按下时

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.**  

【讨论】:

【参考方案7】:

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

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

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

【讨论】:

【参考方案8】:

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

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

我自己从未做过,但这只是一种替代解决方案......

【讨论】:

只要手机有激活的密码(或其他安全锁),它就可以工作。

以上是关于有没有办法检查 iOS 设备是不是被锁定/解锁?的主要内容,如果未能解决你的问题,请参考以下文章

如何解除物料的相互锁定

Android Activity 生命周期和锁定/解锁设备

Oracle数据库老是被锁表,而且用解锁语句解开后,紧跟着又出来一堆锁,解也解不完

121SAP中程序提示被某用户使用或锁定,解锁办法

121SAP中程序提示被某用户使用或锁定,解锁办法

电脑的监控控制锁如何解锁