How can I create a simple timer which will give me simply the Time in millisecond. Something like the following...
Timer *timer = [Timer alloc] init];
[timer start]; //Which will start from Zero
[timer pause]; //Which will Pause timer
[timer stop]; //Which will stop the timer
[timer getCurrentTime]; //Which will give me time elapsed in millisecond since [timer start] is called
NSTimer doesn't seem to Work as it provides more functionality but not this. Can I get this functionality using NSTimer? What would be the best way to achieve this?
NOTE: I don't want to call any function at any specific time period but just want the milliseconds since the timer started.
Why do you need a timer for that?
Just store the start time and subtract it from the current time when you stop measuring:
static NSTimeInterval startTime;
static BOOL isRunning;
- (IBAction)toggle:(id)sender
{
if(!isRunning)
{
startTime = [NSDate timeIntervalSinceReferenceDate];
isRunning = YES;
}
else
{
NSLog(#"%f", ([NSDate timeIntervalSinceReferenceDate] - startTime) * 1000.0);
isRunning = NO;
}
}
Actually i Was looking for this but somehow i missed this question
How Do I write a Timer in Objective-C?
Related
A strange situation:
If I started my Timer again and again without stopping it first, it will count increasingly fast. I guess it is because it starts multiple timers now?
However, when I finally want to stop it, it cannot be stopped...keep going forever.
(Maybe for design consideration, I should disable users from pressing start again, but I'm wondering what is really behind this and why the timer can't be stopped.)
- (IBAction)Start:(id)sender {
countInt = 0;
self.Time.text = [NSString stringWithFormat:#"%i", countInt];
timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(countTimer) userInfo:nil repeats:YES];
}
- (IBAction)Stop:(id)sender {
[timer invalidate];
}
- (void) countTimer {
countInt += 1;
self.Time.text = [NSString stringWithFormat:#"%i", countInt];
}
#end
The simple solution is to call stop at the beginning of the start method.
Note that in stop you should also set timer = nil;
Assuming there is a property timer
#property NSTimer *timer;
the most reliable way to start and stop the timer only once respectively is to create two methods.
- (void)startTimer
{
if (self.timer == nil) {
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(countTimer)
userInfo:nil
repeats:YES];
}
}
- (void)stopTimer
{
if (self.timer != nil) {
[self.timer invalidate];
self.timer = nil;
}
}
Both methods perform a check, so the timer can't be restarted while it's running and vice versa.
Now just call the methods in the start/stop IBActions (the names should start with a lowercase letter).
- (IBAction)Start:(id)sender {
countInt = 0;
self.Time.text = [NSString stringWithFormat:#"%i", countInt];
[self startTimer];
}
- (IBAction)Stop:(id)sender {
[self stopTimer];
}
The benefit is pressing Start has no effect when the timer is already running.
When you hit 'start' multiple times you are creating multiple timers. So you are getting multiple timers firing and executing your timer callback. In this timer callback you increment counters. Since there are many timers now, they are all incrementing your counter, hence explaining your rapid increase in the counter.
You can allow the user to tap Start twice, as long you can define what happens when you hit Start while the timer is already going. But you definitely need to invalidate the old timer before creating a new one.
- (IBAction)Start:(id)sender {
...
// Stop previous timer before creating a new timer.
if (timer != nil) {
[timer invalidate]
}
...
}
My timer does not stop even if i am doing "invalidate" and "nil" after reading other links. My code is as below:
#property(nonatomic,strong) NSTimer *mytimer;
- (void)viewDidLoad {
[self performSelectorOnMainThread:#selector(updateProgressBar:) withObject:nil waitUntilDone:NO];
<do some other work>
}
- (void) updateProgressBar :(NSTimer *)timer{
static int count =0;
count++;
NSLog(#"count = %d",count);
if(count<=10)
{
self.DownloadProgressBar.progress= (float)count/10.0f;
}
else{
NSLog(#"invalidating timer");
[self.mytimer invalidate];
self.mytimer = nil;
return;
}
if(count <= 10){
NSLog(#"count = %d **",count);
self.mytimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(updateProgressBar:) userInfo:nil repeats:YES];
}
}
1) The timer goes on infinetly even when invalidating timer else condition is hit after count >10 and count keeps on incrementing.
2) i want to do this on a non-main thread . i want to continue in viewdidload() after starting the timer. How to do this ?
I visited other links on SO, all i understood was to call invalidate and nil on timer pointer. I am still facing problems. Could anyone tell me what i am missing here and what i can i do to run the updateProgressBar on background thread and update the progress bar ?
don't need to schedule a timer each time, schedule it once and timer will fire every second for example u can do like below,
- (void)viewDidLoad
{
[super viewDidLoad];
[self performSelectorOnMainThread:#selector(startTimerUpdate) withObject:nil waitUntilDone:NO]; //to start timer on main thread
}
//hear schedule the timer
- (void)startTimerUpdate
{
self.mytimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(updateProgressBar:) userInfo:nil repeats:YES];
}
- (void) updateProgressBar :(NSTimer *)timer{
static int count =0;
count++;
NSLog(#"count = %d",count);
if(count<=10)
{
//self.DownloadProgressBar.progress= (float)count/10.0f;
NSLog(#"progress:%f",(float)count/10.0f);
}
else
{
NSLog(#"invalidating timer");
[self.mytimer invalidate];
self.mytimer = nil;
return;
}
if(count <= 10){
NSLog(#"count = %d **",count);
}
}
I think you are scheduling timer multiple time. I think 10 time. just schedule time one time or if require many time then invalidate it that many time as schedule.
Update according to comment : Schedule timer from viewdidload and addobserver means notification on task. when your task will completed invalidate timer. and update your progress in selector method of timer so when you invalidate it it will automatically stop progress bar.
Second thing : you should invalidate timer before moving another viewcontroller also because this objects remains live untill invalidate.
Hope this will hellp :)
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.
I use NSTimer to periodically download data in the background, when wifi is enabled.
However there is also the option to press a button that downloads the data manually. Therefore it is possible to enable and disable the autoupdater.
When a period is "interrupted" by diabling the autoupdate, I want to schedule the next update when the remaining time of this period after the point of time when enabling it is over.
Because NSTimer has no pause method, I use NSTimer's initWithFireDate method.
timer = [[NSTimer alloc] initWithFireDate:fireDate interval:updateFrequency target:self selector:#selector(timerTick:) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
the fireDate is definitely after the given date:
NSDate *fireDate = [NSDate dateWithTimeIntervalSinceNow:remainingWaitingTime];
despite that,the timer is scheduled after the timer's initialization. I am disabling the timer and calculating the remaining time the following way:
NSDate *lastFireDate = [timer fireDate];
NSDate *current = [NSDate date];
[timer invalidate];
double timeSinceLastFire = [current timeIntervalSinceDate:lastFireDate];
remainingWaitingTime = updateFrequency - timeSinceLastFire;
The calculated time (remainingWaitingTime) has a correct value.
What am I doing wrong?
Best Regards,
Fabian
You have to add your timer to the run loop by doing the following:
[[NSRunLoop currentRunLoop] addTimer: timer forMode: NSDefaultRunLoopMode];
Is this the right way to check AVAudioPlayer's current playback time.
[audioPlayer play];
float seconds = audioPlayer.currentTime;
float seconds = CMTimeGetSeconds(duration);
How I can code after
[audioPlayer play];
that when currentTime is equal to 11 seconds then
[self performSelector:#selector(firstview) withObject:nil];
and after firstview when currentTime is equal to 23 seconds
[self performSelector:#selector(secondview) withObject:nil];
You could set up a NSTimer to check the time periodically. You would need a method like:
- (void) checkPlaybackTime:(NSTimer *)theTimer
{
float seconds = audioPlayer.currentTime;
if (seconds => 10.5 && seconds < 11.5) {
// do something
} else if (seconds >= 22.5 && seconds < 23.5) {
// do something else
}
}
Then set up the NSTimer object to call this method every second (or whatever interval you like):
NSTimer *myTimer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(checkPlaybackTime:)
userInfo:nil
repeats:YES];
Check the NSTimer documentation for more details. You do need to stop (invalidate) the timer at the right time when you're through with it:
https://developer.apple.com/documentation/foundation/nstimer
EDIT: seconds will probably not be exactly 11 or 23; you'll have to fiddle with the granularity.