I'm doing a simple game for the iPhone and now that is finished, I'd like to add a timer. I managed to implement the timer, but now I want to pause it when an incoming SMS or Phone Call minimize the application.
I thought that I should put the timer in the app delegate and, when applicationWillResignActive/applicationDidBecomeActive will be called, save/restore the timer object with NSUserDefaults, but I see that I can only save "raw" data, not entire objects.
How can i manage this trouble?
You'll need to store the elapsed time in a variable (I wouldn't use NSUserDefaults) and then create a new timer with the elapsed time subtracted.
See this question for some more info: How can I programmatically pause an NSTimer?
Related
I'm working on a multiview app. One of the views is a table view. Each cell has a stopwatch. I'm planning to use NSTimer for the stopwatches. Do I need to implement multithreading for the timers to work properly even when the user switches the view and then comes back later?
I did my research but most of the tutorials cover one NSTimer in a single view. I want to make sure the user can do other things while the timers are running, like use the interface, navigation, etc. In another post Placing an NSTimer in a separate thread someone said you need a different runloop for the timer. Would I need one runloop for each timer in my case? Is it advisable? Any performance drawbacks?
Thanks a lot!
One run loop should be just fine. Your interface will still be responsive.
Keep in mind that timers are never guaranteed to be accurate. They are affected by how much other stuff is on the same loop. Its ok to use the timer to update the display but not to actually measure time. Set an NSDate when you start a stop watch then compare the current date with that start date each time your display timer updates the display.
Since you should only use the NSTimer to update the display, could you just use one generic display update timer that updates all running stopwatches, instead of having one for each stopwatch?
I have 2 viewControllers in my app, from first view when i navigate to next one there is a button named startTimer with a timer action as selector method on Click of startTimer the timer starts up in HH:mm:ss format, i am not invalidating timer,but When i go back to 1st viewController and again if i come to again 2nd viewController and if i press startTimer button the timer again starts from 0, but i want it to be retained the previous value,how can i achieve this? i know that since i'm loading again the viewController the nib will be loaded freshly to memory but how can i retain the timer label and timer value?
Any help is appreciated in advance.thank you.
You've broken MVC (Model-View-Controller) by putting your data into your view controller. Moreover, you're asking a mechanism with no solid promises about time (NSTimer) to keep track of time for you. NSTimer does not fire exactly at the interval you request. It can fire at any arbitrary point after that interval. Using NSTimer as a stopwatch will almost always lose time (sometimes quite a lot of time, particularly if there's a scrollview around). (That last bit is overstated. A repeating timer schedules itself correctly so won't usually lose time. You'll just lose time if a repeat is completely skipped, which can happen during long scrolls or other things that can keep timers for firing for a full second.)
Create a new model object to hold the stopwatch information. Let's call it Stopwatch. Assuming you need it to be startable and stoppable, it needs an NSTimeInterval accumulatedTime property and an NSDate lastStarted property (you could also make lastStarted an NSTimeInterval if you like). So to start the stopwatch, you set lastStarted to "now." To stop the stopwatch, you clear lastStarted and move the current accumulated time to accumulatedTime. To find out the current time, you add accumulatedTime to now - lastStarted.
OK, now that you have that, what can you do with it? You can pass it to your view controllers and they can ask "what's the current stopwatch value?" They can start and stop it as they like.
Now your view controller would like to update its display every second, so you have a timer that does that. Every second it asks the stopwatch, "what's the current time" and it displays it. But it does not set the time. It just asks.
BTW, you can also use KVO on Stopwatch, but it's a little trickier, since Stopwatch needs to run its own timer to send out the change notifications. I generally find this more trouble than its worth.
Don't do like that, this way every time you load the view containing the NSTimer, a new NSTimer object is created and the old one is still in the memory since you're not invalidating it.
The best way is that you must put NSTimer in the Application Delegate and then start it only when you first time load that View Controller.
For achieving this, you must put a flag to check that the View is loaded first time or not.
If the NSTimer is one of your instance variable, I guess you could do a check to see if it is allocated or not.
//NSTimer *timer; declare this in your interface
if (timer==nil)
{
// allocate timer
}
//Do nothing if it is allocated all ready
What I would do is to mantain the second view controller as an ivar of your first view controller. This way, you can instantiate it just once and your NSTimer will remain in memory.
However, if you want to maintain your style, you should save the current time in any kind of preferences (look at [NSUserDefaults standardUserDefaults] or create your own singleton class). Then in view did load method from your second view controller, load that value and add it as an offset for your timer.
Hope to help!
How can I use an NSTimer and applicationDidEnterBackground method at background for calling locationManager function?
Why would you need a timer? If you set your delegate to receive the callbacks from location updates, you won't need a timer. If you need active location tracking, you will be taking a huge battery hit if it runs for extended periods of time. You would be better off when you enter background to switch to -monitorForSignificantLocationChanges instead. Register your AppDelegate as as the Location Manager Delegate and just do what you need to do from the call backs.
In my iPhone app I want to logout the user if nothing happens till about 2 minutes (e.g. the user puts down the phone). Does anybody has such issue? What is the best way to implement this feature? I think I save the date of last event to NSUserDefaults, then on the next event first I check the current date. If the difference is larger than 2 minutes go to login screen, else refresh the stored date. But how can I get the touch event generally?
Thanks, madik
There's a method in UIApplicationDelegate for that:
- (void)applicationWillResignActive:(UIApplication *)application
{
/*
Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
*/
}
Note that it also will be called when the app is going to background state. That will help you store the data whenever the app is going to inactive state. If you want to check if a certain amount of time has passed, you will have to use a NSTimer and store the last touch event. I think it cannot be done because you can't intercept all the touch events (Maybe it's over an object managed by the system. The status bar is an example). I guess is better to let the system to manage all the activity/inactivity stuff and store your data when necessary.
EDIT: I didn't understand what you mean the first time. Check this accepted answer, it accomplish what you need. Basically you have to subclass UIApplication and override sendEvent method.
'NSTimer'
When you say "how can I get the touch event generally?", if you mean how can you tell if the user is idle or not, you'll have to set up some system to gather all touch events at a higher level in your app. You could update the last touch time you mentioned in NSUserDefaults but that may be inefficient during the run of the app, so you could just post the touch event to your main app delegate and have it save the time of last touch. Which would also be where you could set up the 2 minute timer.
Something like:
- (void) someAppDelegateMethodThatYouCallForAnyUserEvent
{
[self.idleTimer invalidate];
self.lastEvent = [NSDate now];
self.idleTimer = [NSTimer scheduledTimerWithTimeInterval:120 target:self selector:#selector(logoutAndGotoLogin) userInfo:nil repeats:NO];
...
}
You'll also have to do some cleanup in your app delegate methods when the app goes to background etc if you support that behavior.
I have a simple countdown timer that updates a label every second. How do I keep state or the illusion of it when hitting the home button or when the app gets put in the background?
Actually, you don't need to run in the background if all you need to do is maintain a timer. In your app delegate's applicationWillTerminate:, create an NSDictionary containing the NSTimer's fire time and write it to a plist using -[NSDictionary writeToFile:atomically:], then read it back in using -[NSDictionary initWithContentsOfFile:] somewhere in your app delegate'sapplication:didFinishLaunchingWithOptions:.
If you are running in the background anyway, do the same in applicationDidEnterBackground: and applicationWillEnterForeground:. If you use this solution, be sure to invalidate the timer after you write the plist.