Timer decrement gets varied for each page load - iphone

I have a timer for my project, each time it decrements by 1 second. But if the counter starts working for the second time it gets decremented by 2 seconds and for the third time by 3 seconds etc. what should I do to get 1 sec decrement for all the time?
-(void)viewDidAppear:(BOOL)animated {
count=15; //timer set as 15 seconds
[NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(updateCounter:)userInfo:nil repeats:YES]; //for decrementing timer
}
- (void)updateCounter:(NSTimer *)theTimer {
count -= 1;
NSString *s = [[NSString alloc] initWithFormat:#"%d", count];
if(count==0) // alert for time expiry
{
alert = [[UIAlertView alloc] initWithTitle:#"Time Out!!" message:#"Your time is expired." delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:nil];
[alert show];
self.timer.hidden=YES;
[self dismissModalViewControllerAnimated:YES];
}
else {
self.timer.text = s;
}
[s release];
}

From what you've posted, you're creating multiple timers and not stopping any of them. So after 3 times, you have 3 timers firing each second.
At a minimum, when the timer hits zero you want to invalidate it:
[theTimer invalidate]
But you may also want to consider holding onto the timer you create (in a #property) so that you can invalidate and release it if the user leaves this view some other way before your counter actually goes to zero.
Hope that helps.

EDIT
(Just as I posted this I notice that you have know accepted the above answer and seemed to have removed your 'its not working' comment for the answer from #Firoze Lafeer. But I will leave this here any way.)
Running the code below as a test I do not get your issue. And even when I did not invalidate I just got multiple outputs in the log.
Instead of seeing what is going on by looking at what the textfield in the app is doing. Try and use logging as I have below to see if that gives you a better idea of what is happening.
-(IBAction)runTimer:(id)sender { // attached to a button
[self invalidateTimer];
count=15; //timer set as 15 seconds
//for decrementing timer
countimer = [NSTimer scheduledTimerWithTimeInterval:1.0f
target:self
selector:#selector(updateCounter:)
userInfo:nil
repeats:YES];
}
-(void)updateCounter:(NSTimer *)theTimer {
count -= 1;
NSLog (#"count =#%d", count);
if(count==0) // alert for time expiry
{
[self invalidateTimer];
}
}
-(void)invalidateTimer {
if ([countimer isValid]) {
[countimer invalidate];
NSLog(#"countimer invalidated ");
countimer = nil;
}
}

Related

Create number of dynamic NSTimers not repeating on very first load, fine after that

I am creating a number (~5) NSTimer instances from data stored in a SQLite database using the following code below:
-(void)setupNotificationTimer:(NSDictionary *)timerDetails{
NSLog(#"TIMER REPEATS VALUE: %#", [timerDetails objectForKey:#"repeats"]);
NSLog(#"INTERVAL: %f", [[timerDetails objectForKey:#"interval"] floatValue]);
bool repeat = [[timerDetails objectForKey:#"repeats"] boolValue];
if (repeat) {
NSLog(#"Should Repeat");
}
else{
NSLog(#"Should Not Repeat");
}
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:[[timerDetails objectForKey:#"interval"] floatValue]
target:self
selector:#selector(fireTimer:)
userInfo:timerDetails
repeats:repeat];
[timer fire];
[timers addObject:timer]; //'timers' is a property of the AppDelegate
}
-(void)fireTimer:(NSTimer *)timer {
NSLog(#"Timer Fired");
NSMutableDictionary *dict = [timer userInfo];
[[NSNotificationCenter defaultCenter] postNotificationName:[dict objectForKey:#"notificationName"] object:nil];
}
When I setup these timers the very first time the app loads the timer is setup correctly as the NSLog output below shows but does not repeat despite clearly being set to repeat. Each timer fires once then does not continue.
2013-04-03 11:12:53.541 Test[2635:752b] TIMER REPEATS VALUE: 1
2013-04-03 11:12:53.542 Test[2635:752b] INTERVAL: 10.000000
2013-04-03 11:12:53.543 Test[2635:752b] Should Repeat
2013-04-03 11:12:53.544 Test[2635:752b] Timer Fired
The strange thing happens when I close the app and then re-open it, the timers are created and actually fire repeatedly. This app is also a universal iPhone and iPad app and I only seem to get this behaviour when running the app on iPhone. I really am stumped by this and any help is appreciated.
Edit
As requested the code to cancel the timers:
-(void)viewWillDisappear:(BOOL)animated{
NSArray *viewControllersSet = self.navigationController.viewControllers;
if (viewControllersSet.count > 1 && [viewControllersSet objectAtIndex:viewControllersSet.count-2] == self) {
// View is disappearing because a new view controller was pushed onto the stack
NSLog(#"New view controller was pushed");
} else if ([viewControllersSet indexOfObject:self] == NSNotFound) {
// View is disappearing because it was popped from the stack
NSLog(#"View controller was popped");
for(int i = 0; i < [[appDelegate timers] count]; i ++){
NSTimer *timer = [[appDelegate timers] objectAtIndex:i];
[timer invalidate];
timer = nil;
}
[[appDelegate timers] removeAllObjects];
}
}
If an NSTimer does not fire after being created with scheduledTimerWithTimeInterval, it usually means that it has not been attached to the correct runloop. Could it be that your method setupNotificationTimer() is called with different runloops during initialization and when returning to the foreground? (i.e. in the context of a different thread or similar)
This code would work around the problem:
NSTimer* timer = [NSTimer timerWithTimeInterval:[[timerDetails objectForKey:#"interval"] floatValue]
target:self
selector:#selector(fireTimer:)
userInfo:timerDetails
repeats:repeat];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
This ensures that your timers are scheduled on the main runloop and will fire.

NSTimer some times getting fired before the fire date?

I'm setting up a timer to fire at an interval of 13 mins. But the timer seems to be getting fired at irregular intervals. This is an intermittent issue. Some users reported the issue and I'm not able to reproduce the same.
- (void)resetIdleTimer
{
if (_idleTimer)
{
[_idleTimer invalidate];
[_idleTimer release];
_idleTimer = nil;
}
_idleTimer = [[NSTimer scheduledTimerWithTimeInterval:13*60.0
target:self
selector:#selector(idleTimerExceeded)
userInfo:nil
repeats:NO] retain];
}
- (void)idleTimerExceeded
{
// do some processing
[_idleTimer release];
_idleTimer = nil;
}
Timer will be reset (resetIdleTimer) depending on some conditions, but that's anyhow resets the timer with 13 mins.
When I looked into code, I can see only the issue that is not passing timer parameter to the selector. I'm not sure whether that's the reason for this issue or not ? Did anyone come across this kind of weird ness ? (Any how I will be updating the code to to have timer as an argument).
One user reported that its just happened after 4 mins itself.
Let confirm one thing,not passing timer parameter to the selector will not cause any issues.
Here is the updated code which works fine
#property(nonatomic,strong)NSTimer *myTimer;
or
#property(nonatomic,retain)NSTimer *myTimer;
Methods
- (void)resetIdleTimer
{
if ([self.myTimer isValid])
[self.myTimer invalidate];
self.myTimer = [NSTimer scheduledTimerWithTimeInterval:13*60.0
target:self
selector:#selector(idleTimerExceeded)
userInfo:nil
repeats:NO];
}
- (void)idleTimerExceeded
{
NSLog(#"tiggered");
}
i don't find any error in your code, the only idea about is that a user enter your class creating an instance, let's call it instance "A", then start the timer, then exit your class before 13 minutes, then enter again with a new instance "B", a new timer starts...
at this point user expects your timer methods fires after 13 minutes... but timer of "A" is still pending (because there may be an error in the way you close your class) and executes...
You may try to add some logs and try to do as i said, and check all logs (in particular if you enter the dealloc, and that the log in idleTimerExceeded print instances equals to the last instances printed in resetIdleTimer):
-(void)resetIdleTimer {
if (_idleTimer)
{
[_idleTimer invalidate];
[_idleTimer release];
_idleTimer = nil;
}
NSLog(#"...resetIdleTimer: instance: %p", self);
_idleTimer = [[NSTimer scheduledTimerWithTimeInterval:13*60.0
target:self
selector:#selector(idleTimerExceeded)
userInfo:nil
repeats:NO] retain];
NSLog(#"...resetIdleTimer: _idleTimer: %p", _idleTimer);
}
- (void)idleTimerExceeded
{
// do some processing
NSLog(#"...idleTimerExceeded: instance: %p", self);
NSLog(#"...idleTimerExceeded: _idleTimer: %p", _idleTimer);
[_idleTimer release];
_idleTimer = nil;
}
-(void) dealloc
{
NSLog(#"...dealloc: instance: %p", self);
[super dealloc];
}
just an idea... but you may have forgotten that timers retain the instance passed in target:parameter (you pass "self", so your class instance is retained), so a common error is to forget to close the timer when closing the class instance.
a correct way should be:
-a root class (AAA) instantiate your class (BBB) with timer
-BBB start its timer
-AAA wanna dealloc/resetIdleTimer class BBB: before to release it must stop/invalidate BBB.timer (if not timer retain BBB, and BBB won't be released until timer fires/executes its method)
so, if it's your case... add a method that could be called from class AAA that stop/invalidate the timer, something like:
-(void)stopTimer{
if (_idleTimer)
{
[_idleTimer invalidate];
[_idleTimer release];
_idleTimer = nil;
}
}
If you want your timer to fire in exact 13 minutes, maybe this will help:
https://stackoverflow.com/a/3519913/1226304

read nsmutablearray at different time intervals

I have this NSMutable array. Reading it one by one after every two seconds and updating textview. But now i want is to read from NSMutable array one by one at different timeintervals than every two seconds. How i can do that.
- (void)viewDidLoad{
myArray = [[NSMutableArray alloc]initWithObjects:#"String1",#"String2",#"String3",#"String4", #"String5",..... nil];
[NSTimer scheduledTimerWithTimeInterval:2.0
target:self
selector:#selector(updateText:)
userInfo:nil
repeats:YES];
- (void)updateText:(NSTimer *)theTimer
{
if (index < [myArray count])
{
myTextView.text = [myArray objectAtIndex:index];
index++;
}
else
index = 0;
}
}
Thanks for help.
If all you need is to change the interval value every time the view loads you can try with a random number, in the next example it'll return a random number between 2 and 10:
[NSTimer scheduledTimerWithTimeInterval:arc4random() % 10 + 2
target:self
selector:#selector(updateText:)
userInfo:nil
repeats:YES];
Simple: cancel the timer each time and recreate it with the desired interval (hang onto a reference to the timer of course).
in your updateText selector just create a new NSTimer with different intervals until there are no more objects in the array.
Use performSelector:withObject:afterDelay, but using a random delay!
[self performSelector:#selector(updateText:) withObject:nil afterDelay:arc4random()%10+2];

How do I wait on a timer before returning location?

I am trying to write a method to return the current location. While I'm waiting a period of 30sec, 1 min, or 2 minutes (set in settings) I display an alert view. I allow the user to bypass the timer wait with an alertView button accepting the current accuracy which I will display and update in the alert view. My code has a couple of holes in it I need help with. Essentially, it is to know how to have the getCurrentLocation method wait on the timer. I can't just use a delay because I plan to force the timer to expire if the user hits the button or if the location accuracy is met (this is checked in timer). Here is the code:
- (void)timerFireMethod {
static int elapsedTime = 0;
elapsedTime++;
if (elapsedTime >= gpsTimeout) {
[locationManager stopUpdatingLocation];
elapsedTime = 0; // reset static variable
// !!! How do I do the following !!!
// do something to allow getCurrentLocation to return
}
if ((currentLocation.horizontalAccuracy <= gpsDesiredAccuracy) & (currentLocation.verticalAccuracy <= 2*gpsDesiredAccuracy)) {
[locationManager stopUpdatingLocation];
elapsedTime = 0; // reset static variable
// do something to allow getCurrentLocation to return
}
}
- (CLLocation *)getCurrentLocation {
// check if current accuracy is good enough and return if true
if ((currentLocation.horizontalAccuracy <= gpsDesiredAccuracy) & (currentLocation.verticalAccuracy <= 2*gpsDesiredAccuracy)) {
[locationManager stopUpdatingLocation];
return currentLocation;
} else {
// show alert with count down timer
UIAlertView *gpsAlertView = [[UIAlertView alloc] initWithTitle:nil message:#"tbd put in timer and list location accuracy updates" delegate:self cancelButtonTitle:#"Continue With Current Accuracy" otherButtonTitles:nil];
[gpsAlertView show];
// start timer
NSTimer *gpsTimer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self selector:#selector(timerFireMethod:)
userInfo:nil repeats:YES];
// !!! How do I do the following !!!
// wait for when timer expires,
// the user to press "Continue With Current Accuracy" button (can force timer to expire?)
return currentLocation;
}
}
The way to wait on any timer or for any accelerometer or user event is to simply exit the current method back to the UI run loop by using a return statement.
Anything you want done after the wait can go in the timer or event callback method.

NStimer and for loop

I'm struggling with this. I saw some code where you can do this :
- (void)startTimer {
pauseTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(doActions) userInfo:nil repeats:YES];
}
Then call it in doActions.
Problem is i want to call it while a button is being pressed, and do actions is an IBaction. I keep getting a sigAbrt.
Can someone give me some sample code where you cay change a label from 'on' to 'off' every 1 second while a button is being pressed?
EDIT
i mean if doActions looks like this
- (IBAction) doActions {
for(int j; j<100; j++){
theLabel.hidden != theLabel.hidden;
//Does invalidate timer go here?
}
}
It still isn't clear to me, what you're trying to accomplish: I would have found it much easier, if you simply had it written down in plain english, after all.
That said, here's what I think will get you where you want to go:
// Assuming ivars:
// NSTimer* toggleTimer
// NSUInteger toggleCount
- (void)toggleTimerFired:(NSTimer*)timer
{
if ( toggleCount++ >= 100 ) {
[self stopToggling:self];
return;
}
// do whatever you need to do a hundred times here
}
- (IBAction)stopToggling:(id)sender
{
[toggleTimer invalidate], toggleTimer = nil; // you don't want dangling pointers...
// perform any other needed house-keeping here
}
- (IBAction)startToggling:(id)sender
{
[self stopToggling:self]; // if `startToggling:` will NEVER be called when a timer exists, this line CAN be omitted.
toggleCount = 0;
toggleTimer = [NSTimer scheduledTimerWithTimeInterval:1. target:self selector:#selector(toggleTimerFired:) userInfo:nil repeats:YES];
}
Depending on what exactly you want to do, startToggling: needs to be sent on either touchUpInside or touchDown. In the second case, stopToggling: probably needs to be called on any touchUp... event of the button.
- (void)startTimer {
pauseTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(doActions) userInfo:nil repeats:YES];
}
- (void) doActions {
theLabel.hidden != theLabel.hidden;
}