Cómo recibir NSNotifications desde UIWebView incrustado la reproducción de videos 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 = @"\ \ \ "; 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. Puede ejecutar la lista de reproducción y verá el primer video. Cuando el primer video ha terminado, el segundo video comienza a reproducirse automáticamente, y así sucesivamente.

Necesito soportar ios 4.1 y superior.

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

   function endMovie() {document.location.href="somefakeurlscheme://video-ended";}    - (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 } 

ACTUALIZACIÓN3 @Till, no puedo capturar UIMoviePlayerControllerDidExitFullscreenNotification, pero encontré MPAVControllerItemPlaybackDidEndNotification. MPAVControllerItemPlaybackDidEndNotification aparece cuando finaliza la reproducción de video.

Pero no entiendo cómo puedo ver las notificaciones de OnDone?

No hay notificaciones documentadas enviadas por el UIWebView películas incrustado UIWebView .

De hecho, la implementación cerrada utilizada dentro de UIWebView difiere del MPMoviePlayerController público en muchos aspectos (por ejemplo, DRM).

Las clases más importantes utilizadas para reproducir contenido de video dentro de ese UIWebView se llaman MPAVController y UIMoviePlayerController . El último hace que el jugador parezca la interfaz de pantalla completa de MPMoviePlayerController .

En caso de que se atreva a arriesgarse a un rechazo por parte de Apple, en realidad hay formas de lograr lo que está buscando.

NOTA Esto no está documentado y está sujeto a interrupción 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 también puede funcionar en otras versiones.

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


Estado de pantalla completa

Si, por ejemplo, tiene la intención de reactjsr cuando el usuario toca el botón HECHO , puede hacerlo de esta manera:

ACTUALIZAR La versión anterior de esta respuesta recomendaba usar UIMoviePlayerControllerDidExitFullscreenNotification mientras que esta nueva versión (actualizada para iOS6) recomienda usar UIMoviePlayerControllerWillExitFullscreenNotification .

Nivel de lenguaje C:

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

Nivel Objective-C:

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

Dibujé ambas opciones, C-Level y Objective-C-Level, porque la mejor manera de descubrir todo esto es usar las funciones C-Level (CoreFoundation) como se muestra al final de mi respuesta. Si el remitente de una notificación no usa Objective-C (NSNotifications), en realidad no podrá atraparlos utilizando NSNotification-mechanics.


Estado de reproducción

Para examinar el estado de la reproducción, busque "MPAVControllerPlaybackStateChangedNotification" (como se "MPAVControllerPlaybackStateChangedNotification" más arriba) y examine userInfo que puede verse así:

 { MPAVControllerNewStateParameter = 1; MPAVControllerOldStateParameter = 2; } 

Ingeniería inversa adicional

Para ingeniería inversa y explorar todas las notificaciones enviadas, use el siguiente fragmento.

 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+ puede usar las notificaciones 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 } 

Por lo que yo sé, los detalles de implementación de UIWebView (y todas las clases del sistema hechas por Apple) no son confiables cuando se hace una aplicación Cocoa Touch. Tal vez sea el caso de que un reproductor de video de UIWebView no sea una clase MPMoviePlayerController estándar y que tenga un sistema de delegación / notificación totalmente diferente, que el usuario no debe tener acceso.

Le sugiero que use el elemento HTML5 y detecte el evento “activado” de esta etiqueta:

       

De hecho, desde la función endMovie JavaScript, puede redireccionar a una URL falsa que puede capturar en su método -webView: shouldStartLoadWithRequest: (UIWebViewDelegate) para 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 desired } 

Espero que esto ayude.

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

Esto no usa ninguna API privada que lo haga más resistente al futuro.

Aquí está el código para incrustar su video de Youtube. Consulte la API para obtener más formas de personalizar esto.

    

Y así es como se te notifica que el video terminó (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 desired } 

en ViewDidLoad agregue 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 métodos son para mostrar el mensaje / funciones para el proceso respectivo de entrada / salida a / desde 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 / elimina otras ventanas cuando se recibe 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]; } 

Para iOS8 (también tengo un video incrustado que no es un video de youtube), la única solución que pude obtener para trabajar fue capturar uno de viewWill/DidLayoutSubviews , y como un bono adicional, no es necesario cambiar el HTML o usarlo 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 una UITableViewCell así que tuve que encontrar una forma de comunicarme entre la celda y el controlador de visualización, y también para evitar el uso de 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 fines de ingeniería inversa también puedes usar Cocoa API como

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

En este caso, recibirás todas las notificaciones