call a method with NSDate? - iphone

Im writing a simple clock app and am looking for a way to call a method when the hour changes (or minute of second etc etc). If someone could point me in the right direction for this i would much appreciate it.Perhaps the kind of instance that calls a method for an alarm, im not sure?

You could schedule an NSTimer with -initWithFireDate:..., with the fire date set to whole hours.
If you're writing a clock app with precision down to seconds for humans, it is easier to ignore sub-second differences and use +scheduledTimerWithTimeInterval:... directly, e.g.
NSTimer* timer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(updateClock)
userInfo:nil
repeats:YES];
...
-(void)updateClock {
NSDate* now = [NSDate date];
NSCalendar* calendar = [NSCalendar currentCalendar];
NSDateComponents* dateComps = [calendar components:NSHourCalendarUnit|NSMinuteCalendarUnit|NSSecondCalendarUnit
fromDate:now];
NSInteger hourNow = [dateComps hour];
NSInteger minuteNow = [dateComps minute];
NSInteger secondNow = [dateComps second];
// update clock view using 'hourNow', 'minuteNow' and 'secondNow'
if (secondNow == 0) {
// whole minute
if (minuteNow == 0) {
// whole hour
}
// (note: not very reliable since a timer does not need to fire on schedule)
}

Related

Getting the current time and keeping it updated - Objective C

I know how to use NSDate to get the time and display it inside UILabel.
i need to display the date + hours and minutes.
any idea how can i keep it updated without busy-waiting?
Thanks!
Use NSTimer to update time on the label
- (void)viewDidLoad
{
[super viewDidLoad];
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(updateTime) userInfo:nil repeats:YES];
}
-(void)updateTime
{
NSDate *date= [NSDate date];
NSDateFormatter *formatter1 = [[NSDateFormatter alloc]init]; //for hour and minute
formatter1.dateFormat = #"hh:mm a";// use any format
clockLabel.text = [formatter1 stringFromDate:date];
[formatter1 release];
}
As your comments said , if you want when minutes changes you change the label.text
you should do like this :
1st: get the current time:
NSDate *date = [NSDate date];
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *dateComponents = [calendar components:NSHourCalendarUnit fromDate:date];
and set the label.text = CURRENTHOUR_AND_YOURMINNUTS;
and then refresh the label next minute,like this :
the first , you can check after 60 - nowSeconds
[self performSelector:#selector(refreshLabel) withObject:nil afterDelay:(60 - dateComponents.minute)];
- (void)refreshLabel
{
//refresh the label.text on the main thread
dispatch_async(dispatch_get_main_queue(),^{ label.text = CURRENT_HOUR_AND_MINUTES; });
// check every 60s
[self performSelector:#selector(refreshLabel) withObject:nil afterDelay:60];
}
It will check every minute , so the effecent is more than answers above.
When refreshLabel invocated , it means the minutes changed
You can use the NSTimer to periodically get the current time.
[NSTimer scheduledTimerWithTimeInterval:2 target:self selector:#selector(timerFired:) userInfo:nil repeats:YES];
- (void)timerFired:(NSTimer*)theTimer{
//you can update the UILabel here.
}
You can use NSTimer , but , given the above methods , the UILabel won't update on touch Events as the main thread will be busy tracking it.You need to add it to mainRunLOOP
NSTimer* timer = [NSTimer timerWithTimeInterval:1.0f target:self selector:#selector(updateLabelWithDate) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
-(void)updateLabelWithDate
{
//Update your Label
}
You can change the time interval(rate at which you want Updation).

Count down timer - iPhone [closed]

This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 10 years ago.
I want to display count down timer. I have the start date and end date. I need to display the remaining time like
days : hours : minutes : seconds
How can I do this?
you can set coundown like my below code :-
-(void) viewWillAppear:(BOOL)animated
{
timer = [NSTimer scheduledTimerWithTimeInterval: 1.0 target:self selector:#selector(updateCountdown) userInfo:nil repeats: YES];
}
AND
-(void) updateCountdown
{
NSString *dateString = #"14-12-2012";
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"dd-MM-yyyy"];
NSDate *dateFromString = [[NSDate alloc] init];
// voila!
dateFromString = [dateFormatter dateFromString:dateString];
NSDate *now = [NSDate date];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *componentsHours = [calendar components:NSHourCalendarUnit fromDate:now];
NSDateComponents *componentMint = [calendar components:NSMinuteCalendarUnit fromDate:now];
NSDateComponents *componentSec = [calendar components:NSSecondCalendarUnit fromDate:now];
NSCalendar *gregorianCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *componentsDaysDiff = [gregorianCalendar components:NSDayCalendarUnit
fromDate:now
toDate:dateFromString
options:0];
lblDaysSetting.text=[NSString stringWithFormat:#"%02d",componentsDaysDiff.day];
lblHouresSetting.text=[NSString stringWithFormat:#"%02d",(24-componentsHours.hour)];
lblMinitSetting.text=[NSString stringWithFormat:#"%02d",(60-componentMint.minute)];
lblSecSetting.text=[NSString stringWithFormat:#"%02d",(60-componentSec.second)];
}
now just set your logic
its code output as my project as bellow::-
this is the code for .h file:
#interface UIMyContoller : UIViewController {
NSTimer *timer;
IBOutlet UILabel *myCounterLabel;
}
#property (nonatomic, retain) UILabel *myCounterLabel;
-(void)updateCounter:(NSTimer *)theTimer;
-(void)countdownTimer;
#end
and here is the code for .m file:
#implementation UIMyController
#synthesize myCounterLabel;
int hours, minutes, seconds;
int secondsLeft;
- (void)viewDidLoad {
[super viewDidLoad];
secondsLeft = 16925;
[self countdownTimer];
}
- (void)updateCounter:(NSTimer *)theTimer {
if(secondsLeft > 0 ){
secondsLeft -- ;
hours = secondsLeft / 3600;
minutes = (secondsLeft % 3600) / 60;
seconds = (secondsLeft %3600) % 60;
myCounterLabel.text = [NSString stringWithFormat:#"%02d:%02d:%02d", hours, minutes, seconds];
}
else{
secondsLeft = 16925;
}
}
-(void)countdownTimer{
secondsLeft = hours = minutes = seconds = 0;
if([timer isValid])
{
[timer release];
}
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(updateCounter:) userInfo:nil repeats:YES];
[pool release];
}
hope this helps. happy coding
adrian
I would recommend the below method of getting time components of From Date to To Date.
This method is more elegant than manually decrementing 3 integer variables for hour, minute and seconds because doing so would mean you have to manually check when a second hit 0, you need to manually reset the minute back to 59 and so on. I went down this route once, it wasn't very good.
Also, when you minimize your app, the count down clock will stop. If you were decrementing your count down timer manually using 3 integers (hour, minute and seconds), minimizing your app will cause the count down to screw up.
Since this method auto calculates the difference between two dates, even when the app returns from the background minimized state, it automatically recalculates the remaining time for you without any extra code.
-(void)viewDidLoad
{
// instantiate a calendar object.
gregorianCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
countDownTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(updateClock:) userInfo:nil repeats:YES];
[countDownTimer fire];
}
-(void)updateClock:(NSTimer *)timer
{
countDownDateFormatter = [[NSDateFormatter alloc] init];
[countDownDateFormatter setDateFormat:#"hh:mm:ss"];
NSDate *now = [NSDate date];
NSDateComponents *comp = [gregorianCalendar components:NSHourCalendarUnit|NSMinuteCalendarUnit|NSSecondCalendarUnit
fromDate:now
toDate:countDownEndDate
options:0];
NSString *strTimeRemaining = nil;
// if date have not expired
if([now compare:countDownEndDate] == NSOrderedAscending)
{
strTimeRemaining = [[NSString alloc] initWithFormat:#"%02d:%02d:%02d", [comp hour], [comp minute], [comp second]];
}
else
{
// time has expired, set time to 00:00 and set boolean flag to no
strTimeRemaining = [[NSString alloc] initWithString:#"00:00:00"];
[countDownTimer invalidate];
countDownTimer = nil;
}
lblCountDown.text = strTimeRemaining;
[countDownDateFormatter release];
[strTimeRemaining release];
}

Objective-C Perform method at given time daily - messy implementation

I have the following (terible) method that constantly checks the current time, and when a certain time is reached (in this case midnight) an NSLog statement is run once to signify something useful being done:
- (void) checkTime {
while (true){
NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init];
NSDate *now = [[NSDate alloc] init];
NSDateFormatter *outputFormatter = [[NSDateFormatter alloc] init];
[outputFormatter setDateFormat:#"HH:mm"];
NSString *nowDateString = [outputFormatter stringFromDate:now];
if([nowDateString isEqualToString:#"00:00"]){
NSLog(#"Store previous days data..");
BOOL stillMidnight = YES;
while(stillMidnight == YES){
NSDate *latestNow = [[NSDate alloc] init];
NSDateFormatter *latestOutputFormatter = [[NSDateFormatter alloc] init];
[latestOutputFormatter setDateFormat:#"HH:mm"];
NSString *latestString = [latestOutputFormatter stringFromDate:latestNow];
//Check if it is still midnight
if([latestString isEqualToString:#"00:01"]){
//leave while
stillMidnight = NO;
}
}
NSLog(#"No longer midnight");
}
[loopPool drain];
}
}
The above method gets called as follows from the applicationDidFinishLaunchingWithOption method:
[self performSelectorInBackground:#selector(checkTime) withObject:nil];
This code runs the NSLog(#"Store previous days data..") once at midnight, which is what I need, but is there a more elegant solution to this problem?
Thanks,
Jack
You'd be better off:
getting the current date;
working out when midnight was today;
scheduling a one-shot timer to occur one day after that; and
repeating
It's tempting to just schedule a repeating timer that first fires with the date calculated at (3) and every 24 hours hence, but that would fail to allow for daylight savings. So, e.g. (coded directly in here, untested)
- (void)scheduleNextTimedAction
{
// get the date now and the calendar the user is using
// (which will include their time zone, helpfully)
NSDate *dateNow = [NSDate date];
NSCalendar *relevantCalendar = [NSCalendar currentCalendar];
// decompose the current date to components; we'll
// just ask for month, day and year here for brevity;
// check out the other calendar units to decide whether
// that's something you consider acceptable
NSDateComponents *componentsForNow =
[relevantCalendar components:
NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit
fromDate:dateNow];
// we could explicitly set the time to midnight now,
// but since that's 00:00 it'll already be the value
// in the date components per the standard Cocoa object
// creation components, so...
// get the midnight that last occurred
NSDate *lastMidnight = [relevantCalendar dateFromComponents:componentsForNow];
// can we just add 24 hours to that? No, because of DST. So...
// create components that specify '1 day', however long that may be
NSDateComponents *oneDay = [[NSDateComponents alloc] init];
oneDay.day = 1;
// hence ask the calendar what the next midnight will be
NSDate *nextMidnight = [relevantCalendar
dateByAddingComponents:oneDay
toDate:lastMidnight
options:0];
[oneDay release];
// now create a timer to fire at the next midnight, to call
// our periodic function. NB: there's no convenience factory
// method that takes an NSDate, so we'll have to alloc/init
NSTimer *timer = [[NSTimer alloc]
initWithFireDate:nextMidnight
interval:0.0 // we're not going to repeat, so...
target:self
selector:#selector(doTimedAction:)
userInfo:nil
repeats:NO];
// schedule the timer on the current run loop
[[NSRunLoop currentRunLoop]
addTimer:timer
forMode: NSDefaultRunLoopMode];
// timer is retained by the run loop, so we can forget about it
[timer release];
}
- (void)doTimedAction:(NSTimer *)timer
{
NSLog(#"do action");
[self scheduleNextTimedAction];
}
If you want to execute code at an arbitrary point in time, you best set up Local Notifications.
You can use either an UILocalNotification if the timer should also alert the user when your app is not running Push notification guide or NSTimer, which can be initialized with firing date or interval as well as selector to be called. Note that NSTimer will not fire if your app is in the background but in this case rather will fire the moment your app gets active again.

Consistent 1-Second-Long lag at 0:02 during NSTimer in "stopwatch-like" app

I have an app that uses a stopwatch-style count up from 0 in HH:mm:ss format. The code looks pretty straightforward to me, and I can't think of a more efficient way to run it.
For some reason, when I run it, there is a very noticeable and consistent (every time I run it, in the same place) lag when the timer gets to 00:00:02. It stays on 00:00:02 for a full second, and then counts on normally. Why would this happen?
-(IBAction)startAndStop;
{
if (!timer) {
NSLog(#"Pressing Start Button");
[startAndStopButton setTitle:#"Stop" forState:0];
startDate = [[NSDate date] retain];
timerLabel.text = #"00:00:00";
timer = [NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:#selector(timerStart)
userInfo:nil
repeats:YES];
} else {
NSLog(#"Pressing Stop Button");
[startAndStopButton setTitle:#"Start" forState:0];
[startDate release];
[timer invalidate];
timer = nil;
[timer release];
}
}
-(void)timerStart
{
NSDate *currentDate = [NSDate date];
NSTimeInterval countInSeconds = [currentDate timeIntervalSinceDate:startDate];
NSDate *timerDate = [NSDate dateWithTimeIntervalSinceReferenceDate:countInSeconds];
NSDateFormatter *df = [[NSDateFormatter alloc] init];
[df setDateFormat:#"HH:mm:ss"];
[df setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0.0]];
NSString *timeString = [df stringFromDate:timerDate];
[df release];
timerLabel.text = timeString;
}
NSTimer does not fire at exact times or time intervals (check the specification for the likely error). Thus it is possible for one late firing and one early firing to occur during the same clock second, when rounded to the nearest second, and you will see a stutter effect.
Instead, use a much faster timer (or CADisplaylink), say at 30 Hz, check the time, and update the label only if the time has changed enough to change the label (one second).
The interval you are passing is in seconds:
http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSTimer_Class/Reference/NSTimer.html
My guess is that it is getting called immediately, and then every 1 second afterwards, since you are passing 1 second as the timer interval. Try passing something like 1.0/20.0 to update at a higher frame rate.

Registering a Notification in iPhone SDK 3.0

In iPhone SDK 3.0, I would like to register for a notification, which would alert my application when a certain time is reached. Is it possible?
Thanks
Set up an NSTimer that runs a selector every 30 seconds (or whatever granularity you need):
[NSTimer scheduledTimerWithTimeInterval:30.0 target:self selector:#selector(timerFired:) userInfo:nil repeats:YES];
The -timerFired: selector (method) will run every thirty seconds and check the hour, minute and second components, firing a notification if elements match the desired time:
- (void) timerFired:(NSNotification *)notification {
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSCalendarUnit unitFlags = NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;
NSDate *date = [NSDate date];
NSDateComponents *dateComponents = [calendar components:unitFlags fromDate:date];
NSInteger hour = [dateComponents hour];
NSInteger min = [dateComponents minute];
NSInteger sec = [dateComponents second];
if ((hour == kDesiredHour) && (min == kDesiredMinute) && (sec == kDesiredSecond)) {
[[NSNotificationCenter defaultCenter] postNotificationName:#"kTimeComponentsWereMatched" object:nil userInfo:nil];
}
}
You register to listen for this notification in some other class somewhere:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(doSomething:) name:#"kTimeComponentsWereMatched" object:nil];
Accordingly, you have a method in that same class that does something interesting:
- (void) doSomething:(NSNotification *)notification {
// do something interesting here...
}
You can merge this code if it is all in one class. Or specify the target in the NSTimer to point to the class instance that you want to run the selector in.
Assuming you have a NSDate in the variable date, and want to fire the method dateIsHere: at that date, do this:
NSTimer* timer = [[NSTimer alloc] initWithFireDate:date
interval:0.0f
target:self
selector:#selector(dateIsHere:)
userInfo:nil
repeats:NO];
[[NSRunLoop mainRunLoop] addTimer:timer
forMode:NSDefaultRunLoopMode];
[timer release];
I'm assuming you want this timer to fire, even if the app is closed. You can use a notification for that but you would have to have a server that issued the notification.
Furthermore, the iPhone would pull up an alert asking the user to open the app - but they could chose not to do so.
You would start by setting an NSTimer to fire at a certain date, and the selector that fires could be whatever you want. No need to use NSNotifications.
You are looking for "Local Notifications", which is being implemented in iOS since version 4.0:
http://developer.apple.com/iphone/library/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Introduction/Introduction.html#//apple_ref/doc/uid/TP40008194-CH1-SW1
This should be the correct answer for >=4.0. For earlier versions, there is probably still only the NSNotifications (implementing push is too much of a hassle for most)