NSTimer stops after incrementing once - ios

I have an NSTimer which I want to update a label every second. My code is:
- (IBAction)OnClickEmergencyButton:(id)sender
{
emergencyAlertTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(emergencyTimer) userInfo:nil repeats:YES];
[emergencyAlertTimer fire];
}
- (void)emergencyTimer
{
int i = 0;
_emergencyAlertTriggerTimerLabel.text = [NSString stringWithFormat:#"%d", ++i];
}
When I ran it, the label displayed "1" initially and then stopped.
I want the label to continuously count up every second, like "1", "2", "3", ...

There is no issue with your timer. The issue is with the variable declaration inside the emergencyTimer, you declared it as a local variable. So each time when the timer fires the variable will be initialized to 0 again. So declare the variable as static, so that it can preserve the value.
Change the method like:
-(void)emergencyTimer
{
static int timeValue = 0;
_emergencyAlertTriggerTimerLabel.text = [NSString stringWithFormat:#"%d",++timeValue];
}
Why static variable and Why not instance variable ?
I didn't used instance variable, for keeping the variable "Scope" safe. If I put it as an instance variable, it can be accessed by other methods of the same class, if there is no need of that functionality, I think using a static variable will be better.

Issue is with this code
int i=0;
Every time when timer method gets called, the integer i gets initialized and label will be displayed as "1" always.
Make this variable global or static to fix your issue.

Remove int i=0; from your timer action method because it will always have a value of zero. You should be using an instance variable (#property) to store the timerCounter and increment that and use it to populate the label.
At some point in time you need to invalidate the timer. This is particularly important to do before you create a new timer and replace the reference to the old timer. Currently, if you press the button twice, you will then have 2 timers running and your label will increment twice a second...
#property (nonatomic, assign) NSInteger timerCounter;
- (IBAction)OnClickEmergencyButton:(id)sender {
[emergencyAlertTimer invalidate];
emergencyAlertTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(emergencyTimer) userInfo:nil repeats:YES];
[emergencyAlertTimer fire];
}
- (void)emergencyTimer {
_emergencyAlertTriggerTimerLabel.text = [NSString stringWithFormat:#"%d", self.timerCounter];
self.timerCounter++;
}
You should also invalidate the timer when the view is removed from display / deallocated.

Always timer get called emergencyTimer but your value of i won't changed because it's an local variable, scope of i will remain at end of function call. Try this with static variable which remain globally...
-(void)emergencyTimer{
static int i=0; // initialize at first time only..
_emergencyAlertTriggerTimerLabel.text = [NSString stringWithFormat:#"%d",++i];
if ( i == 100)
[ emergencyAlertTimer invalidate] // stop at certain condition
}

Firstly everyone is right you will only display a 0 no matter what you do currently so use an instance variable.
With regards to the only firing once, instead of [NSTimer fire] try this:
emergencyAlertTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(emergencyTimer) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:emergencyAlertTimer forMode:NSRunLoopCommonModes];

Related

Why isn't my NSTimer calling my method upon expiration?

I am using an NSTimer to call a method which is titled Lose. I had a timer which when it ran out, it called Lose, but I lost everything due to a hard drive error. After trying to code it all again, I can't seem to get the method to be called.
Timer = [NSTimer timerWithTimeInterval:timeMax target:self selector:#selector(Lose) userInfo:nil repeats:NO];
Lose is declared in my .h file, like this:
-(void)Lose;
Also, my method looks like this:
-(void)Lose{
Text.hidden = NO;
scoreLabel.hidden = NO;
Target.hidden = YES;
Targetx.hidden = YES;
if (Score > highScoreNumber) {
highScoreAchieved.hidden = NO;
highScoreNumber = Score;
}
}
The variable timeMax is an int declared in my .h file, like last time.
whenever a target is tapped in my game, timeMax becomes .03 seconds shorter. I do it like this:
timeMax = 5 - (Score * 0.03);
I don't remember it looking different before the massive hardware failure, but why isnt it working?
You have to schedule the timer on a run loop or just use this line instead which schedules it for you:
Timer = [NSTimer scheduledTimerWithTimeInterval:timeMax
target:self
selector:#selector(Lose)
userInfo:nil
repeats:NO];
It's also a good idea to always back up your code...which reminds me.

Issue while using 2 NSTimers

I am working on an iOS app which provides/makes calls from app. We can make two calls one after another. First time we are making 1st call. Once call get established, the NSTimer should be fired and it would show the duration of the call.
For this I am doing following for timer
self.switchTimer1 = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(setTimerLabel1:)
userInfo:nil
repeats:YES];
[self.switchTimer1 fire];
after establishing this first call, user can make second call.
Once second call gets established, it would fire second timer.
if (hasSecondCall)
{
self.switchTimer2 = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(setTimerLabel2:)
userInfo:nil
repeats:YES];
timeSec=0;
timeMin=0;
[self.switchTimer2 fire];
}
It's working fine while making first time of both calls.
Suppose if I ended 2nd call and again I made call, that time 2nd timer before establishing the call the timer automatically calling and once call established, the timer incrementing value very fast like double values showing. Like, 2,4, 6, etc.
For secondtimer after firing method following
- (void)setTimerLabel2:(NSTimer *)timer {
timeSec=timeSec+1;
NSLog(#"timeSec+1 %d",timeSec+1);
if (timeSec == 60)
{
timeSec = 0;
timeMin=timeMin+1;
}
NSString* timeNow = [NSString stringWithFormat:#"%02d:%02d", timeMin, timeSec];
[_switchCallLCD setStatus:timeNow labelNumber:2];
}
While disconnecting call we calling following
- (void)endingCallWithId:(UInt32)call_id {
if (hasSecondCall&& call_id==_current_call) {
if (self.switchTimer2) {
//NSLog(#"self.predictNumber %#",self.predictNumber);
self.predictNumber=self.predictNumber2;
[_lcd setText:self.predictNumber];
[self.switchTimer2 invalidate];
self.switchTimer2 = nil;
[self.switchTimer2 release];
[_switchCallLCD setStatus:NSLocalizedString(#"call ended",nil) labelNumber:2];
timeSec = 0;
timeMin = 0;
}
I have searched so many forums regarding this issue, but I couldn't figure out solution. I heard, if we use multiple timers there is some issue like this.
You likely still have the previous instance of the timer(s) running. When you make a second set of calls you then have 2 instances of switchTimer1 running and 2 instances of switchTimer2 running.
As soon as you end a call, you need to invalidate the timer:
[self.switchTimer1 invalidate];
and then set it to nil for good measure:
self.switchTimer1 = nil;

pass info from Model to viewController without NSNotification

In my viewController, I have a button that calls a method to start a timer in a Timer class. Just like this
main view controller
[self.timer startTimer];
In the timer class, startTimer calls a countdownTime method, both of which you can see below. Very simple. At the end of the countdownTime, method, the time is put in the label in the view like this as I iterate through a loop of the clocks.
Timer *timerblah = self.gameClocks[i];
[self.gameClocks[i] setText: [NSString stringWithFormat:#"%#", self.time]];
In other words, the Timer class has an array property that holds all the clocks and that is connected to the view, hence the ability to set the text.
The problem with this code is that the model (i.e. Timer class) is setting the text in the view. I want the view Controller to get the time from the model and have the text set in the view controller i.e. have the viewController and only the viewController communicate with the view. It is no problem for me to get the array of clocks in the viewController, however, I'm sure how to pass the time back from the countdownTime method to the viewController. It would seem like overkill to set up an NSNotification class to send the time back every second, wouldn't it?
-(void)startTimer{
self.NStimer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(countdownTime:)
userInfo:nil
repeats:YES];
}
-(void)countdownTime:(NSTimer *)timer
{
#logic to countdown time
self.minutes = self.secondsRemaining / 60;
self.stringMinutes = [NSString stringWithFormat:#"%i", self.minutes];
self.seconds = self.secondsRemaining - (self.minutes * 60);
self.stringSeconds = [NSString stringWithFormat:#"%i", self.seconds];
if (self.seconds < 10) self.stringSeconds = [NSString stringWithFormat:#"0%i", self.seconds];
self.time = [NSString stringWithFormat:#"%#:%#", self.stringMinutes, self.stringSeconds];
self.secondsRemaining -= 1;
if (self.secondsRemaining == 0){
[self stopTimer];
}
#adding to the view (from the model i.e. in Timer.m class)
for(int i = 0; i < [self.gameClocks count]; i++){
Timer *timerblah = self.gameClocks[i];
if (timerblah.systemClock == YES){
[self.gameClocks[i] setText: [NSString stringWithFormat:#"%#", self.time]];
}
}
}
Update
There's a comment suggesting that I use a block to do this. In the timer class, I added a method like this to return a string with the time (the time is set to self.time above)
-(NSString *)passTheTime:(NSString (^)(void))returnBlock
{
return self.time;
}
I then, in view controller, call the method passTheTime on the timer class. I created a property in the view controller that will store the time, so I pass that as a parameter to the block,
[self.timer passTheFoo:^NSString * (self.timeToSet){
}];
a) I'm unsure of what to do in the block here.
b) I'm unsure of whether it was necessary to pass self.timeToSet into the block. How do I connect self.time from the Timer class to self.timeToSet in the view controller
c) there's an error incompatible block pointer type sending NSString *((^)(void)) to parameter of type NSString(^)(void)
d) alternatively, can I pass a block to startTimer, and have that block passed in the selector countdownTime and return the self.time once the calculations in countdownTime are finished?
You can use block to update the label in the view controller class as below
In Timer Class, create a block
TimerClass.h
#import <Foundation/Foundation.h>
typedef void(^TimerCallback)(NSString *time);
#interface TimerClass : NSObject{
TimerCallback timerCallback;
NSTimer *timer;
}
- (void)startTimerWithCallBack:(TimerCallback)callback;
#end
In TimerClass.m , update the code as below
#import "TimerClass.h"
#implementation TimerClass
- (void)startTimerWithCallBack:(TimerCallback)callback{
timerCallback = callback;
timer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(countdownTime:)
userInfo:nil
repeats:YES];
}
-(void)countdownTime:(NSTimer *)timer
{
// countdown logic here
// call the callback method with time as a parameter to the viewcontroller class
// here for demo purpose I am passing random number as a value in callback, you need to pass the time that you want to display here
NSString *str = [NSString stringWithFormat:#"%d",1+arc4random()%10];
timerCallback(str);
}
#end
In ViewController class, call the timer start method as follows
TimerClass *timer = [[TimerClass alloc] init];
[timer startTimerWithCallBack:^(NSString *time) {
lbl.text = time; // display time in label
}];

Call an action at specific intervals in iOS

I am trying to invoke a function that contains ccactioninterval in Cocos3d. I want to call that function at specific time intervals.When I tried NSTimer , i found that it works sometimes and sometimes not.
NSTimer makeTarget=[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:#selector(createTargets) userInfo:nil repeats:YES];
Here createTargets is the function that contains action events. when i run the function straightit works fine for single time. Problem comes when i try to schedule it. I ve tried different methods already explained for related questions . But nothing worked for me. . . .
Here is the code
-(void) addTargets {
NSTimer *makeTarget = [NSTimer scheduledTimerWithTimeInterval:2.0
target:self selector:#selector(createTargets) userInfo:nil repeats:YES];
}
-(void)createTargets {
CC3MeshNode *target = (CC3MeshNode*)[self getNodeNamed: #"obj1"];
int minVal=-5;
int maxVal=5;
float avgVal;
avgVal = maxVal- minVal;
float Value = ((float)arc4random()/ARC4RANDOM_MAX)*avgVal+minVal ;
[target setLocation:cc3v(Value, 5.0, 0.0)];
CCActionInterval *moveTarget = [CC3MoveBy actionWithDuration:7.0 moveBy:cc3v(0.0, -10.0, 0.0)];
CCActionInterval *removeTarget = [CCCallFuncN actionWithTarget:self selector:#selector(removeTarget:)];
[target runAction:[CCSequence actionOne:moveTarget two:removeTarget]];
}
-(void)removeTarget:(CC3MeshNode*)targ {
[self removeChild:targ];
targ=nil;
}
Without much code its hard to tell what you issues is, but here are some things to try apologies if any of this is obvious.
Are you holding onto a reference to the timer?
This might be useful for debugging. If you have a property called makeTargetTimer, then you could do this:
NSTimer * makeTargetTimer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:#selector(createTargets) userInfo:nil repeats:YES];
self.makeTargetTimer = makeTargetTimer // Save to a property for later use (or just use an iVar)
The only way to stop a re-occurring timer is to invalidate it. Therefore you could check to see if its been invalidated.
BOOL isInvalidated = [self.makeTargetTimer isValid];
Also you might want to do this in your dealloc method anyway:
- (void) dealloc {
[_makeTargetTimer invalidate]; // Stops the timer from firing (Assumes ARC)
}
Are you scrolling when the even should be received?
If you want the timer to be fired while scrolling then you need to use NSRunLoopCommonModes. There is a excellent expiation in this question.
[[NSRunLoop currentRunLoop] addTimer:makeTargetTimer forMode:NSRunLoopCommonModes];
What is your implementation of createTargets like?
Have you put NSLog statements on the body of this method. Are you certain its not being called?

xcode calling an int redefining value of an int when called in another function

I'm having trouble with incrementing an int from one function by calling it in another function.
At the moment the bit I'm working on looks like this:
in the .h file I declare the int and timer as this:
int count;
NSTimer *sequenceOn;
in the .m file the segment of my function looks like this:
-(void) sequence {
count = 1;
while (count < (target)) {
sequenceOn = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:#selector(imagePlayer) userInfo:nil repeats:NO];
}
}
-(void)imagePlayer {
--CODE HERE FOR PLAYING ANIMATION--
count = count + 1;
}
All my other code is working fine and it should play through a series of images using the count value to decide which one to play. At the moment though it only plays the first animation and wont increment to the next one.
Any help would be much appreciated.
Thanks.
The timers will be fired from the run loop, which your code is blocking while running the loop. You probably want to have a timer regularly call your method:
-(void)stopTimer {
[sequenceOn invalidate];
sequenceOn = nil;
}
-(void)sequence {
[self stopTimer];
count = 1;
// start a repeating timer:
sequenceOn = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self
selector:#selector(imagePlayer) userInfo:nil repeats:YES];
}
-(void)imagePlayer {
//--CODE HERE FOR PLAYING ANIMATION--
count = count + 1;
if (count >= target) {
[self stopTimer];
}
}
I don't see anything in your while loop that increments count or decrements target, so either it's an infinite loop or it never executes, depending on the value of target.
You have the last argument of scheduledTimerWithTimeInterval:target:selector:userInfo:repeats: set to NO, is that what you want, it will only fire once. If not you will have to give us more code to show how imagePlayer is being called repeatedly.

Resources