An iPhone will disable for 1 minute after six failed passcode attempts in a row. The seventh incorrect passcode attempt will lock you out for 5 minutes, the eighth attempt for 15, and the tenth for an hour.
You can set a time limit for a category of apps (for example, Games or Social Networking) and for individual apps. Go to Settings > Screen Time, then turn on screen time if you haven't already. Tap App Limits, then tap Add Limit. Select one or more app categories.
You can use Darwin notifications, to listen for the events. From my testing on a jailbroken iOS 5.0.1 iPhone 4, I think that one of these events might be what you need:
com.apple.springboard.lockstate
com.apple.springboard.lockcomplete
Note: according to the poster's comments to a similar question I answered here, this should work on a non-jailbroken phone, too.
To use this, register for the event like this (this registers for just the first event above, but you can add an observer for lockcomplete
, too):
CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), //center
(void*)self, // observer (can be NULL)
lockStateChanged, // callback
CFSTR("com.apple.springboard.lockstate"), // event name
NULL, // object
CFNotificationSuspensionBehaviorDeliverImmediately);
where lockStateChanged
is your event 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);
}
}
The lockstate
event occurs when the device is locked and unlocked, but the lockcomplete
event is only triggered when the device locks. Another way to determine whether the event is for a lock or unlock event is to use notify_get_state()
. You'll get a different value for lock vs. unlock, as described here.
Round about answer:
Application will resign active gets called in all sorts of scenarios... and from all my testing, even if your application stays awake while backgrounded, there are no ways to determine that the screen is locked (CPU speed doesn't report, BUS speed remains the same, mach_time denom / numer doesn't change)...
However, it seems Apple does turn off the accelerometer when the device is locked... Enable iPhone accelerometer while screen is locked (tested iOS4.2 on iPhone 4 has this behavior)
Thus...
In your application delegate:
- (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;
}
I know... It's kind of a hack, but it has worked like a charm for me so far. Please update if you see any issues that prevent this from working.
There is a prettier way of telling apart task switching and screen locking-originated applicationWillResignActive:
callbacks which doesn't even involve undocumented features such as the accelerometer state.
When the app is moving to the background, the app delegate is first sent an applicationWillResignActive:
, then an applicationDidEnterBackground:
. When the app is interrupted by pressing the Lock button or by an incoming phone call, the latter method is not called. We can use this information to distinguish between the two scenarios.
Say you want to be called back in the screenLockActivated
method if the screen gets locked. Here's the magic:
- (void)applicationWillResignActive:(UIApplication*)aApplication
{
[self performSelector:@selector(screenLockActivated)
withObject:nil
afterDelay:0];
}
- (void)applicationDidEnterBackground:(UIApplication*)aApplication
{
[NSObject cancelPreviousPerformRequestsWithTarget:self];
}
- (void)screenLockActivated
{
NSLog(@"yaay");
}
Explanation:
By default, we assume that every call to applicationWillResignActive:
is because of an active->inactive state transition (as when locking the screen) but we generously let the system prove the contrary within a timeout (in this case, a single runloop cycle) by delaying the call to screenLockActivated
. In case the screen gets locked, the system finishes the current runloop cycle without touching any other delegate methods. If, however, this is an active->background state transition, it also invokes applicationDidEnterBackground:
before the end of the cycle, which allows us to simply cancel the previously scheduled request from there, thus preventing it from being called when it's not supposed to.
Enjoy!
As of the time of writing there are two fairly reliable ways to detect device locking:
By enabling the Data Protection entitlement your app can subscribe to the applicationProtectedDataWillBecomeUnavailable:
and applicationProtectedDataDidBecomeAvailable:
notifications to determine with high probability when a device that uses passcode/TouchID Authentication is locked/unlocked. To determine if a device uses a passcode/TouchID LAContext
can be queried.
Caveats: This method relies on the "protected data becoming unavailable" coinciding with the phone being locked. When the phone is using TouchID and the sleep/lock button is pressed then the phone is locked, protected data becomes unavailable, and a passcode will immediately be required to unlock it again. This means that protected data becoming unavailable essentially indicates that the phone has been locked. This is not necessarily true when someone is using just a passcode since they can set the "requires passcode" time to anywhere from immediately to something like 4 hours. In this case the phone will report being able to handle protected data but locking the phone will not result in protected data becoming unavailable for quite some time.
If your app is in the foreground there will be a noticeable change in time difference between the two lifecycle events UIApplicationWillResignActiveNotification
and UIApplicationDidEnterBackgroundNotification
depending on what triggers them.
(This was tested in iOS 10 and may change in future releases)
Pressing the home button results in a significant delay between the two (even when the Reduced Motion setting is enabled):
15:23:42.517 willResignActive
15:23:43.182 didEnterBackground
15:23:43.184 difference: 0.666346
Locking the device while the app is open creates a more trivial (<~0.2s) delay between the two events:
15:22:59.236 willResignActive
15:22:59.267 didEnterBackground
15:22:59.267 difference: 0.031404
in iOS 8, you lock the screen or push the home button, all of those make app push in background, but you don't know which operator result in this. My solution same with Nits007ak,use notify_register_dispatch to get state.
#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");
}
}
);
As long as the app is running, in foreground or background. not suspend, you can get this event.
And you can use notify_token as parameter of notify_get_state to get current state anywhere, this is useful when you want know the state and the screen state don't change.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With