如何从UIWebViewembedded的YouTubevideo播放接收NSNotifications
我没有收到MPMoviePlayerController
任何通知。 我究竟做错了什么?
我使用以下逻辑。
我开始在UIWebView
播放YouTubevideo。 UIWebView
调用一个标准的MPMoviePlayerController
。 我不控制MPMoviePlayerController
因为我没有实例化MPMoviePlayerController
。
我用自动播放(1秒延迟)运行YouTube剪辑:
[self performSelector:@selector(touchInView:) withObject:b afterDelay:1];
我的代码是:
- (void)viewDidLoad { [super viewDidLoad]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(loadStateDidChange:) name:MPMoviePlayerLoadStateDidChangeNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playbackDidFinish:) name:MPMoviePlayerDidExitFullscreenNotification object:nil]; [self embedYouTube]; } - (void)loadStateDidChange:(NSNotification*)notification { NSLog(@"________loadStateDidChange"); } - (void)playbackDidFinish:(NSNotification*)notification { NSLog(@"________DidExitFullscreenNotification"); } - (void)embedYouTube { CGRect frame = CGRectMake(25, 89, 161, 121); NSString *urlString = [NSString stringWithString:@"http://www.youtube.com/watch?v=sh29Pm1Rrc0"]; NSString *embedHTML = @"<html><head>\ <body style=\"margin:0\">\ <embed id=\"yt\" src=\"%@\" type=\"application/x-shockwave-flash\" \ width=\"%0.0f\" height=\"%0.0f\"></embed>\ </body></html>"; NSString *html = [NSString stringWithFormat:embedHTML, urlString, frame.size.width, frame.size.height]; UIWebView *videoView = [[UIWebView alloc] initWithFrame:frame]; videoView.delegate = self; for (id subview in videoView.subviews) if ([[subview class] isSubclassOfClass: [UIScrollView class]]) ((UIScrollView *)subview).bounces = NO; [videoView loadHTMLString:html baseURL:nil]; [self.view addSubview:videoView]; [videoView release]; } - (void)webViewDidFinishLoad:(UIWebView *)_webView { UIButton *b = [self findButtonInView:_webView]; [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(touchInView:) object:b]; [self performSelector:@selector(touchInView:) withObject:b afterDelay:1]; } - (UIButton *)findButtonInView:(UIView *)view { UIButton *button = nil; if ([view isMemberOfClass:[UIButton class]]) { return (UIButton *)view; } if (view.subviews && [view.subviews count] > 0) { for (UIView *subview in view.subviews) { button = [self findButtonInView:subview]; if (button) return button; } } return button; } - (void)touchInView:(UIButton*)b { [b sendActionsForControlEvents:UIControlEventTouchUpInside]; }
更新:我正在创build播放YouTubevideo的应用程序。 你可以运行播放列表,你会看到第一个video。 当第一个video结束时,第二个video自动开始播放,依此类推。
我需要支持ios 4.1及以上版本。
UPDATE2: @ H2CO3我正在尝试使用您的url-scheme,但它不起作用。 委托方法没有调用退出事件。 我添加了我的htmlurllogin。 它是:
<html><head> <body style="margin:0"> <script>function endMovie() {document.location.href="somefakeurlscheme://video-ended";} </script> <embed id="yt" src="http://www.youtube.com/watch?v=sh29Pm1Rrc0" onended="endMovie()" type="application/x-shockwave-flash" width="161" height="121"></embed> </body></html> - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType { if ([[[request URL] absoluteString] hasPrefix:@"somefakeurlscheme://video-ended"]) { [self someMethodSupposedToDetectVideoEndedEvent]; return NO; // prevent really loading the URL } return YES; // else load the URL as desired }
UPDATE3 @Till,我不能抓住UIMoviePlayerControllerDidExitFullscreenNotification,但我发现MPAVControllerItemPlaybackDidEndNotification。 当播放video结束时,MPAVControllerItemPlaybackDidEndNotification出现。
但我不明白如何捕捉完整的通知?
UIWebView
embedded式电影播放器没有发送任何文档通知。
实际上, UIWebView
中使用的封闭实现在许多方面(如DRM)与公共MPMoviePlayerController
不同。
用于在该UIWebView
中播放video内容的最重要的类称为MPAVController
和UIMoviePlayerController
。 后者让玩家看起来像MPMoviePlayerController
全屏界面。
如果你敢冒苹果拒绝的风险,实际上还是有办法达到你要找的。
注意这没有logging,并且在每一个新的iOS版本中都有可能中断。 它在iOS4.3,5.0和5.01,5.1和6.0上都可以工作,而且它也可以在其他版本上工作。
我无法在iOS 4.1和4.2上testing这个解决scheme,所以这是由您来做的。 我高度怀疑它会起作用。
全屏状态
例如,如果您打算对用户点击“ 完成”button做出响应,则可以这样做:
更新 这个答案的旧版本build议使用 UIMoviePlayerControllerDidExitFullscreenNotification
而这个新版本(iOS6更新)build议使用 UIMoviePlayerControllerWillExitFullscreenNotification
。
C语言水平:
void PlayerWillExitFullscreen (CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo) { //do something... } CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(), NULL, PlayerWillExitFullscreen, CFSTR("UIMoviePlayerControllerWillExitFullscreenNotification"), NULL, CFNotificationSuspensionBehaviorDeliverImmediately);
Objective-C级别:
- (void)playerWillExitFullscreen:(NSNotification *)notification { //do something... } [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerWillExitFullscreen:) name:@"UIMoviePlayerControllerWillExitFullscreenNotification" object:nil];
我确实草拟了C级和Objective-C级的选项,因为最好的方法就是使用C-Level(CoreFoundation)函数。 如果通知的发件人不使用Objective-C(NSNotifications), 则实际上可能无法使用NSNotification-mechanics来捕获它们。
播放状态
为了检查播放状态,请查看"MPAVControllerPlaybackStateChangedNotification"
(如上面所述)并检查userInfo
,如下所示:
{ MPAVControllerNewStateParameter = 1; MPAVControllerOldStateParameter = 2; }
更多的逆向工程
对于反向工程和浏览所有发送的通知,请使用以下代码片段。
void MyCallBack (CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo) { NSLog(@"name: %@", name); NSLog(@"userinfo: %@", userInfo); } CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(), NULL, MyCallBack, NULL, NULL, CFNotificationSuspensionBehaviorDeliverImmediately);
在iOS UIMoviePlayerControllerDidEnterFullscreenNotification
,您可以使用UIMoviePlayerControllerDidEnterFullscreenNotification
和UIMoviePlayerControllerDidExitFullscreenNotification
通知:
-(void)viewDidLoad { ... [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(youTubeStarted:) name:@"UIMoviePlayerControllerDidEnterFullscreenNotification" object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(youTubeFinished:) name:@"UIMoviePlayerControllerDidExitFullscreenNotification" object:nil]; } -(void)youTubeStarted:(NSNotification *)notification{ // your code here } -(void)youTubeFinished:(NSNotification *)notification{ // your code here }
据我所知,在制作Cocoa Touch应用程序时,UIWebView(以及由Apple制作的所有系统类)的实现细节不会被依赖。 也许这是一个UIWebView的video播放器不是一个标准的MPMoviePlayerController类,它可能有一个完全不同的委托/通知系统,这是不应该是用户可以访问。
我build议你使用HTML5元素并检测这个标签的“onended”事件:
<html> <body> <script> function endMovie() { // detect the event here document.location.href="somefakeurlscheme://video-ended"; } </script> <video src="http://youtube.com/watch?v=aiugvdk755f" onended="endMovie()"></video> </body> </html>
实际上,从endMovie JavaScript函数中,您可以redirect到一个可以在-webView:shouldStartLoadWithRequest:(UIWebViewDelegate)方法中捕获的伪造URL,从而得到video已经结束的通知:
- (BOOL) webView:(UIWebView *)wv shouldStartLoadWithRequest:(NSURLRequest *)req { if ([[[req URL] absoluteString] hasPrefix:@"somefakeurlscheme://video-ended"]) { [self someMethodSupposedToDetectVideoEndedEvent]; return NO; // prevent really loading the URL } return YES; // else load the URL as desired }
希望这可以帮助。
基于@ H2CO3答案,但与iframe API 。 这是我能做到的唯一方法。
这不使用任何私人的API,这使得它更有前途。
这里是embedded你的Youtubevideo的代码。 检查API以获取更多的方式来定制这个。
<html> <body> <!-- 1. The <iframe> (and video player) will replace this <div> tag. --> <div id="player"></div> <script> // 2. This code loads the IFrame Player API code asynchronously. var tag = document.createElement('script'); tag.src = "https://www.youtube.com/iframe_api"; var firstScriptTag = document.getElementsByTagName('script')[0]; firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); // 3. This function creates an <iframe> (and YouTube player) // after the API code downloads. var player; function onYouTubeIframeAPIReady() { player = new YT.Player('player', { height: '480', width: '640', videoId: 'aiugvdk755f', events: { 'onStateChange': onPlayerStateChange } }); } // 5. The API calls this function when the player's state changes. function onPlayerStateChange(event) { if (event.data == YT.PlayerState.ENDED) { endedMovie(); } } function endedMovie() { // detect the event here document.location.href="somefakeurlscheme://video-ended"; } </script> </body> </html>
这就是你如何得到video结束的通知(UIWebViewDelegate方法)。
- (BOOL) webView:(UIWebView *)wv shouldStartLoadWithRequest:(NSURLRequest *)req { if ([[[req URL] absoluteString] hasPrefix:@"somefakeurlscheme://video-ended"]) { [self someMethodSupposedToDetectVideoEndedEvent]; return NO; // prevent really loading the URL } return YES; // else load the URL as desired }
在ViewDidLoad中添加下面的代码
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(VideoExitFullScreen:) name:@"UIMoviePlayerControllerDidExitFullscreenNotification" object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(VideoEnterFullScreen:) name:@"UIMoviePlayerControllerDidEnterFullscreenNotification" object:nil];
以下方法用于显示进入/退出全屏幕的各个过程的消息/function
- (void)VideoExitFullScreen:(id)sender{ // Your respective content/function for Exit from full screen } - (void)VideoEnterFullScreen:(id)sender{ // Your respective content/function for Enter to full screen }
这适用于我在iOS 6.1中,当收到AVPlayerItemDidPlayToEndTimeNotification时,它隐藏/删除其他窗口:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerItemEnded:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil]; ... - (void)playerItemEnded:(NSNotification *)notification { for (UIWindow *window in [[UIApplication sharedApplication] windows]) { if (window != self.window) { window.hidden = YES; } } }
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(youTubeStarted:) name:UIWindowDidBecomeVisibleNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(youTubeFinished:) name:UIWindowDidBecomeHiddenNotification object:nil]; -(void)youTubeStarted:(NSNotification *)notification { // Entered Fullscreen code goes here.. AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate]; appDelegate.fullScreenVideoIsPlaying = YES; NSLog(@"%f %f",webViewForWebSite.frame.origin.x,webViewForWebSite.frame.origin.y); } -(void)youTubeFinished:(NSNotification *)notification{ // Left fullscreen code goes here... AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate]; appDelegate.fullScreenVideoIsPlaying = NO; //CODE BELOW FORCES APP BACK TO PORTRAIT ORIENTATION ONCE YOU LEAVE VIDEO. [[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationPortrait animated:NO]; //present/dismiss viewcontroller in order to activate rotating. UIViewController *mVC = [[UIViewController alloc] init]; [self presentViewController:mVC animated:NO completion:Nil]; // [self presentModalViewController:mVC animated:NO]; [self dismissViewControllerAnimated:NO completion:Nil]; // [self dismissModalViewControllerAnimated:NO]; }
对于iOS8 (我也有一个embedded的video不是YouTubevideo)唯一的解决scheme,我可以得到的工作是捕获任何一个viewWill/DidLayoutSubviews
,作为一个额外的奖励,你不需要改变HTML或使用任何私有API:
所以基本上:
@property (nonatomic) BOOL showingVideoFromWebView; ... ... - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType { if (navigationType == UIWebViewNavigationTypeOther) { //Was "other" in my case... Might be UIWebViewNavigationTypeLinkClicked self.showingVideoFromWebView = YES; } } - (void)viewWillLayoutSubviews { [super viewWillLayoutSubviews]; // Do whatever... // Note: This will get called both when video is entering fullscreen AND exiting! self.showingVideoFromWebView = NO; }
在我的情况下,我的web视图是在一个UITableViewCell
里面,所以我必须find一种方法来在单元和视图控制器之间进行通信,并且也避免使用一个BOOL标志,我这样做了:
- (BOOL)webView:(UIWebView *)webView shouldStartLoad..... ... if (opening video check....) { [[NSNotificationCenter defaultCenter] addObserverForName:@"webViewEmbedVidChangedState" object:nil queue:nil usingBlock:^(NSNotification *note) { // Do whatever need to be done when the video is either // entering fullscreen or exiting fullscreen.... [[NSNotificationCenter defaultCenter] removeObserver:self name:@"webViewEmbedVidChangedState" object:nil]; }]; } - (void)viewWillLayoutSubviews..... [[NSNotificationCenter defaultCenter] postNotificationName:@"webViewEmbedVidChangedState" object:nil];
实际上,为了逆向工程的目的,你也可以使用Cocoa API
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleNotification:) name:nil object:nil];
在这种情况下,您将收到所有通知