I have notification on movie player:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePlayBackDidFinish:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:nil];
And it's handler:
- (void) moviePlayBackDidFinish:(NSNotification*)notification
{
[[UIApplication sharedApplication] setStatusBarHidden:YES];
// Remove observer
[[NSNotificationCenter defaultCenter]
removeObserver:self
name:MPMoviePlayerPlaybackDidFinishNotification
object:nil];
[self dismissModalViewControllerAnimated:YES];
}
Here in this handler method I want to check if the done button is sender. Because I have two senders to this method. How ti check this?
Per docs: MPMoviePlayerPlaybackDidFinishNotification userInfo dictionary must contain NSNUmber for MPMoviePlayerPlaybackDidFinishReasonUserInfoKey key indicating the reason playback has finished. Its possible values:
enum {
MPMovieFinishReasonPlaybackEnded,
MPMovieFinishReasonPlaybackError,
MPMovieFinishReasonUserExited
};
You will first need to assign tag to your buttons before the action and then check the value of the sender tag.
Just add these lines of code:
- (void) moviePlayBackDidFinish:(NSNotification*)notification {
NSInteger anyInteger = [sender tag];
//Now check the value of the anyInteger and write the code accordingly.
//switch case or if condition whatever you want.
}
That's it.
This is an old thread but I stumbled upon it while looking for a solution, and the accepted solution doesn't show the final code.
Here is what you have to do:
- (void) moviePlayBackDidFinish:(NSNotification*)notification
{
NSLog(#"moviePlayBackDidFinish");
// Remove observer
[[NSNotificationCenter defaultCenter] removeObserver:self
name:MPMoviePlayerPlaybackDidFinishNotification
object:nil];
NSInteger movieFinishReason= [[[notification userInfo]objectForKey:
MPMoviePlayerPlaybackDidFinishReasonUserInfoKey] intValue];
if(movieFinishReason == 2 || movieFinishReason == 1 || movieFinishReason == 0){
[self dismissViewControllerAnimated:YES completion:nil];
}
/*
MPMovieFinishReasonPlaybackEnded = 0,//played movie sucessfuly.
MPMovieFinishReasonPlaybackError = 1, //error in playing movie
MPMovieFinishReasonUserExited = 2; //user quitting the application / user pressed done button
*/
}
Add tag with the button and put condition according to the tag.
Or check by
if([sender isEqual:btn1])
{
}
else
{
}
Related
I write custom player from AVPlayer for video playback. According to Apple docs set the video layer:
self.player = [IPLPlayer new];
self.player.playerLayer = (AVPlayerLayer *)self.playerView.layer;
Where self.playerView is usual class from those docs:
#implementation PlayerView
+ (Class) layerClass {
return [AVPlayerLayer class];
}
- (AVPlayer *)player {
return [(AVPlayerLayer *)[self layer] player];
}
- (void)setPlayer:(AVPlayer *) player {
[(AVPlayerLayer *) [self layer] setPlayer:player];
}
The problem is:
When close app (Home button), or block screen, the video playback is stopped, and when resume ONLY audio playback resumed, the image on screen is still those was before block screen - it's fully static and note change frames.
How to resume VIDEO playing after screen is blocked?
Seems I must to register notifications, and after app become active resume video layer:
-(void)registerNotification
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(willEnterBackground)
name:UIApplicationWillResignActiveNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(didEnterForeground)
name:UIApplicationDidBecomeActiveNotification object:nil];
}
-(void)unregisterNotification
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
-(void)willEnterBackground
{
NSLog(#"willEnterBackground");
[self.playerView willEnterBackground];
}
-(void)didEnterForeground
{
NSLog(#"didEnterForeground");
[self.playerView didEnterForeground];
}
And one solution that binds all this information together.
Maybe player status should be handled differently, but I like the recursive way.
Note: If you do not need the exact seek time, you can use [_player seekToTime:<#(CMTime)#> completionHandler:<#^(BOOL finished)completionHandler#>] It's faster but it seeks to the nearest key frame.
- (void)viewDidLoad
{
[super viewDidLoad];
....
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(appEnteredForeground) name:UIApplicationWillEnterForegroundNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(appEnteredBackground) name:UIApplicationDidEnterBackgroundNotification object:nil];
}
-(void)viewWillDisappear:(BOOL)animated
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillEnterForegroundNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil];
}
....
-(void) appEnteredForeground {
AVPlayerLayer *player = (AVPlayerLayer *)[playerView layer];
[player setPlayer:NULL];
[player setPlayer:_player];
[self playAt:currentTime];
}
-(void) appEnteredBackground {
[_player pause];
currentTime = [_player currentTime];
}
-(void)playAt: (CMTime)time {
if(_player.status == AVPlayerStatusReadyToPlay && _player.currentItem.status == AVPlayerItemStatusReadyToPlay) {
[_player seekToTime:time toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero completionHandler:^(BOOL finished) {
[_player play];
}];
} else {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self playAt:time];
});
}
}
This works for me on Swift 3
Add somewhere while setting up the view:
NotificationCenter.default.addObserver(self,
selector: #selector(appWillEnterForegroundNotification),
name: .UIApplicationWillEnterForeground, object: nil)
Grab your player and force it to play:
func appWillEnterForegroundNotification() {
myPlayer.play()
}
Don't forget to remove the observers when you don't need them:
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
NotificationCenter.default.removeObserver(self)
}
Use the UIApplicationWillEnterForegroundNotification as well. That way you know your app will be active and visible to the user:
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(appEnteredForeground:) name:UIApplicationWillEnterForegroundNotification object:nil];
The trick is to detach all video layers from their players when the app did enter background and reattaching them when the app did become active again.
So in your -applicationDidEnterBackground: you got to trigger a mechanism that results in
avPlayerLayer.player = nil;
and in your -applicationDidBecomeActive: you reattach the player like
avPlayerLayer.player = self.avPlayer;
Also have a look at this Tech Note (QA1668) from Apple.
After some research I've found, that the same bag is in iOS player (WebBrowser, MPMoviePlayerController). May be because distribution type of content is Progressive Download.
So solution is:
Use below notifications and save current time.
After app is resumed, recreate AVPlayer and start playing from saved
time.
In all other cases image is blocked, or view become black.
I'm currently trying to get NSNotification to work but I'm having some trouble.
I have two (2) ViewControllers: A. MainViewController & B. LoginViewController.
In my MainViewController I have a logout button that will send a url to my LoginViewController to load it (without showing my loginView). However, it's not working.
In my MainViewController this is what I have:
- (IBAction)logout:(id)sender {
NSURL *logoutURL = [NSURL URLWithString:#"https://myurl.com/logout"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"logoutInitiated" object:logoutURL];
}
This is what I have in my LoginViewController:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
WebView.delegate = self;
WebView.scalesPageToFit = YES;
WebView.multipleTouchEnabled = YES;
loadCount = 0;
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(submitLogout) name:#"logoutInitiated" object:nil];
}
- (IBAction)submitLogout:(NSNotification*)notification {
[WebView stopLoading];
NSURL * signOutUrl = (NSURL*)[notification object];
[self loadURL:nil withURL:signOutUrl];
}
My problem is that when I press the logoutButton nothing happens. (Using NSLogs, I see that it never triggers the next step) Thank you!!!!
This is because your method name you are passing in selector is wrong. You need to add colon : at submitLogout: suffix
Use
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(submitLogout:) name:#"logoutInitiated" object:nil];
in place of
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(submitLogout) name:#"logoutInitiated" object:nil];
Hope it helps you.
When you add self as an observer, you use the selector "submitLogout" without a semicolon! But your method has an argument, so the correct selector would be #selector(submitLogout:).
Note the SEMICOLON
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(**submitLogout:**) name:#"logoutInitiated" object:nil];
- (IBAction)logout:(id)sender
{
NSURL *logoutURL = [NSURL URLWithString:#"https://myurl.com/logout"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"logoutInitiated" object:nil userInfo:[NSDictionary dictionaryWithObjectsAndKeys:logoutURL,#"RECEIVED_URL", nil]];
}
- (IBAction)submitLogout:(NSNotification*)notification
{
[WebView stopLoading];
NSURL * signOutUrl = (NSURL*)[notification objectForKey:#"RECEIVED_URL"];
[self loadURL:nil withURL:signOutUrl];
}
i need to add button done on keypad.
Apple does n't provide such felicity but some of application i found that done ,next,previous buttons.
like this.
how can i add these and how can i give click event to them.
can any one please help me.
1.Define the done button (= return key):
textField.returnKeyType = UIReturnKeyDone;
2.Add the action-listener:
[textField addTarget:self action:#selector(textFieldDoneEditing:) forControlEvents:UIControlEventEditingDidEndOnExit];
3.Define the action-event:
- (IBAction)textFieldDoneEditing:(id)sender {
[sender resignFirstResponder];
}
Have fun!
EDIT:
Here you can find detailed instructions how to add a Toolbar with Next & Previous above UITextField Keyboard:
http://www.randomsequence.com/articles/adding-a-toolbar-with-next-previous-above-uitextfield-keyboard-iphone/
EDIT2:
Now, I have a really great example for you: "This view extends UITextView adding on top of the keyboard associated with this UITextView a toolbar with a « Done » Button"
I check the code and it is a lot of easier than the first example:
http://blog.demay-fr.net/2009/07/cocoa-how-to-add-a-toolbar-with-button-on-top-of-a-uitextview-in-order-to-add-a-dismiss-button/
EDIT3:
Hmmm, no, I doesn't test to code. But I will test it now!
1.Problem: the right initialization. If I add the UITextView in IB, initWithCoder gets called:
- (id)init {
NSLog(#"init");
if (self = [super init]) {
//register a specific method on keyboard appearence
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
}
return self;
}
- (id)initWithCoder:(NSCoder *)decoder {
NSLog(#"initWithCoder");
if (self = [super initWithCoder:decoder]) {
//register a specific method on keyboard appearence
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
}
return self;
}
- (id)initWithFrame:(CGRect)frame {
NSLog(#"initWithFrame");
if (self = [super initWithFrame:frame]) {
//register a specific method on keyboard appearence
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
}
return self;
}
2.Problem: There's no view with the the Prefix "UIKeyboard":
for (UIWindow *keyboardWindow in [[UIApplication sharedApplication] windows]) {
NSLog(#"keyboardWindow = %#", keyboardWindow);
for (UIView *keyboard in [keyboardWindow subviews]) {
NSLog(#"keyboard = %#", keyboard);
if([[keyboard description] hasPrefix:#"<UIKeyboard"] == YES) {
// THERE'S NO VIEW 'UIKeyboard'!!!
}
}
}
The code doesn't work, I'm sorry... I don't know why there's no view "UIKeyboard"... Maybe the first example will help you at this point and you can build your own solution.
I'm having lots of trouble displaying a fullscreen movie in my iPad app and then allowing the user to dismiss it with either the Done button or the "un-fullscreen" button on the player controls.
Initially I was using MPMoviePlayerViewController for the movie presentation, but I wasn't receiving the enter/exit fullscreen notifications from its MPMoviePlayerController object, so I switched to doing it myself.
I can make the movie appear fullscreen (although the transition is janky), but when either the "Done" or "un-fullscreen" buttons are pressed, no action is taken by the player. I've posted my code below:
- (void)startPlayingMovieWithURLString:(NSString *)movieURLString {
// I get all of these callbacks **EXCEPT** the "willExitFullScreen:" callback.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(willEnterFullScreen:) name:MPMoviePlayerWillEnterFullscreenNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(willExitFullScreen:) name:MPMoviePlayerWillExitFullscreenNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didFinishPlayback:) name:MPMoviePlayerPlaybackDidFinishNotification object:nil];
[self.moviePlayerController setContentURL:someExistingURL];
// "self" is a UIViewController subclass, and is presented as a "fullscreen" modal view controller from its parent
// I'm setting the movie player's view's frame to take up the full rectangle of my view controller, but really I want the movie to be completely removed when the user presses "done" (that is, removed from the view hierarchy). Not sure when/where to do this.
self.moviePlayerController.view.frame = self.view.frame;
[self.view addSubview:self.moviePlayerController.view];
[self.moviePlayerController setFullscreen:YES animated:YES];
}
And here is the code for my didFinish callback
- (void)didFinishPlayback:(NSNotification *)notification {
// This ends up recursively telling the player that playback ended, thus calling this method, thus…well you get the picture.
// What I'm trying to do here is just make the player go away and show my old UI again.
[self.moviePlayerController setFullscreen:NO animated:YES];
}
So obviously I am doing something wrong but I've been up and down the documentation and I can't figure out how to make the movie just go away. I figured it would be more intuitive than this. What am I doing wrong?
Here are how the events -> notifications work:
User presses 'Done' button
MPMoviePlayerWillExitFullscreenNotification
MPMoviePlayerDidExitFullscreenNotification
User presses 'Leave fullscreen' button on transport
MPMoviePlayerWillExitFullscreenNotification
MPMoviePlayerDidExitFullscreenNotification
Note that playback does not stop
Movie reaches end
MPMoviePlayerPlaybackDidFinishNotification with the MPMoviePlayerPlaybackDidFinishReasonUserInfoKey set to MPMovieFinishReasonPlaybackEnded
If you call setFullscreen:NO animated:YES on your MoviePlayerController instance from this notification, you'll then get the WillExit and DidExit notifications.
Note that you don't get the PlaybackDidFinish notification when the user presses the Done or Leave Fullscreen buttons.
So, typically, if you want to get rid of the MoviePlayer's view, you need to put [self.moviePlayerController.view removeFromSuperview] in the DidExitFullscreen notification handler. WillExitFullscreen is too soon.
Here's my code:
- (void)willEnterFullscreen:(NSNotification*)notification {
NSLog(#"willEnterFullscreen");
}
- (void)enteredFullscreen:(NSNotification*)notification {
NSLog(#"enteredFullscreen");
}
- (void)willExitFullscreen:(NSNotification*)notification {
NSLog(#"willExitFullscreen");
}
- (void)exitedFullscreen:(NSNotification*)notification {
NSLog(#"exitedFullscreen");
[self.movieController.view removeFromSuperview];
self.movieController = nil;
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)playbackFinished:(NSNotification*)notification {
NSNumber* reason = [[notification userInfo] objectForKey:MPMoviePlayerPlaybackDidFinishReasonUserInfoKey];
switch ([reason intValue]) {
case MPMovieFinishReasonPlaybackEnded:
NSLog(#"playbackFinished. Reason: Playback Ended");
break;
case MPMovieFinishReasonPlaybackError:
NSLog(#"playbackFinished. Reason: Playback Error");
break;
case MPMovieFinishReasonUserExited:
NSLog(#"playbackFinished. Reason: User Exited");
break;
default:
break;
}
[self.movieController setFullscreen:NO animated:YES];
}
- (void)showMovie {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(willEnterFullscreen:) name:MPMoviePlayerWillEnterFullscreenNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(willExitFullscreen:) name:MPMoviePlayerWillExitFullscreenNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(enteredFullscreen:) name:MPMoviePlayerDidEnterFullscreenNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(exitedFullscreen:) name:MPMoviePlayerDidExitFullscreenNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(playbackFinished:) name:MPMoviePlayerPlaybackDidFinishNotification object:nil];
NSURL* movieURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"tron" ofType:#"mov"]];
self.movieController = [[MPMoviePlayerController alloc] initWithContentURL:movieURL];
self.movieController.view.frame = self.view.frame;
[self.view addSubview:movieController.view];
[self.movieController setFullscreen:YES animated:YES];
[self.movieController play];
}
Yes. That's great. There are really notifications mentioned above...
However, there are no MPMoviePlayerPlaybackWillFinishNotification somewhy!!!
That's really a problem.
When you call the movie player as modal (no matter which of the following methods used presentViewController/presentModalViewController/presentVideoController), if you defined .fullScreen = YES, it's not expected to call MPMoviePlayerWillExitFullscreenNotification notification at all (obviously, because it's not cosidering we enter/exit from full screen, but only present/dismiss the controller).
But there are really no any notifications that the video is about to finish and close. That's needed (besides any other situations possible) to catch the moment when the transition of dismissing is started. (The transition, of course, starts before the MPMoviePlayerPlaybackDidFinishNotification called). And, at the same time, application:supportedInterfaceOrientationsForWindow: for previously shown controller is called before the notification, and there is no way to say the AppDelegate that our current controller must be shown already in another orientation.
So, since my video is fullscreen and also without any controls shown (this is kind of an intro, so I just until it finishes) my solution was just to have a timer which checks every short tick (0.1 seconds) what is the video current position... and it it's close to the end, then this is the moment for my own notification.
I want to check for whether the request has timed out after some period.
I know NSNotifiactionCenter ,but don't know how to set the request time out notification for it.
thanks.
You could just use a timer and cancel your notification request if not received by then?
e.g. taking Apple's reachability example:
- (void) startNotifier
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(onReachabilityChanged:) name:#"kNetworkReachabilityChangedNotification" object:nil];
notified = NO;
[self performSelector:#selector(onRequestTimeout) withObject:nil afterDelay:5.0]; // 5 secs
}
- (void)onReachabilityChanged:(NSNotification *)note
{
// Do whatever on notification
notified = YES;
}
- (void) onRequestTimeout
{
if (!notified)
{
// Do whatever on request timeout
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"kNetworkReachabilityChangedNotification" object:nil];
}
}