Objective-c timer wont invalidate - objective-c

Here is my situation: I am making a countdown app which works fine but does not appear to stop when I call [stopWatchTimer invalidate];, and I have no idea why. Here is my code:
- (IBAction)btnStartPressed:(id)sender {
//Start countdown with the time on the Date Picker.
timeLeft = [pkrTime countDownDuration];
[self currentCount];
lblTimer.text = time; //sets the label to the time set above
pkrTime.hidden = YES;
btnStart.hidden = YES;
btnStop.hidden = NO;
//Fire this timer every second.
stopWatchTimer = [NSTimer scheduledTimerWithTimeInterval:1.0/1.0
target:self
selector:#selector(reduceTimeLeft:)
userInfo:nil
repeats:YES];
}
- (void)reduceTimeLeft:(NSTimer *)timer {
//Countown timeleft by a second each time this function is called
timeLeft--;
// Get the system calendar
NSCalendar *sysCalendar = [NSCalendar currentCalendar];
// Create the NSDates
NSDate *date1 = [[NSDate alloc] init];
NSDate *date2 = [[NSDate alloc] initWithTimeInterval:timeLeft sinceDate:date1];
// Get conversion to months, days, hours, minutes
unsigned int unitFlags = NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;
NSDateComponents *conversionInfo = [sysCalendar components:unitFlags fromDate:date1 toDate:date2 options:0];
int sec = [conversionInfo second];
int min = [conversionInfo minute];
int hour = [conversionInfo hour];
NSString *seconds = [NSString stringWithFormat:#"%d",sec];
NSString *minutes = [NSString stringWithFormat:#"%d",min];
if (sec <= 9)
seconds = [NSString stringWithFormat:#"0%d", sec];
if (min <= 9)
minutes = [NSString stringWithFormat:#"0%d", min];
if ([conversionInfo hour] == 0)
time = [NSString stringWithFormat:#"%#:%#", minutes, seconds];
else
time = [NSString stringWithFormat:#"%d:%#:%#", hour, minutes, seconds];
lblTimer.text = time; //sets the label to the time set above
NSLog(#"%d", timeLeft);
if (timeLeft == 0) {
[self timerDone];
[stopWatchTimer invalidate];
stopWatchTimer = nil;
}
}
-(void)timerDone {
pkrTime.hidden = NO;
btnStart.hidden = NO;
btnStop.hidden = YES;
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Timer Done" message:nil delegate:self cancelButtonTitle:nil otherButtonTitles:#"Ok", nil];
[alert show];
[self playAlert];
}
Please let me know what the problem is... I cannot find a problem with my code anywhere!

In your btnStartPressed: method, you have nothing preventing a second NSTimer from being allocated and assigned to stopWatchTimer. If you press the button twice, you'll end up with two timers, but only one will ever be invalidated.
Add something like:
if (stopWatchTimer) return;
To the beginning of btnStartPressed:. If that doesn't fix the problem, then there isn't enough context to know for sure what is going on beyond conjecturing that timeLeft is zero?
What Nate said, but here is another explanation.
Imagine if you do this (where stopWatchTimer is a global or instance variable, doesn't matter):
stopWatchTimer = [NSTimer scheduledTimerWithTimeInterval:....];
Now, do this:
stopWatchTimer = nil;
[stopWatchTimer invalidate];
The timer won't invalidate, but it'll still fire. stopWatchTimer is a reference to the object. It isn't the object itself. Thus, when you assign a second timer to stopWatchTimer, you are overwriting the reference to the first timer, but that timer is still going to fire!

Related

How to update UILabel frequently?

I want to update text for label frequently but it seems not working.
for (int i = 0; i <100; i++)
{
mylabel.text =[NSString stringWithFormat:#"%d", i];
}
Does anyone suggest for me an idea ? I thought. Maybe, we should update text label in multiple threads.
Watch is best example hope it will help you....
int second,minute; //set second = 0 and minute = 0
[NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(updateCounter:) userInfo:nil repeats:YES];
- (void)updateCounter:(NSTimer *)theTimer {
second++;
if(second < 60)
{
if (second < 10)
{
timerLbl.text = [NSString stringWithFormat:#"00:0%d", second];
}
else
{
timerLbl.text = [NSString stringWithFormat:#"00:%d",second];
}
}
else
{
minute = second / 60;
int sec = second % 60;
if (minute < 10 && sec < 10)
{
timerLbl.text = [NSString stringWithFormat:#"0%d:0%d", minute, sec];
}
if(minute < 10 && sec >= 10)
{
timerLbl.text = [NSString stringWithFormat:#"0%d:%d", minute, sec];
}
if (minute >= 10 && sec < 10)
{
timerLbl.text = [NSString stringWithFormat:#"%d:0%d", minute, sec];
}
if(minute >= 10 && sec >= 10)
{
timerLbl.text = [NSString stringWithFormat:#"%d:%d", minute, sec];
}
}
}
Add below code in viewDidLoad
self.timer = [NSTimer scheduledTimerWithTimeInterval:0.5f
target:self
selector:#selector(showTime)
userInfo:NULL
repeats:YES];
- (void)showTime
{
NSDate *now=[NSDate date];
NSDateFormatter *dateFormatter=[NSDateFormatter new];
[dateFormatter setDateFormat:#"HH:mm:ss"];
timeLabel.text=[dateFormatter stringFromDate:now];
}
Hope this answer will help you....
use NSTimer for the regular interval and call the method where the lable is printed let suppose
[NSTimer scheduledTimerWithTimeInterval:2.0
target:self
selector:#selector(targetMethod:)
userInfo:nil
repeats:Yes];
in the target method print the label on text
-(void)targetMethod:(NSTimer *)timer{
NSDate* date = [NSDate date];
NSDateFormatter* formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#" HH:MM:SS"];
NSString* str = [formatter stringFromDate:date];
label.text = str;
}
try this it will work surely

How to countdown from a NSDate and display it in hours and minutes

I'm trying to countdown from a NSDate and display it in hours and minutes. Like this: 1h:18min
At the moment my date is updating to a UILabel and counting down but displaying like this:
Here's the code I'm using. A startTimer method and a updateLabel method
- (void)startTimer {
// Set the date you want to count from
// convert date string to date then set to a label
NSDateFormatter *dateStringParser = [[NSDateFormatter alloc] init];
[dateStringParser setDateFormat:#"yyyy-MM-dd'T'HH:mm:ss.000Z"];
NSDate *date = [dateStringParser dateFromString:deadlineDate];
NSDateFormatter *labelFormatter = [[NSDateFormatter alloc] init];
[labelFormatter setDateFormat:#"HH-dd-MM-yyyy"];
NSDate *countdownDate = [[NSDate alloc] init];
countdownDate = date;
// Create a timer that fires every second repeatedly and save it in an ivar
NSTimer *timer = [[NSTimer alloc] init];
timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(updateLabel) userInfo:nil repeats:YES];
}
- (void)updateLabel {
// convert date string to date then set to a label
NSDateFormatter *dateStringParser = [[NSDateFormatter alloc] init];
[dateStringParser setDateFormat:#"yyyy-MM-dd'T'HH:mm:ss.000Z"];
NSDate *date = [dateStringParser dateFromString:deadlineDate];
NSDateFormatter *labelFormatter = [[NSDateFormatter alloc] init];
[labelFormatter setDateFormat:#"HH-dd-MM"];
NSTimeInterval timeInterval = [date timeIntervalSinceNow]; ///< Assuming this is in the future for now.
self.deadlineLbl.text = [NSString stringWithFormat:#"%f", timeInterval];
}
thanks for any help
- (NSString *)stringFromTimeInterval:(NSTimeInterval)interval
{
NSInteger ti = (NSInteger)interval;
NSInteger seconds = ti % 60;
NSInteger minutes = (ti / 60) % 60;
NSInteger hours = (ti / 3600);
return [NSString stringWithFormat:#"%02i:%02i:%02i", hours, minutes, seconds];
}
Since you use NSTimeInterval ,you are getting the time interval ,ie the difference, in seconds, to display it in hours and minutes you need to apply mathematics logic and convert it!
You many need a few loops to do it.
try this
Regards

ios how to change an image at specific time

I have an image that i want to display as a sun during daytime i.e. from 6am to 6pm, and moon from 6pm to 6am.
I have successfully implemented that but the problem is the image would not change when it reaches the specified time unless re-run the apps before the image change itself.
I don't want to use NSTimer to check the time, like every second. The only possible solution i think of is using NSLocalNotification but I'm a newbiew to it. any help? =)
-(void) dayOrNight
{
NSDate* date = [NSDate date];
NSDateFormatter* dateFormat = [[NSDateFormatter alloc]init];
[dateFormat setDateFormat:#"HHmm"];
NSString* dateString = [dateFormat stringFromDate:date];
NSNumber* currentTime = [NSNumber numberWithInt:[dateString intValue]];
NSNumber* daytime = [NSNumber numberWithInt:600];
NSNumber* nightime = [NSNumber numberWithInt:1800];
NSLog(#"current time: %#",dateString);
dayNight = [[UIImageView alloc] initWithFrame:CGRectMake(265, 10, 50, 50)];
if ( [currentTime doubleValue] >= [daytime doubleValue] && [currentTime doubleValue] <= [nightime doubleValue] )
{
dayNight.image = [UIImage imageNamed:#"Sun.png"];
}
else
{
dayNight.image = [UIImage imageNamed:#"moon.png"];
}
[self.view addSubview:dayNight];
}
Local notification should be fine, I guess.
Here you can get all needed code snippets to implement execution of dayOrNight method at needed time. Also, you shouldn't add new view every time you change the picture.
I think it is impossible to do without NSTimer. U can set refreshTime by yourself (for example 1 min/ if u don`t want to do it every second)))
Or u can call this method in other methods, which are working in your class every time...
maybe u have some object, which u use during your class is working...
-(IBAction)myButtonWhichIPressDuringIworkHere {
///some actions
[self dayOrNight];
}
In other case you should to use NSTimer
you don't need to check the time at every second.
this is one of the possible solutions. it has been tested on real device only.
your UIYourViewController.m
- (void)viewWillAppear:(BOOL)animated {
NSDate *_currentDate = [NSDate date];
NSDateFormatter *_dateFormatter = [[NSDateFormatter alloc] init];
[_dateFormatter setDateFormat:#"yyyy-MM-dd' 06:00AM +0000'"]; // set the morning date here
NSString *_morningDateString = [_dateFormatter stringFromDate:_currentDate];
[_dateFormatter setDateFormat:#"yyyy-MM-dd' 06:00PM +0000'"]; // set the evening date here
NSString *_eveningDateString = [_dateFormatter stringFromDate:_currentDate];
[_dateFormatter setDateFormat:#"yyyy-MM-dd hh:mma zzz"];
NSDate *_morningDate = [_dateFormatter dateFromString:_morningDateString];
NSDate *_eveningDate = [_dateFormatter dateFromString:_eveningDateString];
NSTimeInterval _intervalToMorningDate = [_morningDate timeIntervalSinceDate:_currentDate];
NSTimeInterval _intervalToEveningDate = [_eveningDate timeIntervalSinceDate:_currentDate];
if (_intervalToMorningDate > 0) {
// now it is night
dayNight.image = [UIImage imageNamed:#"moon.png"];
[self performSelector:#selector(replaceTheBackgoundForMorning) withObject:nil afterDelay:_intervalToMorningDate];
} else {
// now it is daytime
dayNight.image = [UIImage imageNamed:#"Sun.png"];
[self performSelector:#selector(replaceTheBackgoundForEvening) withObject:nil afterDelay:_intervalToEveningDate];
}
}
- (void)viewDidDisappear:(BOOL)animated {
[NSObject cancelPreviousPerformRequestsWithTarget:self];
}
and finally you should add them to your same .m file:
-(void)replaceTheBackgoundForMorning {
// reaplce the backgound here
dayNight.image = [UIImage imageNamed:#"Sun.png"];
}
- (void)replaceTheBackgoundForEvening {
// reaplce the backgoung here
dayNight.image = [UIImage imageNamed:#"moon.png"];
}

How do I pause a stopwatch, then have it pick back up from where it started?

I am designing a timer that I want to be able to pause and continue from the same place. I tried doing it but the code wouldn't work for me. Here is my code:
.m file:
-(IBAction)buttonPause:(id)sender {
NSString *dateString = timer.text;
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"HH:mm:ss.SS"];
NSDate *dateFromString = [[NSDate alloc] init];
dateFromString = [dateFormatter dateFromString:dateString];
dateFromString = startDate;
}
- (IBAction)buttonStart:(id)sender {
startDate = [NSDate date];
stopWatchTimer = [NSTimer scheduledTimerWithTimeInterval:1.0/100.0
target:self
selector:#selector(updateTimer)
userInfo:nil
repeats:YES];
}
- (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.SS"];
[dateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0.0]];
NSString *timeString = [dateFormatter stringFromDate:timerDate];
timer.text = timeString;
}
My problem appears to lie in the buttonPause method. What am I doing wrong or am not doing? Thank you in advanced.
You can use NSTimeInterval instead of timer. I have a functional code to pause and stop the timer.
#interface PerformBenchmarksViewController () {
int currMinute;
int currSecond;
int currHour;
int mins;
NSDate *startDate;
NSTimeInterval secondsAlreadyRun;
}
#end
- (void)viewDidLoad
{
[super viewDidLoad];
running = false;
}
- (IBAction)StartTimer:(id)sender {
if(running == false) {
//start timer
running = true;
startDate = [[NSDate alloc] init];
startTime = [NSDate timeIntervalSinceReferenceDate];
[sender setTitle:#"Pause" forState:UIControlStateNormal];
[self updateTime];
}
else {
//pause timer
secondsAlreadyRun += [[NSDate date] timeIntervalSinceDate:startDate];
startDate = [[NSDate alloc] init];
[sender setTitle:#"Start" forState:UIControlStateNormal];
running = false;
}
}
- (void)updateTime {
if(running == false) return;
//calculate elapsed time
NSTimeInterval currentTime = [NSDate timeIntervalSinceReferenceDate];
NSTimeInterval elapsed = secondsAlreadyRun + currentTime - startTime;
// extract out the minutes, seconds, and hours of seconds from elapsed time:
int hours = (int)(mins / 60.0);
elapsed -= hours * 60;
mins = (int)(elapsed / 60.0);
elapsed -= mins * 60;
int secs = (int) (elapsed);
//update our lable using the format of 00:00:00
timerLabel.text = [NSString stringWithFormat:#"%02u:%02u:%02u", hours, mins, secs];
//call uptadeTime again after 1 second
[self performSelector:#selector(updateTime) withObject:self afterDelay:1];
}
Hope this will help. Thanks

local notification of events that already happened in the past

I am making a reminder app, where user create some event with date and time and choose when he should get noticed about it by choosing a fire date (interval before this event: right now, 5, 15, 30 minutes ago, and so on) and repeat interval (never, daily, weekly, monthly and every year). The problem is: when user creates an event, that already happens, for example event should occur in 10 april, and today is 16 april, he should be reminded about this event right in time, but user gets notification about this event right after creating it. So it shouldn't happened. How can I avoid this?
Here is the method, that creates notification
- (void)notificationWithNote:(Note *)scheduledNote deleteThisNotification:(BOOL)deleteNotification {
NSCalendar *calendar = [NSCalendar autoupdatingCurrentCalendar];
unsigned int unitFlags = NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit|NSHourCalendarUnit|NSMinuteCalendarUnit;
NSDateComponents *comp = [calendar components:unitFlags fromDate:scheduledNote.date];
ToDoItem *todoitem = [[ToDoItem alloc] init];
todoitem.day = [comp day];
todoitem.month = [comp month];
todoitem.year = [comp year];
todoitem.hour = [comp hour];
todoitem.minute = [comp minute];
todoitem.eventName = scheduledNote.event;
NSDateComponents *dateComps = [[NSDateComponents alloc] init];
[dateComps setDay:todoitem.day];
[dateComps setMonth:todoitem.month];
[dateComps setYear:todoitem.year];
[dateComps setHour:todoitem.hour];
[dateComps setMinute:todoitem.minute];
NSDate *itemDate = [calendar dateFromComponents:dateComps];
[dateComps release];
UILocalNotification *localNotif = [[UILocalNotification alloc] init];
if (localNotif == nil)
return;
if ([scheduledNote.remindTime intValue] == 1)
localNotif.fireDate = itemDate;
else
localNotif.fireDate = [itemDate dateByAddingTimeInterval:-([scheduledNote.remindTime intValue]*60)];
switch ([scheduledNote.repeatOption intValue]) {
case 0:
localNotif.repeatInterval = 0;
break;
case 1:
localNotif.repeatInterval = NSDayCalendarUnit;
break;
case 2:
localNotif.repeatInterval = NSWeekCalendarUnit;
break;
case 3:
localNotif.repeatInterval = NSMonthCalendarUnit;
break;
case 4:
localNotif.repeatInterval = NSYearCalendarUnit;
break;
default:
break;
}
localNotif.timeZone = [NSTimeZone defaultTimeZone];
localNotif.alertBody = [NSString stringWithFormat:NSLocalizedString(#"%# begins", nil), scheduledNote.event];
localNotif.alertAction = NSLocalizedString(#"View Details", nil);
localNotif.soundName = UILocalNotificationDefaultSoundName;
localNotif.applicationIconBadgeNumber = 1;
NSDictionary *infoDict = [NSDictionary dictionaryWithObjectsAndKeys:todoitem.eventName,ToDoItemKey, #"Timenote is coming", MessageTitleKey, nil];
localNotif.userInfo = infoDict;
if (deleteNotification)
[[UIApplication sharedApplication] cancelLocalNotification:localNotif];
else
[[UIApplication sharedApplication] scheduleLocalNotification:localNotif];
NSLog(#"fire date: %#",[localNotif.fireDate description]);
[todoitem release];
[localNotif release];
}
This bug happens only if user doesn't choose any repeat interval. If event repeats every day/week/month/year, remind appears right in time. So the problem is actual if localNotif.repeatInterval == 0
Looks like, I've found the answer. If issue happens only, when there is no repeat interval, then it needs to check this condition: if fireDate is less then current date and if there is no repeat interval, than this notification shouldn't be scheduled.
NSComparisonResult result = [localNotif.fireDate compare:[NSDate date]];
if (((localNotif.repeatInterval == 0) && (result == NSOrderedAscending)) || deleteNotification)
{
[[UIApplication sharedApplication] cancelLocalNotification:localNotif];
}
else
[[UIApplication sharedApplication] scheduleLocalNotification:localNotif];