locking解锁事件iphone
如何检测iPhone上的locking/解锁事件? 假设它只能用于越狱的设备,你能指出我正确的API吗?
通过locking事件 ,我的意思是显示或隐藏locking屏幕(可能需要密码来解锁,或不)。
您可以使用达尔文通知来监听事件。 从我在越狱的iOS 5.0.1 iPhone 4上的testing中,我认为这些事件之一可能是你需要的:
com.apple.springboard.lockstate com.apple.springboard.lockcomplete
注意:根据海报对我在这里回答的类似问题的评论 ,这也适用于非越狱手机。
为了使用这个,注册这样的事件(这个注册只是上面的第一个事件,但是你也可以为lockcomplete
添加一个观察者):
CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), //center (void*)self, // observer (can be NULL) lockStateChanged, // callback CFSTR("com.apple.springboard.lockstate"), // event name NULL, // object CFNotificationSuspensionBehaviorDeliverImmediately);
其中lockStateChanged
是您的事件callback:
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
事件发生在设备被locking和解锁时,但lockcomplete
事件只在设备locking时触发。 确定事件是否用于locking或解锁事件的另一种方法是使用notify_get_state()
。 如此处所述,您将获得locking与解锁的不同值。
回答一下:
应用程序将会在所有情况下都会被主动调用…即使您的应用程序在后台保持清醒时仍然保持清醒状态,也无法确定屏幕是否被locking(CPU速度不报告,总线速度保持不变,mach_time denom / numer不会改变)…
但是,似乎苹果确实closures加速度计,当设备被locking… 启用iPhone加速度计,同时屏幕locking (在iPhone 4上testing的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; }
我知道…这是一种黑客,但迄今为止,它对我来说就像一个魅力。 如果您发现任何阻止此操作的问题,请更新。
有一种更好的方法来区分任务切换和屏幕locking源applicationWillResignActive:
.WinResignActive applicationWillResignActive:
callback,甚至不涉及诸如加速度计状态之类的未公开的function。
当应用程序移动到后台时,应用程序委托首先发送一个applicationWillResignActive:
,然后是一个applicationDidEnterBackground:
当按下“locking”button或来电时应用程序被中断,后一种方法不会被调用。 我们可以使用这些信息来区分这两种情况。
假如你想在screenLockActivated
方法中被调用,如果屏幕被locking。 这是魔法:
- (void)applicationWillResignActive:(UIApplication*)aApplication { [self performSelector:@selector(screenLockActivated) withObject:nil afterDelay:0]; } - (void)applicationDidEnterBackground:(UIApplication*)aApplication { [NSObject cancelPreviousPerformRequestsWithTarget:self]; } - (void)screenLockActivated { NSLog(@"yaay"); }
说明:
默认情况下,我们假定每次调用applicationWillResignActive:
都是因为一个活动 – >非活动状态转换(如locking屏幕时),但是我们慷慨地让系统在超时(在这种情况下,一个runloop周期)通过延迟调用screenLockActivated
。 在屏幕被locking的情况下,系统完成当前的循环周期而不触及任何其他委托方法。 但是,如果这是一个活动 – >后台状态转换,它还会在循环结束之前调用applicationDidEnterBackground:
它允许我们简单地从那里取消先前调度的请求,从而防止它在不应该被调用的时候被调用。
请享用!
只需在使用此代码之前导入#import notify.h即可。 请享用!!
-(void)registerAppforDetectLockState { int notify_token; notify_register_dispatch("com.apple.springboard.lockstate", ¬ify_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]; }); }
在iOS 8中,您locking屏幕或按下主页button,所有这些使得应用程序在后台推送,但是不知道哪个操作符会导致这种情况。 我的解决scheme与Nits007ak一样,使用notify_register_dispatch获取状态。
#import <notify.h> int notify_token notify_register_dispatch("com.apple.springboard.lockstate", ¬ify_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的参数来获取当前状态,当您想知道状态和屏幕状态不会改变时,这是非常有用的。
从大量的反复试验中,发现监控黑屏,locking完成和locking状态事件给出了一致的锁屏指示。 你需要监视一个状态转换。
// 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); }
为了使屏幕locking指示符在后台运行,您需要在应用程序启动时实施后台处理以调用以下内容。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.backgroundTaskIdentifier = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ [[UIApplication sharedApplication] endBackgroundTask:self.backgroundTaskIdentifier]; }]; [self registerforDeviceLockNotif]; }
截至本文撰写时,有两种相当可靠的方法来检测设备locking:
数据保护
通过启用数据保护权利,您的应用程序可以订阅applicationProtectedDataWillBecomeUnavailable:
和applicationProtectedDataDidBecomeAvailable:
通知,以便以高概率确定何时使用密码/ TouchID身份validation的设备被locking/解锁。 要确定设备是否使用密码,可以查询TouchID LAContext
。
生命周期时间
如果您的应用程序处于前台,则根据触发它们的原因,两个生命周期事件UIApplicationWillResignActiveNotification
和UIApplicationDidEnterBackgroundNotification
之间的时间差异将会有明显变化。
(这是在iOS 10中testing,并可能在未来的版本中改变)
按下主页button会在两者之间产生明显的延迟(即使启用了简化运动设置):
15:23:42.517 willResignActive 15:23:43.182 didEnterBackground 15:23:43.184 difference: 0.666346
在应用程序打开的情况下locking设备会在两个事件之间产生一个更微不足道(<〜0.2s)的延迟:
15:22:59.236 willResignActive 15:22:59.267 didEnterBackground 15:22:59.267 difference: 0.031404
如果您的应用程序正在运行,并且用户locking了设备,则您的应用程序委托将收到对“应用程序将重新启用:”的调用。 如果您的应用程序在locking状态下运行,则会在设备解锁时收到对“应用程序无法成为活动:”的调用。 但是,如果用户接到电话,然后select忽略它,则可以获得与您的应用相同的呼叫。 据我所知,你无法分辨这个区别。
如果您的应用程序没有在这些时间运行,则没有办法通知您的应用程序没有运行。
如果设置了密码,您可以在AppDelegate中使用这些事件
-(void)applicationProtectedDataWillBecomeUnavailable:(UIApplication *)application { } - (void)applicationProtectedDataDidBecomeAvailable:(UIApplication *)application { }
获取屏幕locking和解锁事件的最简单方法是在视图控制器中使用NSNotificationCenter添加事件观察器。 我在viewdidload方法中添加了下面的观察者。 这就是我所做的:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationEnteredForeground:) name:UIApplicationWillEnterForegroundNotification object:nil];
然后我将下面的select器添加到视图控制器。 屏幕解锁时,此select器将被调用。
- (void)applicationEnteredForeground:(NSNotification *)notification { NSLog(@"Application Entered Foreground"); }
如果要在屏幕locking时检测事件,则可以使用UIApplicationDidEnterBackgroundNotificationreplaceUIApplicationWillEnterForegroundNotification 。