i am doing one application.In that i have to show the time for how much time user is in the application.For that,i calculated the difference between application opened time and current time for every one second and showing.But if i try to change the device time,that result also changing.But i want to show the exact result if user change the time also.So how to get the exact correct time based on that timezone.
You can set a default time zone to GMT for your application in initialize method.
#interface AppDelegate ()
#property (nonatomic, strong) NSDate startDate;
#end
#implementation AppDelegate
+ (void)initialize
{
[NSTimeZone setDefaultTimeZone:[NSTimeZone timeZoneWithAbbreviation:#"GMT"]];
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
self.startDate = [NSDate date];
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
NSLog(#"User has stayed for %4.2f seconds.", [[NSDate date] timeIntervalSinceDate:self.startDate]);
}
In addition to that, don't forget to set time zone for your NSDateFormatter instances when you convert a string representation of a date into actual NSDate instance.
Related
Question:
How can I make sure that the code executed due to a runloop event (timer, user interaction, performSelector, etc) have the same concept of "now"?
Background:
Say that event handler takes 100ms to execute, that means that [NSDate date] will return a slightly different "now" depending on when in the execution you make the call. If you are very unlucky with the timing you might even end up with different dates between the calls.
This creates problems for things that rely on the current time for doing various calculations since those calculations can differ during the execution.
Of course, for a specific event handler you could just store the date in the AppDelegate or similar or pass it on in each call starting from the entry point.
However, I want something safer and automatic. Ideally I want to know at what time the current run loop started processing the event. Something I can simply replace [NSDate date] with and always get the same result until the next event is fired.
I looked into the documentation of NSRunLoop without much luck. I also looked into CADisplayLink for potential workarounds. Neither provided a clear cut answer.
It feels like this should be a common thing to need, not something that needs "workarounds". My guess is that I am looking in the wrong places or using the wrong search terms.
Code Example:
UIView *_foo, _fie;
NSDate *_hideDate;
- (void)handleTimer
{
[self checkVisible:_foo];
[self checkVisible:_fie];
}
- (void)checkVisible:(UIView *)view
{
view.hidden = [_hideDate timeIntervalSinceNow] < 0];
}
In this case we could end up with _fie being hidden when _foo is still visible since "now" has changed by a very small amount between calls.
This is a very simplified example in which a fix is trivial by simply calling [NSDate date] and sending that instance to all callers. It is the general case that I am interested in though where call chains might be very deep, cyclic, re-entrant, etc.
NSRunLoop is a wrapper for CFRunLoop. CFRunLoop has features that NSRunLoop doesn't expose, so sometimes you have to drop down to the CF level.
One such feature is observers, which are callbacks you can register to be called when the run loop enters different phases. The phase you want in this case is an after-waiting observer, which is called after the run loop receives an event (from a source, or due to a timer firing, or due to a block being added to the main queue).
Let's add a wakeDate property to NSRunLoop:
// NSRunLoop+wakeDate.h
#import <Foundation/Foundation.h>
#interface NSRunLoop (wakeDate)
#property (nonatomic, strong, readonly) NSDate *wakeDate;
#end
With this category, we can ask an NSRunLoop for its wakeDate property any time we want, for example like this:
#import "AppDelegate.h"
#import "NSRunLoop+wakeDate.h"
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSTimer *timer = [NSTimer timerWithTimeInterval:0.5 repeats:YES block:^(NSTimer *timer){
NSLog(#"timer: %.6f", NSRunLoop.currentRunLoop.wakeDate.timeIntervalSinceReferenceDate);
}];
[NSRunLoop.currentRunLoop addTimer:timer forMode:NSRunLoopCommonModes];
return YES;
}
#end
To implement this property, we'll create a WakeDateRecord class that we can attach to the run loop as an associated object:
// NSRunLoop+wakeDate.m
#import "NSRunLoop+wakeDate.h"
#import <objc/runtime.h>
#interface WakeDateRecord: NSObject
#property (nonatomic, strong) NSDate *date;
- (instancetype)initWithRunLoop:(NSRunLoop *)runLoop;
#end
static const void *wakeDateRecordKey = &wakeDateRecordKey;
#implementation NSRunLoop (wakeDate)
- (NSDate *)wakeDate {
WakeDateRecord *record = objc_getAssociatedObject(self, wakeDateRecordKey);
if (record == nil) {
record = [[WakeDateRecord alloc] initWithRunLoop:self];
objc_setAssociatedObject(self, wakeDateRecordKey, record, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
return record.date;
}
#end
The run loop can run in different modes, and although there are a small number of common modes, new modes can in theory be created on the fly. If you want an observer to be called in a particular mode, you have to register it for that mode. So, to ensure that the reported date is always correct, we'll remember not just the date but also the mode in which we recorded the date:
#implementation WakeDateRecord {
NSRunLoop *_runLoop;
NSRunLoopMode _dateMode;
NSDate *_date;
CFRunLoopObserverRef _observer;
}
To initialize, we just store the run loop and create the observer:
- (instancetype)initWithRunLoop:(NSRunLoop *)runLoop {
if (self = [super init]) {
_runLoop = runLoop;
_observer = CFRunLoopObserverCreateWithHandler(nil, kCFRunLoopEntry | kCFRunLoopAfterWaiting, true, -2000000, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
[self setDate];
});
}
return self;
}
When asked for the date, we first check whether the current mode is different from the date in which we recorded the mode. If so, then the date wasn't updated when the run loop awoke in the current mode. That means the observer wasn't registered for the current mode, so we should register it now and update the date now:
- (NSDate *)date {
NSRunLoopMode mode = _runLoop.currentMode;
if (![_dateMode isEqualToString:mode]) {
// My observer didn't run when the run loop awoke in this mode, so it must not be registered in this mode yet.
NSLog(#"debug: WakeDateRecord registering in mode %#", mode);
CFRunLoopAddObserver(_runLoop.getCFRunLoop, _observer, (__bridge CFRunLoopMode)mode);
[self setDate];
}
return _date;
}
When we update the date, we also need to update the stored mode:
- (void)setDate {
_date = [NSDate date];
_dateMode = _runLoop.currentMode;
}
#end
An important warning about this solution: the observer fires once per pass through the run loop. The run loop can service multiple timers and multiple blocks added to the main queue during a single pass. All of the serviced timers or blocks will see the same wakeDate.
I'm sure I'm just missing something simple here, but can't find the answer though I looked in the other examples here, my code seems to be the same.
I'm trying to define a global class with some methods that I can access from the other classes in my project. I can define it, but can not access the methods from my other classes, though I always import the global class header to the class where I want to use the method. Heres the code:
1st The Global class def:
#import <Foundation/Foundation.h>
#interface GlobalMethods : NSObject {}
- (unsigned long long)getMilliSeconds:(NSDate*)d;
- (NSDate *)getDateFromMs:(unsigned long long)ms;
#end
#import "GlobalMethods.h"
#implementation GlobalMethods
//SET DATE TO MILLISECONDS 1970 EPOCH
- (unsigned long long)getMilliSeconds:(NSDate*)d
{
unsigned long long seconds = [d timeIntervalSince1970];
unsigned long long milliSeconds = seconds * 1000;
return milliSeconds;
}
// GET DATE FROM MILLISECONDS 1970 EPOCH
- (NSDate *)getDateFromMs:(unsigned long long)ms
{
unsigned long long seconds = ms / 1000;
NSDate *date = [[NSDate alloc] initWithTimeIntervalSince1970: seconds];
return date;
}
#end
and then where I want to use my methods in another class:
#import "GlobalMethods.h"
// GET MILLISECONDS FROM 1970 FROM THE PICKER DATE
NSDate *myDate = _requestDatePicker.date;
milliSeconds = [self getMilliSeconds: myDate];
Error is : No visable interface for viewcontroller declares the selector getMilliSeconds.
Thanks for the help with this.
You are trying to call the getMilliSeconds: method (which is an instance method of the GlobalMethods class) on an instance of your view controller class. That is the cause of the error.
As written you need to change this line:
milliSeconds = [self getMilliSeconds: myDate];
to:
GlobalMethods *global = [[GlobalMethods alloc] init];
milliSeconds = [global getMilliSeconds:myDate];
A better solution is to first change all of the instance methods of your GlobalMethods class to be class methods. In other words, in both the .h and .m file for GlobalMethods, change the leading - to a + for both methods.
Then in your view controller you can do:
milliSeconds = [GlobalMethods getMilliSeconds:myDate];
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 )
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.
I am trying to set the property of a child view controller (DateViewController) from the parent and getting a bad access error the second time I do so. Here is the code. This is the DateViewController.h. The problem lies with the selectedDate property:
#import <UIKit/UIKit.h>
#protocol DateViewDelegate <NSObject>
-(void) dateViewControllerDismissed:(NSDate *)selectedDate;
#end
#interface DateViewController : UIViewController {
IBOutlet UIDatePicker *dateReceipt;
id myDelegate;
}
-(IBAction)btnDone;
#property(nonatomic,assign)NSDate *selectedDate;
#property(nonatomic,assign)id<DateViewDelegate> myDelegate;
#end
Inside DateViewController.m, I do synthesize selectedDate. Now in the parent view controller (ComdataIOSViewController.m) I set the selectedDate property of the DateViewController to the variable receiptDate which is declared as an NSDate * in the #interface section of ComdataIOSViewController.h. This is a snippet of ComdataIOSViewController.m:
- (void)viewDidLoad
{
[super viewDidLoad];
receiptDate = [NSDate date];
}
-(IBAction)btnSetDate {
dlgDate=[[DateViewController alloc] initWithNibName:nil bundle:nil];
dlgDate.selectedDate = receiptDate;
dlgDate.myDelegate = self;
[self presentModalViewController:dlgDate animated:true];
[dlgDate release];
}
-(void) dateViewControllerDismissed:(NSDate *)selectedDate
{
NSDateFormatter *dateFormat = [[[NSDateFormatter alloc] init] autorelease];
[dateFormat setDateStyle:NSDateFormatterShortStyle];
receiptDate = selectedDate;
dateString = [dateFormat stringFromDate:receiptDate];
lblDate.text = dateString;
}
So the first time I click the set date button on the parent controller, the DateViewController appears, I pick the date from the datepicker control, and the controller is dismissed. In the parent view controller, dateViewControllerDismissed gets called and I set the receiptDate to the selectedDate parameter. The next time I click the date button, I get a bad access error where I set the DateViewController's selectedDate property to the receiptDate. I'm assuming this is some sort of memory issue that I'm not handling correctly. IOS programming is still new to me.
I have found several problems in your code which could lead your application to crash. Actually they are memory management problem.
Assigning autoreleased object to receiptDate:
receiptDate = [NSDate date];
when you will try to use this value later it will cause app crash because memory where receiptDate point could be already released. You could fix it by retaining the value:
receiptDate = [[NSDate date] retain];
and releasing in dealloc or anywhere you are changing it (I dont know how it is declared. It should be retain property).
You are assigning NSDate without retaining it:
receiptDate = selectedDate;
you could fix it by retaining:
receiptDate = [selectedDate retain];
I am sorry because I could not write all aspects of memory management in objective-C. It is better to use ARC if you don't know iOS memory managent well.
You could find a lot of useful information in this two guides from Apple: Advanced Memory Management Programming Guide and Memory Management Programming Guide for Core Foundation
Your property is never retained. What I would suggest to do would be to change the assign to retain in your property declaration. That'll solve your problem and you won't have to call retain everywhere you set selectedDate. The property will do that for you.
If you're not using ARC, don't forget to set the property to nil in your dealloc method, like so:
self.selectedDate = nil;
Note that I use self.selectedDate. It's important so that selectedDate is accessed as a property, not a variable.