NSTimer with NSUserDefaults - ios

How do I have an NSTimer with a UIButton that changes its text from Start to Stop, Also another UIButton will be added to pause the timer. So, You press start and then the timer will show in a label in hours, minutes and seconds. While the timer is running, is there a way to pause the timer ( I heard you have to use NSUserDefaults). Also is there a way to save the time at which the start button was pressed and then to save the time that the button was pressed again to stop? Also when the pause button is pressed to pause the timer and when pause is pressed again it will resume the timer?

If you need a timer that doesn't support pausing, you only need to know the NSDate at which the timer started so that you can calculate the time elapsed. Create an instance variable:
#property (strong, nonatomic) NSDate *timerStartDate;
When the button is tapped:
- (void)startTimer
{
self.timerStartDate = [NSDate date];
}
Your NSTimer is not to keep track of the time, but to periodically update the label. Create an instance variable:
#property (strong, nonatomic) NSTimer *labelUpdateTimer;
and update the -startTimer method:
- (void)startTimer
{
self.timerStartDate = [NSDate date];
// start timer to update label
if (!self.labelUpdateTimer) {
self.labelUpdateTimer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(updateLabel)
userInfo:nil
repeats:YES];
}
}
- (void)updateLabel
{
NSTimeInterval secondsElapsedSinceTimerStart = 0;
if (self.timerStartDate) {
secondsElapsedSinceTimerStart = [[NSDate date] timeIntervalSinceDate:self.timerStartDate];
}
NSString *formattedTime = <format time elapsed the way you like>;
self.label.text = formattedTime;
}
- (void)dealloc
{
// make sure timer is not firing anymore!
if (_labelUpdateTimer) {
[_labelUpdateTimer invalidate];
_labelUpdateTimer = nil;
}
}
However, if you want to be able to pause the timer, besides calculating the time elapsed since the timer last started, you'll need to store the time previously elapsed (if you started/paused the timer before). Create an instance variable:
#property (nonatomic) NSTimeInterval previouslyElapsedSeconds;
And when you pause the timer:
- (void)pauseTimer
{
// update elapsedSeconds
if (self.timerStartDate) {
self.previouslyElapsedSeconds += [[NSDate date] timeIntervalSinceDate:self.timerStartDate];
self.timerStartDate = nil;
}
[self.labelUpdateTimer invalidate];
self.labelUpdateTimer = nil;
}
Update -updateLabel:
- (void)updateLabel
{
NSTimeInterval secondsElapsedSinceTimerStart = 0;
if (self.timerStartDate) {
secondsElapsedSinceTimerStart = [[NSDate date] timeIntervalSinceDate:self.timerStartDate];
}
// account for previously elapsed time
NSTimeInterval totalSecondsElapsed = self.previouslyElapsedSeconds + secondsElapsedSinceTimerStart;
NSString *formattedTime = <format time elapsed the way you like>;
self.label.text = formattedTime;
}
NSUserDefaults will be required only if you want to keep timing even if the app is shut down (not just backgrounded). In that case store previouslyElapsedSeconds and timerStartDate in NSUserDefaults instead of as instance variables.

assuming you have NSDate objects startTime and stopTime
[[NSUserDefaults standardUserDefaults] setObject:startTime forKey:#"startTime"];
[[NSUserDefaults standardUserDefaults] setObject:stopTime forKey:#"stopTime"];
Or you can use floats startTimef and stopTimef
[[NSUserDefaults standardUserDefaults] setFloat:startTimef forKey:#"startTime"];
[[NSUserDefaults standardUserDefaults] setFloat:stopTimef forKey:#"stopTime"];
Check out the NSUserDefaults docs.
Here's a great set of tutorials.

Related

How to start and stop timer at particular times in iOS

My app detect the audio at particular intervel like 10:00AM(startTime) -10:30AM(endTime),etc.Now when the current time reaches start time(means current time is also 10:00 AM) how to call audio detection process and stop detection process at (endTime)10:30 AM . suppose another interval start at 10:15AM(startTime) - 10:45 AM(endTime) how to update endTime from 10:30AM to 10:45AM.how to check when current time reaches startTime and endTime.Please help me its urgent.Thanks here my code is
ViewController.h
#interface ViewController : UIViewController
{
BOOL IsListening;
}
//session array is array of dictionaries contains program start and end time.
ViewController.m
-(void)viewDidLoad
{
[super viewDidLoad];
NSTimer* listener_timer = [NSTimer scheduledTimerWithTimeInterval: 10.0 target: self
selector: #selector(listeningHandler:) userInfo: nil repeats: YES];
}
-(void)listeningHandler:(NSTimer*)time
{
if (!IsListening)
{
double currentTime=[[NSDate date] timeIntervalSince1970];
for (NSDictionary * session in SessionsArray)
{
double startTime=[[session objectForKey:#“startTime”] doubleValue];
double endTime=[[session objectForKey:#“endTime”] doubleValue];
if (currentTime > startTime && currentTime < endTime)
{
//start listening.
IsListening =YES;
/*
//detection process code
*/
NSDate *fireDate = [NSDate dateWithTimeIntervalSinceNow:endTime];
NSTimer *timer = [[NSTimer alloc] initWithFireDate:fireDate
interval:5
target:nil
selector:#selector(fireEndtimeAlarm)
userInfo:nil
repeats:YES];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addTimer:timer forMode:NSDefaultRunLoopMode];
}
}
}
else
{
double currentTime=[[NSDate date] timeIntervalSince1970];
for (NSDictionary * session in SessionsArray)
{
double startTime=[[session objectForKey:#"startTime"] doubleValue];
double endTime=[[session objectForKey:#"endTime”] doubleValue];
if (currentTime > startTime && currentTime < endTime)
{
// in between session
IsListening =YES;
/*
//detection process code
*/
}
}
}
}
-(void)fireEndtimeAlarm
{
IsListening = NO;
//stop detecting process at endTime
}
Start Timer & Stop Timer in ios
start Timer
timer= [NSTimer scheduledTimerWithTimeInterval:60.0f
target: self
selector: #selector(methodFromTimer)
userInfo: nil
repeats: YES];
stop timer
[timer invalidate];
timer=nil;
You can achieve this using 2 Local notifications
First of All schedule a specific notification at a specific time when time is reached then a trigger will run and at that time Start audio and
Schedule a second notification after 30 min when that will be fired then stop audio
For local notifications you can follow these
Link1
Link2

Timer in Today Extension Freezes Extension

I have a timer to a date counting down in my today widget. I am gathering the date from my View Controller and then starting a timer in the extension and from there I am displaying the dates in three labels, Hours, Minutes, and Seconds. In my app... this is perfect, but not so true in the notification center. The countdown loads correctly and works great for about 20 seconds. After that it skips a second and freezes, then it starts again in about 4 seconds and then it will reset the whole widget (everything disappears) and then about 2 seconds later the whole widget resets. Is this a memory warning or something?
View Controller:
mainTextLabel.text = 436496400
NSUserDefaults *sharedDefaults = [[NSUserDefaults alloc] initWithSuiteName:#"group.nicmac.nextgame"];
[sharedDefaults setObject:mainTextLabel.text forKey:#"MyNumberKey"];
[sharedDefaults synchronize];
Today View Controller
- (id)initWithCoder:(NSCoder *)aDecoder {
if (self = [super initWithCoder:aDecoder]) {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(userDefaultsDidChange:)
name:NSUserDefaultsDidChangeNotification
object:nil];
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.preferredContentSize = CGSizeMake(320, 300);
[self performSelector:#selector(updateNumberLabelText) withObject:nil afterDelay:1.0];
}
- (void)userDefaultsDidChange:(NSNotification *)notification {
[self performSelector:#selector(updateNumberLabelText) withObject:nil afterDelay:1.0];
}
- (void)updateNumberLabelText {
NSUserDefaults *defaults = [[NSUserDefaults alloc] initWithSuiteName:#"group.nicmac.nextgame"];
NSString *date = [defaults stringForKey:#"MyNumberKey"];
self.numberLabel.text = [NSString stringWithFormat:#"%#", date];
NSString *doubleString = self.numberLabel.text;
double value = [doubleString doubleValue];
destinationDate = [NSDate dateWithTimeIntervalSince1970:value];
timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(updateNumberLabelText) userInfo:nil repeats:YES];
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
int units = NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond;
NSDateComponents *components = [calendar components:units fromDate:[NSDate date] toDate:destinationDate options:0];
[hour setText:[NSString stringWithFormat:#"%ld", (long)[components day] * 24 + (long)[components hour]]];
[minute setText:[NSString stringWithFormat:#"%ld", (long)[components minute]]];
[second setText:[NSString stringWithFormat:#"%ld", (long)[components second]]];
}
Thanks!
The problem here lies in the - (void)updateNumberLabelText method.
Specifically, here:
timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(updateNumberLabelText) userInfo:nil repeats:YES];
This means that every time you're calling this method, it's scheduling another timer to call this method every second (every second because repeats:YES). That means, every second (with a micro offset because of any lag in the computer), x number of scheduled timers is producing x MORE number of scheduled timers, meaning that every second you have double the number of timers. This is called exponential growth, and can be modelled to 2^x.
So to find the number of timers in 20 seconds:
2^1+2^2+2^3+2^4+2^5+2^6+2^7+2^8+2^9+2^10+2^11+2^12+2^13+2^14+2^15+2^16+2^17+2^18+2^19+2^20
Ew, can we represent this better - we can! We can use sigma notation like so:
<no. of seconds>
Σ 2^i
i=1
But enough math. That averages out to around a whopping 2,000,000 timers initialised in 20 seconds. That is a lot of timers, and the widget eventually struggles to catch breath (no more memory!) and dies/crashes/restarts itself.
Here's how to fix it:
If you really want this method to loop itself, call
[self performSelector:#selector(updateNumberLabelText) withObject:nil afterDelay:1.0];
in the method instead.
You could do it the way you're currently doing it and do repeats:NO instead, but I don't think that the timer will invalidate itself after the 1.0 second, and creating that many timers is not very efficient.

Get the length of time the iPhone was locked

How would I get the length of time a phone was locked if I wanted to use it to increment a timer progress view when the phone was resumed, or schedule a notification to fire when the phone was still locked?
Implement the UIApplicationDelegate method applicationWillResignActive: and applicationDidBecomeActive:.
You will have to store the current time when the former is called and calculate the difference when the latter is called. Specifically, in your application delegate:
#define TIMESTAMP_KEY #"timestamp"
- (void)applicationWillResignActive:(UIApplication *)application
{
NSInteger *timestamp = [[NSDate date] timeIntervalSince1970];
[[NSUserDefault standardUserDefaults] setInteger:timestamp forKey:TIMESTAMP_KEY];
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
NSInteger *newTimestamp = [[NSDate date] timeIntervalSince1970];
NSInteger *oldTimestamp = [[NSUserDefault standardUserDefaults] integerForKey:TIMESTAMP_KEY];
NSInteger *secondsPassed = newTimestamp - oldTimestamp;
// Now you can resynch your timer with the secondsPassed
}

How to update label once a day?

I'd like to have a label in my app that gets updated once a day at a specific time (i.e. 12 a.m.). If you could help me out I'd really appreciate it, thanks.
This is what i currently have but it only works if the app is opened between 12 and 1 am.
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"HH"];
NSString *stringDate = [formatter stringFromDate:[NSDate date]];
if ([stringDate isEqualToString:#"00"]) {
NSLog(#"its working");
[self changeLabel];
}
To clarify, I'm trying to change the text of a label once a day. I'd like to have the label update at a specific time like 12 am so that it updates with the start of each new day. I've tried a few ways of doing this but haven't gotten the desired result. If anyone knows of a way to do this I'd appreciate your input.
If you only need to determine the last time the application was opened, you can use NSUserDefaults to save the date and then check that the approprate number of seconds (86400 = 1 day) have passed.
Something along the lines of the following should point you in the right direction:
//gather last time app was opened
NSDate *lastUpdated = [[NSUserDefaults standardUserDefaults] objectForKey:#"app_last_updated"];
//check date is valid
if(lastUpdated)
{
//determine number of seconds that have passed
NSTimeInterval timeInterval = [lastUpdated timeIntervalSinceNow];
//check time interval is greater than 1 day
if(timeInterval > 86400)
{
//update label
}
}
//save current time
[[NSUserDefaults standardUserDefaults] setObject:[NSDate date] forKey:#"app_last_updated"];
It depends on what you want to do. On iOS, most likely your app is not running in the foreground all the time. So I would just update the label when the application is active in applicationDidBecomeActive:. Then I would create a timer to fire at the specific date/time I want.
For example:
NSDate *d = // create the next date/time
updateTimer_ = [[NSTimer alloc] initWithFireDate: d
interval: 0
target: self
selector:#selector(updateLabel:)
userInfo:nil repeats:NO];
NSRunLoop *runner = [NSRunLoop currentRunLoop];
[runner addTimer:updateTimer_ forMode: NSDefaultRunLoopMode];
I know this is an old post but a few days ago I needed just what Dave123 asked and I came up with this solution after searching a lot on the web:
- (void)applicationDidBecomeActive:(UIApplication *)application {
NSLog(#"applicationDidBecomeActive");
self.masterviewController = [[PBMasterViewController alloc] init];
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
NSLog(#"Seconds --------> %.f",[[NSDate date] timeIntervalSinceDate:[[NSUserDefaults standardUserDefaults] objectForKey:#"OLD_DATE"]]);
NSLog(#"old date: %#", [[NSUserDefaults standardUserDefaults] objectForKey:#"OLD_DATE"]);
// 24 hours must elapse between each update check
if ([[NSDate date] timeIntervalSinceDate:[[NSUserDefaults standardUserDefaults] objectForKey:#"OLD_DATE"]] > 86400) {
NSLog(#"Time elapsed since last update is MORE than 1 day. Check for updates.");
[self.masterviewController checkForUpdates];
} else {
NSLog(#"Time elapsed since last update is LESS than 1 day. Don't check for updates.");
}
// Here I convert the seconds into hours with two decimals, if you want to show the time elapsed in a UILabel
NSString *stringCheckInterval = [NSString stringWithFormat:#"%.02f hours since last update", [[NSDate date] timeIntervalSinceDate:[[NSUserDefaults standardUserDefaults] objectForKey:#"OLD_DATE"]] / 60 / 60];
NSLog(#"%#", stringCheckInterval);
}
is there someone that has the app opened form more than 24h??
Anyway, you can simply use an NSTimer with 86400 as delay time.
When you start the app, set up a timer that will fire at 12:00:01 AM and update your label.

timer in iOS not working

I have this code for a repeated timer in my app:
(inside viewDidAppear)
NSDate *date = [NSDate dateWithTimeIntervalSinceNow: 3];
NSTimer *restrictionTimer = [[NSTimer alloc] initWithFireDate: date
interval: 3
target: self
selector:#selector(changeRestriction)
userInfo:nil repeats:YES];
and
- (void) changeRestriction
{
NSLog(#"inside !!");
}
However, I dont see any output, any help ?
If you're using -initWithFireDate:.. you have to manually add the timer to the NSRunLoop. However you could just use +scheduledTimerWithTimeInterval:.. and it will automatically trigger.

Resources