Get The AVPlayer instance from AppDelegate - objective-c

Im trying to Playing Audio from a Video Asset in the Background
As Apple mentioned in the following example Apple example
We need to set the AVPlayer to nil so that the app continue playing the audio in the background .
the problem is how to get the AVPlayer instance from AppDelegate
I tried this method but nothing work and the app crash when I Enter the Background
- (void)applicationDidEnterBackground:(UIApplication *)application {
Player *PlayerViewController = [UIApplication sharedApplication].keyWindow.rootViewController;
PlayerViewController.VideoPlayer = nil;
}
Terminating app due to uncaught exception
'NSInvalidArgumentException', reason: '-[Home setVideoPlayer:]:
unrecognized selector sent to instance 0x104835000'
also I tried this and its not wokring
- (void)applicationDidEnterBackground:(UIApplication *)application {
Player *PlayerViewController ;
PlayerViewController.VideoPlayer = nil;
}

I found the answer
we need to controll applicationDidEnterBackground and applicationDidEnterBackground in the same Player UIViewController class by adding this 2
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(disconnectAVPlayer) name:UIApplicationDidEnterBackgroundNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(reconnectAVPlayer) name:UIApplicationWillEnterForegroundNotification object:nil];
then we can set VideoPlayer to nil like this
- (void)disconnectAVPlayer{
self.playerLayer.player = nil;
}
- (void)reconnectAVPlayer{
self.playerLayer.player = self.VideoPlayer;
}
NOTE : We need to set AVPlayerLayer to nil not the actual AVPlayer

Related

MPMoviePlayerViewController not playing on 2nd run

I created a MPMoviePlayerViewController which plays a live video. However, if I play the video twice meaning opening the player, clicking done, and playing the stream again. The result is only a black screen with no controls of the MPMoviePlayerViewController. And I need to stop the simulator cause I think the application is crashing. Here's how I did it
- (void) playUrl:(NSURL *)movieInfo
{
NSURL *streamUrl = movieInfo;
MPMoviePlayerViewController *mpvc = [[MPMoviePlayerViewController alloc] initWithContentURL:streamUrl];
[[mpvc view] setFrame:self.view.bounds];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(movieFinishedCallback:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:nil];
mpvc.moviePlayer.movieSourceType = MPMovieSourceTypeStreaming;
[mpvc.moviePlayer setControlStyle:MPMovieControlStyleFullscreen];
[mpvc.moviePlayer setShouldAutoplay:YES];
[mpvc.moviePlayer setFullscreen:NO animated:YES];
[mpvc setModalTransitionStyle:UIModalTransitionStyleCoverVertical];
[mpvc.moviePlayer setScalingMode:MPMovieScalingModeNone];
[mpvc.moviePlayer setUseApplicationAudioSession:NO];
[self presentMoviePlayerViewControllerAnimated:mpvc];
}
- (void) movieFinishedCallback:(NSNotification*) aNotification
{
MPMoviePlayerController *player = [aNotification object];
[player stop];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:MPMoviePlayerPlaybackDidFinishNotification
object:player];
[player.view removeFromSuperview];
NSLog(#"stopped?");
}
I see that in your movieFinishedCallback: implementation, you remove the MPMoviePlayerController view, but in your playUrl: implementation, you are only setting the view's frame, presumably after you have already added the view in viewDidLoad.
One obvious change which is worth trying, is update you code to use the AVPictureInPictureController or AVPlayerViewController class from the AVKit framework, or the WKWebView class from WebKit. According to the MPMoviePlayerViewController docs, it is deprecated as of iOS 9:
The MPMoviePlayerViewController class is formally deprecated in iOS 9. (The MPMoviePlayerController class is also formally deprecated.) To play video content in iOS 9 and later, instead use the AVPictureInPictureController or AVPlayerViewController class from the AVKit framework, or the WKWebView class from WebKit.
Try moving the line where you add the view to the hierarchy, to the playUrl: method. Generally, it is good practice to have countering implementations in opposing methods for your event counterparts. For instance, implement a method to build and add a view when an event starts, and have a corresponding method where you tear down and remove the same view when the same event ends. But, I say 'generally' because there are always exceptions, and you may have very compelling reasons for not doing so. So, in this case, the opposing calls are presentMoviePlayerViewControllerAnimated: and dismissMoviePlayerViewControllerAnimated:, available from the UIViewController category.
After changing the view access to using dot-notation, to be consistent with your callback implementation, here is what your new playUrl: implemntation would look like, assuming you're adding the view to self.view:
- (void) playUrl:(NSURL *)movieInfo
{
NSURL *streamUrl = movieInfo;
MPMoviePlayerViewController *mpvc = [[MPMoviePlayerViewController alloc] initWithContentURL:streamUrl];
[mpvc.view setFrame:self.view.bounds];
[self.view addSubview:mpvc.view];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(movieFinishedCallback:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:nil];
mpvc.moviePlayer.movieSourceType = MPMovieSourceTypeStreaming;
[mpvc.moviePlayer setControlStyle:MPMovieControlStyleFullscreen];
[mpvc.moviePlayer setShouldAutoplay:YES];
[mpvc.moviePlayer setFullscreen:NO animated:YES];
[mpvc setModalTransitionStyle:UIModalTransitionStyleCoverVertical];
[mpvc.moviePlayer setScalingMode:MPMovieScalingModeNone];
[mpvc.moviePlayer setUseApplicationAudioSession:NO];
[self presentMoviePlayerViewControllerAnimated:mpvc];
}
Another option is to simply not remove the player's view in your callback method. If that is not the culprit, then the next thing I would investigate is check if you are sending messages to nil objects. Also, see what happens when you take out all the implementation from movieFinishedCallback:, except for getting and stopping the player.
I hope that helps!
Fixed the issue by removing the [player.view removeFromSuperview] line

Pause game on orientation change, objective-c / Sprite Kit

I want my game to pause when device orientation is initiated. I have this method in my viewcontroller, which works fine:
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
NSLog(#"I am starting to rotate, should pause game..");
}
But how to listen for device rotation from within my SKScene, where the actual game is playing. Hope I have made myself clear enough. Thanks for any help!
You can use NSNotification. Keeping with your code, add the following line in your ViewController init:
[[NSNotificationCenter defaultCenter]
postNotificationName:#"RotateNotification"
object:self];
Then in your SKScene init add this line:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(receiveNotification:)
name:#"RotateNotification"
object:nil];
Also add the called method to SKScene:
-(void) receiveNotification:(NSNotification *)notification
{
NSLog (#"Received notification");
// do what you have to do here...
}

Cocoa: saveAction called 2 times with notification

I have a custom class for NSButton called MyButton where I post a notification for a quicksave
MyButton.m:
-(void)mouseDown:(id)sender{
[super mouseDown:sender];
[super mouseUp:sender];
[[NSNotificationCenter defaultCenter] postNotificationName:#"quickSave" object:nil userInfo:nil];
}
in AppDelegate I get the notification for the quick save:
AppDelegate.m:
- (IBAction)saveAction:(id)sender{
NSLog(#"Saving...");
NSError *error = nil;
if (![[self managedObjectContext] commitEditing]) {
NSLog(#"%#:%# unable to commit editing before saving", [self class], NSStringFromSelector(_cmd));
}
if (![[self managedObjectContext] save:&error]) {
[[NSApplication sharedApplication] presentError:error];
}
}
-(void)awakeFromNib{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(saveAction:) name:#"quickSave" object:nil];
}
Via the NSLog "Saving..." I see that the saveAction is called 2 times. Why?
P.S: the notification calls 2 times every function I insert in the selector: field, so maybe it has to do with the -(void)awakeFromNib{...} because I see that it's called twice (there are two different self inside the awakeFromNib).
UPDATE: I've "solved" the problem binding in Interface Builder the Application as an AppDelegate delegate and then adding the [[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(saveAction:) name:#"quickSave" object:nil]; inside the -(void)applicationDidFinishLaunching:(NSNotification *)aNotification{...}. I don't know if it's a real solution and obviously it is not an answer to my question (why awakeFromNib is called 2 times), but it might be useful to someone. Does anyone have a clue?
UPDATE2: the right managedobjectcontext is the one called in awakeFromNib the second time, the first one (identical in awakeFromNib and applicationDidFinishLaunching) is wrong.
My app is a statusbar app, the first awakeFromNib is called when i start the app and the second one when a preference window is opened.
The log message indicates that two different instances of AppDelegate receive a single notification. Probably you instantiated AppDelegate twice. Ensure you are not manually doing [[AppDelegate alloc] init] or something, and not putting more than one AppDelegate object in NIB.

How to resume a movie that's being played with presentMoviePlayerViewControllerAnimated

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.

Send NSNotification from classA to classB

So i have an app with an In App purchase. The In App purchase is managed in FirstViewController. When the user has purchased the product, i want to send out a Notification to my MainTableViewController to reload the tables data and show the new objects that were purchased in the In App purchase. So basically i want to send a notification from class A to class B and class B reloads the data of the tableview then. I have tried using NSNotificationCenter, but with no success, but i know that its possible with NSNotificationCenter i just don't know how.
In class A : post the notification
[[NSNotificationCenter defaultCenter] postNotificationName:#"DataUpdated"
object:self];
In class B : register first for the notification, and write a method to handle it.
You give the corresponding selector to the method.
// view did load
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(handleUpdatedData:)
name:#"DataUpdated"
object:nil];
-(void)handleUpdatedData:(NSNotification *)notification {
NSLog(#"recieved");
[self.tableView reloadData];
}
Ok I'm adding a little bit more information to vince's answer
In class A : post the notification
[[NSNotificationCenter defaultCenter] postNotificationName:#"DataUpdated"
object:arrayOfPurchasedObjects];
In class B : register first for the notification, and write a method to handle it.
You give the corresponding selector to the method. Make sure your class B is allocated before you post the notification otherwie notification will not work.
- (void) viewDidLoad {
// view did load
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(handleUpdatedData:)
name:#"DataUpdated"
object:nil];
}
-(void)handleUpdatedData:(NSNotification *)notification {
NSLog(#"recieved");
NSArray *purchased = [notification object];
[classBTableDataSourceArray addObjectsFromArray:purchased];
[self.tableView reloadData];
}
- (void) dealloc {
// view did load
[[NSNotificationCenter defaultCenter] removeObserver:self
name:#"DataUpdated"
object:nil];
[super dealloc];
}
Maybe you trying to send notification from another thread? NSNotification won't be delivered to the observer from another thread.