force swift uiviewcontroller portrait device-orientation

force landscape swift 4



Cómo bloquear la orientación de un controlador de vista al modo vertical solo en Swift (14)

A partir de iOS 10 y 11, iPad es compatible con Slide Over y Split View. Para habilitar una aplicación en Slide Over y Split View, Requires full screen debe estar desmarcada. Eso significa que la respuesta aceptada no se puede usar si la aplicación desea admitir Slide Over y Split View. Vea más de las Adopción de mejoras multitarea de Apple en iPad aquí .

Tengo una solución que permite (1) desmarcar Requires full screen , (2) solo se debe implementar una función en appDelegate (especialmente si no desea / no puede modificar los controladores de vista de destino) y (3) evitar llamadas recursivas No necesita clases auxiliares ni extensiones.

appDelegate.swift (Swift 4)

func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask { // Search for the visible view controller var vc = window?.rootViewController // Dig through tab bar and navigation, regardless their order while (vc is UITabBarController) || (vc is UINavigationController) { if let c = vc as? UINavigationController { vc = c.topViewController } else if let c = vc as? UITabBarController { vc = c.selectedViewController } } // Look for model view controller while (vc?.presentedViewController) != nil { vc = vc!.presentedViewController } print("vc = " + (vc != nil ? String(describing: type(of: vc!)) : "nil")) // Final check if it''s our target class. Also make sure it isn''t exiting. // Otherwise, system will mistakenly rotate the presentingViewController. if (vc is TargetViewController) && !(vc!.isBeingDismissed) { return [.portrait] } return [.all] }

Editar

@bmjohns señaló que esta función no se llama en iPad. Lo verifiqué y sí, no se llamó. Entonces, hice un poco más de pruebas y descubrí algunos hechos:

  1. Desmarqué Requires full screen porque quiero habilitar Slide Over y Slide View en iPad. Eso requiere que la aplicación admita las 4 orientaciones para iPad, en Info.plist: Supported interface orientations (iPad) .

Mi aplicación funciona de la misma manera que Facebook: en iPhone, la mayoría de las veces está bloqueada para retrato. Al visualizar la imagen en pantalla completa, permite a los usuarios rotar el paisaje para una mejor visualización. En iPad, los usuarios pueden rotar a cualquier orientación en cualquier controlador de vista. Por lo tanto, la aplicación se ve bien cuando el iPad está en Smart Cover (paisaje a la izquierda).

  1. Para que el iPad llame a la application(_:supportedInterfaceOrientationsFor) SupportInterfaceOrientationsFor application(_:supportedInterfaceOrientationsFor) , en Info.plist, solo mantenga el retrato para iPad. La aplicación perderá la capacidad Slide Over + Split View. Pero puede bloquear o desbloquear la orientación para cualquier controlador de vista, en un solo lugar y sin necesidad de modificar la clase ViewController.

  2. Finalmente, esta función se llama en el ciclo de vida del controlador de vista, cuando se visualiza / elimina la vista. Si su aplicación necesita bloquear / desbloquear / cambiar la orientación en otro momento, es posible que no funcione

Desde mi aplicación obtuvo soporte para toda orientación. Me gustaría bloquear solo el modo vertical para UIViewController específico.

(por ejemplo, suponga que fue una aplicación con pestañas y cuando la vista de inicio de sesión aparece de manera modal, solo quiero que esa vista de inicio de sesión se muestre solo en modo vertical, sin importar cómo el usuario gire el dispositivo o cómo será la orientación actual del dispositivo)


Agregue este código para forzar el retrato y bloquearlo:

override func viewDidLoad() { super.viewDidLoad() // Force the device in portrait mode when the view controller gets loaded UIDevice.currentDevice().setValue(UIInterfaceOrientation.Portrait.rawValue, forKey: "orientation") } override func shouldAutorotate() -> Bool { // Lock autorotate return false } override func supportedInterfaceOrientations() -> Int { // Only allow Portrait return Int(UIInterfaceOrientationMask.Portrait.rawValue) } override func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation { // Only allow Portrait return UIInterfaceOrientation.Portrait }

En su AppDelegate, establezca supportInterfaceOrientationsForWindow a las orientaciones que desee que admita toda la aplicación:

func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask { return UIInterfaceOrientationMask.All }


Aquí hay una manera simple que funciona para mí con Swift 4.2 (iOS 12.2), ponga esto en un UIViewController para el que desea deshabilitar debeAutorotate:

override var supportedInterfaceOrientations: UIInterfaceOrientationMask { return .portrait }

La parte .portrait le dice en qué orientación (s) permanecer, puede cambiar esto a su gusto. Las opciones son: .portrait , .allButUpsideDown , .landscape , .landscapeLeft , .landscapeRight , .portraitUpsideDown , .portraitUpsideDown .


Crea una nueva extensión con

import UIKit extension UINavigationController { override open var supportedInterfaceOrientations: UIInterfaceOrientationMask { return .portrait } } extension UITabBarController { override open var supportedInterfaceOrientations: UIInterfaceOrientationMask { return .portrait } }


Esta es una solución genérica para su problema y otros relacionados.

1. Cree la clase auxiliar UIHelper y aplique los siguientes métodos:

/**This method returns top view controller in application */ class func topViewController() -> UIViewController? { let helper = UIHelper() return helper.topViewControllerWithRootViewController(rootViewController: UIApplication.shared.keyWindow?.rootViewController) } /**This is a recursive method to select the top View Controller in a app, either with TabBarController or not */ private func topViewControllerWithRootViewController(rootViewController:UIViewController?) -> UIViewController? { if(rootViewController != nil) { // UITabBarController if let tabBarController = rootViewController as? UITabBarController, let selectedViewController = tabBarController.selectedViewController { return self.topViewControllerWithRootViewController(rootViewController: selectedViewController) } // UINavigationController if let navigationController = rootViewController as? UINavigationController ,let visibleViewController = navigationController.visibleViewController { return self.topViewControllerWithRootViewController(rootViewController: visibleViewController) } if ((rootViewController!.presentedViewController) != nil) { let presentedViewController = rootViewController!.presentedViewController; return self.topViewControllerWithRootViewController(rootViewController: presentedViewController!); }else { return rootViewController } } return nil }

2. Cree un protocolo con su comportamiento de deseo, para su caso específico será vertical.

protocolo de orientaciónIsOnlyPortrait {}

Nota: Si lo desea, agréguelo en la parte superior de la clase UIHelper.

3. Extienda su controlador de vista

En tu caso:

class Any_ViewController: UIViewController,orientationIsOnlyPortrait { .... }

4. En la clase delegado de la aplicación, agregue este método:

func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask { let presentedViewController = UIHelper.topViewController() if presentedViewController is orientationIsOnlyPortrait { return .portrait } return .all }

Notas finales

  • Si tienes más clases en modo vertical, solo extiende ese protocolo.
  • Si desea otros comportamientos de los controladores de vista, cree otros protocolos y siga la misma estructura.
  • Este ejemplo resuelve el problema con los cambios de orientación después de los controladores de vista push

Experimenté un poco y logré encontrar una solución limpia para este problema. El enfoque se basa en el etiquetado de vista a través de view-> tag

En el ViewController de destino, simplemente asigne la etiqueta a la vista raíz como en el siguiente ejemplo de código:

class MyViewController: BaseViewController { // declare unique view tag identifier static let ViewTag = 2105981; override func viewDidLoad() { super.viewDidLoad(); // assign the value to the current root view self.view.tag = MyViewController.ViewTag; }

Y finalmente en el AppDelegate.swift verifique si la vista que se muestra actualmente es la que etiquetamos:

func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask { if (window?.viewWithTag(DesignerController.ViewTag)) != nil { return .portrait; } return .all; }

Este enfoque ha sido probado con mi simulador y parece que funciona bien.

Nota: la vista marcada también se encontrará si el MVC actual se superpone con algún ViewController secundario en la pila de navegación.


Gracias a la respuesta de @ bmjohn arriba. Aquí hay una versión funcional de Xamarin / C # del código de esa respuesta, para salvar a otros el tiempo de transcripción:

AppDelegate.cs

public UIInterfaceOrientationMask OrientationLock = UIInterfaceOrientationMask.All; public override UIInterfaceOrientationMask GetSupportedInterfaceOrientations(UIApplication application, UIWindow forWindow) { return this.OrientationLock; }

Clase OrientationUtility.cs estática:

public static class OrientationUtility { public static void LockOrientation(UIInterfaceOrientationMask orientation) { var appdelegate = (AppDelegate) UIApplication.SharedApplication.Delegate; if(appdelegate != null) { appdelegate.OrientationLock = orientation; } } public static void LockOrientation(UIInterfaceOrientationMask orientation, UIInterfaceOrientation RotateToOrientation) { LockOrientation(orientation); UIDevice.CurrentDevice.SetValueForKey(new NSNumber((int)RotateToOrientation), new NSString("orientation")); } }

Controlador de vista:

public override void ViewDidAppear(bool animated) { base.ViewWillAppear(animated); OrientationUtility.LockOrientation(UIInterfaceOrientationMask.Portrait, UIInterfaceOrientation.Portrait); } public override void ViewWillDisappear(bool animated) { base.ViewWillDisappear(animated); OrientationUtility.LockOrientation(UIInterfaceOrientationMask.All); }


Las cosas pueden volverse bastante complicadas cuando tienes una jerarquía de vista complicada, como tener múltiples controladores de navegación y / o controladores de vista de pestañas.

Esta implementación lo pone en los controladores de vista individuales para establecer cuándo les gustaría bloquear las orientaciones, en lugar de confiar en el delegado de la aplicación para encontrarlos iterando a través de subvistas.

Swift 3 y 4

En AppDelegate:

/// set orientations you want to be allowed in this property by default var orientationLock = UIInterfaceOrientationMask.all func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask { return self.orientationLock }

En alguna otra estructura global o clase auxiliar, aquí creé AppUtility:

struct AppUtility { static func lockOrientation(_ orientation: UIInterfaceOrientationMask) { if let delegate = UIApplication.shared.delegate as? AppDelegate { delegate.orientationLock = orientation } } /// OPTIONAL Added method to adjust lock and rotate to the desired orientation static func lockOrientation(_ orientation: UIInterfaceOrientationMask, andRotateTo rotateOrientation:UIInterfaceOrientation) { self.lockOrientation(orientation) UIDevice.current.setValue(rotateOrientation.rawValue, forKey: "orientation") UINavigationController.attemptRotationToDeviceOrientation() } }

Luego, en el ViewController deseado, desea bloquear las orientaciones:

override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) AppUtility.lockOrientation(.portrait) // Or to rotate and lock // AppUtility.lockOrientation(.portrait, andRotateTo: .portrait) } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) // Don''t forget to reset when view is being removed AppUtility.lockOrientation(.all) }

Si iPad o aplicación universal

Asegúrese de que "Requiere pantalla completa" esté marcado en Configuración de destino -> General -> Información de implementación. supportedInterfaceOrientationsFor delegate no será llamado si eso no está marcado.


Para configurar la orientación horizontal a todas las vistas de su aplicación y permitir solo una vista a todas las orientaciones (para poder agregar el carrete de la cámara, por ejemplo):

En AppDelegate.swift:

var adaptOrientation = false

En: didFinishLaunchingWithOptions

NSNotificationCenter.defaultCenter().addObserver(self, selector: "adaptOrientationAction:", name:"adaptOrientationAction", object: nil)

En otra parte de AppDelegate.swift:

func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> Int { return checkOrientation(self.window?.rootViewController) } func checkOrientation(viewController:UIViewController?)-> Int{ if (adaptOrientation == false){ return Int(UIInterfaceOrientationMask.Landscape.rawValue) }else { return Int(UIInterfaceOrientationMask.All.rawValue) } } func adaptOrientationAction(notification: NSNotification){ if adaptOrientation == false { adaptOrientation = true }else { adaptOrientation = false } }

Luego, en la vista que sigue a la que desea tener Todas las orientaciones:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) { if (segue.identifier == "YOURSEGUE") { NSNotificationCenter.defaultCenter().postNotificationName("adaptOrientationAction", object: nil) } } override func viewWillAppear(animated: Bool) { if adaptOrientation == true { NSNotificationCenter.defaultCenter().postNotificationName("adaptOrientationAction", object: nil) } }

Lo último es marcar la orientación del dispositivo: - Vertical - Horizontal a la izquierda - Horizontal a la derecha


Un montón de excelentes respuestas en este hilo, pero ninguna satisfizo mis necesidades. Tengo una aplicación con pestañas con controladores de navegación en cada pestaña, y una vista necesitaba rotar, mientras que las otras debían estar bloqueadas en vertical. El controlador de navegación no cambiaba el tamaño de sus subvistas correctamente, por alguna razón. Encontré una solución (en Swift 3) combinando con this respuesta, y los problemas de diseño desaparecieron. Cree la estructura como sugiere @bmjohns:

import UIKit struct OrientationLock { static func lock(to orientation: UIInterfaceOrientationMask) { if let delegate = UIApplication.shared.delegate as? AppDelegate { delegate.orientationLock = orientation } } static func lock(to orientation: UIInterfaceOrientationMask, andRotateTo rotateOrientation: UIInterfaceOrientation) { self.lock(to: orientation) UIDevice.current.setValue(rotateOrientation.rawValue, forKey: "orientation") } }

Luego subclase UITabBarController:

import UIKit class TabBarController: UITabBarController, UITabBarControllerDelegate { required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) self.delegate = self } func tabBarControllerSupportedInterfaceOrientations(_ tabBarController: UITabBarController) -> UIInterfaceOrientationMask { if tabBarController.selectedViewController is MyViewControllerNotInANavigationControllerThatShouldRotate { return .allButUpsideDown } else if let navController = tabBarController.selectedViewController as? UINavigationController, navController.topViewController is MyViewControllerInANavControllerThatShouldRotate { return .allButUpsideDown } else { //Lock view that should not be able to rotate return .portrait } } func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool { if viewController is MyViewControllerNotInANavigationControllerThatShouldRotate { OrientationLock.lock(to: .allButUpsideDown) } else if let navController = viewController as? UINavigationController, navController.topViewController is MyViewControllerInANavigationControllerThatShouldRotate { OrientationLock.lock(to: .allButUpsideDown) } else { //Lock orientation and rotate to desired orientation OrientationLock.lock(to: .portrait, andRotateTo: .portrait) } return true } }

No olvide cambiar la clase de TabBarController en el guión gráfico a la subclase recién creada.


bmjohns -> Eres mi salvador de vida. Esa es la única solución de trabajo (con la estructura AppUtility)

He creado esta clase:

class Helper{ struct AppUtility { static func lockOrientation(_ orientation: UIInterfaceOrientationMask) { if let delegate = UIApplication.shared.delegate as? AppDelegate { delegate.orientationLock = orientation } } /// OPTIONAL Added method to adjust lock and rotate to the desired orientation static func lockOrientation(_ orientation: UIInterfaceOrientationMask, andRotateTo rotateOrientation:UIInterfaceOrientation) { self.lockOrientation(orientation) UIDevice.current.setValue(rotateOrientation.rawValue, forKey: "orientation") } } }

y seguí tus instrucciones, y todo funciona perfectamente para Swift 3 -> xcode versión 8.2.1


Solución probada real para esto . En mi ejemplo, necesito que toda mi aplicación esté en modo vertical, pero solo la orientación de una pantalla debe estar en modo horizontal.

Codifique en AppDelegate como se describieron las respuestas anteriores.

var orientationLock = UIInterfaceOrientationMask.all func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask { return self.orientationLock } struct AppUtility { static func lockOrientation(_ orientation: UIInterfaceOrientationMask) { if let delegate = UIApplication.shared.delegate as? AppDelegate { delegate.orientationLock = orientation } } static func lockOrientation(_ orientation: UIInterfaceOrientationMask, andRotateTo rotateOrientation:UIInterfaceOrientation) { self.lockOrientation(orientation) UIDevice.current.setValue(rotateOrientation.rawValue, forKey: "orientation") } }

Luego, escriba este código antes de que se presente / presione su controlador de vista de orientación horizontal.

override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) AppDelegate.AppUtility.lockOrientation(UIInterfaceOrientationMask.portrait, andRotateTo: UIInterfaceOrientation.portrait) }

Luego, escriba este código en el controlador de vista real (para vista horizontal)

override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) AppDelegate.AppUtility.lockOrientation(UIInterfaceOrientationMask.landscape, andRotateTo: UIInterfaceOrientation.landscape) }


Swift 3 y 4

Establezca la propiedad supportedInterfaceOrientations de UIViewControllers específicos de esta manera:

class MyViewController: UIViewController { var orientations = UIInterfaceOrientationMask.portrait //or what orientation you want override var supportedInterfaceOrientations : UIInterfaceOrientationMask { get { return self.orientations } set { self.orientations = newValue } } override func viewDidLoad() { super.viewDidLoad() } //... }

ACTUALIZAR

Esta solución solo funciona cuando su viewController no está incrustado en UINavigationController , porque la orientación hereda de viewController primario.
Para este caso, puede crear una subclase de UINavigationViewController y establecer estas propiedades en él.


Swift 4

AppDelegate

var orientationLock = UIInterfaceOrientationMask.all func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask { return self.orientationLock } struct AppUtility { static func lockOrientation(_ orientation: UIInterfaceOrientationMask) { if let delegate = UIApplication.shared.delegate as? AppDelegate { delegate.orientationLock = orientation } } static func lockOrientation(_ orientation: UIInterfaceOrientationMask, andRotateTo rotateOrientation:UIInterfaceOrientation) { self.lockOrientation(orientation) UIDevice.current.setValue(rotateOrientation.rawValue, forKey: "orientation") } }

Su ViewController Agregue la siguiente línea si solo necesita orientación vertical. tiene que aplicar esto a todas las necesidades de ViewController para mostrar el modo vertical.

override func viewWillAppear(_ animated: Bool) { AppDelegate.AppUtility.lockOrientation(UIInterfaceOrientationMask.portrait, andRotateTo: UIInterfaceOrientation.portrait) }

y eso hará que la orientación de la pantalla para otros Viewcontroller de acuerdo con la orientación física del dispositivo.

override func viewWillDisappear(_ animated: Bool) { AppDelegate.AppUtility.lockOrientation(UIInterfaceOrientationMask.all) }