【问题标题】:Lock Unlock events iphone锁定解锁事件 iphone
【发布时间】:2010-10-16 22:06:02
【问题描述】:

如何检测 iPhone 上的锁定/解锁事件?假设它只适用于越狱设备,你能指出正确的 API 吗?

锁定事件是指显示或隐藏锁定屏幕(可能需要密码才能解锁)。

【问题讨论】:

  • “锁定/解锁事件”是什么意思?

标签: iphone ios api locking


【解决方案1】:

如果您的应用正在运行并且用户锁定了设备,您的应用委托将收到对“应用程序将退出活动:”的调用。如果您的应用程序在锁定时正在运行,它会在设备解锁时收到对“应用程序已变为活动:”的调用。但是,如果用户接到电话然后选择忽略它,您的应用程序也会收到相同的电话。据我所知,你无法区分。

如果您的应用在这些时间没有运行,则由于您的应用没有运行,因此无法收到通知。

【讨论】:

  • 有用,但我认为用户只想检测“锁定”事件。无论用户按下“主页”按钮还是“锁定”按钮,这些方法都会触发。
【解决方案2】:

四舍五入的回答:

应用程序将退出活动在各种情况下被调用...并且根据我的所有测试,即使您的应用程序在后台保持唤醒状态,也无法确定屏幕已锁定(CPU 速度不报告, BUS 速度不变,mach_time denom / numer 不变)...

但是,当设备被锁定时,Apple 似乎确实关闭了加速度计...Enable iPhone accelerometer while screen is locked (在 iPhone 4 上测试 iOS4.2 有这种行为)

这样……

在您的应用程序委托中:

- (void)applicationWillResignActive:(UIApplication *)application
{
    NSLog(@"STATUS - Application will Resign Active");
    // Start checking the accelerometer (while we are in the background)
    [[UIAccelerometer sharedAccelerometer] setDelegate:self];
    [[UIAccelerometer sharedAccelerometer] setUpdateInterval:1]; // Ping every second
    _notActiveTimer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(deviceDidLock) userInfo:nil repeats:NO]; // 2 seconds for wiggle

}
//Deprecated in iOS5
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
{
    NSLog(@"STATUS - Update from accelerometer");
    [_notActiveTimer invalidate];
    _notActiveTimer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(deviceDidLock) userInfo:nil repeats:NO];
}

- (void)deviceDidLock
{
    NSLog(@"STATUS - Device locked!");
    [[UIAccelerometer sharedAccelerometer] setDelegate:nil];
    _notActiveTimer = nil;
}

- (void)applicationDidBecomeActive:(UIApplication *)application
{
    NSLog(@"STATUS - Application did become active");
    [[UIAccelerometer sharedAccelerometer] setDelegate:nil];
    [_notActiveTimer invalidate];
    _notActiveTimer = nil;
}

我知道...这是一种 hack,但到目前为止它对我来说就像是一种魅力。如果您发现任何阻止此操作的问题,请更新。

【讨论】:

  • - (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration 已弃用。
  • @BadPirate 我尝试了这段代码,即使设备被锁定也能获取加速度计数据。
  • 呃哦@Geek 我猜这与新的 iPhone 5s 和它的低能量运动芯片有关(锁定后可能会继续工作)......还有其他人验证吗?
  • @BadPirate 我在 iPhone 4 上测试过。
【解决方案3】:

有一种更漂亮的方法可以区分任务切换和源自屏幕锁定的applicationWillResignActive: 回调,它甚至不涉及加速度计状态等未记录的功能。

当应用程序移至后台时,首先向应用程序委托发送一个applicationWillResignActive:,然后是一个applicationDidEnterBackground:。当应用程序因按下锁定按钮或来电而中断时,不会调用后一种方法。我们可以使用这些信息来区分这两种情况。

假设如果屏幕被锁定,您想在screenLockActivated 方法中被回调。这就是魔法:

- (void)applicationWillResignActive:(UIApplication*)aApplication
{
    [self performSelector:@selector(screenLockActivated)
               withObject:nil
               afterDelay:0];
}

- (void)applicationDidEnterBackground:(UIApplication*)aApplication
{
    [NSObject cancelPreviousPerformRequestsWithTarget:self];
}

- (void)screenLockActivated
{
    NSLog(@"yaay");
}

解释:

默认情况下,我们假设对applicationWillResignActive: 的每次调用都是由于活动->非活动状态转换(如锁定屏幕时),但我们慷慨地让系统在超时内证明相反的情况(在这种情况下,单个运行循环周期)通过延迟调用screenLockActivated。如果屏幕被锁定,系统会在不触及任何其他委托方法的情况下完成当前的 runloop 循环。然而,如果这是一个 active->background 状态转换,它还会在循环结束之前调用applicationDidEnterBackground:,这使我们可以简单地从那里取消先前安排的请求,从而防止它在不应该被调用时被调用到。

享受吧!

【讨论】:

  • 不幸的是,有时您需要区分情况,手机被锁定以及应用程序通过通知中心选项卡或任务切换选项卡进入非活动状态(
  • 我现在无法在 iOS 5.1 中重现这一点,applicationDidEnterBackground 总是通过锁定屏幕来调用
【解决方案4】:

您可以使用Darwin notifications 来监听事件。根据我在越狱的 iOS 5.0.1 iPhone 4 上的测试,我认为这些事件之一可能是您需要的:

com.apple.springboard.lockstate
com.apple.springboard.lockcomplete

注意:根据发帖者的comments to a similar question I answered here,这应该也适用于未越狱的手机。

要使用它,请像这样注册事件(这仅注册上面的第一个事件,但您也可以为lockcomplete 添加观察者):

CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), //center
                                (void*)self, // observer (can be NULL)
                                lockStateChanged, // callback
                                CFSTR("com.apple.springboard.lockstate"), // event name
                                NULL, // object
                                CFNotificationSuspensionBehaviorDeliverImmediately);

lockStateChanged 是您的事件回调:

static void lockStateChanged(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo) {
    NSLog(@"event received!");
    if (observer != NULL) {
        MyClass *this = (MyClass*)observer;
    }

    // you might try inspecting the `userInfo` dictionary, to see 
    //  if it contains any useful info
    if (userInfo != nil) {
        CFShow(userInfo);
    }
}

lockstate 事件在设备被锁定解锁时发生,但lockcomplete 事件仅在设备锁定时触发。确定事件是针对锁定事件还是解锁事件的另一种方法是使用notify_get_state()。您将获得不同的锁定值和解锁值,as described here

【讨论】:

  • 我在 iOS 8 和 iPhone 6 上使用了这个,现在仍然可以使用。请记住,它是 C 而不是 Objective-C,它的函数也是静态的,因此如果您想更改视图控制器中的某些内容,您可以将其作为观察者而不是 NULL 传递。也要小心引用计数。
  • @thibautnoah,为什么?没有其他方法可以直接获取此信息。下面的加速度计答案涉及大量电池消耗。
  • @thibautnoah 当您的代码发送通知时,您可以根据需要选择使用NSDistributedNotificationCenter。在这种情况下,我们正在侦听我们的代码没有生成的通知。我们无法控制 iOS 使用哪个通知中心。事实证明,iOS 使用 Darwin 通知系统在锁定状态更改时发布事件。我们无法控制它。我的回答只是显示了您如何收听此事件。这有意义吗?
  • @RahulPatel,我不为 Apple 工作,所以我不能肯定。 CFNotificationCenterAddObserver() API 是公开的,所以应该没问题。 com.apple.springboard.lockstate 字符串是undocumented,所以它可能是个问题。但是,我猜它会被批准。如果您担心,您可以随时混淆该字符串。
  • @Moxarth,评论部分不是长时间讨论许多单独问题的地方。如果您无法使其正常工作,您应该在此处发布一个新问题,显示您尝试使用的代码。您也可以链接到此问题和答案,但这些信息不足以让我为您提供帮助。
【解决方案5】:

如果设置了密码,您可以在 AppDelegate 中使用这些事件

-(void)applicationProtectedDataWillBecomeUnavailable:(UIApplication *)application
{
}

- (void)applicationProtectedDataDidBecomeAvailable:(UIApplication *)application
{
}

【讨论】:

  • 在所有帖子中,我认为这是锁定状态的最佳答案。
【解决方案6】:

在使用此代码之前只需导入#import notify.h。享受吧!

-(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】:

获取屏幕锁定和解锁事件的最简单方法是在视图控制器中使用 NSNotificationCenter 添加事件观察器。我在 viewdidload 方法中添加了以下观察者。这就是我所做的:

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(applicationEnteredForeground:)
                                             name:UIApplicationWillEnterForegroundNotification
                                           object:nil];

然后我将以下选择器添加到视图控制器。屏幕解锁时将调用此选择器。

 - (void)applicationEnteredForeground:(NSNotification *)notification {
    NSLog(@"Application Entered Foreground");
    }

如果您想在屏幕被锁定时检测事件,您可以将 UIApplicationWillEnterForegroundNotification 替换为 UIApplicationDidEnterBackgroundNotification

【讨论】:

  • 这是不正确的。这还将检测应用切换后进入前台的应用。
【解决方案8】:

在 iOS 8 中,您锁定屏幕或按下主页按钮,所有这些都会使应用程序在后台推送,但您不知道哪个操作员会导致这种情况。我的解决方案与 Nits007ak 相同,使用 notify_register_dispatch 获取状态。

#import <notify.h>
        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");
                                 }
                             }
                             );

只要应用程序在前台或后台运行。不暂停,你可以得到这个事件。

你可以使用 notify_token 作为 notify_get_state 的参数来获取任何地方的当前状态,这在你想知道状态并且屏幕状态不改变时很有用。

【讨论】:

  • 有什么方法可以快速做到这一点?
  • @VanditMehta 但它在以正确状态移动到前台后被调用
【解决方案9】:

通过大量试验和错误,发现监控黑屏、锁定完成和锁定状态事件提供了一致的锁定屏幕指示器。您需要监控状态转换。

// call back
void displayStatusChanged(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo)
{
    // notification comes in order of
    // "com.apple.springboard.hasBlankedScreen" notification
    // "com.apple.springboard.lockcomplete" notification only if locked
    // "com.apple.springboard.lockstate" notification

    AppDelegate *appDelegate = CFBridgingRelease(observer);

    NSString *eventName = (__bridge NSString*)name;
    NSLog(@"Darwin notification NAME = %@",name);

    if([eventName isEqualToString:@"com.apple.springboard.hasBlankedScreen"])
    {
        NSLog(@"SCREEN BLANK");

        appDelegate.bDeviceLocked = false; // clear
    }
    else if([eventName isEqualToString:@"com.apple.springboard.lockcomplete"])
    {
        NSLog(@"DEVICE LOCK");

        appDelegate.bDeviceLocked = true; // set
    }
    else if([eventName isEqualToString:@"com.apple.springboard.lockstate"])
    {
        NSLog(@"LOCK STATUS CHANGE");

        if(appDelegate.bDeviceLocked) // if a lock, is set
        {
            NSLog(@"DEVICE IS LOCKED");
        }
        else
        {
            NSLog(@"DEVICE IS UNLOCKED");
        }
    }
}

-(void)registerforDeviceLockNotif
{
    // screen and lock notifications
    CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), //center
                                    CFBridgingRetain(self), // observer
                                    displayStatusChanged, // callback
                                    CFSTR("com.apple.springboard.hasBlankedScreen"), // event name
                                    NULL, // object
                                    CFNotificationSuspensionBehaviorDeliverImmediately);

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

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

要让屏幕锁定指示器在后台运行,您需要在应用启动时调用以下代码来实现后台处理。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.backgroundTaskIdentifier =
    [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{

        [[UIApplication sharedApplication] endBackgroundTask:self.backgroundTaskIdentifier];
    }];

    [self registerforDeviceLockNotif];
}

【讨论】:

  • 我们也可以有 swift 版本
【解决方案10】:

在撰写本文时,有两种相当可靠的方法来检测设备锁定:


数据保护

通过启用Data Protection entitlement,您的应用可以订阅applicationProtectedDataWillBecomeUnavailable:applicationProtectedDataDidBecomeAvailable: 通知,以高概率确定使用密码/TouchID 身份验证的设备何时被锁定/解锁。要确定设备是否使用密码/TouchID,可以查询LAContext

注意事项:此方法依赖于“受保护的数据变得不可用”与手机被锁定同时发生。当手机使用 TouchID 并按下睡眠/锁定按钮时,手机将被锁定,受保护的数据将不可用,并且将立即需要密码才能再次解锁。这意味着受保护的数据变得不可用基本上表明手机已被锁定。 当有人只使用密码时,这不一定是正确的,因为他们可以将“需要密码”时间设置为从立即4小时之类的任何时间时间>。在这种情况下,手机将报告能够处理受保护的数据,但锁定手机不会导致受保护的数据在很长一段时间内不可用。


生命周期时序

如果您的应用处于前台,则两个生命周期事件 UIApplicationWillResignActiveNotificationUIApplicationDidEnterBackgroundNotification 之间的时间差将发生明显变化,具体取决于触发它们的原因。

(已在 iOS 10 中测试,可能会在未来版本中更改)

按下主页按钮会导致两者之间出现明显延迟(即使启用了减少运动设置):

15:23:42.517 willResignActive
15:23:43.182 didEnterBackground
15:23:43.184 difference: 0.666346

在应用打开时锁定设备会在两个事件之间产生更微不足道的 (

15:22:59.236 willResignActive
15:22:59.267 didEnterBackground
15:22:59.267 difference: 0.031404

【讨论】:

  • 谢谢@Warpling。我一直在收听UIApplicationProtectedDataWillBecomeUnavailable 并发现它通常不会在屏幕锁定时触发——尽管它偶尔会触发。但是,每次解锁设备时,我都会收到UIApplicationProtectedDataDidBecomeAvailable 的通知。任何想法为什么会这样?
  • @VikasYendluri 如果设备设置为立即需要密码(如果使用任何生物特征认证方法应该是这样),并且如果您的应用在阻止它在后台运行时被挂起的背景。
猜你喜欢
  • 2013-10-11
  • 1970-01-01
  • 1970-01-01
  • 2015-01-03
  • 2021-06-29
  • 2012-07-08
  • 2020-01-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多