I know that it is possible to detect a touch on iOS using
UITouch *touch = [[event allTouches] anyObject];
However, is it possible to find out when a user does not touch?
EDIT
I want a method to be executed when the user does not touch the screen for 5 seconds. Is this possible?
I do not have any custom methods that react to touches. I only have the existing methods
-touchesBegan
-touchesMoved and
-touchesEnded
To be more specific, The user can keep touching the screen as many times as he wants, for how long ever he wants. But, when the user does not touch the screen for more than 5 seconds, then a method -sampleMethod needs to be fired.
You can start a timer with a 5 second interval and every time you get a touch, restart the timer:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self.timer invalidate];
self.timer = [NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:#selector(yourMethod) userInfo:nil repeats:NO];
}
- (void)yourMethod {
NSLog(#"not touched for 5 seconds");
}
Depending on your specific needs, you might want to use touchesEnded:withEvent instead.
I'm going to take a whack at an answer here. Because in the comments you clarified what you're trying to do. Something after 5 seconds with no response. What I'm showing here is typically used in opengl apps which all my apps are. But something like it should work for you even if your not in open gl.
You need something that runs continuously...
- (void) startAnimation
{
if (!animating)
{
displayLink = [NSClassFromString(#"CADisplayLink") displayLinkWithTarget:self selector:#selector(drawView)];
[displayLink setFrameInterval:animationFrameInterval];
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
animating = TRUE;
}
}
- (void)stopAnimation
{
if (animating)
{
[displayLink invalidate];
displayLink = nil;
animating = FALSE;
}
}
We use this in oepngl apps to run the drawview function every 60th of a second timed with the refresh of the display. I don't see why you couldn't do it. Then in you drawView method check the time at the beginning and take care of any other crap you need to, like advancing pieces in a game or just checking to see how long messages have been up..
- (void)drawView
{
timeThisRound = CFAbsoluteTimeGetCurrent();
And check it against whatever event triggers the start of the 5 seconds. If you're past 5 seconds then do whatever you're going to do instead of waiting any longer for them to tap the button.
I have my own messaging system that does this. I can set for any message that comes up if it should go away on it's own after 5 seconds. Or if they tap it, it goes away faster. I use the timeThisRound method (a global property) everywhere to track when NOW is so that I can have timing based things as well as touch based things.
Sure, the rest of the time. What do you mean? The only way would be to set a boolean flag to false, and in your "touched" method, set it to true. Then anytime it's false, there's been no touch...
Launch a method after a certain delay, starting when user stops touching the view.
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[self performSelector:#selector(sampleMethod) withObject:nil afterDelay:5.0f];
}
If user touches the view again, you should cancel the pending method call
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(sampleMethod) object:nil];
}
Remember to put a cancel of the pending method call in dealloc too
- (void)dealloc {
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(sampleMethod) object:nil];
}
Related
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
self.theTimer = [NSTimer scheduledTimerWithTimeInterval:0.2 target:self selector:#selector(timerFire:) userInfo:nil repeats:NO];
//if(timerFire not called) {other code}
}
How can I detect if timerFire: not called. If timerFire: is not called then I want to perform some other activity.
It's not quite clear what you're asking here.
In any normal situation the timer is going to fire and thus timerFire: will be called.
This will not happen if:
you did not actually implement timerFire: in the class which you set as a target (so, in this case it's self). you can check this with respondsToSelector:?
the timer is invalidated before the interval of 0.2 sec is over, in that case you can use a global variable in your class (something like BOOL timerDidFire) and set it to YES in timerFire:
try this
if ([self respondsToSelector:#selector(timerFire:)])
{
// Your Code
}
else
{
}
I need to perform a particular action in a view controller when user don't touch a particular screen for 3 seconds.
How can I do that in iOS?
Override the view's touches began method and reset a timer when you get touches.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self.timer invalidate];
self.timer = [NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:#selector(inactivityTimerFired) userInfo:nil repeats:NO];
[super touchesBegan:touches withEvent:event];
}
You could target the view controller instead of self if you need to.
Add an NSTimer property/ivar to your view controller
Add a tap gesture recognizer to the view you want to monitor (or use touchesBegan:)
When the tap recognizer is hit, start the timer with an interval of 3.0s to call a function
If tap recognizer is hit before the function the timer calls, invalidate the timer and set it to nil
If the function that the timer was calling is hit, the user has been inactive for 3 seconds
I'm trying to make a sort of free runner game. I make the character jump by setting an integer to 30 or so every time the screen is tapped, and moving the character up the screen using CGPointMake(). Then I have a timer start when the player taps the screen which pulls the character down until he's reached a certain point (y=260). Then the timer invalidates and the character's downward movement stops.
This goes on and on and works perfectly well until you tap on the screen before the character reaches y=260. This makes him jerk up and come speeding down very fast and he doesn't stop at y=260 like he should. Here's the code that I think is causing the problem:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
playerMoving = 33;
gravity = [NSTimer scheduledTimerWithTimeInterval:.2
target:self
selector:#selector(playerJump)
userInfo:nil
repeats:YES];
}
Every time I double tap on the screen the timer gets activated again, even though it is already activated, and this is what I think is causing the problem.
Is there any way to not let the player tap on the screen a second time until the character has reached y=260 (so basically the player won't be able to double jump)?
Don't create the timer if it already exists. Nil out the variable when you're done with the timer, and check it before you create it again.
- (void)playerJump:(NSTimer *)tim
{
// Do stuff...
if( /* Done repeating */ ){
[gravity invalidate];
gravity = nil;
}
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
playerMoving = 33;
if( !gravity ){
gravity = [NSTimer scheduledTimerWithTimeInterval:.2
target:self
selector:#selector(playerJump:)
userInfo:nil
repeats:YES];
}
}
I'd like to create a custom UIWindow class that will delay all events by 200 ms.
Is it possible? If yes, how?
OK, I created a custom UIApplication class and everything worked fine:
- (void)sendEvent:(UIEvent *)event
{
[super sendEvent:event]; NSLog(#"Viola!");
}
But once I added this extra code to make it delay the events it didn't send any events.
- (void)sendEvent:(UIEvent *)event
{
[self performSelector:#selector(handleEventsAfterDelay:) withObject:event afterDelay:0.500];
}
- (void)handleEventsAfterDelay:(UIEvent *)event
{
[super sendEvent:event]; NSLog(#"Viola!");
}
Also, this doesn't work:
-(void)sendEvent:(UIEvent *)event
{
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:.2]];
[super sendEvent:event];
}
Everything that delays the events doesn't fire at the end. I'm standing frustrated staring in a blank black screen as the app won't even start.
EDIT: I found a way around this problem. Yet, to this day, there's no officially plausible and safe way of doing this but I guess it's just not significant at all.
First, I don't think it's a good idea to delay device event. I would try to work around it some how. Also, I'm not sure if Apple will aprove delayed events.
Another reason for me not to try to delay events, is because I'm no guru. I have no idea how the app will start to behave.
That said, as SomeGuy suggested, you could subclass UIApplication and override all event handling methods relevant to your app. Take a look at the UIApplication class's reference.
I've never delayed native events, but I did work with some event manipulation bafore. I remember that back then, overriding sendEvent: was enough for what I was doing.
You could override sendEvent: as follows:
- (void)sendEvent:(UIEvent *)event
{
[NSThread sleepForTimeInterval:.2];
[super sendEvent:event];
}
This will delay all event 200 ms. This will however block your main thread.
I've tried to use a call to dispatch_after(3) but it didn't work.
If you only want to delay certain event types, use the UIEvent's type and subtype properties. Take a look at the UIEvent class's reference.
Again, I never had to delay, nor do I know what you're exactly trying to accomplish, but I would suggest to find an alternative to delaying the device's event.
Also, don't forget you have to tell Cocoa Touch to use your custom UIApplication. Go to the main.m file and add a principal class name to the UIApplicationMain call:
int main(int argc, char *argv[])
{
#autoreleasepool {
return UIApplicationMain(argc, argv, NSStringFromClass([YourApplication class]), NSStringFromClass([YourAppDelegate class]));
}
}
As a reference, this was my overridden method:
- (void)sendEvent:(UIEvent *)event
{
[super sendEvent:event];
// Was it a touch?
if (event.type == UIEventTypeTouches) {
// Get touch phase.
NSSet *allTouches = [event allTouches];
UITouchPhase phase = [((UITouch *)[allTouches anyObject]) phase];
// Check what to do.
switch (phase) {
case UITouchPhaseBegan:
// Reset counter.
[self.counter resetCount];
break;
case UITouchPhaseEnded:
case UITouchPhaseCancelled:
// Start counter.
[self.counter startCount];
break;
default:
break;
}
}
}
This was used to auto-sign out users from the app after some inactive time (it was a kiosk app).
Unfortunately, it's the best I can do. Maybe someone with more experience can give their input.
I hope this help.
I suppose the problem you have is caused by the UIEvent timestamp. I suppose that events will expire if it is not delivered within a short interval.
You could try adding setTimestamp: by a category and modify it accordingly but the change has to be very exact.
I would like to implement the time out functionality like windows session expiration.
My case is If i didn't touch the screes in 30 minutes I need to redirect the Login screen, other wise normal will need to work.
[[NSNotificationCenter defaultCenter]addObserver:self
selector:#selector(applicationDidTimeout:)name:10 object:nil];
then implement the method,
-(void)applicationDidTimeout:(NSNotification*)notif
{
NSLog(#"10 mints looping");
//calling login screen.
}
but if I'm working in the views also the time expires.How to avoid this.Please any one suggest me to achieve this.
Sorry for the poor english and text formation.
Try This.
Implement a subclass of UIApplication to monitor all the touches in
application.
And implement the following code also in that class
- (void)sendEvent:(UIEvent *)event {
if (event) {
[super sendEvent:event];
NSSet *allTouches = [event allTouches];
if ([allTouches count] > 0) {
UITouchPhase phase = ((UITouch *)[allTouches anyObject]).phase;
if (phase == UITouchPhaseBegan){
[self resetIdleTimer];
}
}
}
}
More Details:
- (void)sendEvent:(UIEvent *)event
Parameters
event: A UIEvent object encapsulating the information about an event, including the touches involved.
Discussion
Subclasses may override this method to intercept
incoming events. Any intercepted events should be dispatched by
calling [super sendEvent:event] after examining the intercepted event.
- (void)resetIdleTimer is the method in which you can schedule your timer.
go on .. :)