¿Cómo forzar la orientación del controlador de vista en iOS 8?

149

Antes de iOS 8, utilizamos el siguiente código junto con compatiblesInterfaceOrientations y debeAutoRotate los métodos de delegado para forzar la orientación de la aplicación a cualquier orientación particular. Utilicé el fragmento de código a continuación para rotar mediante programación la aplicación a la orientación deseada. En primer lugar, estoy cambiando la orientación de la barra de estado. Y luego, simplemente presentar y descartar inmediatamente una vista modal gira la vista a la orientación deseada.

[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeRight animated:YES];
UIViewController *c = [[UIViewController alloc]init];
[self presentViewController:vc animated:NO completion:nil];
[self dismissViewControllerAnimated:NO completion:nil];

Pero esto está fallando en iOS 8. Además, he visto algunas respuestas en el desbordamiento de pila donde la gente sugirió que siempre debemos evitar este enfoque desde iOS 8 en adelante.

Para ser más específico, mi aplicación es un tipo universal de aplicación. Hay tres controladores en total.

  1. Controlador First View : debe admitir todas las orientaciones en iPad y solo retrato (botón de inicio hacia abajo) en iPhone.

  2. Segundo controlador de vista : debe admitir solo el paisaje justo en todas las condiciones

  3. Tercer controlador de vista : debe admitir solo el paisaje justo en todas las condiciones

Estamos utilizando el controlador de navegación para la navegación de la página. Desde el primer controlador de vista, en una acción de clic de botón, estamos empujando el segundo en la pila. Entonces, cuando llega el segundo controlador de vista, independientemente de la orientación del dispositivo, la aplicación debe bloquearse solo en el paisaje.

A continuación se muestra mi shouldAutorotatey supportedInterfaceOrientationsmétodos en el controlador de segunda y tercera vista.

-(NSUInteger)supportedInterfaceOrientations{
    return UIInterfaceOrientationMaskLandscapeRight;
}

-(BOOL)shouldAutorotate {
    return NO;
}

¿Existe alguna solución para esto o alguna otra forma mejor de bloquear un controlador de vista en una orientación particular para iOS 8. Por favor, ayuda!

Rashmi Ranjan mallick
fuente
La implementación de los métodos que mencionó en el VC presentado generalmente debería funcionar (al menos esa es mi experiencia en iOS 8). ¿Quizás su configuración específica está causando problemas?
Vladimir Gritsenko
Quizás pueda aclarar un poco la pregunta. Editaré la pregunta un poco.
Rashmi Ranjan mallick
@VladimirGritsenko: Por favor verifique ahora. Lo he editado
Rashmi Ranjan mallick
El código en la pregunta no usa la pila del controlador de navegación, sino la presentación modal, por lo que aún no está claro qué está haciendo exactamente. Diré que en nuestro código, devolver YES desde shouldAutoRotate y devolver las orientaciones deseadas desde supportInterfaceOrientations en el VC presentado correctamente orienta ese VC.
Vladimir Gritsenko
Bueno, no es realmente una falla, es solo un gran cambio de concepto. Si es un buen cambio es completamente otro tema.
Kegluneq

Respuestas:

315

Para iOS 7-10:

C objetivo:

[[UIDevice currentDevice] setValue:@(UIInterfaceOrientationLandscapeLeft) forKey:@"orientation"];
[UINavigationController attemptRotationToDeviceOrientation];

Swift 3:

let value = UIInterfaceOrientation.landscapeLeft.rawValue
UIDevice.current.setValue(value, forKey: "orientation")
UINavigationController.attemptRotationToDeviceOrientation()

Simplemente llámelo desde - viewDidAppear:el controlador de vista presentado.

Sunny Shah
fuente
61
Esta no es una API privada. Este es un uso no seguro de las API. No está utilizando ningún método privado, sino 'cosas internas'. Si Apple cambia el valor de "orientación", falla. Peor aún: si Apple cambia el significado del valor de "orientación", usted no sabe qué está cambiando, estableciendo el valor.
sonxurxo
44
Para desactivar la animación, se puso [UIView setAnimationsEnabled:NO];en viewWillAppeary [UIView setAnimationsEnabled:YES];en viewDidAppear, relacionada: stackoverflow.com/a/6292506/88597
Ohho
3
Buen truco, pero el problema con eso es que no está disparando didRotateFromInterfaceOrientation después de establecer el nuevo valor de orientación (suspiro)
loretoparisi
2
En iOS 9.2, esto solo cambia la orientación pero no se bloquea.
Mark13426
2
Para todos los que se comportan bien el 90% del tiempo y tienen errores el otro 10% del tiempo, llame a esto después de configurar la clave de orientación:[UINavigationController attemptRotationToDeviceOrientation];
Albert Renshaw
93

La rotación de orientación es un poco más complicada si está dentro de un UINavigationControlleroUITabBarController . El problema es que si un controlador de vista está incrustado en uno de estos controladores, el controlador de navegación o barra de pestañas tiene prioridad y toma las decisiones sobre autorrotación y orientaciones compatibles.

Utilizo las siguientes 2 extensiones en UINavigationController y UITabBarController para que los controladores de vista integrados en uno de estos puedan tomar las decisiones.

¡Dale a los controladores de vista el poder!

Swift 2.3

extension UINavigationController {
    public override func supportedInterfaceOrientations() -> Int {
        return visibleViewController.supportedInterfaceOrientations()
    }
    public override func shouldAutorotate() -> Bool {
        return visibleViewController.shouldAutorotate()
    }
}

extension UITabBarController {
    public override func supportedInterfaceOrientations() -> Int {
        if let selected = selectedViewController {
            return selected.supportedInterfaceOrientations()
        }
        return super.supportedInterfaceOrientations()
    }
    public override func shouldAutorotate() -> Bool {
        if let selected = selectedViewController {
            return selected.shouldAutorotate()
        }
        return super.shouldAutorotate()
    }
}

Swift 3

extension UINavigationController {
    open override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        return visibleViewController?.supportedInterfaceOrientations ?? super.supportedInterfaceOrientations
    }

    open override var shouldAutorotate: Bool {
        return visibleViewController?.shouldAutorotate ?? super.shouldAutorotate
    }
}

extension UITabBarController {
    open override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        if let selected = selectedViewController {
            return selected.supportedInterfaceOrientations
        }
        return super.supportedInterfaceOrientations
    }

    open override var shouldAutorotate: Bool {
        if let selected = selectedViewController {
            return selected.shouldAutorotate
        }
        return super.shouldAutorotate
    }
}

Ahora puede anular el supportedInterfaceOrientationsmétodo o puede anularshouldAutoRotate en el controlador de vista que desea bloquear; de lo contrario, puede omitir las anulaciones en otros controladores de vista que desee heredar el comportamiento de orientación predeterminado especificado en la lista de aplicaciones.

Deshabilitar rotación

class ViewController: UIViewController {
    override func shouldAutorotate() -> Bool {
        return false
    }
}

Bloquear a orientación específica

class ViewController: UIViewController {
    override func supportedInterfaceOrientations() -> Int {
        return Int(UIInterfaceOrientationMask.Landscape.rawValue)
    }
}

En teoría, esto debería funcionar para todas las jerarquías complejas de controladores de vista, pero he notado un problema con UITabBarController. Por alguna razón, quiere usar un valor de orientación predeterminado. Consulte la siguiente publicación de blog si está interesado en aprender sobre cómo solucionar algunos de los problemas:

Rotación de pantalla de bloqueo

Korey Hinton
fuente
1
Sé que esta es una publicación muy antigua, pero ¿dónde pondrías el código de extensión?
dwinnbrown
77
Marcar hacia abajo para una respuesta rápida solo en una pregunta de Objective-C.
Charlie Scott-Skinner
17
Esta es una buena solución para Swift y ObjC. No entiendo por qué la gente rechaza votar. Tienes la idea y luego escribir código es fácil. ¿Por qué quejarse?
Zhao
2
@Krodak, en las extensiones intente cambiar "-> Int" a "-> UIInterfaceOrientationMask". Hizo el truco para mí.
the_pantless_coder
2
La idea y el código son perfectos, me encanta, pero hay un problema al usar UINavigationController y al volver de un UIViewController en modo horizontal a uno que debe mostrarse en modo vertical. ¿Algunas ideas?
crisisGriega
24

Esto es lo que funcionó para mí:

https://developer.apple.com/library//ios/documentation/UIKit/Reference/UIViewController_Class/index.html#//apple_ref/occ/clm/UIViewController/attemptRotationToDeviceOrientation

Llámalo en tu vistaDidAppear: método.

- (void) viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];

    [UIViewController attemptRotationToDeviceOrientation];
}
Vincil Bishop
fuente
3
Muy útil, si el dispositivo ya está girado y necesita girar su controlador de vista
avdyushin
2
Gran respuesta. Cuando se usa en combinación con la respuesta aceptada, esto funciona bien para mí cada vez en Swift y iOS 9+.
AndyDunn
1
Aunque creo que no es la respuesta a la pregunta formulada, realmente me ayudó.
Abdurrahman Mubeen Ali
21

Descubrí que si se trata de un controlador de vista presentado, puede anular preferredInterfaceOrientationForPresentation

Rápido:

override func supportedInterfaceOrientations() -> Int {
  return Int(UIInterfaceOrientationMask.Landscape.rawValue)
}

override func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation {
  return UIInterfaceOrientation.LandscapeLeft
}

override func shouldAutorotate() -> Bool {
  return false
}
Zipme
fuente
44
Parece que la respuesta aceptada simplemente desencadena un cambio de orientación al paisaje. Lo que ha mostrado aquí es cómo forzar a un controlador de vista a estar solo en paisaje y no afectar el controlador de vista de presentación (que es exactamente lo que necesito). Gracias.
Clifton Labrum
1
@CliftonLabrum, ¿hiciste algo más para forzar solo el paisaje? Con el mismo código, el controlador de vista aún puede girar a otra orientación.
Crashalot
Más tarde me di cuenta de que estás en lo correcto. Todavía no he encontrado una solución.
Clifton Labrum
1
Esto funcionó para mí. Pude mantener un solo controlador de vista en modo vertical usando este código (reemplazando horizontal por vertical). +1
Mark Barrasso
¡Si! Esto también funcionó para mí en Xcode 7.3 y Swift 2.2. Pero aún necesita definir la aplicación func para supportInterfaceOrientationsForWindow en AppDelegate.
charles.cc.hsu
15

Así funciona para mí en Swift 2 iOS 8.x:

PD (este método no requiere anular las funciones de orientación, como debe ser la auto-rotación en cada viewController, solo un método en AppDelegate)

Marque "requiere pantalla completa" en la información general de su proyecto. ingrese la descripción de la imagen aquí

Entonces, en AppDelegate.swift crea una variable:

var enableAllOrientation = false

Entonces, pon también esta función:

func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask {
        if (enableAllOrientation == true){
            return UIInterfaceOrientationMask.All
        }
        return UIInterfaceOrientationMask.Portrait
}

Por lo tanto, en cada clase de su proyecto, puede configurar esta var en viewWillAppear:

override func viewWillAppear(animated: Bool)
{
        super.viewWillAppear(animated)
        let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
        appDelegate.enableAllOrientation = true
}

Si necesita hacer una elección basada en el tipo de dispositivo, puede hacer esto:

override func viewWillAppear(animated: Bool)
    {
        super.viewWillAppear(animated)
        let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
        switch UIDevice.currentDevice().userInterfaceIdiom {
        case .Phone:
        // It's an iPhone
           print(" - Only portrait mode to iPhone")
           appDelegate.enableAllOrientation = false
        case .Pad:
        // It's an iPad
           print(" - All orientation mode enabled on iPad")
           appDelegate.enableAllOrientation = true
        case .Unspecified:
        // Uh, oh! What could it be?
           appDelegate.enableAllOrientation = false
        }
    }
Alessandro Ornano
fuente
muy buena explicación
Mihir Oza
11

En primer lugar, esta es una mala idea, en general, algo anda mal con la arquitectura de su aplicación, pero sh..t happens, si es así, puede intentar hacer algo como a continuación:

final class OrientationController {

    static private (set) var allowedOrientation:UIInterfaceOrientationMask = [.all]

    // MARK: - Public

    class func lockOrientation(_ orientationIdiom: UIInterfaceOrientationMask) {
        OrientationController.allowedOrientation = [orientationIdiom]
    }

    class func forceLockOrientation(_ orientation: UIInterfaceOrientation) {
        var mask:UIInterfaceOrientationMask = []
        switch orientation {
            case .unknown:
                mask = [.all]
            case .portrait:
                mask = [.portrait]
            case .portraitUpsideDown:
                mask = [.portraitUpsideDown]
            case .landscapeLeft:
                mask = [.landscapeLeft]
            case .landscapeRight:
                mask = [.landscapeRight]
        }

        OrientationController.lockOrientation(mask)

        UIDevice.current.setValue(orientation.rawValue, forKey: "orientation")
    }
}

Que en AppDelegate

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    // do stuff
    OrientationController.lockOrientation(.portrait)
    return true
}

// MARK: - Orientation

func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
    return OrientationController.allowedOrientation
}

Y cuando quiera cambiar de orientación, haga lo siguiente:

OrientationController.forceLockOrientation(.landscapeRight)

Nota: A veces, el dispositivo puede no actualizarse desde dicha llamada, por lo que es posible que deba hacer lo siguiente

OrientationController.forceLockOrientation(.portrait)
OrientationController.forceLockOrientation(.landscapeRight)

Eso es todo

gbk
fuente
9

Esto debería funcionar desde iOS 6 en adelante, pero solo lo he probado en iOS 8. Subclase UINavigationControllery anular los siguientes métodos:

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
    return UIInterfaceOrientationLandscapeRight;
}

- (BOOL)shouldAutorotate {
    return NO;
}

O pregunte al controlador de vista visible

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
    return self.visibleViewController.preferredInterfaceOrientationForPresentation;
}

- (BOOL)shouldAutorotate {
    return self.visibleViewController.shouldAutorotate;
}

e implementar los métodos allí.

Mojo66
fuente
Acabo de probar esto en mi aplicación en Xcode 8.2.1 con iOS 10.0 y funciona. Mi menú del juego es la primera pantalla y eso no gira; todas las demás vistas rotan. ¡Perfecto! Le di un uno. Solo necesitaba la función 'PreferInterfaceOrientationForPresentation' dada por Mojo66.
Philip Borges
9

Esta es una respuesta a los comentarios en la respuesta de Sid Shah , sobre cómo deshabilitar las animaciones usando:

[UIView setAnimationsEnabled:enabled];

Código:

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:NO];
    [UIView setAnimationsEnabled:NO];

    // Stackoverflow #26357162 to force orientation
    NSNumber *value = [NSNumber numberWithInt:UIInterfaceOrientationLandscapeRight];
    [[UIDevice currentDevice] setValue:value forKey:@"orientation"];
}

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:NO];
    [UIView setAnimationsEnabled:YES];
}
Oh ho
fuente
¿Cómo puedo usar esto para Retrato?
Muju
6

Si está utilizando navigationViewController, debe crear su propia superclase para esto y anular:

- (BOOL)shouldAutorotate {
  id currentViewController = self.topViewController;

  if ([currentViewController isKindOfClass:[SecondViewController class]])
    return NO;

  return YES;
}

esto deshabilitará la rotación en SecondViewController, pero si presiona su SecondViewController cuando su dispositivo está en orientación vertical, su SecondViewController aparecerá en modo vertical.

Suponga que está usando el guión gráfico. Debe crear segue manual ( Cómo ) y en su método "onClick":

- (IBAction)onPlayButtonClicked:(UIBarButtonItem *)sender {
  NSNumber *value = [NSNumber numberWithInt:UIInterfaceOrientationLandscapeLeft];
  [[UIDevice currentDevice] setValue:value forKey:@"orientation"];
  [self performSegueWithIdentifier:@"PushPlayerViewController" sender:self];
}

Esto forzará la orientación horizontal antes de que su superclase desactive la función de autorrotación.

Lau
fuente
6

En Xcode 8los métodos se convierten en propiedades, por lo que lo siguiente funciona con Swift:

override public var supportedInterfaceOrientations: UIInterfaceOrientationMask {
    return UIInterfaceOrientationMask.portrait
}

override public var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
    return UIInterfaceOrientation.portrait
}

override public var shouldAutorotate: Bool {
    return true
}
Ali Momen Sani
fuente
¿Esto bloquea la orientación?
bnussey
1
@bnussey Quería evitar la rotación automática y estar siempre en portraitmodo sin importar cuál sea la orientación en el iPad, y al shouldAutorotatevolver cierto, lo logré.
Ali Momen Sani
4

He intentado muchas soluciones, pero la que funcionó es la siguiente:

No hay necesidad de editar los info.plistios 8 y 9.

- (BOOL) shouldAutorotate {
    return NO;
}   

- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
    return (UIInterfaceOrientationPortrait | UIInterfaceOrientationPortraitUpsideDown);
}

Posibles orientaciones de la documentación de Apple :

UIInterfaceOrientationUnknown

La orientación del dispositivo no se puede determinar.

UInterfazOrientaciónRetrato

El dispositivo está en modo vertical, con el dispositivo en posición vertical y el botón de inicio en la parte inferior.

UInterfaceOrientationPortraitUpsideDown

El dispositivo está en modo vertical pero al revés, con el dispositivo en posición vertical y el botón de inicio en la parte superior.

UInterfazOrientaciónPaisajeIzquierda

El dispositivo está en modo horizontal, con el dispositivo en posición vertical y el botón de inicio en el lado izquierdo.

UInterfazOrientaciónPaisajeDerecho

El dispositivo está en modo horizontal, con el dispositivo en posición vertical y el botón de inicio en el lado derecho.

hoogw
fuente
3

La combinación de las respuestas de Sids y Koreys funcionó para mí.

Extender el controlador de navegación:

extension UINavigationController {
    public override func shouldAutorotate() -> Bool {
        return visibleViewController.shouldAutorotate()
    }
}

Luego deshabilitar la rotación en la vista única

class ViewController: UIViewController {
    override func shouldAutorotate() -> Bool {
        return false
    }
}

Y girando a la orientación apropiada antes del segue

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    if (segue.identifier == "SomeSegue")
    {
        let value = UIInterfaceOrientation.Portrait.rawValue;
        UIDevice.currentDevice().setValue(value, forKey: "orientation")
    }
}
Misha
fuente
¿Sabes si esto podría ayudar a mi problema en el que el controlador de vista que se muestra está en horizontal por un segundo antes de que gire en vertical como siempre debería estar? la pregunta está aquí: stackoverflow.com/questions/30693964/…
dwinnbrown
3

La solución superior anterior:

let value = UIInterfaceOrientation.LandscapeLeft.rawValue
UIDevice.currentDevice().setValue(value, forKey: "orientation")

no funcionó para mí cuando lo llamé en viewDidAppear del controlador de vista presentado. Sin embargo, funcionó cuando lo llamé en preparForSegue en el controlador de vista de presentación.

(Lo siento, no hay suficientes puntos de reputación para comentar sobre esa solución, así que tuve que agregarla así)

guido
fuente
Lo tengo funcionando, pero cuando paso de un controlador de vista horizontal a un controlador de vista vertical, el controlador de vista que debería mostrarse en vertical se muestra brevemente en horizontal antes de rotar por sí solo. si alguien sabe cómo ayudar, mi pregunta está aquí: stackoverflow.com/questions/30693964/…
dwinnbrown
@dwinnbrown en viewDidAppear el controlador de vista ya está visible. ¿Quizás intente desde viewDidLoad en su lugar?
Crashalot
@Crashalot Ahora he arreglado esto. Por favor, vea la respuesta que marqué como correcta.
dwinnbrown
@dwinnbrown el enlace a su pregunta está muerto. ¿actualizar? ¡Gracias!
Crashalot
@Crashalot la comunidad lo eliminó pero he votado para recuperarlo. Te lo haré saber pronto
dwinnbrown
3

[iOS9 +] Si alguien arrastró todo el camino hasta aquí, ya que ninguna de las soluciones anteriores funcionó, y si presenta la vista que desea cambiar de orientación por segue, es posible que desee comprobar esto.

Verifique el tipo de presentación de su segue. La mía estaba "sobre el contexto actual". Cuando lo cambié a pantalla completa, funcionó.

Gracias a @GabLeRoux, encontré esta solución.

  • Estos cambios solo funcionan cuando se combinan con las soluciones anteriores.
Goh
fuente
2

Mis requisitos son

  1. bloquear todas las vistas en modo vertical
  2. usar AVPlayerViewControllerpara reproducir video

Cuando se reproduce un video, si es un paisaje, entonces permita que la pantalla gire el paisaje a la derecha y el paisaje a la izquierda. Si es un retrato, bloquee la vista solo en modo vertical.

Primero, defina supportedInterfaceOrientationsForWindowenAppDelegate.swift

var portrait = true
func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask {
    if portrait {
        return .Portrait
    } else {
        return .Landscape
    }
}

Segundo, en su controlador de vista principal, defina las siguientes funciones

override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
    print("\(#function)")
    return .Portrait
}

override func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation {
    return .Portrait
}

override func shouldAutorotate() -> Bool {
    return false
}

Entonces, necesitas subclase AVPlayerViewController

class MyPlayerViewController: AVPlayerViewController {

    var size: CGSize?

    var supportedOrientationMask: UIInterfaceOrientationMask?
    var preferredOrientation: UIInterfaceOrientation?

    override func viewDidLoad() {
        super.viewDidLoad()

        if let size = size {
            if size.width > size.height {
                self.supportedOrientationMask =[.LandscapeLeft,.LandscapeRight]
                self.preferredOrientation =.LandscapeRight
            } else {
                self.supportedOrientationMask =.Portrait
                self.preferredOrientation =.Portrait
            }
        }
    }

Anular estas tres funciones en MyPlayerViewController.swift

override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
    return self.supportedOrientationMask!
}

override func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation {
    return self.preferredOrientation!
}

Debido a que el usuario puede rotar el dispositivo horizontalmente a la izquierda o horizontal derecha, necesitamos configurar la rotación automática para que sea verdadera

override func shouldAutorotate() -> Bool {
    return true
}

Finalmente, cree una MyPlayerViewControllerinstancia en su controlador de vista y establezca el valor del tamaño de la propiedad.

let playerViewController = MyPlayerViewController()

// Get the thumbnail  
let thumbnail = MyAlbumFileManager.sharedManager().getThumbnailFromMyVideoMedia(......)

let size = thumbnail?.size
playerViewController.size = size

Inicie a su jugador con el adecuado videoUrl, luego asigne su jugador a playerViewController. ¡Feliz codificación!

charles.cc.hsu
fuente
2
- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    [UIViewController attemptRotationToDeviceOrientation];
}
Solodyashkin romano
fuente
1

Todavía parece haber cierto debate sobre la mejor manera de lograr esta tarea, así que pensé en compartir mi enfoque (de trabajo). Agregue el siguiente código en su UIViewControllerimplementación:

- (void) viewWillAppear:(BOOL)animated
{
    [UIViewController attemptRotationToDeviceOrientation];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
    return (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft);
}

-(BOOL)shouldAutorotate
{
    return NO;
}

- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskLandscapeLeft;
}

Para este ejemplo, también deberá establecer las orientaciones de dispositivo permitidas en 'Paisaje a la izquierda' en la configuración de su proyecto (o directamente en info.plist). Simplemente cambie la orientación específica que desea forzar si desea algo más queLandscapeLeft.

La clave para mí fue la attemptRotationToDeviceOrientationllamada viewWillAppear, sin eso la vista no rotaría correctamente sin rotar físicamente el dispositivo.

ViperMav
fuente
Método obsoleto
Saqib Omer
1

De acuerdo a la respuesta de Korey Hinton

Swift 2.2:

extension UINavigationController {
    public override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
        return visibleViewController!.supportedInterfaceOrientations()
    }
    public override func shouldAutorotate() -> Bool {
        return visibleViewController!.shouldAutorotate()
    }
}


extension UITabBarController {
    public override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
        if let selected = selectedViewController {
            return selected.supportedInterfaceOrientations()
        }
        return super.supportedInterfaceOrientations()
    }
    public override func shouldAutorotate() -> Bool {
        if let selected = selectedViewController {
            return selected.shouldAutorotate()
        }
        return super.shouldAutorotate()
    }
}

Deshabilitar rotación

override func shouldAutorotate() -> Bool {
    return false
}

Bloquear a orientación específica

override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
    return UIInterfaceOrientationMask.Portrait
}
phnmnn
fuente
1

Intenté algunas soluciones aquí y lo importante es entender que es el controlador de vista raíz el que determinará si rotará o no.

Creé el siguiente proyecto de Objective -C github.com/GabLeRoux/RotationLockInTabbedViewChild con un ejemplo funcional de unTabbedViewController en el que una vista secundaria se permite girar y la otra vista secundaria se bloquea en vertical.

No es perfecto, pero funciona y la misma idea debería funcionar para otro tipo de vistas raíz como NavigationViewController. :)

La vista secundaria bloquea la orientación de los padres

GabLeRoux
fuente
0

De acuerdo con la solución mostrada por @ sid-sha, debe poner todo en el viewDidAppear:método, de lo contrario no será didRotateFromInterfaceOrientation:despedido, así que algo como:

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    UIInterfaceOrientation interfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation];
    if (interfaceOrientation == UIInterfaceOrientationLandscapeLeft ||
        interfaceOrientation == UIInterfaceOrientationLandscapeRight) {
        NSNumber *value = [NSNumber numberWithInt:interfaceOrientation];
        [[UIDevice currentDevice] setValue:value forKey:@"orientation"];
    }
}
loretoparisi
fuente
0

Mi solución

En AppDelegate:

func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask {
    if let topController = UIViewController.topMostViewController() {
        if topController is XXViewController {
            return [.Portrait, .LandscapeLeft]
        }
    }
    return [.Portrait]
}

XXViewController es el ViewController que desea admitir en modo horizontal.

Entonces la solución de Sunny Shah funcionaría en XXViewControllercualquier versión de iOS:

let value = UIInterfaceOrientation.LandscapeLeft.rawValue
UIDevice.currentDevice().setValue(value, forKey: "orientation")

Esta es la función de utilidad para encontrar el ViewController más superior.

extension UIViewController {

    /// Returns the current application's top most view controller.
    public class func topMostViewController() -> UIViewController? {
        let rootViewController = UIApplication.sharedApplication().windows.first?.rootViewController
        return self.topMostViewControllerOfViewController(rootViewController)
    }



    /// Returns the top most view controller from given view controller's stack.
    class func topMostViewControllerOfViewController(viewController: UIViewController?) -> UIViewController? {
        // UITabBarController
        if let tabBarController = viewController as? UITabBarController,
           let selectedViewController = tabBarController.selectedViewController {
            return self.topMostViewControllerOfViewController(selectedViewController)
        }

        // UINavigationController
        if let navigationController = viewController as? UINavigationController,
           let visibleViewController = navigationController.visibleViewController {
            return self.topMostViewControllerOfViewController(visibleViewController)
        }

        // presented view controller
        if let presentedViewController = viewController?.presentedViewController {
            return self.topMostViewControllerOfViewController(presentedViewController)
        }

        // child view controller
        for subview in viewController?.view?.subviews ?? [] {
            if let childViewController = subview.nextResponder() as? UIViewController {
                return self.topMostViewControllerOfViewController(childViewController)
            }
        }

        return viewController
    }

}
duan
fuente
0

Use esto para bloquear la orientación del controlador de vista, probado en iOS 9:

// Bloquear la orientación al paisaje a la derecha

-(UIInterfaceOrientationMask)supportedInterfaceOrientations {
    return UIInterfaceOrientationMaskLandscapeRight;
}

-(NSUInteger)navigationControllerSupportedInterfaceOrientations:(UINavigationController *)navigationController {
    return UIInterfaceOrientationMaskLandscapeRight;
}
robegamesios
fuente
0

Tengo el mismo problema y malgasto mucho tiempo. Entonces ahora tengo mi solución. La configuración de mi aplicación es solo compatible con retrato. Sin embargo, algunas pantallas en mi aplicación solo necesitan tener un paisaje. Lo soluciono con una variable isShouldRotate en AppDelegate . Y la función en AppDelegate :

func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> Int {
    if isShouldRotate == true {
        return Int(UIInterfaceOrientationMask.Landscape.rawValue)
    }
    return Int(UIInterfaceOrientationMask.Portrait.rawValue)
}

Y finalmente cuando un ViewControllerA necesita un estado horizontal. Simplemente haga eso: antes de enviar / presentar a ViewController, una asignación debe ser Rotar a verdadero . No olvide cuando emerge / descarta que la asignación del controlador esShouldRotate a false en viewWillDisappear .

sotavento
fuente
0

Para mí, el VC de nivel superior necesitaba implementar las anulaciones de orientación. El uso de VC en la pila no tendrá efecto si el VC superior no se está implementando.

VC-main
    |
    -> VC 2
        |
        -> VC 3

Solo se escucha VC-Main, esencialmente en mis pruebas.

bduhbya
fuente
0

Parece que incluso aquí tienes tantas respuestas que nadie fue suficiente para mí. Quería forzar la orientación y luego, al regresar, volver a la orientación del dispositivo, pero[UIViewController attemptRotationToDeviceOrientation]; simplemente no funcionó. Lo que también hizo que todo se complicara es que agregué shouldAutorotate to false en función de alguna respuesta y no pude obtener los efectos deseados para rotar correctamente en todos los escenarios.

Entonces esto es lo que hice:

Antes de presionar el controlador en llamada en su constructor init esto:

_userOrientation = UIDevice.currentDevice.orientation;
[UIDevice.currentDevice setValue:@(UIInterfaceOrientationPortrait) forKey:@"orientation"];
[self addNotificationCenterObserver:@selector(rotated:)
                               name:UIDeviceOrientationDidChangeNotification];

Así que guardo la última orientación del dispositivo y me registro para el evento de cambio de orientación. El evento de cambio de orientación es simple:

- (void)rotated:(NSNotification*)notification {
    _userOrientation = UIDevice.currentDevice.orientation;
}

Y a la vista de desestimación, simplemente vuelvo a cualquier orientación que tenga como usuario.

- (void)onViewDismissing {
    super.onViewDismissing;
    [UIDevice.currentDevice setValue:@(_userOrientation) forKey:@"orientation"];
    [UIViewController attemptRotationToDeviceOrientation];
}

Y esto tiene que estar allí también:

- (BOOL)shouldAutorotate {
    return true;
}

- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
    return UIInterfaceOrientationMaskPortrait;
}

Y también el controlador de navegación tiene que delegar en shouldAutorotate y SupportInterfaceOrientations, pero eso creo que la mayoría de las personas ya lo tienen.

PD: Lo siento, uso algunas extensiones y clases base, pero los nombres son bastante significativos, por lo que el concepto es comprensible, hará aún más extensiones porque ahora no es demasiado bonito.

Renetik
fuente