In my app I used the timer,
When First time start the timer I want to stop that timer when it going in background,
So I used this code to stop the timer,
- (void)applicationWillEnterForeground:(UIApplication *)application
{
FirstViewController *view = [[FirstViewController alloc]init];
[view.myticker invalidate]; //myticker is timer
view.myticker = nil;
[view release];
}
I have invalidate the timer when Foreground delegate method call,
But when come to viewcontroller the old timer is still running,
How can i invalidate the timer when timer going in Background?
There is no need to move your timer to your delegate, in fact that is quite a bad idea as it has no purpose being there!
You should observe the following notifications (depending on OS version, the latter being in OS4+)...
UIApplicationWillResignActiveNotification
UIApplicationDidEnterBackgroundNotification
Observe these from within your view controller and invalidate the timer. There are corresponding notifications when the app starts again...
UIApplicationDidBecomeActiveNotification
UIApplicationWillEnterForegroundNotification
Declare your NSTimer variable in AppDelegate method and synthesize it. Then using sharedApplication of AppDelegate, use that variable in whole project and do not create any new variable. Also perform invalidate method on the same instance variable.
In one global file create a time.
In app delegate method,
- (void)applicationDidEnterBackground:(UIApplication *)application {
NSTimer *t ;
if ([t isValid]) {
[t invalidate];
}
}
use above code shall help you to stop timer when application goes into background.
Related
I don't understand it at all but NSTimer in my app definitely is running in background. I have a NSLog in method run by the timer and it is logging while it's in background. It's on iPhone 4 with iOS 4.2.1. I have declared location background support in Info.plist.
I read the docs and many discussions here and elsewhere and it shouldn't be possible. Is it an iOS bug? Or undocumented feature? I don't want to use it and find out in near future, for example with coming of iOS 4.3 that Apple silently "fixed" it and the app won't be working.
Does anybody know more about it?
NSTimer is going to fire whenever the main runloop is running. Apple makes no promises that I know of to unschedule timers or to prevent the main runloop from running. It's your responsibility to unschedule your timers and release resources when you move to the background. Apple isn't going to do it for you. They may, however, kill you for running when you are not supposed to or using too many seconds.
There are many holes in the system that will allow an app to run when it isn't authorized to. It would be very expensive for the OS to prevent this. But you cannot rely on it.
You can have a timer fire while in background execution mode. There are a couple of tricks:
You need to opt into background execution with beginBackgroundTaskWithExpirationHandler.
If you create the NSTimer on a background thread, you need to add it to the mainRunLoop manually.
- (void)viewDidLoad
{
// Avoid a retain cycle
__weak ViewController * weakSelf = self;
// Declare the start of a background task
// If you do not do this then the mainRunLoop will stop
// firing when the application enters the background
self.backgroundTaskIdentifier =
[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:self.backgroundIdentifier];
}];
// Make sure you end the background task when you no longer need background execution:
// [[UIApplication sharedApplication] endBackgroundTask:self.backgroundTaskIdentifier];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Since we are not on the main run loop this will NOT work:
[NSTimer scheduledTimerWithTimeInterval:0.5
target:self
selector:#selector(timerDidFire:)
userInfo:nil
repeats:YES];
// This is because the |scheduledTimerWithTimeInterval| uses
// [NSRunLoop currentRunLoop] which will return a new background run loop
// which will not be currently running.
// Instead do this:
NSTimer * timer =
[NSTimer timerWithTimeInterval:0.5
target:weakSelf
selector:#selector(timerDidFire:)
userInfo:nil
repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:timer
forMode:NSDefaultRunLoopMode];
// or use |NSRunLoopCommonModes| if you want the timer to fire while scrolling
});
}
- (void) timerDidFire:(NSTimer *)timer
{
// This method might be called when the application is in the background.
// Ensure you do not do anything that will trigger the GPU (e.g. animations)
// See: http://developer.apple.com/library/ios/DOCUMENTATION/iPhone/Conceptual/iPhoneOSProgrammingGuide/ManagingYourApplicationsFlow/ManagingYourApplicationsFlow.html#//apple_ref/doc/uid/TP40007072-CH4-SW47
NSLog(#"Timer did fire");
}
Notes
Apps only get ~ 10 mins of background execution - after this the timer will stop firing.
As of iOS 7 when the device is locked it will suspend the foreground app almost instantly. The timer will not fire after an iOS 7 app is locked.
I want to know how we can handle interrupts like incoming call, sms when the app is in foreground. I read the apple documentation and it says that the app will temporarily go into inactive state ie. 'applicationWillResignActive' function will be called. And things like Stopping timers , periodic tasks and running metadata queries should be performed and when it comes back to active state, again these tasks have to be disabled. What kind of tasks are they referring to when they say timers and periodic tasks. And how can we establish these coding wise. Any help will be appreciated. thanks
Yes, when receiving a temporary interruption the following method from UIApplication gets called [Apple's documentation]:
- (void)applicationWillResignActive:(UIApplication *)application
The tasks they are referring with timers and periodic tasks are these kind that are being executed periodically with a timer. For instance, you could have a timer running in the background to update the content of your view. Then, when application will resign active, is where you should stop that timer.
For instance, let's suppose that you are running a timer to perform a task every 10 seconds:
// AppDelegate.m
// When application becomes active the timer is started
- (void)applicationDidBecomeActive:(UIApplication *)application {
self.timer = [NSTimer scheduledTimerWithTimeInterval:10.0
target:self
selector:#selector(scheduledTask:)
userInfo:nil
repeats:YES];
}
// When the application will resign active the timer is stopped
- (void)applicationWillResignActive:(UIApplication *)application {
[self.timer invalidate];
self.timer = nil;
}
The scheduled task would be:
- (void)scheduledTask:(NSTimer *)timer {
// Up to you... for instance: web service call
}
is it possible not to pause the application while in background mode (when you press the home button and the app minimizes)? I have some timers and variables that i don't want to get paused.
EDIT:
I have followed this example http://evilrockhopper.com/2010/01/iphone-development-keeping-the-ui-responsive-and-a-background-thread-pattern/
I have called a timer inside however it's not getting called when i enter background mode:
- (void)applicationDidEnterBackground:(UIApplication *)application {
if(self.viewController.timerquest != NULL)
{
if(self.viewController.timerquest.timerRunning){
// Save varibales
[self performSelectorInBackground:#selector(performLongTaskInBackground) withObject:nil];
}
}
}
- (void) performLongTaskInBackground
{
// Set up a pool for the background task.
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
// perform some long task here, say fetching some data over the web.
//...
timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(updateTimer) userInfo:nil repeats:YES];
// Always update the components back on the main UI thread.
[self performSelectorOnMainThread:#selector(completeLongRunningTask) withObject:nil waitUntilDone:YES];
[pool release];
}
-(void) updateTimer{
// Update my timer. This method is not being called in background mode
}
What should I do?
Thanks.
use Long Running Background tasks according to manual: http://developer.apple.com/library/ios/#documentation/iphone/conceptual/iphoneosprogrammingguide/BackgroundExecution/BackgroundExecution.html
Have a read here at the Apple non-technical documentation or at the technical reference.
Could you replace the timer with delayed background notifications?
Depending on what happens when your timers fire, you want to set up a local notification that fires at the same time the timer would have; this is useful when the timer would present something for the user to act on. As far as saving variables, you'll want to use -applicationDidEnterBackground: to save whatever state you need to, so that the correct variables can be loaded/generated when the app relaunches (which may not happen until the app has been exited and completely restarted again).
The types of tasks that are allowed to perform long running background tasks are pretty limited, specifically for things like GPS and playing audio. Everything else needs to decide on a task-by-task basis whether to simulate continued running (such as turning a timer to a local notification), pausing and saving necessary state to continue the next time the app is run, simply cancelling the task and gracefully restarting/notifying the user upon resuming the app, or asking for a finite length of time to finish a task (for things like finishing a download).
I have a NSTimer that I am stopping when my application resigns active, which I later restart when the application again becomes active. What I did not realise was that applicationWillResignActive would fire when my application first started (pretty obvious really). So what is happening is my NSTimer is incorrectly starting (when the application first starts). My question is, is there a way to check for the application resuming from inactive as apposed to it first starting?
- (void)applicationWillResignActive:(UIApplication *)application {
// STOP CORE TIMER
[[[self mapController] coreTimer] invalidate];
[[self mapController] setCoreTimer:nil];
}
.
- (void)applicationDidBecomeActive:(UIApplication *)application {
// START CORE TIMER
[[self mapController] setCoreTimer:[NSTimer
scheduledTimerWithTimeInterval:ONE_SECOND
target:[self mapController]
selector:#selector(timerDisplay:)
userInfo:nil
repeats:YES]];
}
There is the applicationWillEnterForeground which seems to only fire when the app comes back from the background. Just tested it, won't be called on launch.
Discussion
In iOS 4.0 and later, this method is
called as part of the transition from
the background to the active state.
You can use this method to undo many
of the changes you made to your
application upon entering the
background. The call to this method is
invariably followed by a call to the
applicationDidBecomeActive: method,
which then moves the application from
the inactive to the active state.
I have a question that we might answer together i have a tickertape in my iphone app (Like those stick tickers) and i use a NSThread to keep the memory away from the main thread so it will not slow down the app. Now the thing is it does its job well but when i scroll on a UITableView that i have on the same view i notice that my ticker tape animation stops to work.
ViewController.m (Main view of this object has the ticker tape on it)
-(void)startTicker {
[NSThread detachNewThreadSelector:#selector(start) toTarget:ticker withObject:nil];
}
TickerView.c (This handles the tickertape animation)
// Called from the viewcontroller
-(void) start {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[self performSelectorOnMainThread:#selector(loop) withObject:nil waitUntilDone:YES];
[pool release];
}
-(void)loop {
timerHandle = [NSTimer scheduledTimerWithTimeInterval:.01f target:self selector:#selector(render) userInfo:nil repeats:YES];
}
-(void) render {
// Does a *** load of calculations here and moves the items in the tickertape..
}
My Question: How can i prevent the UITableview or any other view / touch event to block this thread from updating the tickertape animation ?.
Your NSTimer is not running on a background thread, but on the main thread. It will block anytime something else runs on the main thread. -performSelectorOnMainThread: means that anything done within the method called will run on the main thread.
To make your loop truly independent of the main thread, you could set up a while loop within your start method that sleeps for a given interval on every pass, then calls your render method. You'd need to make sure that all user interface updates within your render method get performed on the main thread, but make waitUntilDone NO for those method calls. I've also done this using NSOperations, where as one operation finishes I add another to the queue.
Also, running this render operation 100 times per second is excessive. I'd back that down a bit, or even better, look at using Core Animation for your ticker to make your drawing more efficient.