ios objective-c uiwebview uinavigationcontroller screen-rotation

ios - Permitir video en el paisaje con la aplicación de solo retratos



objective-c uiwebview (8)

Tengo un UIWebView incluido en un UIViewController que es un descendiente de UINavigationController. Se parece a esto:

La aplicación es solo retrato. Cuando reproduzco el video, quiero que el usuario pueda rotar el dispositivo y ver el video en modo horizontal. Yo uso este código para permitirlo:

- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window { id presentedViewController = [self topMostController]; NSString *className = presentedViewController ? NSStringFromClass([presentedViewController class]) : nil; if ([className isEqualToString:@"MPInlineVideoFullscreenViewController"] || [className isEqualToString:@"MPMoviePlayerViewController"] || [className isEqualToString:@"AVFullScreenViewController"]) { return UIInterfaceOrientationMaskAllButUpsideDown; } return UIInterfaceOrientationMaskPortrait; } - (UIViewController *)topMostController { UIViewController *topController = [UIApplication sharedApplication].keyWindow.rootViewController; while (topController.presentedViewController) { topController = topController.presentedViewController; } return topController; }

Y luego en mi UINavigationController (para que cuando el video termine la vista no se muestre en el paisaje, sino solo en vertical):

- (BOOL)shouldAutorotate { return NO; } - (NSUInteger)supportedInterfaceOrientations { return UIInterfaceOrientationMaskPortrait; } - (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation { return UIInterfaceOrientationPortrait; }

Todo funciona a la perfección

Pero luego el video se termina de reproducir (o el usuario toca ''Listo'') y las pantallas vuelven a la vista subyacente, esto es lo que sucede:

Como puede ver, la barra de navegación se desliza debajo de la barra de estado. Además, recibo muchos errores de diseño automático en los registros: http://pastebin.com/09xHzmgJ

Alguna idea sobre como resolver esto?


En iOS 11 la solución aceptada no funcionó para mí. Parece que la barra de navegación deja de reflejar los cambios en el cuadro. Pero hay una solución. Al principio, tenemos que modificar el método supportedInterfaceOrientationsForWindow para devolver UIInterfaceOrientationMaskLandscape para los controladores de video en lugar de UIInterfaceOrientationMaskAllButUpsideDown . En mi caso, cuando hago clic en el video de YouTube incorporado, el sistema siempre abre AVFullScreenViewController, por lo que eliminé otras comprobaciones del ejemplo original. Código:

- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window { __kindof UIViewController *presentedViewController = [self topMostController]; // Allow rotate videos NSString *className = presentedViewController ? NSStringFromClass([presentedViewController class]) : nil; if ([className isEqualToString:@"AVFullScreenViewController"]) { return UIInterfaceOrientationMaskLandscape; } return UIInterfaceOrientationMaskPortrait; }

Esto no modificó el comportamiento de AVFullScreenViewController en iOS 10 y menos, pero corrige la barra de navegación en iOS 11, por lo que no es necesario actualizar el marco (también hay un efecto secundario en iOS 11 que el video gira desde el paisaje cuando comienza a reproducirse, pero es una compensación). A continuación, debemos agregar control en el método UIWindowDidBecomeHiddenNotification :

- (void)videoDidExitFullscreen { if (@available(iOS 11, *)) { // Fixes status bar on iPhone X [self setNeedsStatusBarAppearanceUpdate]; } else { self.navigationController.navigationBar.frame = CGRectMake(0, 0, self.view.bounds.size.width, statusAndNavBarHeight); } }

Sin setNeedsStatusBarAppearanceUpdate texto de setNeedsStatusBarAppearanceUpdate en la barra de estado no aparecerá en el iPhone X, para otros dispositivos no es necesario.


Encontré exactamente el mismo problema y usé el mismo código que @entropid. Sin embargo, la solución aceptada no funcionó para mí.

Me tomó horas llegar a la siguiente solución de una línea que hizo que las cosas funcionen para mí:

- (void)viewWillLayoutSubviews { [[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationNone]; }


Es muy simple, como dice @Stanislav Pankevich, pero en

versión rápida 3

override func viewWillLayoutSubviews() { super.viewWillLayoutSubviews(); UIApplication.shared.isStatusBarHidden = false }


Estaba teniendo el mismo problema y, al usar la solución de @entropid, pude solucionar el problema de la barra de navegación. Pero mi vista permanece debajo de la barra de navegación que corrijo usando "sizeToFit".

[[NSNotificationCenter defaultCenter] addObserverForName:@"UIWindowDidRotateNotification" object:nil queue:nil usingBlock:^(NSNotification *note) { if ([note.userInfo[@"UIWindowOldOrientationUserInfoKey"] intValue] >= 3) { [self.navigationController.navigationBar sizeToFit]; self.navigationController.navigationBar.frame = (CGRect){0, 0, self.view.frame.size.width, 64}; } }];

No lo he probado correctamente pero funciona para mí en este momento


Me enfrenté a este problema ayer, donde la respuesta de @entropid funcionó para iOS 9 y abajo, pero para iOS 10 no fue así (ya que iOS 10 realmente ocultaba la barra de estado, donde en iOS 9 y debajo solo UINavigationBar cambiaba su marco sin ocultar la barra de estado y, por lo tanto, se superpuso a esa barra).

Además, la suscripción a la notificación MPMoviePlayerControllerDidExitFullScreen tampoco funcionaba, a veces simplemente no se llamaba (en mi caso particular, era porque era un video de UIWebView , que usaba una clase diferente de jugador que se parece a MPMoviePlayerController ).

Así que lo resolví utilizando una solución como la sugerida por @StanislavPankevich, pero me suscribí a notificaciones cuando una UIWindow ha ocultado (que puede ser en varios casos, como cuando un video ha finalizado, pero también cuando un UIActivityViewController descarta y otros casos) en lugar de viewWillLayoutSubviews . Para mi caso particular (una subclase de UINavigationController ), métodos como viewDidAppear , viewWillAppear , etc. simplemente no se llamaban.

viewDidLoad

- (void)viewDidLoad { [super viewDidLoad]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(videoDidExitFullscreen:) name:UIWindowDidBecomeHiddenNotification object:nil]; // Some other logic... }

dealloc

- (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; }

Y finalmente, videoDidExitFullscreen

- (void)videoDidExitFullscreen:(NSNotification *)notification { // You would want to check here if the window dismissed was actually a video one or not. [[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationFade]; }

UIStatusBarAnimationFade porque parece mucho más suave que UIStatusBarAnimationNone , al menos desde la perspectiva del usuario.


Mi respuesta a esta pregunta funciona muy bien. Here reproducirá el video dentro de su WebView normalmente, pero si inclina su teléfono, se reproducirá en Landscape.

También es importante tener en cuenta que si incluyes youtube.com como URL base, se cargará mucho más rápido.

Haga una UIWebView en su guión gráfico y conéctela @property, luego haga referencia a continuación.

CGFloat width = self.webView.frame.size.height; CGFloat height = self.webView.frame.size.width; NSString *youTubeVideoCode = @"dQw4w9WgXcQ"; NSString *embedHTML = @"<iframe width=/"%f/" height=/"%f/" src=/"http://www.youtube.com/embed/%@/" frameborder=/"0/" style=/"margin:-8px;padding:0;/" allowfullscreen></iframe>"; NSString *html = [NSString stringWithFormat:embedHTML, width, height, youTubeVideoCode]; self.webView.scrollView.bounces = NO; [self.webView loadHTMLString:html baseURL:[NSURL URLWithString:@"http://www.youtube.com"]];


Versión Swift:

//AppDelegate: func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> Int { var presentedVC = application.keyWindow?.rootViewController while let pVC = presentedVC?.presentedViewController { presentedVC = pVC } if let pVC = presentedVC { if contains(["MPInlineVideoFullscreenViewController", "MPMoviePlayerViewController", "AVFullScreenViewController"], pVC.nameOfClass) { return Int(UIInterfaceOrientationMask.AllButUpsideDown.rawValue) } } return Int(UIInterfaceOrientationMask.Portrait.rawValue) } //Extension: public extension NSObject{ public class var nameOfClass: String{ return NSStringFromClass(self).componentsSeparatedByString(".").last! } public var nameOfClass: String{ return NSStringFromClass(self.dynamicType).componentsSeparatedByString(".").last! } } //View controller: override func supportedInterfaceOrientations() -> Int { return Int(UIInterfaceOrientationMask.Portrait.rawValue) } override func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation { return UIInterfaceOrientation.Portrait } override func shouldAutorotate() -> Bool { return false } override func viewWillLayoutSubviews() { UIApplication.sharedApplication().setStatusBarHidden(false, withAnimation: UIStatusBarAnimation.None) }


viewDidLoad temporalmente (a través de un hack ) con el siguiente código en viewDidLoad de mi controlador. Tengo que especificar que el código está específicamente hecho para mi caso: dado que explícitamente desaprobo la orientación horizontal de mi UINavigationController (ver código arriba), la notificación usual "UIDeviceOrientationDidChange" no se llama cuando la reproducción finaliza y la ventana vuelve a la posición vertical. Sin embargo, espero que haya una mejor opción y este es un error del SDK, ya que no aparece en iOS 7 y dada la cantidad de errores de diseño automático que me relacionan con el reproductor de video (del cual no tengo control) .

- (void)viewDidLoad { [super viewDidLoad]; // […] /* Hack to fix navigation bar position/height on iOS 8 after closing fullscreen video Observe for “UIWindowDidRotateNotification” since “UIDeviceOrientationDidChangeNotification” is not called in the present conditions Check if the notification key (“UIWindowOldOrientationUserInfoKey”) in userInfo is either 3 or 4, which means the old orientation was landscape If so, correct the frame of the navigation bar to the proper size. */ [[NSNotificationCenter defaultCenter] addObserverForName:@"UIWindowDidRotateNotification" object:nil queue:nil usingBlock:^(NSNotification *note) { if ([note.userInfo[@"UIWindowOldOrientationUserInfoKey"] intValue] >= 3) { self.navigationController.navigationBar.frame = (CGRect){0, 0, self.view.frame.size.width, 64}; } }]; }

Y entonces…

- (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self forKeyPath:@"UIWindowDidRotateNotification"]; }