Currently I am developing a game which needs a stop watch say 1 minute,when user taps the play button it enters the game screen,I want the to run the stop watch count down of 1 minute 01:00,00:59,00:58 etc. For that reason I searched and found NSTimer would be the ideal choice to implement a stop watch.Hence I took a label,created an instance of NSTimer,assigned time interval and started decrementing the value in timer label,i.e.:
-(void)viewDidAppear:(BOOL)animated
{
self.stopWatchTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(viewWillAppear:) userInfo:nil repeats:YES];
[super viewDidAppear:YES];
}
-(void)viewWillAppear:(BOOL)animated
{
static int currentTime = 60;
int newTime = currentTime--;
int minutesRemaining = newTime / 60; // integer division, truncates fractional part
int secondsRemaining = newTime % 60; // modulo division
self.timerLabel.text = [NSString stringWithFormat:#"%02d:%02d", minutesRemaining, secondsRemaining];
if ([self.timerLabel.text isEqualToString:#"00:00"])
{
[stopWatchTimer invalidate];
}
[super viewWillAppear:YES];
}
The problem here is the timer starts running 01:00,00:59,00:58 and goes on,say at 53rd second,I navigated to another view and came back,it is running from 00:53,00:52 and so on,but I want it to run from 01:00 and for that I implemented invalidating NSTimer in viewDidDisappear i.e.
-(void)viewDidDisappear:(BOOL)animated
{
if ([stopWatchTimer isValid])
{
[stopWatchTimer invalidate];
self.stopWatchTimer = nil;
}
[super viewDidDisappear:YES];
}
Still the same issue exists!
Done lots of Research on the issue and found no answer useful and working.
Can some one please guide me,any help is appreciated.
Thanks every one in advance :)
You are use the selector viewWillAppear for the timer. viewWillAppear is a method on a viewController that gets called when the view appears on screen, you should not call it yourself. Instead, create your own method that decrements the timer:
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
self.stopWatchTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(updateTime:) userInfo:nil repeats:YES];
currentTime = 60; // This could be an instance variable
self.timerLabel.text = #"01:00";
}
-(void)updateTime {
int newTime = currentTime--;
int minutesRemaining = newTime / 60; // integer division, truncates fractional part
int secondsRemaining = newTime % 60; // modulo division
self.timerLabel.text = [NSString stringWithFormat:#"%02d:%02d", minutesRemaining, secondsRemaining];
if ([self.timerLabel.text isEqualToString:#"00:00"])
{
[self.stopWatchTimer invalidate];
}
}
-(void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
if ([self.stopWatchTimer isValid])
{
[self.stopWatchTimer invalidate];
self.stopWatchTimer = nil;
}
}
With this technique the timer is responsible for calling every second, but you will probably have timing issues after some seconds. To have a more accurate timing, you should store the time that you started the countdown and then on every updateTime call, compare the current time with the stored started time.
Add to your interface:
#property (nonatomic) NSTimeInterval startTime;
then in the implementation:
-(void)viewDidAppear:(BOOL)animated
{
self.startTime = [NSDate timeIntervalSinceReferenceDate];
self.duration = 60; // How long the countdown should be
self.stopWatchTimer = [NSTimer scheduledTimerWithTimeInterval:0.1
target:self
selector:#selector(updateTime)
userInfo:nil
repeats:YES];
self.timerLabel.text = #"01:00"; // Make sure this represents the countdown time
}
-(void)updateTime {
int newTime = self.duration - (round([NSDate timeIntervalSinceReferenceDate] - self.startTime));
int minutesRemaining = newTime / 60; // integer division, truncates fractional part
int secondsRemaining = newTime % 60; // modulo division
self.timerLabel.text = [NSString stringWithFormat:#"%02d:%02d", minutesRemaining, secondsRemaining];
if (newTime < 1)
{
[self.stopWatchTimer invalidate];
/* Do more stuff here */
}
}
-(void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
if ([self.stopWatchTimer isValid])
{
[self.stopWatchTimer invalidate];
self.stopWatchTimer = nil;
}
}
Some extra (unrelated) comments on the code:
when implementing viewDidDisappear: pass the animated parameter to the call to super:
[super viewDidDisappear:animated];
when implementing viewDidAppear: pass the animated parameter to the call to super, also, make sure you call super first this in the method, before doing anything else
Related
I am developing a QuiZ app. It needs a countdown timer, In that I want to start a timer at 75:00.00 (mm:ss.SS) and decrease it to 00:00.00 (mm:ss.SS) by reducing 1 millisecond. I want to display an alert that Time UP! when time reaches to 00:00.00 (mm:ss.SS).
I am displaying the time by following below link
Stopwatch using NSTimer incorrectly includes paused time in display
Here is a simple solutions to your problem.
Declarations
#interface ViewController : UIViewController
{
IBOutlet UILabel * result;
NSTimer * timer;
int currentTime;
}
- (void)populateLabelwithTime:(int)milliseconds;
-(IBAction)start;
-(IBAction)pause;
#end
Definition
- (void)viewDidLoad
{
[super viewDidLoad];
currentTime = 270000000; // Since 75 hours = 270000000 milli seconds
}
-(IBAction)start{
timer = [NSTimer scheduledTimerWithTimeInterval:.01 target:self selector:#selector(updateTimer:) userInfo:nil repeats:YES];
}
-(IBAction)pause{
[timer invalidate];
}
- (void)updateTimer:(NSTimer *)timer {
currentTime -= 10 ;
[self populateLabelwithTime:currentTime];
}
- (void)populateLabelwithTime:(int)milliseconds {
int seconds = milliseconds/1000;
int minutes = seconds / 60;
int hours = minutes / 60;
seconds -= minutes * 60;
minutes -= hours * 60;
NSString * result1 = [NSString stringWithFormat:#"%#%02dh:%02dm:%02ds:%02dms", (milliseconds<0?#"-":#""), hours, minutes, seconds,milliseconds%1000];
result.text = result1;
}
Use following code for reducing timer.
double dblRemainingTime=60; //1 minute
if(dblRemainingTime >0)
{
dblRemainingTime -= 1;
int hours,minutes, seconds;
hours = dblRemainingTime / 3600;
minutes = (dblRemainingTime - (hours*3600)) / 60;
seconds = fmod(dblRemainingTime, 60);
[lblRemainingTime setText:[NSString stringWithFormat:#"%02d:%02d",minutes, seconds]];
}
else
alert('Time up buddy');
First, here is some working code for a stopwatch in Xcode. I got two buttons, Startand Stop. Their titles change when the buttons are pressed. Now I want to add a pause functionality. I know that there are many threads about this, but (I don't know why) I was not able to get it working.
So what is the best approach to implement this function in my code?
I already tried to use a pause date and subtract it from my NSTimeIntervalbut got negative values ...
Thanks so far!
So I did this:
//use timer to update the ui only, store start date (NSDate) and time interval elapsed (NSTimeInterval)
//this is called when the timer is not running, nor paused - a sort of 'first run' function
-(void)onTimerStart
{
//zero the time elapsed
time_passed = 0;
//save the starting date
start_date = [NSDate date];
//start a timer that will update the ui in the onTimer function
timer = [NSTimer timerWithTimeInterval:1.0/10.0 target:self selector:#selector(onTimer) userInfo:nil repeats:YES];
}
//called when the timer is running to pause it
-(void)onPause
{
//calculate the time that passed ( += not = )
time_passed += [[NSDate date] timeIntervalSinceDate: start_date];
//stop the timer
[timer invalidate];
//you can get rid of the start date now (using ARC ........)
}
//restarting the timer that was paused
-(void)onUnpause
{
//get new start date
start_date = [NSDate date];
//start the timer to update ui again
timer = [NSTimer timerWithTimeInterval:1.0/10.0 target:self selector:#selector(onTimer) userInfo:nil repeats:YES];
}
//use this function if you are stopping - not pausing! - the timer.
-(void)onReset
{
//stop timer
[timer invalidate];
//calculate the final time that passed
//THE NEXT LINE IS PROBABLY WRONG AND HAS TO BE time_passed = 0; THEN IT WORKS
time_passed += [[NSDate date] timeIntervalSinceDate: start_date];
//get rid of the start date now
}
//use this function to update UI - this is what gets called by the timer
-(void)onTimer
{
//when timer ticks use ([[NSDate date] timeIntervalSinceDate: start_date] + time_passed)
//to get the amount of time passed for display
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self onTimerStart];
}
WORKING CODE:
#pragma mark - Timer
- (void)timer
{
NSDate *currentDate = [NSDate date];
NSTimeInterval timeInterval = [currentDate timeIntervalSinceDate:startDate];
NSDate *timerDate = [NSDate dateWithTimeIntervalSince1970:timeInterval];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"mm:ss.S"];
[dateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0.0]];
NSString *timeString=[dateFormatter stringFromDate:timerDate];
timerLabel.text = timeString;
}
#pragma mark - Stopwatch
- (IBAction)onStartPressed:(UIButton *)sender
{
if ([sender.titleLabel.text isEqualToString:#"Start"] && (![timer isValid]) && ([timerLabel.text isEqualToString:#"00:00.0"]))
{
startDate = [NSDate date];
timer = [NSTimer scheduledTimerWithTimeInterval:1.0/10.0
target:self
selector:#selector(timer)
userInfo:nil
repeats:YES];
}
}
- (IBAction)onStopPressed:(UIButton *)sender
{
if ((![timer isValid]) && ([sender.titleLabel.text isEqualToString:#"Reset"]))
{
[timer invalidate];
timer = nil;
timerLabel.text = #"00:00.0";
[sender setTitle:#"Stop" forState:UIControlStateNormal];
}
if (([timer isValid]) && ([sender.titleLabel.text isEqualToString:#"Stop"]))
{
[timer invalidate];
timer = nil;
[sender setTitle:#"Reset" forState:UIControlStateNormal];
}
}
There is no pause/resume functionality built into NSTimer, so you have to implement something along the lines of:
//THIS IS PSEUDOCODE
//use timer to update the ui only, store start date (NSDate) and time interval elapsed (NSTimeInterval)
//this is called when the timer is not running, nor paused - a sort of 'first run' function
- onTimerStart:
{
//zero the time elapsed
time_passed = 0;
//save the starting date
start_date = [NSDate date];
//start a timer that will update the ui in the onTimer function
[timer start]
}
//called when the timer is running to pause it
- onPause
{
//calculate the time that passed ( += not = )
time_passed += [[NSDate date] timeIntervalSinceDate: start_date];
//stop the timer
[timer invalidate];
//you can get rid of the start date now
}
//restarting the timer that was paused
- onUnpause
{
//get new start date
start_date = [NSDate];
//start the timer to update ui again
[timer start];
}
//use this function if you are stopping - not pausing! - the timer.
- onReset
{
//stop timer
[timer invalidate];
//calculate the final time that passed
time_passed += [[NSDate date] timeIntervalSinceDate: start_date];
//get rid of the start date now
}
//use this function to update UI - this is what gets called by the timer
- onTimer
{
//when timer ticks use ([[NSDate date] timeIntervalSinceDate: start_date] + time_passed)
//to get the amount of time passed for display
}
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Change the time interval of a Timer
So I have a timer:
timer=[NSTimer scheduledTimerWithTimeInterval:2 target:self selector:#selector(createNewImage) userInfo:nil repeats:YES];
And I would like that the timer decrease every ten seconds, that scheduledTimerWithTimeInterval goes to 1.5 after ten seconds then to 1.0 after 10 seconds... Is it possible to do this, and if it is possible, how can I do it?
You can't modify a timer after you created it. To change the timer interval, invalidate the old timer and create a new one with the new interval.
You don't have to use several timers, all you have to do is add a variable to use for the time interval, then create a method that invalidates the timer, changes the variable and starts the timer again. For instance you could make a method that starts the timer.
int iTimerInterval = 2;
-(void) mTimerStart {
timer = [NSTimer scheduledTimerWithTimeInterval:iTimerInterval target:self selector:#selector(createNewImage) userInfo:nil repeats:YES];
}
-(void) mTimerStop {
[timer invalidate];
iTimerInterval = iTimerInterval + 5;
[self mTimerStart];
}
This would be the simple way to decrease the timer interval and keep the timer going, but I would personally prefer using the one below, because it makes sure that the timer has only ran once, that way it will not duplicate the instance, forcing your app to become glitchy and it also makes things easier for you.
int iTimerInterval = 2;
int iTimerIncrementAmount = 5;
int iTimerCount;
int iTimerChange = 10; //Variable to change the timer after the amount of time
bool bTimerRunning = FALSE;
-(void) mTimerToggle:(BOOL)bTimerShouldRun {
if (bTimerShouldRun == TRUE) {
if (bTimerRunning == FALSE) {
timer = [NSTimer scheduledTimerWithTimeInterval:iTimerInterval target:self selector:#selector(mCreateNewImage) userInfo:nil repeats:YES];
bTimerRunning = TRUE;
}
} else if (bTimerShouldRun == FALSE) {
if (bTimerRunning == TRUE) {
[timer invalidate];
bTimerRunning = FALSE;
}
}
}
-(void) mCreateNewImage {
//Your Code Goes Here
if (iTimerCount % iTimerChange == 0) { //10 Second Increments
iTimerInterval = iTimerInterval + iTimerIncrementAmount; //Increments by Variable's amount
[self mTimerToggle:FALSE]; //Stops Timer
[self mTimerToggle:TRUE]; //Starts Timer
}
iTimerCount ++;
}
-(void) mYourMethodThatStartsTimer {
[self mTimerToggle:TRUE];
}
I didn't finish all of the coding, but this is most of what you will need. Just change a few things and you'll be good to go!
You will need a set or sequence of timers, one for each different time interval. Stop/invalidate one timer and start the next timer when you want to change to the next time interval in your time sequence.
I have looked everywhere but I cannot find out how to do so. I need a simple timer that can count up in milliseconds and can be stopped. (XCode 3.1, Objective-C, iPhone OS Development)
Am I just being daft or is "count-up timer" the same as a stopwatch.
If it is, check out this video tutorial
Here is the sample code which i have done one timer with minute, second, and mili seconds.. So, its helpful for you. And also do it release after not use..
-(void)showTime:(NSTimer *)sender
{
miniSeconds++;
if (miniSeconds == 10)
{
miniSeconds = 0;
seconds++;
if (seconds == 60)
{
seconds = 0;
minutes++;
}
}
timeLabel.text = [NSString stringWithFormat:#"%02i:%02i:%02i",minutes,seconds,miniSeconds];
}
-(void)nextLevelTime
{
[labelTimer invalidate];
miniSeconds = 0;
seconds = 0;
minutes = 0;
}
The nextLevel method use for to stop timer and release all thing..
Use it, I have done with this code..
NSTimer should do it:
Class Reference
Timer Programming Topics
#interface ViewController()
{
UILabel *progress;
NSTimer *timer;
int currMinute;
int currSeconds;
int currHours;
}
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
progress=[[UILabel alloc] initWithFrame:CGRectMake(80, 15, 100, 50)];
progress.textColor=[UIColor redColor];
[progress setText:#"Time : 00:00:00"];
progress.backgroundColor=[UIColor clearColor];
[self.view addSubview:progress];
currMinute=00;
currSeconds=00;
currHours=00;
// Do any additional setup after loading the view, typically from a nib.
}
-(void)start
{
timer=[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(timerFired) userInfo:nil repeats:YES];
}
-(void)timerFired
{
currSeconds+=1;
if(currSeconds==60)
{
currSeconds=0;
currMinute+=1;
if(currMinute==60)
{
currMinute=0;
currHours+=1;
}
}
[_progress setText:[NSString stringWithFormat:#"%#%02d%#%02d%#%02d",#"Time : ",currHours,#":",currMinute,#":",currSeconds]];
if(currMinute==2)//stop at 2 minute
{
[timer invalidate];
}
}
I am working on a count down timer and am having trouble getting the if statement to stop the timer when the count is less that 0. Any guidance on solving this would be greatly appreciated. Thanks in advance for your help..
-(void) startCountdown{
time = 90;
//NSLog[#"Time Left %d Seconds", time];
//This timer will call the function updateInterface every 1 second
myTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(updateInterface:) userInfo:nil repeats:YES];
}
-(void) updateInterface:(NSTimer*)theTimer{
if(time >= 0){
time --;
CountDownText.text = [NSString stringWithFormat:#"%d", time];
NSLog(#"Time Left %d Seconds", time);
}
else{
CountDownText.text =#"Times Up!";
NSLog(#"Times Up!");
// Timer gets killed and no longer calls updateInterface
[myTimer invalidate];
}
}
I tested your code and it worked perfectly and the timer stopped at -1. So my best guess is that time might be declared as unsigned value, so it never becomes less than zero.
Looks like your countdown won't stop until it gets to -1 (instead of 0) because you're checking for greater-than-or-equal to zero and then decrementing (and then displaying).
Either decrement first and then check if time is greater-than zero:
-(void) updateInterface:(NSTimer*)theTimer{
time --;
if(time > 0){
CountDownText.text = ...
or check if time is greater-than 1:
-(void) updateInterface:(NSTimer*)theTimer{
if(time > 1){
time --;
CountDownText.text = ...