NSTimer time increment - objective-c

I'm a beginner in obj-C for iOS platform and am trying to build a few simple project to build my foundation.
I have a button which increase the NSTimer time for the label, but when I use NSLog to log the time, it uses the value before time increment was implemented. I need to be able to log a updated time (after increment), as I require that value and am implementing more function into the IBAction after I solve this portion.
E.g at 15min I press, the NSLog will read it as "00:15:00.0" rather than "00:35:00.0".
- (IBAction)onSkipPressed:(id)sender {
startDate = [startDate dateByAddingTimeInterval:-1200];
NSLog(#"%#",self.timeLabel.text);
}
Any one know the reason for this issue? And how should I solve it such that NSLog will read it as "00:35:00.0" if I invoke this IBAction at 15min.
EDIT - The start button will start the timer and timeLabel will get the string. Sorry for missing out such a important detail. I don't think there are any other code in the project which is related to this functionality already. Thank you for pointing it out to me.
- (void)updateTimer
{
NSDate *currentDate = [NSDate date];
NSTimeInterval timeInterval = [currentDate timeIntervalSinceDate:startDate];
NSDate *timerDate = [NSDate dateWithTimeIntervalSince1970:timeInterval];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"HH:mm:ss.S"];
[dateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0.0]];
NSString *timeString=[dateFormatter stringFromDate:timerDate];
timeLabel.text = timeString;
}
my IBAction to fire the timer
- (IBAction)onStartPressed:(id)sender {
startDate = [NSDate date];
gameTimer = [NSTimer scheduledTimerWithTimeInterval:1.0/10.0
target:self
selector:#selector(updateTimer)
userInfo:nil
repeats:YES];
//hide start button and show timeLabel
startButton.hidden=true;
timeLabel.hidden=false;
}

I went back to do a few revision with tutorials involving NSTimer. And turns out all I was missing was 1 line [self updateTimer]
- (IBAction)onSkipPressed:(id)sender {
startDate = [startDate dateByAddingTimeInterval:-1200];
[self updateTimer];
NSLog(#"%#",self.timeLabel.text);
}
This solve my issue and the timeLabel.text is updated for me to log the information.

Um, why are you passing in negative 1200?
// this subtracts 1200 seconds from your date, no?
startDate = [startDate dateByAddingTimeInterval:-1200];
Shouldn't you do:
// add 30 minutes (60 seconds a minute x 30 minutes) to your time interval
startDate = [startDate dateByAddingTimeInterval:(60 * 30)];
...
NSLog(#"%#",self.timeLabel.text);
Or am I misunderstanding something?

Related

How to get the millisecond part of current time?

I'm trying to record an event at its happening time by accuracy of milliseconds. Below is my code:
NSDate * now = [NSDate date];
NSDateFormatter *outputFormatter = [[NSDateFormatter alloc] init];
[outputFormatter setDateFormat:#"HH:mm:ss.SSS"];
NSString *timeString = [outputFormatter stringFromDate:now];
However, the results are as this:
45:11.3
It looks like time record has been rounded to 0.1 second, while all the second and third decimal accuracy is lost. Can anyone suggest where the problem is and how I can get the millisecond information?
Thank you all!
Paul
Your code works fine for me. Here it is (in Swift):
let f = NSDateFormatter()
f.dateFormat = "HH:mm:ss.SSS"
let timeString = f.stringFromDate(NSDate()) // "20:26:19.118"
You do not show the code by which you read the resulting timeString but perhaps the fault lies in how you are doing that. It is odd that the Hour part is missing from your output too.

How to display the time whenever value of the current minute changes (with Cocoa)?

Ok this one really has me stumped. The challenge seems simple but has a few restrictions that are really throwing me off.
Gist of the program:
(1) When the minute changes the application should output the current time with the format HH:mm:ss.
(2) When the second changes the application should output a character for every second and a different character for every tenth second (i.e. ".........|.........|"). Here it is important to note that I cannot just count seconds; rather, the program must know the value of the second of the current time and output the corresponding character only if it is divisible by 10.
Restrictions:
(1) The program can have 2 classes -- one for the minute display and one for the second display.
(2) Each class can contain only one void method that takes a timer as a parameter.
(3) The main function will create an object of each class and 2 NSTimers. The first timer must have its interval set at 0.5 and must target the minutes object. The second timer must have its interval set at 0.1 and must target the seconds object.
I would love to just set the first time to fire every minute and the second timer to fire ever second, but I am not allowed to tamper with that. This is as far as I got before I was stumped:
main.m:
MinuteDisplay *minutes = [[MinuteDisplay alloc] init];
__unused NSTimer *minutesTimer =
[NSTimer scheduledTimerWithTimeInterval:0.5
target:minutes
selector:#selector(minuteChange:)
userInfo:nil
repeats:YES];
[[NSRunLoop currentRunLoop] run];
MinuteDisplay.m:
- (void)minuteChange:(NSTimer *)timer
{
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"HH:mm:ss"];
NSDateFormatter *testFormatter = [[NSDateFormatter alloc] init];
[testFormatter setDateFormat:#"ss"];
NSDate *now = [NSDate date];
NSString *nowString = [dateFormatter stringFromDate:now];
NSString *testString = [testFormatter stringFromDate:now];
if ([testString isEqual:#"00"]) {
NSLog(#"%#", nowString];
}
}
Example output:
01:09:00
//.5 second interval
01:09:00
I haven't even bothered with the second half of this challenge yet because I got stumped here. This technically works, but I know I'm doing it wrong, since the output isn't dependent upon the program knowing the value of the minute has changed. Also, because the timer is set to fire every .5 seconds, I get two lines of output every minute instead of just one (which does not satisfy the specs).
Can anyone suggest where to go from here? This one really has my mind boggled.
If you can not modify the timer interval, the other alternative is to use an instance variable in your MinuteDisplay class, for example, displayString. And modify the minuteChange method as follows:
- (void)minuteChange:(NSTimer *)timer
{
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"HH:mm:ss"];
NSDateFormatter *testFormatter = [[NSDateFormatter alloc] init];
[testFormatter setDateFormat:#"ss"];
NSDate *now = [NSDate date];
NSString *nowString = [dateFormatter stringFromDate:now];
NSString *testString = [testFormatter stringFromDate:now];
if ([testString isEqual:#"00"]) {
if(![nowString isEqualToString:dispString]) {
dispString = nowString;
NSLog(#"*************** %#", dispString);
}
}
}
This will make sure that it is logged only once a minute
Working minuteChange method:
- (void)minuteChange:(NSTimer *)timer
{
NSDate *now = [NSDate date];
if (!self.lastTime) {
self.lastTime = [NSDate date];
}
if (now > self.lastTime) {
if ([[self.seconds stringFromDate:now] isEqualToString:#"00"]) {
printf("\n%s", [[self.output stringFromDate:now] UTF8String]);
self.lastTime = now;
}
}
self.output = [[NSDateFormatter alloc] init];
[self.output setDateFormat:#"HH:mm:ss"];
self.seconds = [[NSDateFormatter alloc] init];
[self.seconds setDateFormat:#"ss"];
}
Working secondChange method:
- (void)secondChange:(NSTimer *)timer
{
long secondsSince1970 = time(NULL);
if (!self.lastSecond) {
self.lastSecond = secondsSince1970;
}
if (secondsSince1970 > self.lastSecond) {
if (secondsSince1970 % 10 == 0) {
printf("|");
self.lastSecond = secondsSince1970;
} else {
printf(".");
self.lastSecond = secondsSince1970;
}
}
}
Changes to minuteChange:
The most important change was the addition of the lastTime ivar to the class. Now it's simple...if lastTime is null, assign it the value of the current date. If current time is greater than last time, test if it's a new minute and then output the time if it is. ONLY update lastTime if a new minute was output (this ensures that the time is only displayed once per minute).
secondChange:
Took a different approach with this one and now I'm wishing I had gone with straight C the first time around. This one is so simple! Check if the current second is greater than the last second. If so, check if it's divisible by 10. If so, print "|". If not, print ".". Super easy and actually more accurate than setting the timer to fire every second. Sheesh.

How does this NSTimer Pause / Resume work?

I'm using the following code I found to pause and unpause an NSTimer. I hate not knowing how code works, so I'm hoping someone can give me a brief explanation of what's happening here. What's the significance of the two NSDate objects used in the pause method? What's the significance of the -1 in the resume method? Thanks in advance!
- (void) pause
{
pauseStart = [NSDate dateWithTimeIntervalSinceNow: 0];
previousFireDate = [self fireDate];
[self setFireDate: [NSDate distantFuture]];
}
- (void) resume
{
//
float pauseTime = -1 * [pauseStart timeIntervalSinceNow];
[self setFireDate: [previousFireDate initWithTimeInterval: pauseTime sinceDate: previousFireDate]];
}
Let's assume that your timer would fire today at 11:00.
(For simplicity, I use only HH:MM for dates in the comments.)
At 09:00 the pause action is executed:
// pauseStart = current date/time ("now") = 09:00:
pauseStart = [NSDate dateWithTimeIntervalSinceNow: 0];
// previousFireDate = 11:00:
previousFireDate = [self fireDate];
// set new fireDate to something far far away to prevent timer from firing:
[self setFireDate: [NSDate distantFuture]];
At 09:10 the resume action is executed:
// pauseTime = difference between "pause" and "resume" (now) = 10 minutes:
float pauseTime = -1 * [pauseStart timeIntervalSinceNow];
// set new fireDate = previous fireDate + pauseTime = 11:10:
[self setFireDate: [previousFireDate initWithTimeInterval: pauseTime sinceDate: previousFireDate]];
The factor (-1) is required because pauseStart is earlier than "now".
So pause "pauses" the timer by setting the fire date to a distant date in the future (Jan 1st, 4001).
resume restores the fire date to the old fire date plus the time interval that elapsed
between "pause" and "resume".
Remark: As #JeremyP noticed in his comment,
[self setFireDate: [previousFireDate initWithTimeInterval: pauseTime sinceDate: previousFireDate]];
is not correct, because it sends an init message to the already initialized object previousFireDate. Correct (and even shorter) is:
[self setFireDate:[previousFireDate dateByAddingTimeInterval:pauseTime]];

Frames per second timecode display with NSTimer

I am working on an iPhone/iPad app that needs to display a running timecode clock. I have gotten it to display the correct hours, minutes, and seconds with no problem using this code:
- (void) viewDidLoad {
// Start the Timer method for here to start it when the view loads.
runTimer = [NSTimer scheduledTimerWithTimeInterval: .01 target: self selector: #selector(updateDisplay) userInfo: nil repeats: YES];
}
- (void)updateDisplay {
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
NSDate *date = [NSDate date];
// Display each hour, minute, second and frame.
[formatter setDateFormat:#"hh"];
[timecodeHourLabel setText:[formatter stringFromDate:date]];
[formatter setDateFormat:#"mm"];
[timecodeMinuteLabel setText:[formatter stringFromDate:date]];
[formatter setDateFormat:#"ss"];
[timecodeSecondLabel setText:[formatter stringFromDate:date]];
}
The issue is when I need to display frames per second. I know that calculating 1/24 * 1000 gives me how many milliseconds are in one frame. I just don't know how to make the NSDate and NSTimer functions work with this code and allow it to update a UILabel as quickly as needed for running timecode.
Any suggestions?
If your timer is running with the period of 0.01 sec, then it's frequency is 100 frames/sec (well, it's better to say it has 100 function calls per second). But if you need to display the precise time period (cause sometimes the timer may be delayed), then you need to store the previous call date and then use
NSDate* new_date = [NSDate date];
double freq = 1.0 / [new_date timeIntervalSinceDate: old_date];
[old_date release];
old_date = [new_date retain];
Here's a Processing/Java equivalent that's fairly straightforward to repurpose.
String timecodeString(int fps) {
float ms = millis();
return String.format("%02d:%02d:%02d+%02d", floor(ms/1000/60/60), // H
floor((ms/1000/60)%60), // M (edit: added %60)
floor(ms/1000%60), // S
floor(ms/1000*fps%fps)); // F
}

NSDate - Offsetting the Time Zone

I must be missing something small her but can't figure it out. Trying to create a date for comparison, but I can't seem to offset currentDate from GMT to EST:
// current date (gmt) //
NSDate *currentDate = [NSDate date];
NSTimeZone *currentDateTimeZone = [NSTimeZone timeZoneWithAbbreviation:#"EST"];
NSDateFormatter *currentDateFormat = [[NSDateFormatter alloc]init];
[currentDateFormat setTimeZone:currentDateTimeZone];
[currentDateFormat setDateFormat:#"yyyy-MM-dd HH:mm:ss zzz"];
NSString *currentDateString = [currentDateFormat stringFromDate:currentDate];
NSLog(#"currentDateString: %#", currentDateString); // returns 2011-01-05 13:30:30 EST
NSDate *currentDateWithOffset = [currentDateFormat dateFromString:currentDateString];
NSLog(#"currentDateWithOffset: %#", currentDateWithOffset); // returns 2011-01-05 18:30:30 +0000
Thanks!
Edit:
I'm calling a method in a separate class (trying to make this portable) using the following line:
[Expiration expires:[[NSDate alloc] initWithString:#"2011-01-07 12:00:00 +0000"] within:1.0]
in the expires method, I have these lines:
NSComparisonResult comparison = [currentDateWithOffset compare:expires]; // check for a fixed date to disable the demo
double withinRange = [installDate timeIntervalSinceDate:currentDateWithOffset]; // check for number of seconds between "within" and the install date
I'm then comparing these two values like so:
if(withinRange >= within && withinRange > 0.0) {
// app is expired //
}
else {
// app is still enabled (so far...) //
if(comparison == NSOrderedDescending || comparison == NSOrderedSame) {
// app is expired //
}
else {
// app is still enabled //
}
}
Does this help? Thanks for your patience!
Edit:
Here's the entire expires:within method as it currently stands...
+(BOOL)expire:(NSDate*)expires within:(double)within {
// default expired value //
BOOL expired = NO;
// convert within value from days to seconds //
within *= 24.0 * 60.0 * 60.0;
// current date (gmt) //
NSDate *currentDate = [NSDate date];
// install date //
NSDate *installDate = [[NSUserDefaults standardUserDefaults]objectForKey:#"installDate"];
// check for a value in installDate //
if (nil == installDate) {
// app is running for the first time //
[[NSUserDefaults standardUserDefaults]setObject:currentDate forKey:#"installDate"];
[[NSUserDefaults standardUserDefaults]synchronize];
installDate = currentDate;
}
if([installDate timeIntervalSinceNow] < (-within)) {
expired = YES;
}
else {
if([expires timeIntervalSinceNow] < 0) {
expired = YES;
}
}
NSLog(#"installDate:%#", installDate);
NSLog(#"expires:%#", expires);
NSLog(#"currentDate:%#", currentDate);
return expired;
}
I'm then calling it from another class with
message.text = (YES == [Expiration expire:[[NSDate alloc] initWithString:#"2011-01-07 12:00:00 -0500"] within:(0.015625/2)]) ? #"This App is Expired" : #"This App is Active";
When running in the simulator (fresh app install), NSLog displayed this...
[Session started at 2011-01-06 10:43:46 -0500.]
2011-01-06 10:43:48.146 TimeBasedDemo[14717:207] installDate:2011-01-06 15:43:48 +0000
2011-01-06 10:43:48.147 TimeBasedDemo[14717:207] expires:2011-01-07 17:00:00 +0000
2011-01-06 10:43:48.147 TimeBasedDemo[14717:207] currentDate:2011-01-06 15:43:48 +0000
None of these answers gave me an NSDate object with the current, local date. So here's how I did it:
NSDate *currentDateWithOffset = [NSDate dateWithTimeIntervalSinceNow:[[NSTimeZone localTimeZone] secondsFromGMT]];
An NSDate object represents an instant in time irrespective of time zone and calendar considerations. Time zone info is relevant when you print or parse a date, but it is not stored within the NSDate.
Say you are creating your expiration date like this:
NSDate *exp=[[NSDate alloc] initWithString:#"2011-01-07 12:00:00 +0000"]
That says you want the expiration to occur at noon GMT, on the 7th Jan. If you want it to expire at noon EST, create it with -0500 instead. What you should not have to do is mess with the current time when you do a comparison.
An easy way just to see if the time has passed is then
if ([exp timeIntervalSinceNow]<0) { /* it's expired */ }
and you can see if within seconds have passed since the install date like this:
if ([installDate timeIntervalSinceNow]<(-within)]) { /* it's expired */}
In Cocoa, NSDate is an abstract representation of a date with no time zone information applied. Note that currentDateWithOffset is the same date as the date string, just in a different time zone (five hours ahead). This is expected behavior, as NSDate does not persist the time zone used to create it.
I tinkered around a bit more and found a way to 'cheat' the offset to suit my needs. From other reading, I'm guessing that NSCalendar might be a better long term-solution, but for now I ended up changing
NSDate *currentDateWithOffset = [currentDateFormat dateFromString:currentDateString];
to
NSDate *currentDateWithOffset = [[NSDate alloc] initWithString:[NSString stringWithFormat:#"%# +0000", currentDateString]];
That got me the results I needed and works in the later comparisons I'm using. Thanks for the background info all!