How to detect NSTimer selector called or not - ios

- (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
{
}

Related

NSTimer's isValid always returns YES

So I've got a timer that is not repetitive. Each time it fires, the method that being executed decide if to reschedule it or not according to some inner logic of my app.
This method is available from other parts of the app, so the first thing that I'm doing in the method is to check if the timer is still valid (to know if the initiator was the timer or a different entity) so in case it wasn't initiated by the timer I want to invalidate it:
if (self.pollingTimer.isValid) {
[self.pollingTimer invalidate];
self.pollingTimer = nil;
}
I've noticed that if the method is being called due to the timer being fired - I always receive a true value from the isValid property, even though when looking at the NSTimer documentations under the scheduledTimerWithTimeInterval:(NSTimeInterval)seconds target:(id)target selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)repeats method:
repeats
If YES, the timer will repeatedly reschedule itself until invalidated. If NO, the timer will be invalidated after it fires.
Discussion
After seconds seconds have elapsed, the timer fires,
sending the message aSelector to target.
I'm having hard time to understand when the timer is being automatically invalidated which bring me to my questions:
Any idea why I always get YES from isValid?
What is the exact definition of the timer fires? Is it just sending the message aSelector to target as stated in the documentation? or is it finishing the execution of the method? (which might explain what I'm experiencing)
Thanks in advance.
A timer is not a real-time mechanism; it fires only when one of the run loop modes to which the timer has been added is running and able to check if the timer’s firing time has passed. Therefore, the timer does not immediately invalidate itself, but at the end of the run loop.
As a simple test, you can see:
- (void)viewDidLoad {
[super viewDidLoad];
_timer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:#selector(timerFired) userInfo:nil repeats:NO];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
if (self.timer.isValid){
NSLog(#"--> TIMER VALID");
} else {
NSLog(#"TIMER INVALID!");
}
});
}
- (void) timerFired {
if (self.timer.isValid){
NSLog(#"--> TIMER VALID");
} else {
NSLog(#"TIMER INVALID!");
}
}
This will log --> TIMER VALID from the timerFired method and when the block from dispatch_after is called, you will see TIMER INVALID!. So, when you schedule a timer with repeats:NO, it is guaranteed to not reschedule itself but it will not invalidate immediately.
So, to answer your question:
repeats
If YES, the timer will repeatedly reschedule itself until
invalidated. If NO, the timer will be invalidated after it fires (but not immediately)
I made a test like this:
self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(xx) userInfo:nil repeats:NO];
- (void)xx
{
if ([self.timer isValid]) {
NSLog(#"isvalid");
}
dispatch_async(dispatch_get_main_queue(), ^{
if ([self.timer isValid]) {
NSLog(#"isvalid");
}else{
NSLog(#"isInvalid");
}
});
static int i = 0;
NSLog(#"%d",i++);
}
and the result is:
isvalid
0
isInvalid
thus, I guess when timer is fired,the function is execute like this:
void __NSFireTimer(){
id yourObj;
[yourObj performSelector:#selector(yourSelector)];
timer.isvalid = NO;
}
what you believe is:
void __NSFireTimer(){
id yourObj;
timer.isvalid = NO;
[yourObj performSelector:#selector(yourSelector)];
}
So, just accept it.You can put your check valid code in dispatch_asyn() ,like the test code.
This is how I used my timers. First initialise it on the top as
#property (strong, nonatomic) NSTimer *refreshTimer;
then this two methods, to create and invalidate the timer. "Its very important to invalidate the current timer if you want to create another timer with same name" otherwise their will be two timers.
- (void)startTimer {
if (_refreshTimer) {
[self invalidateTimer];
}
_refreshTimer = [NSTimer scheduledTimerWithTimeInterval:15.0
target:self
selector:#selector(determineIfPartOfgroup)
userInfo:nil
repeats:YES];
}
- (void)invalidateTimer {
if (_refreshTimer) {
[_refreshTimer invalidate];
_refreshTimer = nil;
}
}
I hope this will help you.

How to detect user inactivity since last touch on a particular viewcontroller in iOS?

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

Stop a timer from being created again before it's done its work

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];
}
}

Appropriate way to call and cancel a selector in Objective-c?

So I have an app that when a user touches a certain object, I kick-off a selector via delay. I am not sure I want or need the delay, but am not sure of best practice, maybe a queue? Anyway, here is what I need, regardless of what I have now.
WHAT I HAVE NOW
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(doSomething) object:self];
[self performSelector:#selector(doSomething) withObject:nil afterDelay:2.0];
When the user touches a certain object I need to kick-off a method, but if he/she touches the object again, I want to not call the method.
Use case #1:
User touches object
User does nothing for 2 seconds
Call selector
Use case #2:
User touches object then
User touches object .5 seconds later (so cancel selector call)
User touches object .3 seconds later (so cancel selector call)
User touches object .9 seconds later (so cancel selector call)
User doesn't touch anything for 2 seconds
Call selector
If feel like performSelector and cancelPrevious are hacky. Should I be using some sort of queue and then clearing out the queue every time the user touches again?
Or should I use a timer and just restart the timer each time the user touches it?
I wrote something quick, hopefully it'll help. Every time start is hit the timer resets
#interface ViewController ()
{
NSTimer *timer;
NSInteger seconds;
}
#end
- (IBAction)start:(id)sender
{
seconds = 5;
[timer invalidate];
timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(execute) userInfo:nil repeats:YES];
}
- (void)execute
{
if(seconds > 0) {
NSLog(#"seconds: %li", (long)seconds);
seconds--;
}
else {
NSLog(#"fire");
[timer invalidate];
}
}

Find out if no touches were made?

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];
}

Resources