NSDates and TimeIntervals - ios

I'm struggling to understand how to use NSDates properly.
I have an event that instantiates a timingDate = [NSDate date];
I then later on what to record the time intervals betweeen user's touches.
So I want to find the interval between the timingDate and the user touch in milliseconds.
Then I want to reset the timingDate to be equal to the touchTime so that the next time the screen is touched I can find the differnce between the previous touch and the present touch. I hope that makes sense. But I am going around in circles because I don't understand how to use NSDates or NSIntervals. The properties interval touchTime and timingDate are all currently NSDate types - Is this right?
So I've tried a lot of different things like
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
touchTime = timingDate;
interval = [[NSDate date] timeIntervalSinceDate:timingDate]; // should be the time difference from when the timingDate was first set and when the user touched the screen.
touchTime = [[[NSDate date]timeIntervalSinceDate:timingDate]doubleValue];
timingDate = [NSDate dateWithTimeIntervalSinceReferenceDate:touchTime];
NSLog(#"Time taken Later: %f", [[NSDate date]timeIntervalSinceDate:timingDate]);
}

Your code is a bit complex! You just need to calculate the difference between timingDate and the time that the touch occurred, and then set timingDate to the current time so that you can perform this calculation on every touch event.
To find the difference between timingDate and the first touch, you can use NSDate's timeIntervalSinceDate with the current time. This will return an NSTimeInterval value, which represents a time value in seconds with sub-millisecond precision. Here's an example:
NSDate *currentDate = [NSDate date];
NSTimeInterval timeInterval = [currentDate timeIntervalSinceDate:timingDate];
NSLog(#"Time taken: %f seconds / %f milliseconds",timeInterval,timeInterval*1000);
Then, in order to set your timingDate to the current time, simply use timingDate = currentDate;. This will allow you to continuously measure the time difference between touches.

NSTimeInterval is a double which represents seconds.
NSDate is an object that holds the date/time.
Here is an example:
#property(nonatomic, strong) NSDate * lastTouchDate;
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSTimeInterval secondsSinceLastTouch = 0;
if(self.lastTouchDate){
secondsSinceLastTouch = [[NSDate date] timeIntervalSinceDate:self.lastTouchDate];
NSLog(#"It's been %.1f seconds since the user touched the screen", secondsSinceLastTouch);
}else{
NSLog(#"This is the first time the user touched the screen");
}
self.lastTouchDate = [NSDate date];
}
If you don't want the interval between the last time you touched it, do not update the self.lastTouchDate after initialization and it will be seconds since the date was initialized.

So, the first thing that you need to understand is that -[NSDate timeIntervalSinceDate:] returns an NSTimeInterval, which is really just a double value.
In your example above, you haven't declared your variables with types, but if you look at the value your variable interval it should be a decimal value of seconds since the time represented by timingDate.
Assuming that timingDate is an NSDate object, and it is set before this code is run, this code should print the time (seconds) to the debug console.
NSTimeInterval interval = [[NSDate date] timeIntervalSinceDate:timingDate];
NSLog(#"Time between touches %f", interval);
Here is the NSDate class documentation, in case you were having trouble finding it.
( https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSDate_Class/index.html#//apple_ref/doc/c_ref/NSDate )

Related

NSTimeIntervalSinceDate gives 86,399 seconds between consecutive days, why?

In iOS, Xcode9, and Obj-C, I am trying to find out how much time has elapsed in days between two dates. I know the NSTimeIntervalSince method yields a result in seconds. But it seems that when I calculate the seconds between consecutive days (like 11-2-2016 and 11-3-2016), I am 1 second short, yielding 86,399. If I calculate seconds between larger intervals, I am always a second short.
11-2-2016 to 11-4-2016: 172,799 (two 24-hour days but missing a second)
11-2-2016 to 11-5-2016: 259,199 (three 24-hour days but missing a second)
Here's my code:
#property (strong, nonatomic) NSDate *firstDate;
#property (strong, nonatomic) NSDate *secondDate;
#synthesize firstDate, secondDate;
int secondsIntervalFirstDatetoSecondDate = (fabs([firstDate timeIntervalSinceDate:secondDate]));
NSLog(#"seconds between firstDate and secondDate %i", secondsIntervalFirstDatetoSecondDate);
firstDate and secodDate are NSDate
They are entered by the user using a datePicker and date formatting is set as
[formatter setDateFormat:#"MM-dd-yyyy"];
I've searched for an answer but could find none.
First post for a newbie, sorry for any etiquette errors, I'll try to improve.
When you create a UIDatePicker and put it in "date" mode, the time is the time the date picker was created. If you're creating two pickers, they're going to be created at slightly different times. Even a very small difference in those times can lead to a 1-second difference.
You should never compute time-differences this way. There are not 86,400 seconds in a day. "How many days are between two points in time" is complicated, and you should use the tools designed for it: NSDateComponents and NSCalendar.
NSCalendar *calendar = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian];
NSInteger diffInDays = [[calendar components:NSCalendarUnitDay
fromDate:[calendar startOfDayForDate:date1]
toDate:[calendar startOfDayForDate:date2]
options:0] day];

Synchronized countdown timer with `NSTimeInterval` without skipping seconds? (presumably due to rounding)

I was wondering if there was a clean way to do a countdown timer with Grand Central Dispatch (then display for example in a UILabel) that's synchronized to the system clock... based on a reference date? — So if my reference date is an NSDate that's 20 minutes from now, I'd have a countdown displayed in seconds (don't worry about formatting) that's synced to the system clock.
Just doing a quick version of this skips seconds every once in a while in case the update method call doesn't arrive on schedule.
To me this seems like a pretty basic question, so I'm looking ideally for a basic/clean solution besides increasing the update interval to be 10x or something.
Also, the solution shouldn't use NSTimer.
I would use a recursive method like this:
- (void) updateTimer{
NSDate *now = [NSDate date];
NSTimeInterval secondsToDate = [now timeIntervalSinceDate:self.referenceDate];
_timerLabel.text = [NSString stringWithFormat:#"%.0f", secondsToDate];
if( secondsToDate < 0 ){
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[self updateTimer];
});
}else{
NSLog(#"Timer triggered!");
}
}
You just have to call it the first time, then it will update the countdown every 0.1 seconds.

Setting UIDateTime Picker time zone

I have rather unusual requirement, but anyway..
#property (weak, nonatomic) IBOutlet UIDatePicker *dateDatePicker;
When I get reading of dateDatePicker.date it returns NSDate set to current system's time zone. But I want it to be in different specific time zone.
So, pretend right now it's 3:50pm Central Time.
dateDatePicker.date returns 3:50pm CST
I want to have something like this:
[dateDatePicker setTimeZone:#"EST"]
NSDate *ESTDate = dateDatePicker.date;
... and then I'd like debugger to show "2:50pm CST" - because 3:50pm in EST IS 2:50CST
This may be simpler that you think. If your date is 3:50pm CST, and you want it to be 2:50pm CST, just subtract an hour:
date = [date dateByAddingTimeInterval:-3600];
Of course, the local time zone might not always be CST, so you can get the difference between the two time zones and use that instead of hard coding -3600:
NSString *targetTimeZoneName = #"US/Eastern";
NSInteger localOffset = [[NSTimeZone localTimeZone] secondsFromGMTForDate:date];
NSInteger targetOffset = [[NSTimeZone timeZoneWithName:targetTimeZoneName] secondsFromGMTForDate:date];
NSInteger timeZoneDelta = localOffset - targetOffset;
date = [date dateByAddingTimeInterval:timeZoneDelta];
UIDatePicker's timeZone property is not a string, as you are using it. You have to pass it an NSTimeZone object.
Also, NSDates do not have a timezone. Its just a moment in history and its string representation depends on the time zone. Use an NSDateFormatter to format the date object for the timezone you want.

Can I make a button do an NSDate comparison in iOS?

Is it possible to have an action from a button a) do an NSDate store when tapped, and then b) for that method to do a date comparison with an if statement?
So, for instance, if the stored NSDate was less than 12 hours ago, the user could not press the button again?
Haven't come across a tip on this from my research.
Sure. You can get current date by code
[NSDate date]
Then save him into NSUserDefaults
And check a current date with saved date on button press.
How
if ( [[NSDate date] timeIntervalSinceDate: savedDate] >= 12*60*60) // 12 hours
{
// do your steps
}

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