iPhone: Detecting user inactivity/idle time since last screen touch
Here's the answer I had been looking for:
Have your application delegate subclass UIApplication. In the implementation file, override the sendEvent: method like so:
- (void)sendEvent:(UIEvent *)event {
[super sendEvent:event];
// Only want to reset the timer on a Began touch or an Ended touch, to reduce the number of timer resets.
NSSet *allTouches = [event allTouches];
if ([allTouches count] > 0) {
// allTouches count only ever seems to be 1, so anyObject works here.
UITouchPhase phase = ((UITouch *)[allTouches anyObject]).phase;
if (phase == UITouchPhaseBegan || phase == UITouchPhaseEnded)
[self resetIdleTimer];
}
}
- (void)resetIdleTimer {
if (idleTimer) {
[idleTimer invalidate];
[idleTimer release];
}
idleTimer = [[NSTimer scheduledTimerWithTimeInterval:maxIdleTime target:self selector:@selector(idleTimerExceeded) userInfo:nil repeats:NO] retain];
}
- (void)idleTimerExceeded {
NSLog(@"idle time exceeded");
}
where maxIdleTime and idleTimer are instance variables.
In order for this to work, you also need to modify your main.m to tell UIApplicationMain to use your delegate class (in this example, AppDelegate) as the principal class:
int retVal = UIApplicationMain(argc, argv, @"AppDelegate", @"AppDelegate");
Detecting inactivity (no user interaction) in iOS for showing separate Screen like an Overlay
From @Prabhat Kesara's comment and @Vanessa Forney answer in the helper link. I come with the below solution and it is working fine. If someone comes with the requirement like this they can use this
import UIKit
import Foundation
extension NSNotification.Name {
public static let TimeOutUserInteraction: NSNotification.Name = NSNotification.Name(rawValue: "TimeOutUserInteraction")
}
class UserInterractionSetup: UIApplication {
static let ApplicationDidTimoutNotification = "AppTimout"
// The timeout in seconds for when to fire the idle timer.
let timeoutInSeconds: TimeInterval = 1 * 60 //5 * 60 //
var idleTimer: Timer?
// Listen for any touch. If the screen receives a touch, the timer is reset.
override func sendEvent(_ event: UIEvent) {
super.sendEvent(event)
if idleTimer != nil {
self.resetIdleTimer()
}
if let touches = event.allTouches {
for touch in touches {
if touch.phase == UITouch.Phase.began {
self.resetIdleTimer()
}
}
}
}
// Resent the timer because there was user interaction.
func resetIdleTimer() {
if let idleTimer = idleTimer {
// print("1")
let root = UIApplication.shared.keyWindow?.rootViewController
root?.dismiss(animated: true, completion: nil)
idleTimer.invalidate()
}
idleTimer = Timer.scheduledTimer(timeInterval: timeoutInSeconds, target: self, selector: #selector(self.idleTimerExceeded), userInfo: nil, repeats: false)
}
// If the timer reaches the limit as defined in timeoutInSeconds, post this notification.
@objc func idleTimerExceeded() {
print("Time Out")
NotificationCenter.default.post(name:Notification.Name.TimeOutUserInteraction, object: nil)
let screenSaverVC = UIStoryboard(name:"ScreenSaver", bundle:nil).instantiateViewController(withIdentifier:"LandingPageViewController") as! LandingPageViewController
if let presentVc = TopMostViewController.sharedInstance.topMostViewController(controller: screenSaverVC){
let root: UINavigationController = UIApplication.shared.keyWindow!.rootViewController as! UINavigationController
if let topView = root.viewControllers.last?.presentedViewController {
topView.dismiss(animated: false) {
root.viewControllers.last?.navigationController?.present(presentVc, animated: true, completion: nil)
}
}else if let topViewNavigation = root.viewControllers.last {
topViewNavigation.navigationController?.present(presentVc, animated: true, completion: nil)
}
}
}
}
the difficulty I was faced was presenting overlay or screensaver above an already presented a view. That case, also we are handling in the above solution.
Happy coding :)
Detecting user inactivity in any VC
Here's how I do it in my app. Create the timer to check every so often:
self.timer = NSTimer.scheduledTimerWithTimeInterval(
10, target: self, selector: "decrementScore", userInfo: nil, repeats: true)
Now we know that if decrementScore() is called, 10 seconds of idle time went by. If the user does something, we need to restart the timer:
func resetTimer() {
self.timer?.invalidate()
self.timer = NSTimer.scheduledTimerWithTimeInterval(
10, target: self, selector: "decrementScore", userInfo: nil, repeats: true)
}
Detect Touch in Base Class
So, I figured out a way. In my base class, I was setting tap gesture on view earlier.
let tap = UITapGestureRecognizer(target: self, action: nil)
tap.delegate = self
tap.cancelsTouchesInView = false
view.addGestureRecognizer(tap)
so instead of view.addGestureRecognizer(tap)
now I am adding gesture on window
if let window = UIApplication.shared.keyWindow {
window.addGestureRecognizer(tap)
}
and I implemented UIGestureRecognizerDelegate
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
// My Code here
return true
}
So, Now my base class is able to detect any taps and calling the delegate
.
Please Note: Adding this delegate/ gesture
is not effecting the touch or gesture on any of the other views of my child classes.
Related Topics
How to Create a Button Programmatically
Can't Get Correct Value of Keyboard Height in Ios8
How to Clear the Entered Textfield When the User Taps on the Button
Detect Tap on a Button in Uitableviewcell for Uitableview Containing Multiple Sections
Passing Data Between View Controllers
Test iOS App on Device Without Apple Developer Program or Jailbreak
Opening the Settings App from Another App
How to Set Cornerradius For Only Top-Left and Top-Right Corner of a Uiview
Array Extension to Remove Object by Value
How to Change the Push and Pop Animations in a Navigation Based App
Periodic iOS Background Location Updates
How to Create Dispatch Queue in Swift 3
How to Record Audio on Iphone With Avaudiorecorder
How to Preserve Identifierforvendor in iOS After Uninstalling iOS App on Device
Image Resolution For New Iphone 6 and 6+, @3X Support Added
How to Play Video With Avplayerviewcontroller (Avkit) in Swift