5

What code can be used to detect when a user is interacting with an app and when the user is not interacting with an app?

Currently I have a ViewController with a UIView that is receiving touches by overriding touches, also by receiving panning gestures and tap gestures. A solution to this problem needs to be separate to those current gestures or sit above those gestures.

Is there a gesture recognizer that sits above everything else that can tell my app when it received gestures and when there is no gesture received?

When the app is active, is there a way to monitor if the app is receiving touches and when it isn't and call a function as required, for example:

func appActive(){
    print("App received input from a touch, tap, swipe, long press etc.")
}

func appInactive(){
    print("App stopped receiving any input.")
}

Thank you.

user4806509
  • 2,925
  • 5
  • 37
  • 72
  • you can't get any touch event when the is not is the active state , for active state you may get touch by putting the gesture in window etc... – HardikDG Mar 23 '16 at 16:51
  • @Sulthan The question is in OC not Swift. It's not clear how that answer works as there is no main.m file in Swift. The answer here is in Swift as the question has been asked. – user4806509 Mar 29 '16 at 16:52
  • @Sulthan I never asked to "give me code". S/O S/E are Q&A. Got an answer? Great! If not, don't destroy other good answers. The OC question isn't enough if you've only learnt Swift. If you can't help with Swift then don't close the question, especially when the language is entirely different! In the event no one was able to help, I sought out two answers, reaching across other web tutorials, referencing along the way to arrive at combining them into a neat cohesive answer instead of one that is entirely cryptic so others could learn step by step and openly use the code as they chose. – user4806509 Mar 29 '16 at 17:44
  • @Sulthan It's not a duplicate of another Swift answer. I've taken the best bits of a few of answers, reference and linked to them, to derive a better answer that fits the problem that this question asked. – user4806509 Mar 29 '16 at 18:09
  • @user4806509 The *question* is a duplicate. If you have a better answer for that question, please, post it there. You can also improve (edit or comment) the already existing answer there. You don't have to create a duplicate question only to post a new answer. – Sulthan Mar 29 '16 at 18:25

2 Answers2

6

Swift 2. Xcode 7.2. Tested on iOS 7 - 9.

Adapted from: How to detect all touches in Swift 2 and Subclass UIApplication with Swift


1 - Locate your project's Swift file AppDelegate.swift, and comment out @UIApplicationMain:

//@UIApplicationMain

2 - Add a new Swift file to your project named exactly main.swift, and add the code:

import UIKit

UIApplicationMain(  
CommandLine.argc, UnsafeMutableRawPointer(CommandLine.unsafeArgv)  
    .bindMemory( to: UnsafeMutablePointer<Int8>.self,  
        capacity: Int(CommandLine.argc)), nil, NSStringFromClass(AppDelegate.self))  

3 - Add a new Swift file to your project named exactly UIApplication.swift, and add the code:

import UIKit

@objc(MyApplication)

class MyApplication: UIApplication {

    override func sendEvent(event: UIEvent) {

        // Ignore .Motion and .RemoteControl event simply everything else then .Touches
        if event.type != .Touches {
            super.sendEvent(event)
            return
        }
    
        // .Touches only
        var restartTimer = true
        if let touches = event.allTouches() {
            // At least one touch in progress? Do not restart timer, just invalidate it
            for touch in touches.enumerate() {
                if touch.element.phase != .Cancelled && touch.element.phase != .Ended {
                    restartTimer = false
                    break
                }
            }
        }
    
        if restartTimer {
            // Touches ended || cancelled, restart timer
            print("Touches ended. Restart timer")
        } else {
            // Touches in progress - !ended, !cancelled, just invalidate it
            print("Touches in progress. Invalidate timer")
        }
    
        super.sendEvent(event)
    }
}

4 - Locate your project's Info.plist file, and add a new key (Xcode menu: Editor > Add Item), select or type key Principal class, with string value MyApplication.

enter image description here


5 - Run your project!


Community
  • 1
  • 1
user4806509
  • 2,925
  • 5
  • 37
  • 72
  • I am getting an error that Use of unresolved identifier 'Process' – Bharathi Nov 01 '16 at 07:31
  • @Bharathi did u find a way? – Harris Jun 21 '17 at 11:12
  • use this line of code instead.UIApplicationMain(CommandLine.argc, UnsafeMutableRawPointer(CommandLine.unsafeArgv) .bindMemory( to: UnsafeMutablePointer.self, capacity: Int(CommandLine.argc)), nil, NSStringFromClass(AppDelegate)) – Liang Lan Jul 03 '17 at 04:15
5

A way to intercept any touches of your application is to create a custom UIWindow which will catch the touches without canceling them.

class CustomWindow: UIWindow {

    override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool {
        // Do any action you would like to perform to indicate the application is active
        return false
    }

}

You have to add this window in your Application Delegate, and set its level above the main window.

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?
    var topWindow: CustomWindow?


    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool
    {
        topWindow = CustomWindow(frame: UIScreen.mainScreen().bounds)
        topWindow?.rootViewController = UIViewController()
        topWindow?.windowLevel = UIWindowLevelNormal + 1
        topWindow?.hidden = false
        return true
    }
Tanguy G.
  • 2,143
  • 19
  • 18
  • Thanks. This seems to tell me when a user starts interacting, but it doesn't seem to tell me when a user stops interacting. For example, if a user touches the screen continuously for 5 seconds ie touches moved or panning or holding down a button, I seem to get the initial interaction, but it doesn't tell me that the user is still interacting or when the user has finished interacting for example touching up. How can the code be modified to alert when interaction has stopped? Also, is it possible to find our what type of touch event was triggered (ie long press, touches moved etc). – user4806509 Mar 24 '16 at 18:30
  • @tanguy-g Nice solution, I am almost ashamed I had to google this... – Tomas Sykora Feb 09 '19 at 23:23