Search code examples
iosiphoneswiftnavigationscreensaver

Detecting inactivity (no user interaction) in iOS for showing separate Screen like an Overlay


How to detect inactivity or no user interaction in iOS App. For showing separate Screen like an Overlay or something like a screensaver. With the following link link that used to present screen overlay when the app became inactive I'm able to show the screen saver in the app. But with normal dismiss view controller code not being able to dismiss the presented view controller.

 we are presenting the custom overlay using

// The callback for when the timeout was fired.
    func applicationDidTimout(notification: NSNotification) {

       let storyboard = UIStoryboard(name: "MyStoryboard", bundle: nil)
              let vc = 
 storyboard.instantiateViewControllerWithIdentifier("myStoryboardIdentifier")
        UIApplication.shared.keyWindow?.rootViewController = vc       
    }

The problem that we are facing in this is. Once the View is presented we are not able to dismiss or remove the view controller.


Solution

  • 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 :)