In my iOS app I have to create a Digital timer (Not the system Time) in the HH:MM:SS format which should start from 00:00:00 on tap of a button, can I make use of any standard library to do so? Or I should write my own logic?
So one thing you can do is to create a timer and remember time in which you created it.
#IBAction func buttonTapped() {
// Store date / time in which you tapped the button
self.initialDate = NSDate()
// Create timer that fires every second starting now (scheduled), and repeats
self.timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: Selector("timerTick"), userInfo: nil, repeats: true)
}
Then, when you have initial stuff, you can do timerTick method. In here, you get current date, do difference between your stored one and current and present it:
func timerTick() {
// Get calendar and components of the dates in interval <initialDate, currentDate>
let calendar = NSCalendar.currentCalendar()
let components = calendar.components(.CalendarUnitSecond | .CalendarUnitMinute | .CalendarUnitHour, fromDate: self.initialDate, toDate: NSDate(), options: NSCalendarOptions.allZeros)
// In this point you have minutes, seconds and hours, you can just present it
// "%02d:%02d:%02d" in format means "number, always at least 2 numbers, fill with zeroes if needed")
self.label.text = String(format: "%02d:%02d:%02d", components.hour, components.minute, components.second)
}
If you want to stop the timer, you can do it by calling self.timer.invalidate()
Hope it helps!
The iOS Foundation framework includes the NSDateFormatter class (and the NSDate data type) which does just this.
In your .m file add these properties:
#import "MyVC.h"
#interface MyVC()
#property (strong, nonatomic) NSTimer* timer; // our timer
#property (nonatomic) NSInteger secondsPassed; // how many seconds have been passed since the start of the timer
#end
In viewDidLoad or in the IBAction method of your UIButton:
- (void)viewDidLoad {
[super viewDidLoad];
self.myLabel.text = #"00:00:00"; // start text
// invoke updateTimer every second
self.timer = [NSTimer scheduledTimerWithTimeInterval: 1.0
target:self
selector:#selector(updateTimer)
userInfo:nil
repeats: YES];
}
This method will be called every second to update the UILabel
-(void) updateTimer{
NSInteger hours, minutes, seconds;
// increase the passed seconds
self.secondsPassed++;
// calculate the hours, minutes, seconds from the total number of seconds
hours = self.secondsPassed / 3600;
minutes = ( self.secondsPassed % 3600) / 60;
seconds = ( self.secondsPassed %3600) % 60;
// update the label with the time
self.myLabel.text = [NSString stringWithFormat:#"%02zd:%02zd:%02zd", hours, minutes, seconds];
}
alternatively you can use MZTimerLabel
Related
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
}
Please excuse my english, I'm not very fluent.
I have an application with 10 workouts, and i would like to give to each workout an amount of time, for ex :
1st workout : 30 seconds
2nd workout : 40 seconds
But for now, i only managed to give each workout the same amount of seconds for all workout..
This is what i used to work this :
#define EXERCISELENGTH 30
#define EXERCISELENGTH1 40
#define RESTLENGTH 10
- (void)tick
{
if (self.inCountdown && (countdown == 0)) {
self.inCountdown = NO;
self.inExercise = YES;
countdown = EXERCISELENGTH;
[self workoutUI];
[self updateUI];
workoutIndex++;
}
if (self.inExercise && (countdown == 0)) {
self.inExercise = NO;
self.inRest = YES;
countdown = RESTLENGTH;
[self restUI];
[self updateUI];
}
if (self.inRest && (countdown == 0)) {
self.inRest = NO;
self.inExercise = YES;
countdown = EXERCISELENGTH;
[self workoutUI];
[self updateUI];
workoutIndex++;
if (workoutIndex == 10) {
[self doneUI];
[self.timer invalidate];
return;
}
}
[self updateUI];
countdown--;
}
Any help would be appreciated.
Thank you
you have many options:
If you want to call a workout method every 30 seconds, you use this:
[NSTimer scheduledTimerWithTimeInterval:30.0
target:self
selector:#selector(workoutMethod)
userInfo:nil
repeats:YES];
If you want to call the workout method after 30 seconds, you use this:
[self performSelector:#selector(workoutMethod) withObject:nil afterDelay:30.0];
If you want to start a countdown timer after you start the workout method, you use this example : CountDown Timer ios tutorial?
If you want a countdown timer, you should set up a repeating NSTimer that triggers an action that updates the display every second or so. However, that action should calculate the time remaining using the system time. So, your code should:
get the current time and calculate the expected finish time according to the desired time period (20 sec, 30 sec, etc.)
start a repeating timer with a 1 sec interval
Your timer's action should:
get the current system time
compare to the expected finish time
update the display
invalidate itself if it has reached the finish time
What you don't want to do is to rely on the timer to trigger at exactly 1 second intervals. NSTimer's resolution is such that each 'tick' can be off by as much as 0.1 sec, which means that 30 ticks could be off by up to 3 seconds. Using the system time, e.g. CACurrentMediaTime(), will provide a much more accurate clock and avoid accumulation of error.
I'm starting out my iOS experience with a project that incorporates a large clock as a portion of the user interface. When I try to incorporate a user-adjustable toggle to switch from 12-hr to 24-hr time format, I get issues. After the initial user change of the on-screen toggle, the displayed time "flickers" between the 2 formats.
My approach:
viewDidLoad sets the attribute used for 12-hr time format and calls the startTimer method
startTimer method: NSDateFormatter is set. NSTimer is initiated with scheduled interval, passing the DateFormatter to the upDateTime method
updateTimer method checks current time and sets the on-screen
If the user, clicks the on-screen 12-hr/24-hr segmented control, the IBAction invalidates the timer and passes the desired time format attribute to the startTimer method
Testing/Observations:
Code below shows an approach where I modified the time format by setting the Locale and passing it to the DateFormatter. I thought this might be an issue conflicting with the device Locale. I tried using a custom Date Formatter (example #"hh:mm a") instead and had the same flickering issue.
NSTimer scheduled interval shows as 1 sec. I have this for testing, but saw same behavior at 0.1 sec.
NSLog calls within the updateTime method show that the DateFormatter object id and the displayed time are changing between successive loops, even though the user did not adjust the toggle switch.
I suspect this may have something to do with how the Timer is initiated and stopped. Perhaps the old "calls" with the previous format is still running in the background? I experimented with moving the [updateTimer invalidate] to a couple of places in the methods without success
Root Question:
Any suggestions or better approach to having an on-screen clock with a user switch for 12-hr vs. 24-hr time format? Any reason why the displayed time format keeps cycling?
Code:
- (void)startTimer:(NSString *)displayedClockMode {
// using locale within formatter overrides device system behavior chosen by user
NSString *localeValue = nil;
NSDateFormatter *timeFormatter = [[NSDateFormatter alloc] init];
if([displayedClockMode isEqual:#"12-hr"]){
//then 12 hr format - based on US locale
localeValue = #"en_US_POSIX";
//[timeFormatter setDateFormat:#"hh:mm a"];
}
else { //assume no other value exists
// 24 hr format - based on GB locale
localeValue = #"en_GB";
//[timeFormatter setDateFormat:#"HH:mm"];
}
NSLocale *clockLocale = [NSLocale localeWithLocaleIdentifier:localeValue];
[timeFormatter setLocale:clockLocale];
[timeFormatter setTimeStyle:NSDateFormatterMediumStyle];
//stop timer before re-starting with new format
[self.updateTimer invalidate];
self.updateTimer = nil;
NSTimer *updateTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(updateTime:) userInfo:timeFormatter repeats:YES];
}
- (void)updateTime:(NSTimer *)updateTimer {
NSDate *currentTime = [NSDate date];
NSLog(#"old display time: %#",self.displayedTime.text);
self.displayedTime.text = [updateTimer.userInfo stringFromDate:currentTime];
NSLog(#"new display time: %#",self.displayedTime.text);
NSLog(#"new timeformatter: %#",updateTimer.userInfo);
}
- (IBAction)displayedTimeMode:(id)sender {
[self.updateTimer invalidate];
self.updateTimer = nil;
NSString *timeFormat = nil;
if(self.displayedTimeToggle.selectedSegmentIndex == 0){
//if 0, then 12 hr format
timeFormat = #"12-hr";
}
else {
// is 1, 24 hr format
timeFormat = #"24-hr";
}
[self startTimer:timeFormat];
}
The problem is that you have two different updateTimers -- self.updateTimer the class variable and updateTimer the local variable. You're invalidating the class variable, but initializing and running multiple local NSTimers with different locales during each call to startTimer. That's why you see this "flickering" -- it's because multiple NSTimers are setting the label using different localeValues.
To fix this, change:
NSTimer *updateTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(updateTime:) userInfo:timeFormatter repeats:YES];
to
self.updateTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(updateTime:) userInfo:timeFormatter repeats:YES];
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
I am looking for a way to write a for loop that iterates over an NSDate. Each loop should increment the NSDate by 10 seconds.
I want to have 2 timestamps. Say timestamp A is Midnight Monday and timestamp B is Midnight Tuesday.
What I then want is some code to say for A to B incrementing at 10 second intervals between the two points in time, use the timestamp at current position, and the timestamp at the last position, so I can run a query based on the intervals.
Would someone be so good as to show me how I would do this?
Many thanks
A for loop needs three parts, an initialisation, a compare, and an increment. It could look like this:
for (NSDate *date = startDate; // initialisation
[date compare:endDate] == NSOrderedAscending; // compare
date = [date dateByAddingTimeInterval:10]) // increment
{
// do something with date here, eg:
NSDate *rangeStart = date;
NSDate *rangeEnd = [date dateByAddingTimeInterval:10];
[runQuery begin:rangeStart end:rangeEnd];
}
You might prefer to refactor to use a while loop so that the dateByAddingTimeInterval doesn't need to be repeated.
This is the same structure as a normal for loop:
for (int i = 0; // initialisation
i < 10; // compare
i++) // increment
{
// do something with i here
}
It sounds like you may want to look into NSTimer's timerWithTimeInterval. The interface looks like this:
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)seconds target:(id)target selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)repeats
and then you can start one ten seconds later using:
initWithFireDate:interval:target:selector:userInfo:repeats:
You can sign up for a call that is sent every seconds as shown below:
// SomeClass.m
#import "SomeClass.h"
#interface ()
#property (nonatomic, strong) NSTimer timer1;
#property (nonatomic, strong) NSTimer timer2;
#end
#implementation SomeClass
- (id)init
{
self = [super init];
if (self) {
NSDate* date = [[NSDate date] dateByAddingTimeInterval:10];
self.timer1 = [NSTimer timerWithTimeInterval:10 target:self selector:#selector(timerFireMethod:) userInfo:nil repeats:YES];
self.timer2 = [[NSTimer alloc] initWithFireDate:date interval:10 target:self selector:#selector(timerFireMethod:) userInfo:nil repeats:YES];
}
return self;
}
- (void)timerFireMethod:(NSTimer *)timer
{
if (timer == self.timer1) {
NSLog(#"timer1 fired");
}
}
#end
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];
}