Cómo recibir NSNotifications de UIWebView reproducción de videos incorporados de YouTube

No recibí ninguna notificación para MPMoviePlayerController . ¿Qué estoy haciendo mal?

Yo uso la siguiente lógica.

Estoy empezando a reproducir videos de YouTube en UIWebView . UIWebView llama a un MPMoviePlayerController estándar. No controlo MPMoviePlayerController porque no MPMoviePlayerController .

Corro el clip de YouTube con reproducción automática (1 segundo de retraso):

 [self performSelector:@selector(touchInView:) withObject:b afterDelay:1]; 

Mi código es:

 - (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]; } 

ACTUALIZACIÓN: Estoy creando una aplicación que reproduce el video de YouTube. Puedes ejecutar la list de reproducción y verás el primer video. Cuando el primer video ha finalizado, el segundo video comienza a reproducirse automáticamente y así sucesivamente.

Necesito apoyar ios 4.1 y superiores.

ACTUALIZACIÓN2: @ H2CO3 Estoy intentando usar tu url-scheme, pero no funciona. El método de delegado no llamó al evento de salida. Agregué mi url html para iniciar session. Es:

 <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 desinetworking } 

UPDATE3 @ Hasta ahora, no capturé UIMoviePlayerControllerDidExitFullscreenNotification, pero encontré MPAVControllerItemPlaybackDidEndNotification. MPAVControllerItemPlaybackDidEndNotification aparece cuando finaliza el video de reproducción.

¿Pero no entiendo cómo capturar notifications onDone?

No hay notifications documentadas enviadas por el UIWebView películas embedded UIWebView .

De hecho, la implementación cerrada utilizada en el UIWebView difiere del MPMoviePlayerController público en muchos aspectos (p. Ej., DRM).

Las classs más importantes usadas para reproducir contenido de video dentro de ese UIWebView se denominan MPAVController y UIMoviePlayerController . El último hace que el jugador aparezca como la interfaz de pantalla completa de MPMoviePlayerController .

En caso de que te atrevas a arriesgar un rechazo de Apple, en realidad hay forms de lograr lo que estás buscando.

NOTA Esto no está documentado y está sujeto a interrupciones en todas y cada una de las nuevas versiones de iOS. Sin embargo, funciona en iOS4.3, 5.0 y 5.01, 5.1 y 6.0 y puede funcionar también en otras versiones.

No puedo probar esta solución en iOS 4.1 y 4.2, por lo que depende de ti. Sospecho que funcionará.


Estado de pantalla completa

Si, por ejemplo, tiene la intención de reactjsr cuando el usuario presiona el button DONE , puede hacerlo de esta manera:

ACTUALIZACIÓN La versión anterior de esta respuesta recomendaba utilizar UIMoviePlayerControllerDidExitFullscreenNotification mientras que esta nueva versión (actualizada para iOS6) recomienda utilizar UIMoviePlayerControllerWillExitFullscreenNotification .

C-Nivel de idioma:

 void PlayerWillExitFullscreen (CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo) { //do something... } CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(), NULL, PlayerWillExitFullscreen, CFSTR("UIMoviePlayerControllerWillExitFullscreenNotification"), NULL, CFNotificationSuspensionBehaviorDeliverImmediately); 

Objetivo-C Nivel:

 - (void)playerWillExitFullscreen:(NSNotification *)notification { //do something... } [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerWillExitFullscreen:) name:@"UIMoviePlayerControllerWillExitFullscreenNotification" object:nil]; 

Hice borradores de las opciones de C, Level y Objective-C, porque la mejor manera de descubrir todo esto es usar las funciones de C-Level (CoreFoundation) como se muestra al final de mi respuesta. Si el remitente de una notificación no utiliza Objective-C (NSNotifications), es posible que no pueda atraparlos utilizando NSNotification-mechanics.


Estado de reproducción

Para examinar el estado de reproducción, busque "MPAVControllerPlaybackStateChangedNotification" (como se menciona anteriormente) y examine el userInfo que puede tener este aspecto:

 { MPAVControllerNewStateParameter = 1; MPAVControllerOldStateParameter = 2; } 

Ingeniería inversa adicional

Para la ingeniería inversa y explorar todas las notifications enviadas, use el siguiente fragment.

 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); 

En iOS 4.3+ puedes usar las notifications UIMoviePlayerControllerDidEnterFullscreenNotification y 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 } 

Hasta donde yo sé, los detalles de implementación de UIWebView (y de todas las classs de sistema fabricados por Apple) no deben confiarse en la creación de una aplicación Cocoa Touch. Tal vez sea el caso de que el reproductor de video de un UIWebView no sea una class estándar de MPMoviePlayerController y podría tener un sistema de notificación / delegación totalmente diferente, que el usuario no debe tener acceso.

Te sugiero que uses el elemento HTML5 y detectes el evento "onended" de esta label:

 <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> 

De hecho, desde la function de JavaScript de EndMovie, puede networkingirigir a una URL falsa que puede capturar en su método -webView: shouldStartLoadWithRequest: (UIWebViewDelegate) así recibirá una notificación de que el video ha finalizado:

 - (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 desinetworking } 

Espero que esto ayude.

Basado en la respuesta @ H2CO3 pero con la API iframe . Era la única forma en que podía hacer que funcionara.

Esto no utiliza ninguna API privada que lo haga más comprobable en el futuro.

Aquí está el código para insert tu video de YouTube. Consulte la API para get más forms de personalizar esto.

 <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> 

Y así es como se le notifica que el video finalizó (método 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 desinetworking } 

en ViewDidLoad agrega el siguiente código

 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(VideoExitFullScreen:) name:@"UIMoviePlayerControllerDidExitFullscreenNotification" object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(VideoEnterFullScreen:) name:@"UIMoviePlayerControllerDidEnterFullscreenNotification" object:nil]; 

Los siguientes methods son para mostrar el post / funciones para el process respectivo de ingresar / salir a / de la pantalla completa

 - (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 } 

Esto funciona para mí en iOS 6.1, oculta / quita otras windows cuando se recibe AVPlayerItemDidPlayToEndTimeNotification:

 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerItemEnded:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil]; ... - (void)playerItemEnded:(NSNotification *)notification { for (UIWindow *window in [[UIApplication shanetworkingApplication] 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 { // Entenetworking Fullscreen code goes here.. AppDelegate *appDelegate = (AppDelegate *)[[UIApplication shanetworkingApplication] 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 shanetworkingApplication] delegate]; appDelegate.fullScreenVideoIsPlaying = NO; //CODE BELOW FORCES APP BACK TO PORTRAIT ORIENTATION ONCE YOU LEAVE VIDEO. [[UIApplication shanetworkingApplication] 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]; } 

Para iOS8 (También tengo un video embedded que no es un video de YouTube), la única solución que pude conseguir fue capturar cualquiera de las viewWill/DidLayoutSubviews , y como una bonificación adicional no es necesario cambiar el HTML o usar cualquier API privada:

Así que básicamente:

 @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; } 

En mi caso, mi vista web está dentro de un UITableViewCell así que tuve que encontrar una forma de comunicarse entre la celda y el controller de vista, y también para evitar usar una bandera BOOL. Hice esto:

 - (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]; 

En realidad, para los propósitos de ingeniería inversa, también puede usar Cocoa API como

  [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleNotification:) name:nil object:nil]; 

En este caso, recibirá todas las notifications.