Monitoring all iOS touches

Guides | Tutorial By 5 years ago

There are probably a dozen ways to capture and monitor touches on the iOS. Some require a lot of work, including method swizzling, or using private/undocumented APIs which might result in your application being rejected from the AppStore.

When I was given the task of monitoring all touches in an application, I wanted to do it using 100% public APIs. As the goal of this project was to be compiled into a Framework, subclassing UIApplication was a bit useless, and method swizzling wasn’t something I wanted to do. I turned to UIGestureRecognizers.

You can subclass a UITapGestureRecognizer and intercept (without cancelling) all touches on a view the recognizer is attached to. There’s actually very little coding for this.

First make a subclass of UITapGestureRecognizer. Import the <UIKit/UIGestureRecognizerSubclass.h> header, which gives us extra methods to override in our subclass. You will notice this header defines the touchesBegan:, touchesMoved: etc etc methods you might recognise from the UIResponder class. Override all of those methods, for now I will just log when the event is called so we can see it working.

- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
	NSLog(@"touchesBegan: %@", touches.allObjects);

	[super touchesBegan:touches withEvent:event];
}

- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
	NSLog(@"touchesMoved: %@", touches.allObjects);

	[super touchesMoved:touches withEvent:event];
}

- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
	NSLog(@"touchesEnded: %@", touches.allObjects);

	[super touchesCancelled:touches withEvent:event];
}

- (void) touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
	NSLog(@"touchesCancelled: %@", touches.allObjects);

	[super touchesCancelled:touches withEvent:event];
}

In your app delegate’s application:didFinishLaunchingWithOptions: method, after your UIWindow is initialised add the gesture recognizer. Don’t forgot to make it not cancel touches otherwise they will never be seen by your window.

AllTouchesGestureRecognizer* recognizer = [[AllTouchesGestureRecognizer alloc] initWithTarget:nil action:nil];
[recognizer setCancelsTouchesInView:NO];
[self.window addGestureRecognizer:recognizer];
[recognizer release];

Run and play with the application, you will see touches being logged. You may notice that if you move your finger around only the first few touches are logged, but none after this point. For this you will need to override the ignoreTouch:forEvent: method, but don’t pass it up to super.

- (void) ignoreTouch:(UITouch *)touch forEvent:(UIEvent *)event
{
	//	Overriding this prevents touchesMoved:withEvent:
	//	not being called after moving a certain threshold
}

Run the application again and presto, all touches are being logged. The endpoint for this project can be downloaded here.

When a keyboard is shown in your application, it sits on a new window, you can look at the UIWindowDidBecomeVisibleNotification message to add one of these recognizers to any new windows that appear.

  • Charles Robertson

    This is an excellent article. Thanks…

    I cannot believe that objective c does not have a simple in built notification that can be used for detecting global touch events, but I guess Apple are making us work for our money;)

    I have used your subclass as a delegate and then created method calls to the caller inside each of your methods.
    I can then import this subclass into any other class, rather than just appDelegate.
    I only need to use this routine in one or two viewcontrollers.
    So, it is a bit of an overkill calling it in: – (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions;

    I also think you mean:

    – (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
    {
    NSLog(@”touchesEnded: %@”, touches.allObjects);

    [super touchesEnded:touches withEvent:event];
    }

    Your code reads at the moment:

    – (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
    {
    NSLog(@”touchesEnded: %@”, touches.allObjects);

    [super touchesCancelled:touches withEvent:event];
    }