Reputation: 3937
Is it possible to somehow listen to, and catch, all the touch events occurring in an app?
The app I'm currently developing will be used in showrooms and information kiosks and I would therefore like to revert to the start section of the app if no touches has been received for a given couple of minutes. A sort of screensaver functionality, if you will. I'm planning to implement this by having a timer running in the background, which should be reset and restarted every time a touch event occurs somewhere in the app. But how can I listen to the touch events? Any ideas or suggestions?
Upvotes: 24
Views: 16910
Reputation: 130172
You need a subclass of UIApplication
(let's call it MyApplication
).
You modify your main.m
to use it:
return UIApplicationMain(argc, argv, @"MyApplication", @"MyApplicationDelegate");
And you override the method [MyApplication sendEvent:]
:
- (void)sendEvent:(UIEvent*)event {
//handle the event (you will probably just reset a timer)
[super sendEvent:event];
}
Upvotes: 39
Reputation: 8802
In Swift 4.2 1. Create subclass of UIApplication object and print user action:
import UIKit
class ANUIApplication: UIApplication {
override func sendAction(_ action: Selector, to target: Any?, from sender: Any?, for event: UIEvent?) -> Bool {
print("FILE= \(NSStringFromSelector(action)) METHOD=\(String(describing: target!)) SENDER=\(String(describing: sender))")
return super.sendAction(action, to: target, from: sender, for: event)
}
}
import UIKit
UIApplicationMain( CommandLine.argc, CommandLine.unsafeArgv, NSStringFromClass(ANUIApplication.self), NSStringFromClass(AppDelegate.self))
ANUIApplication is class where we added action logs. AppDelegate is default app delegate where we wrote application delegate methods.(Helpful for tracking action and file name in big project)
Upvotes: 0
Reputation: 1095
Create a Class "VApplication" that extends from UIApplication and paste these code to corresponding class
VApplication.h
#import <Foundation/Foundation.h>
// # of minutes before application times out
#define kApplicationTimeoutInMinutes 10
// Notification that gets sent when the timeout occurs
#define kApplicationDidTimeoutNotification @"ApplicationDidTimeout"
/**
* This is a subclass of UIApplication with the sendEvent: method
* overridden in order to catch all touch events.
*/
@interface VApplication : UIApplication
{
NSTimer *_idleTimer;
}
/**
* Resets the idle timer to its initial state. This method gets called
* every time there is a touch on the screen. It should also be called
* when the user correctly enters their pin to access the application.
*/
- (void)resetIdleTimer;
@end
VApplication.m
#import "VApplication.h"
#import "AppDelegate.h"
@implementation VApplication
- (void)sendEvent:(UIEvent *)event
{
[super sendEvent:event];
// Fire up the timer upon first event
if(!_idleTimer) {
[self resetIdleTimer];
}
// Check to see if there was a touch event
NSSet *allTouches = [event allTouches];
if ([allTouches count] > 0)
{
UITouchPhase phase = ((UITouch *)[allTouches anyObject]).phase;
if (phase == UITouchPhaseBegan)
{
[self resetIdleTimer];
}
}
}
- (void)resetIdleTimer
{
if (_idleTimer)
{
[_idleTimer invalidate];
}
// Schedule a timer to fire in kApplicationTimeoutInMinutes * 60
// int timeout = [AppDelegate getInstance].m_iInactivityTime;
int timeout = 3;
_idleTimer = [NSTimer scheduledTimerWithTimeInterval:timeout
target:self
selector:@selector(idleTimerExceeded)
userInfo:nil
repeats:NO];
}
- (void)idleTimerExceeded
{
/* Post a notification so anyone who subscribes to it can be notified when
* the application times out */
[[NSNotificationCenter defaultCenter]
postNotificationName:kApplicationDidTimeoutNotification object:nil];
}
@end
Replace the Class name "VApplication" to our
Main.m
file like this
int main(int argc, char * argv[])
{
@autoreleasepool {
return UIApplicationMain(argc, argv, @"VApplication", NSStringFromClass([AppDelegate class]));
}
}
Register a notification for your corresponding view controller
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidTimeout:) name:kApplicationDidTimeoutNotification object:nil];
And once the timeout occur the notification will fire and handle the event like this
- (void) applicationDidTimeout:(NSNotification *) notif //inactivity lead to any progress
{
}
Upvotes: 3
Reputation: 12839
You can use a tap gesture recognizer for this. Subclass UITapGestureRecognizer
and import <UIKit/UIGestureRecognizerSubclass.h>
. This defines touchesBegan:
, touchesMoved:
, touchesEnded:
and touchesCancelled:
. Put your touch-handling code in the appropriate methods.
Instantiate the gesture recognizer in application:didFinishLaunchingWithOptions:
and add it to UIWindow
. Set cancelsTouchesInView
to NO
and it'll pass all touches through transparently.
Credit: this post.
Upvotes: 3
Reputation: 92384
A subclass of UIWindow
could be used to do this, by overriding hitTest:
. Then in the XIB of your main window, there is an object usually simply called Window
. Click that, then on the right in the Utilities pane go to the Identities (Alt-Command-3). In the Class text field, enter the name of your UIWindow
subclass.
MyWindow.h
@interface MyWindow : UIWindow
@end
MyWindow.m
@implementation MyWindow
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
UIView *res;
res = [super hitTest:point withEvent:event];
// Setup/reset your timer or whatever your want to do.
// This method will be called for every touch down,
// but not for subsequent events like swiping/dragging.
// Still, might be good enough if you want to measure
// in minutes.
return res;
}
@end
Upvotes: 7
Reputation:
You could put a transparent view at the top of the view hierarchy, and choose in that view whether to handle the touch events it receives or pass them through to lower views.
Upvotes: 1