web-dev-qa-db-fra.com

Comment verrouiller l’orientation d’un contrôleur de vue en mode portrait uniquement dans Swift

Depuis que mon application a reçu un soutien pour toutes les orientations. Je voudrais verrouiller uniquement le mode portrait sur UIViewController spécifique.

(Par exemple, supposons qu'il s'agisse d'une application à onglets et que lorsque SignIn View apparaisse de manière modale, je souhaite uniquement que SignIn View soit en mode portrait uniquement no quelle que soit la rotation de l'utilisateur ou l'orientation actuelle du périphérique )

58
Thiha Aung

Lorsque vous avez une hiérarchie de vues complexe, les choses peuvent devenir compliquées, comme avoir plusieurs contrôleurs de navigation et/ou des contrôleurs de vue par onglet.

Cette implémentation impose aux contrôleurs de vue individuels de définir quand ils souhaitent verrouiller les orientations, au lieu de compter sur le délégué de l'application pour les rechercher en effectuant une itération dans des sous-vues.

Swift 3 & 4

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

Dans une autre classe globale de struct ou helper, j'ai créé ici 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()
    }

}

Ensuite, dans le ViewController souhaité, vous souhaitez verrouiller les orientations:

 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 ou application universelle

Assurez-vous que l'option "Nécessite le plein écran" est cochée dans Paramètres de la cible -> Général -> Informations de déploiement. supportedInterfaceOrientationsFor délégué ne sera pas appelé si cela n'est pas coché .  enter image description here

171
bmjohns

Swift 4

 enter image description here 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")
    }
}

Votre ViewController Ajouter la ligne suivante si vous n’avez besoin que d’une orientation portrait. vous devez appliquer cela à tous les ViewController nécessaires pour afficher le mode portrait.

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

et qui orientera d’autres écrans Viewcontroller en fonction de l’orientation physique du périphérique.

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

    }
15
Nahid Raihan

Ajoutez ce code pour forcer le portrait et le verrouiller:

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
}

Dans votre AppDelegate - définissez supportedInterfaceOrientationsForWindow sur les orientations que vous souhaitez que l'application entière prenne en charge:

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

Ceci est une solution générique pour votre problème et d'autres liés.

1. Créez la classe auxiliar UIHelper et appliquez les méthodes suivantes:

    /**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. Créez un protocole avec votre comportement souhaité, pour votre cas spécifique sera portrait.

protocole orientationIsOnlyPortrait {}

Note: Si vous voulez, ajoutez-le en haut de UIHelper Class.

3. Étendez votre contrôleur de vue

Dans ton cas: 

class Any_ViewController: UIViewController,orientationIsOnlyPortrait {

   ....

}

4. Dans la classe de délégué d'application, ajoutez cette méthode:  

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

Notes finales:

  • Si vous êtes plus en mode portrait, étendez simplement le protocole
  • Si vous souhaitez que d'autres contrôleurs d'affichages se comportent, créez d'autres protocoles et suivez la même structure.
  • Cet exemple résout le problème des changements d’orientation après l’affichage des contrôleurs de vue
4

Un tas de bonnes réponses dans ce fil, mais aucune ne correspondait vraiment à mes besoins. J'ai une application à onglets avec des contrôleurs de navigation dans chaque onglet, et une vue doit pivoter, tandis que les autres doivent être verrouillées en mode portrait. Le contrôleur de navigation ne redimensionnait pas correctement ses sous-vues, pour une raison quelconque. Nous avons trouvé une solution (dans Swift 3) en combinant avec ceci réponse, et les problèmes de mise en page ont disparu. Créez la structure comme suggéré par @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")
    }
} 

Ensuite, sous-classe 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
    }
}

N'oubliez pas de changer la classe de TabBarController dans le storyboard pour la nouvelle sous-classe.

4
russdub

Pour définir l'orientation Paysage sur toutes les vues de votre application et n'autoriser qu'une seule vue sur Toutes les orientations (pour pouvoir ajouter une pellicule, par exemple):

Dans AppDelegate.Swift:

var adaptOrientation = false

Dans: didFinishLaunchingWithOptions

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

Ailleurs dans 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
    }
}

Puis dans la vue qui se sépare de celle que vous voulez pouvoir avoir Toutes les orientations:

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

La dernière chose à faire est de cocher Orientation de l'appareil: - Portrait - Paysage gauche - Paysage droite

2
iOS Flow

bmjohns -> Tu es mon sauveur de vie. C’est la seule solution qui fonctionne (avec la structure AppUtility)

J'ai créé cette classe:

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

    }
}

et suivi vos instructions, et tout fonctionne parfaitement pour Swift 3 -> xcode version 8.2.1

1
Stjepan

Créer une nouvelle extension avec

import UIKit

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

extension UITabBarController {
    override open var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        return .portrait
    }
}
1
Felipe Kimio

À partir d'iOS 10 et 11, l'iPad prend en charge les fonctions glisser sur et fractionnées. Pour activer une application en mode Diapositive et fractionnée, désélectionnez Requires full screendoit être. Cela signifie que la réponse acceptée ne peut pas être utilisée if l'application veut prendre en charge le mode de glissement et d'affichage fractionné. En savoir plus sur Apple Adopter des améliorations du multitâche sur iPadici .

J'ai une solution qui permet (1) de décocher Requires full screen, (2) une seule fonction à implémenter dans appDelegate (surtout si vous ne voulez pas/ne pouvez pas modifier les contrôleurs de vue cibles) et (3) éviter les appels récursifs. . Pas besoin de classe d'assistance ni d'extensions.

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

Modifier

@bmjohns a souligné que cette fonction n'est pas appelée sur iPad. J'ai vérifié et oui il n'a pas été appelé. J'ai donc fait un peu plus de tests et découvert quelques faits:

  1. J'ai décoché Requires full screen parce que je veux activer le mode glisser sur et le mode diapositive sur iPad. Cela nécessite que l'application prenne en charge les 4 orientations pour iPad, dans Info.plist: Supported interface orientations (iPad).

Mon application fonctionne de la même manière que Facebook: sur iPhone, elle est généralement verrouillée en mode portrait. Lors de l'affichage d'images en plein écran, permet aux utilisateurs de faire pivoter le paysage pour une meilleure vue. Sur iPad, les utilisateurs peuvent faire pivoter n’importe quelle orientation dans n’importe quel contrôleur de vue. Ainsi, l'application a l'air agréable lorsque l'iPad est installé sur Smart Cover (paysage à gauche).

  1. Pour que l'iPad appelle application(_:supportedInterfaceOrientationsFor), dans Info.plist, conservez uniquement portrait pour iPad. L'application perdra la capacité Slide Over + Split View. Mais vous pouvez verrouiller ou déverrouiller l'orientation de n'importe quel contrôleur de vue, en un seul endroit et sans avoir à modifier la classe ViewController.

  2. Enfin, cette fonction est appelée sur le cycle de vie du contrôleur de vue, lorsque la vue est affichée/supprimée. Si votre application doit verrouiller/déverrouiller/changer d'orientation ultérieurement, cela pourrait ne pas fonctionner.

1
John Pang

Swift 3 & 4

Définissez la propriété supportedInterfaceOrientations de UIViewControllers spécifique comme ceci:

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

    //...
}
1
Arash Etemad

Voici un moyen simple qui fonctionne pour moi avec Swift 4.2 (iOS 12.2), mettez ceci dans une UIViewController pour laquelle vous souhaitez désactiver devraitAutorotate:

override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
    return .portrait
}

La partie .portrait indique dans quelle orientation rester, vous pouvez changer cela à votre guise. Les choix sont les suivants: .portrait, .all, .allButUpsideDown, .landscape, .landscapeLeft, .landscapeRight, .portraitUpsideDown.

0
GregT

Merci à la réponse de @ bmjohn ci-dessus. Voici une version Xamarin/C # de travail du code de cette réponse, permettant aux autres de gagner du temps lors de la transcription:

AppDelegate.cs

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

Classe statique OrientationUtility.cs:

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

Contrôleur de vue:

    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);
    }
0
ccs_dev

Solution actuellement testée pour ce problème .Dans mon exemple, j'ai besoin que toute mon application soit en mode portrait, mais l'orientation d'un seul écran doit être en mode paysage .  Make a Portrait orientation for app , by check only portrait mode

Code dans AppDelegate comme décrit ci-dessus. 

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

Ensuite, écrivez ce code avant que votre contrôleur de vue d'orientation paysage ne soit présenté/Appuyez. 

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

Puis écrivez ce code dans viewcontroller réel (Pour une vue paysage) 

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