AVCapturesession handling after returning from background - ios

I am implementing a VideoRecorder using the AVCaptureSession.
I am starting AVCaptureSession at viewWillAppear and tearing it down at viewWillDisappear on recommendation of this question AVCaptureSession fails when returning from background
.
Now when the Video is Recording and the app goes to background I want to stop recording and pause the capture session. But each time the app comes to foreground at this point I get one of the following
Capture Session is not paused but recording and the Preview Layer keeps updating
Capture Session provides Preview Layer with black-screen at this point app may or may not crash.
Any suggestions on handling the AVCaptureSession at this point. I would like to just show the last frame recorded on the previewLayer, once recording stops.

I have encountered a similar situation and in my experience I have found that viewWillDisappear: doesn't get called. I'm really not sure why, but I solved it by subscribing for notifications when the app goes inactive. Here's an example:
In viewWillAppear:
// Detect this for ending recording
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(appInactive:) name:UIApplicationWillResignActiveNotification object:[UIApplication sharedApplication]];
And the appropriate callback method:
- (void)appInactive:(NSNotification *)notification {
NSLog(#"App going inactive, stopping recording...");
taskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler: ^{
[[UIApplication sharedApplication] endBackgroundTask:taskId];
taskId = UIBackgroundTaskInvalid;
}];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
question.prepTimeRemaining = [prepEndTime timeIntervalSinceNow];
// Stop camera stuff
if (recording)
[self stopRecording]; // Method to handle shutting down the session, any other cleanup, etc.
// End task
[[UIApplication sharedApplication] endBackgroundTask:taskId];
taskId = UIBackgroundTaskInvalid;
});
}
In viewWillDisappear:
[[NSNotificationCenter defaultCenter] removeObserver:self];
I immediately move to the next view when I detect this, so I'm not sure what it leaves behind on the preview layer, but I suspect it would do what you want. Hope this helps!

This is late but I was experiencing some of the same. In order to get around the problem you first have to realize ViewWillAppear and ViewWillDisappear are strictly for in app transitions from one View Controller to another. They don't work for foreground to background and back again transitions. I used a similar fix above:
//application became active
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationEnteredForeground:)
name:UIApplicationWillEnterForegroundNotification
object:nil];
//application went into background
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationBecameActive:)
name:UIApplicationDidBecomeActiveNotification
object:nil];
In the selector methods just stop and start your camera session and as the other stackoverflow post suggest, it would be a good idea to lazily instantiate your avcapturesession so that your app is memory conservative

Related

iOS 9 UIApplicationDidBecomeActiveNotification callback not called

In iOS 9 the following code for detecting a notification does not fire the selector method. In previous versions (e.g. 8.4) it's running fine. Does anyone know why?
[[NSNotificationCenter defaultCenter]addObserver:self
selector:#selector(yourMethod)
name:UIApplicationDidBecomeActiveNotification
object:nil];
- (void)yourMethod {NSLog(#"aaaaaaa");}
This link as below maybe help your problem.
Foundation Release Notes for OS X v10.11
Use "addObserverForName" instead of "addObserver".
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidBecomeActiveNotification
object:nil
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification * Nonnull note) {
[self yourMethod];
}];
It will be work.
I hade the same problem and for me it worked to move the addObserver code to awakeFromNib. Another solution could be to add a delay to the addObserver as in the example below:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(applicationDidBecomeActive) name:UIApplicationDidBecomeActiveNotification object:nil];
});
While doing some tests, I noted that the notification is actually triggered, albeit not at launch time (or at least too early to be caught by your observer) (on the simulator only).
Try pulling the notification center down and up or the control center up and down, and you'll see your method will actually be called.
I'd suggest maybe calling your method manually on iOS 9, when your app starts?
On a real device, the method is called just as on iOS 8.
Edit: after further investigation, it seems the notification is actually not triggered every time on devices :/
I've also bumped into this problem and adding the observer with a delay didn't solve it in my case. Managed to get rid of it by calling the method directly in AppDelegate and from the main queue, without a delay.
Swift:
func applicationDidBecomeActive(application: UIApplication) {
dispatch_async(dispatch_get_main_queue()) { () -> Void in
yourMethod()
}
}
I had the exact same issue only on real devices running iOS 9.0 and up.
I ended up defining my own notification:(this define should be globally available, constants.h or alike)
#define myAppBecameActiveNotif #"Tito, your app is active"
Then, in your AppDelegate implementation (AppDelegate.m most of the cases) you implement applicationDidBecomeActive delegate method:
- (void)applicationDidBecomeActive:(UIApplication *)application {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:myAppBecameActiveNotif
object:nil];
});
}
(Just delay the dispatch of your notification a bit, to let your components come to life)
Now, in the class you'd like to be notified the app became active you do
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(whatToDoWhenAppActive:)
name:myAppBecameActiveNotif
object:nil];
Working good now. You'll see you cannot feel this 500ms delay...
I do notification register like that.
[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(yourMethod:) name:UIApplicationDidBecomeActiveNotification object:nil];
Write Method like that.
- (void)yourMethod:(NSNotification *)notification
{
NSLog(#"aaaaaaa");
}
Also you need to NSLog in AppDelegate.m and see log in console.
-(void)applicationDidBecomeActive:(UIApplication *)application{
NSLog(#"applicationDidBecomeActive");
}

Stop location updates when app terminate

I'm developing an app which sends notifications when you are nearby of promoted places.
My problem is when I go to background and then I quit the app, I don't want the location services working when the app doesn't work (but I want them to work in background).
I saw only 3 apps which close the gps when the app is closed and I want to know how they did that, Facebook, Google Maps and Apple Maps, not Foursquare, not FieldTrips...
Thank you everybody.
you can add an observer for UIApplicationWillTerminateNotification where you start locationManager and than stop location updates
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationWillTerminate:)
name:UIApplicationWillTerminateNotification
object:nil];
method to perform when you receive the notification
- (void)applicationWillTerminate:(NSNotification *)notification {
//stop location updates
}
I found the correct answer to my question becouse of #GuyS second post:
Adding that in your AppDelegate.m applicationDidEnterBackground
- (void)applicationDidEnterBackground:(UIApplication *)application
{
UIApplication *app = [UIApplication sharedApplication];
if ([app respondsToSelector:#selector(beginBackgroundTaskWithExpirationHandler:)]) {
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
// Synchronize the cleanup call on the main thread in case
// the task actually finishes at around the same time.
dispatch_async(dispatch_get_main_queue(), ^{
if (bgTask != UIBackgroundTaskInvalid)
{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}
});
}];
}
}
And declaring that variable:
UIBackgroundTaskIdentifier bgTask;
After that you only have to stop your location services in applicationWillTerminate...
Thank you for your replies.
The solution provided by #GuyS in this topic should work. I'm getting the UIApplicationWillTerminateNotification in case the app is in background and then I close it by swiping up the snapshot. Please check whether you work correctly with NSNotificationCenter (especially adding and removing notification). Plus, please check the object you subscribed on the notification is alive when the app is in background.
Another similar solution is to place the code that disables GPS in appropriate UIApplicationDelegate callback in your AppDelegate method.
- (void)applicationWillTerminate:(UIApplication *)application {
//stop location updates
}

How can I stop iOS displaying snapshot on resume?

I am creating an app with functionality like a stopwatch. When the app moves to the background, iOS takes a snapshot, and when it moves back into the foreground it uses that snapshot for the animation while switching back to the app.
This means that if the app was backgrounded for 10 seconds, the stopwatch will have the wrong time (by 10 seconds) displayed during the opening animation.
Is there some way to stop iOS taking this snapshot, or stop iOS from using it when the app moves back to the foreground?
You can not stop iOS from taking snapshot.
But you can display and hide a temporary view using below notifications :
UIApplicationDidEnterBackgroundNotification
UIApplicationWillEnterForegroundNotification
The temporary view will be then taken as the snapshot.
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(displayTempView) name:UIApplicationDidEnterBackgroundNotification object:nil];
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(hideTempView) name:UIApplicationWillEnterForegroundNotification object:nil];
- (void) displayTempView {
tempWebView.hidden = NO;
}
- (void) hideTempView {
tempWebView.hidden = YES;
}
Hope it helps.

UIApplicationDidBecomeActiveNotification filter notifications

I registered my main view controller for listening to UIApplicationDidBecomeActiveNotification because I want to display a UIAlertView each time the user enters my app :
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(someMethod:)
name:UIApplicationDidBecomeActiveNotification
object:nil];
It's working like a charm, my only problem is if my app gets interrupted (by an UIAletView, such as a calendar event, or a popup asking for picture access confirmation), the notification gets called once the alert view's dismissed.
Any idea on how to detect ONLY when my app comes back from background mode ?
why don't you use AppDelegate method,
- (void)applicationWillEnterForeground:(UIApplication *)application
{
//do whatever you want when app comes from background to foreground
}
I know this is an old thread, but there is a UIApplicationWillEnterForegroundNotification. Works like this:
[[NSNotificationCenter defaultCenter]addObserver:self
selector:#selector(myMethod)
name:UIApplicationWillEnterForegroundNotification
object:nil];
Best regards,
Gabriel Tomitsuka
Check state (active/background) of your application by following code:
UIApplicationState state = [[UIApplication sharedApplication] applicationState];
if (state == UIApplicationStateActive)
{
/// your stuff of code:
}
Above code might be useful in your case:

MPMoviePlayerController getting paused when app enters in background [duplicate]

I'm uisng this code to display a movie:
MPMoviePlayerViewController *mp = [[MPMoviePlayerViewController alloc]
initWithContentURL:movieURL];
mp.moviePlayer.movieSourceType = MPMovieSourceTypeUnknown;
[self presentMoviePlayerViewControllerAnimated:mp]; [mp.moviePlayer play];
The code is working fine. However when the application goes to the background while playing a movie, when the app comes back in the foreground the movieplayer is not displayed. (I see the view of the controller that called presentMoviePlayerViewControllerAnimated:mp
Is it possible when entering the foregound to resume the movie that was playing before the app went to the background?
Have you set the UIBackgroundmode to audio and also there has been problem with playing the video after app enters foreground .Refer this Tutorial on MPMoviePlayerViewController Also you can try using MPMoviePlayerViewController which has options for implementing various notifications .
you can implement notification techniques to handle it. Add a notification in the class where movie player is playing and associate with it a selector. When app goes to background then in the delegate method
- (void)applicationDidEnterBackground:(UIApplication *)application
{
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
UIApplication *app = [UIApplication sharedApplication];
UIBackgroundTaskIdentifier bgTask = 0;
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
}];
}
write this code.Actually when app goes background it pauses the MPMoviePlayerController so when it is coming to foreground you post the notification which call the method in class where movie controller is implemented and play it again in this method.
-(void)playIntroAnimationAgain
{
[[NSNotificationCenter defaultCenter]removeObserver:self name:NOTIFICATION_PlayAgain_Player object:nil];
[self.moviePlayerController play];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(playIntroAnimationAgain)name:NOTIFICATION_PlayAgain_Player object:nil];
}
It solved my problem.

Resources