What's the minimum valid time interval of an NSTimer? - ios

I want to use NSTimer to increase the number which show on a label.
Here is my code:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.numberLabel = [[UILabel alloc]initWithFrame:CGRectMake(90, 90, 90, 30)];
[self.view addSubview:self.numberLabel];
self.timer = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:#selector(refreshText) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop]addTimer:self.timer forMode:NSRunLoopCommonModes];
}
- (void)refreshText{
NSDate *beginDate = [NSDate date];
static NSInteger a = 0;
a ++;
self.numberLabel.text = [NSString stringWithFormat:#"%ld",a];
if (a == 1000) {
NSDate *endDate = [NSDate date];
NSTimeInterval durationTime = [endDate timeIntervalSinceDate:beginDate];
NSTimeInterval intervalTime = self.timer.timeInterval;
NSLog(#"durationTime = %f",durationTime);
NSLog(#"intervalTime = %f",intervalTime);
[self.timer invalidate];
self.timer = nil;
}
}
and the console showed:
then I changed the timer's timeInterval from 0.01 to 0.001,the console showed:
What confused me is that why the durationTime is not 0.0000056 when the timeInterval is 0.001.What's more,is there a min value for NSTimer's timeInterval we can set?

The time period of an NSTimer is a value of type NSTimeInterval, while this provides sub-millisecond precision that does not help you. From the start of the NSTimer documentation:
Timers work in conjunction with run loops. Run loops maintain strong references to their timers, so you don’t have to maintain your own strong reference to a timer after you have added it to a run loop.
To use a timer effectively, you should be aware of how run loops operate. See Threading Programming Guide for more information.
A timer is not a real-time mechanism. If a timer’s firing time occurs during a long run loop callout or while the run loop is in a mode that isn't monitoring the timer, the timer doesn't fire until the next time the run loop checks the timer. Therefore, the actual time at which a timer fires can be significantly later. See also Timer Tolerance.
So the minimum time interval for an NSTimer is tied to the the length of a run loop iteration. While internal optimisations, if they exist, could fire a timer as soon as it is set if the interval is really small in general the shortest period you'll get is dependent on the remaining execution time of the run loop iteration in which the timer is set, which is pretty much indeterminate for general purpose programming.
If you really need a high-resolution timer (see #bbum's comment on your question) then you'll need to research that topic - just search something like "high resolution timing macOS" as a starting point.
HTH

There is a better approach to your problem. Use CADisplayLink instead of NSTimer. CADisplayLink allows you to update a UI every time the screen refreshes - as quickly as possible. There is no point to updating the UI more often than the screen can refresh it, so NSTimer is not the best tool fast UI updates.
func beginUpdates() {
self.displayLink = CADisplayLink(target: self, selector: #selector(tick))
displaylink.add(to: .current, forMode: .defaultRunLoopMode)
}
func tick() {
// update label
}
func endUpdates(){
self.displayLink.invalidate()
self.displayLink = nil
}

Related

How to Pause and Resume Multiple NSTimer?

I want to play a sound (bells) with multiple scheduledTimerWithTimeInterval set like Every 5, Every 9 Every 35 after 6 ....etc now I Bells start playing with Interval 55, 56, 59, 60, 45, 42....but i want to pause time on 52 then i invalidate all timer because not got pause method or property of NSTimer after that I again start timer is start with new interval like 48 , 47 ,43 .... but I want maintain old Interval So any one have idea about it please help me .
-(void) bellsSchedual {
arrBellsListAllData = [DBModel getDataFromBellsList:prop.userId];
DBProperty *bellProp = [[DBProperty alloc]init];
for (int i = 0; i < arrBellsListAllData.count; i++) {
bellProp=[arrBellsListAllData objectAtIndex:i];
NSString* bellsTime=bellProp.bTime;
if ([bellProp.bTimeSchedule isEqualToString: #"after"]) {
NSTimer* bellTimer = [NSTimer scheduledTimerWithTimeInterval: [bellsTime intValue]
target: self
selector: #selector(playSound:)
userInfo: nil
repeats: NO]
[arrTimers addObject:bellTimer];
} else if ([bellProp.bTimeSchedule isEqualToString: #"every"]) {
NSTimer* bellTimer = [NSTimer scheduledTimerWithTimeInterval: [bellsTime intValue]
target: self
selector: #selector(playSound:)
userInfo: nil
repeats: YES];
[arrTimers addObject: bellTimer];
}
}
}
Thanks
If I understand your question correctly you require a timer that can be paused, and have correctly determined that an NSTimer cannot be paused.
What you could consider in outline is:
Your own timer class which provides a method, say tick, which causes the timer to progress. Use an instance of this class for each bell; and
Use a repeating NSTimer to provide the tick - when it fires it calls the tick methods of all your registered custom timers. Invalidating this NSTimer will stop the ticks, effectively pausing the custom timers. Creating an new NSTimer to provide the ticks will restart your custom timers.
HTH
Question is not really clear…
As far as I understand, each bell has it's own timer and varying interval time.
Add a property bCurrentTime to your bellProp. You set it to bTime when you reset your system or create the bellProp object.
Use this value when you fire your timers instead of the bTime.
In your playSound method, you update this value with the current time interval.
This way, when you invalidate all your timers and restart them, all previous times are stored.
Add a reset method to set all bells bCurrentTime to bTime.
If you want control over your timers, you need to make them properties. That way you can start, stop, and do whatever you want within the entire scope of the file under which you declared them.

Obj-c NSTimer - Slower than expected

I have to do heavy operation every < 1s. I'm using NSTimer, but its not that accurate as i expected... I'm using 2 timers. One to update data in my model and 2nd to update my views (few labels and custom 'fancy-circle' progress bar)
Ok... my code:
_valueTimer =
[NSTimer scheduledTimerWithTimeInterval:0.01
target:self
selector:#selector(counterTask)
userInfo:nil
repeats:YES];
_progressTimer =
[NSTimer scheduledTimerWithTimeInterval:0.1
target:self
selector:#selector(updateViews) // heavy stuff here
userInfo:nil
repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:_progressTimer
forMode:NSRunLoopCommonModes];
As you can see _progressTimer selector is run every 0.1 sec.
One of the views is my HH:MM:SS time (data from first timer!). It display different time than real time when my progressbar is updating (heavy operation) -> 10 sec in app == 12 sec in real time... Its too much difference. When I comment my progress bar update - it all works correctly
Could you tell me how to force my timer to run exactly after my interval? Or skip one cycle when its too much for it to handle? The most important thing is to not slow down...
Thanks
You can try using a CADisplayLink timer to synchronize animation/drawing with the refresh rate of the device's display (ie, 60 times per second for iOS devices).
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:#selector(handleDisplayLinkTimerFired:)];
//_displayLink.frameInterval = 3; // slows the updates for debugging
[_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
Because your method (here, handleDisplayLinkTimerFired:) will be called frequently, you don't want to do heavy processing or you risk dropping frames.
Ok. I found solution. Maybe not perfect, but it works.
In counterTask method instead of increasing my _actualTime value by 0.01... I calculate real interval between 'ticks' like this:
NSDate *currentDate = [NSDate date];
NSTimeInterval timeInterval = [currentDate timeIntervalSinceDate:startDate];
startDate = currentDate;
_actualTime += timeInterval;

Play sounds after different time intervals

I want to use a speech synthesised sentence in an application demo. After pressing a button, a timer runs and after for example 12 seconds the first sentence is being spoken, then after 1.30min and so on.
The approach I was thinking of, is an NS Timer. But as far as I can see it only plays after a defined time. So do I need for any timespan a new timer? or can I track the time left and invoke a method call when a specific time is reached?
Thanks
I would do this using a dictionary with NSNumbers as the keys (representing seconds), and the sentence you want spoken as the value. Use a simple timer firing once every second, that updates an int, which (when converted to an NSNumber) would be checked against the dictionaries keys. I'm just logging in this example, but this should get you started,
- (void)viewDidLoad {
[super viewDidLoad];
self.dict = #{#3:#"First", #5:#"second", #11:#"Third", #35:#"Fourth"};
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(doStuff:) userInfo:nil repeats:YES];
}
-(void)doStuff:(NSTimer *) aTimer {
static int i = 0;
i++;
if ([self.dict.allKeys containsObject:#(i)]) {
NSLog(#"%#", self.dict[#(i)]);
}
}

Counter implementation using NsTimer in objective-c

I have surfed on a bunch of resources from the internet but still couldn't get any idea of what I'm trying to implement.
I would like to record user preferences by detecting how much time they have stayed in each information pages.
In order to make this question simpler, that says I have a entrance page with 5 different theme pages which represent different information.
I would like to know which page is the page that user most interesting.
What I wish to do is to put a counter in each theme pages and calculate how much time they stay in that page (the counter should be able to pause for reentrance), and then when I press a button on the entrance page, an alert will tell me which page is the page that user spent most of time on it.
I hope this make sense!
Does anyone have any experience on this? I would be most appreciative if anyone can provide some codes and examples for me.
ViewController A:
- (void)viewDidLoad {
[super viewDidLoad];
//create iVar of NSInteger *seconds
seconds = 0;
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:#selector(increaseTimeCount) userInfo:nil repeats:YES];
[timer fire];
}
- (void)increaseTimeCount {
seconds++;
}
- (void)dealloc {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
// you can add to array too , if you want and get average of all values later
[defaults setInteger:seconds forKey: NSStringFromClass(self)];
}
now in Entrance View ..
get the time as
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSInteger *secondsInView = [defaults integerForKey:NSStringFromClass(View1ClassName)];
Firstly I'd like to draw your attention to the Cocoa/CF documentation (which is always a great first port of call). The Apple docs have a section at the top of each reference article called "Companion Guides", which lists guides for the topic being documented (if any exist). For example, with NSTimer, the documentation lists two companion guides:
Timer Programming Topics for Cocoa
Threading Programming Guide
For your situation, the Timer Programming Topics article is likely to be the most useful, whilst threading topics are related but not the most directly related to the class being documented. If you take a look at the Timer Programming Topics article, it's divided into two parts:
Timers
Using Timers
For articles that take this format, there is often an overview of the class and what it's used for, and then some sample code on how to use it, in this case in the "Using Timers" section. There are sections on "Creating and Scheduling a Timer", "Stopping a Timer" and "Memory Management".There are a couple of ways of using a timer. From the article, creating a scheduled, non-repeating timer can be done something like this:
1) scheduled timer & using selector
NSTimer *t = [NSTimer scheduledTimerWithTimeInterval: 2.0
target: self
selector:#selector(onTick:)
userInfo: nil repeats:NO];
if you set repeats to NO, the timer will wait 2 seconds before
running the selector and after that it will stop;
if repeat: YES, the timer will start immediatelly and will repeat
calling the selector every 2 seconds;
to stop the timer you call the timer's -invalidate method: [t
invalidate]; As a side note, instead of using a timer that doesn't
repeat and calls the selector after a specified interval, you could
use a simple statement like this:
[self performSelector:#selector(onTick:) withObject:nil afterDelay:2.0];
this will have the same effect as the sample code above; but if you want to call the selector every nth time, you use the timer with repeats:YES;
2) self-scheduled timer
NSDate *d = [NSDate dateWithTimeIntervalSinceNow: 60.0];
NSTimer *t = [[NSTimer alloc] initWithFireDate: d
interval: 1
target: self
selector:#selector(onTick:)
userInfo:nil repeats:YES];
NSRunLoop *runner = [NSRunLoop currentRunLoop];
[runner addTimer:t forMode: NSDefaultRunLoopMode];
[t release];
this will create a timer that will start itself on a custom date
specified by you (in this case, after a minute), and repeats itself
every one second
3) unscheduled timer & using invocation
NSMethodSignature *sgn = [self methodSignatureForSelector:#selector(onTick:)];
NSInvocation *inv = [NSInvocation invocationWithMethodSignature: sgn];
[inv setTarget: self];
[inv setSelector:#selector(onTick:)];
NSTimer *t = [NSTimer timerWithTimeInterval: 1.0
invocation:inv
repeats:YES];
and after that, you start the timer manually whenever you need like this:
NSRunLoop *runner = [NSRunLoop currentRunLoop];
[runner addTimer: t forMode: NSDefaultRunLoopMode];
And as a note, onTick: method looks like this:
-(void)onTick:(NSTimer *)timer {
//do smth
}
Try this simple method:
- (void)viewDidLoad
{
[super viewDidLoad];
count = 0; // Declare int * count as global variable;
[NSTimer scheduledTimerWithTimeInterval:2 target:self selector:#selector(timerAction) userInfo:nil repeats:YES];
}
- (void)timerAction
{
[self custom_method:count++]
}
Might I suggest a different route. If you take the time since reference date, when the user enters the page:
NSTimeINterval time = [NSDate timeIntervalSinceReferenceDate]
then do the same when they leave the page and compare them.
timeOnPage = time - time2;
This is much more efficient than firing a timer on another thread unnecessary.
You do not need to use NSTimers for this at all.
Store the date/time when the user starts viewing, and calculate the time difference when they stop viewing using simple time arithmetic.
Exactly As Dave Wood says You should use date and time for starting and ending viewing that screen and calculate the difference and then save it to any integer variable.Using NSTimer will make the performance effect in your app and make the compiler busy while incrementing the count.

Add a running countup display timer to an iOS app, like the Clock stopwatch?

I'm working with an app that processes device motion events and updates interface in 5 second increments. I would like to add an indicator to the app that would display the total time the app has been running. It seems that a stopwatch-like counter, like the native iOS Clock app is a reasonable way to count time that the app has been running and display it to the user.
What I'm not sure of is the technical implementation of such a stopwatch. Here's what I'm thinking:
if I know how long between interface updates, I can add up seconds between events and keep a count of seconds as a local variable. Alternatively, a 0.5 second interval scheduled timer can provide the count.
If I know the start date of the app, I can convert the local variable to date for each interface update using [[NSDate dateWithTimeInterval:(NSTimeInterval) sinceDate:(NSDate *)]
I can use a NSDateFormatter with a short time style to convert the updated date to a string using stringFromDate method
The resulting string can be assigned to a label in the interface.
The result is that the stopwatch is updated for each "tick" of the app.
It appears to me that this implementation is a bit too heavy and is not quite as fluid as the stopwatch app. Is there a better, more interactive way to count up time that the app has been running? Maybe there's something already provided by iOS for this purpose?
If you look in the iAd sample code from Apple in the basic banner project they have a simple timer:
NSTimer *_timer;
_timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(timerTick:) userInfo:nil repeats:YES];
and the the method they have
- (void)timerTick:(NSTimer *)timer
{
// Timers are not guaranteed to tick at the nominal rate specified, so this isn't technically accurate.
// However, this is just an example to demonstrate how to stop some ongoing activity, so we can live with that inaccuracy.
_ticks += 0.1;
double seconds = fmod(_ticks, 60.0);
double minutes = fmod(trunc(_ticks / 60.0), 60.0);
double hours = trunc(_ticks / 3600.0);
self.timerLabel.text = [NSString stringWithFormat:#"%02.0f:%02.0f:%04.1f", hours, minutes, seconds];
}
It just runs from start up, pretty basic.
Almost what #terry lewis suggested but with an algorithm tweak:
1) schedule a timer
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(timerTick:) userInfo:nil repeats:YES];
2) when the timer fires, get the current time (that's the tweak, don't count ticks because if there is wobble in the timer, tick counting will accumulate the error), then update the UI. Also, NSDateFormatter is a simpler and more versatile way to format time for display.
- (void)timerTick:(NSTimer *)timer {
NSDate *now = [NSDate date];
static NSDateFormatter *dateFormatter;
if (!dateFormatter) {
dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.dateFormat = #"h:mm:ss a"; // very simple format "8:47:22 AM"
}
self.myTimerLabel.text = [dateFormatter stringFromDate:now];
}

Resources