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


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

Main view

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

Video portrait  Video landscape

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:

Navigation bar issue

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?


32
2017-10-27 23:58


origen


Respuestas:


Resolví temporalmente (a través de cortar) con el siguiente código en el 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"];
}

17
2017-10-28 21:25



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

5
2018-01-14 13:20



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

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

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

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

}

solía UIStatusBarAnimationFade porque se ve mucho más suave que UIStatusBarAnimationNone, al menos desde la perspectiva del usuario.


4
2017-11-24 13:34



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

3
2018-05-21 14:41



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

versión rápida 3

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

1
2017-11-30 11:50



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


0
2018-03-09 13:18



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 supportedInterfaceOrientationsForWindow método para regresar UIInterfaceOrientationMaskLandscape para 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, tenemos que agregar el control UIWindowDidBecomeHiddenNotification método:

- (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 el texto en la barra de estado no aparecerá en el iPhone X, para otros dispositivos no es necesario.


0
2017-11-16 15:53