web-dev-qa-db-de.com

So sperren Sie die Ausrichtung eines View-Controllers im Hochformat nur in Swift

Da meine App Unterstützung für alle Orientierung erhielt. Ich möchte nur den Portrait-Modus an einen bestimmten UIViewController sperren.

(zB Angenommen, es handelte sich um eine Anwendung mit Registerkarten, und wenn SignIn View modal angezeigt wird, möchte ich nur, dass SignIn View im Hochformatmodus nur Nein ist.

58
Thiha Aung

Wenn Sie eine komplizierte Ansichtshierarchie haben, z. B. mehrere Navigationscontroller und/oder Registerkarten-View-Controller, können die Dinge sehr unübersichtlich werden.

Bei dieser Implementierung müssen die einzelnen Ansichtscontroller festlegen, wann sie Orientierungen sperren möchten, statt sich auf den App-Delegierten zu verlassen, der sie durch das Durchlaufen von Unteransichten sucht.

Swift 3 & 4

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

In einer anderen globalen Struktur- oder Helferklasse habe ich hier AppUtility erstellt:

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

}

Dann möchten Sie im gewünschten ViewController Orientierungen sperren:

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

Wenn iPad oder Universal App

Stellen Sie sicher, dass unter Zieleinstellungen -> Allgemein -> Informationen zur Bereitstellung "Vollbild erforderlich" aktiviert ist. supportedInterfaceOrientationsFor delegate wird nicht aufgerufen, wenn dies nicht markiert ist .  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")
    }
}

Ihr ViewController Fügen Sie folgende Zeile hinzu, wenn Sie nur Hochformat benötigen. Sie müssen dies auf alle ViewController anwenden, die zum Anzeigen des Hochformatmodus erforderlich sind.

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

und .__, die die Bildschirmausrichtung für andere Viewcontroller entsprechend der physischen Ausrichtung des Geräts bestimmt.

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

    }
15
Nahid Raihan

Fügen Sie diesen Code hinzu, um das Portrait zu erzwingen und zu sperren:

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
}

Stellen Sie in Ihrem AppDelegate - unterstütztInterfaceOrientationsForWindow die Ausrichtung ein, die die gesamte Anwendung unterstützen soll:

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

Dies ist eine generische Lösung für Ihr Problem und andere.

1. Erstellen Sie die Zusatzklasse UIHelper und wenden Sie die folgenden Methoden an:

    /**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. Erstellen Sie ein Protokoll mit Ihrem Wunschverhalten, für Ihren speziellen Fall wird ein Porträt angezeigt.

protokollorientierungIsOnlyPortrait {}

Hinweis: Wenn Sie möchten, fügen Sie ihn oben in der UIHelper-Klasse hinzu.

3. Erweitern Sie Ihren View Controller

In Ihrem Fall: 

class Any_ViewController: UIViewController,orientationIsOnlyPortrait {

   ....

}

4. Fügen Sie in der App-Delegatenklasse diese Methode hinzu:  

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

Abschließende Anmerkungen:

  • Wenn Sie mehr Klassen im Hochformat haben, erweitern Sie einfach das Protokoll
  • Wenn Sie möchten, dass sich andere Benutzer von View-Controllern verhalten, erstellen Sie andere Protokolle und folgen Sie derselben Struktur.
  • Dieses Beispiel behebt das Problem mit Orientierungsänderungen nach Push-View-Controllern

Eine Reihe großartiger Antworten in diesem Thread, aber keine entsprach meinen Bedürfnissen. Ich habe eine App mit Registerkarten und Navigationssteuerungen in jeder Registerkarte. Eine Ansicht musste gedreht werden, während die anderen im Hochformat gesperrt werden mussten. Aus irgendeinem Grund hat der Navigationscontroller die Unteransichten nicht richtig angepasst. Eine Lösung (in Swift 3) durch Kombination mit this gefunden, und die Layoutprobleme sind verschwunden. Erstellen Sie die Struktur wie von @bmjohns vorgeschlagen:

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

Dann Unterklasse 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
    }
}

Vergessen Sie nicht, die Klasse des TabBarController im Storyboard in die neu erstellte Unterklasse zu ändern.

4
russdub

So legen Sie die Querformatausrichtung für alle Ansichten Ihrer App fest und erlauben nur eine Ansicht für alle Ausrichtungen (um beispielsweise eine Kamerarolle hinzufügen zu können):

In AppDelegate.Swift:

var adaptOrientation = false

In: didFinishLaunchingWithOptions

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

An anderer Stelle in 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
    }
}

Dann wechseln Sie in der Ansicht zu der Ansicht, die alle Orientierungen haben soll:

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

Das letzte, was Sie ankreuzen sollten, ist Geräteorientierung: - Portrait - Landscape Left - Landscape Right

2
iOS Flow

bmjohns -> Du bist mein Lebensretter. Das ist die einzige funktionierende Lösung (mit der AppUtility struct)

Ich habe diese Klasse erstellt:

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

    }
}

und befolgte deine Anweisungen, und alles funktioniert perfekt für Swift 3 -> xcode Version 8.2.1

1
Stjepan

Neue Erweiterung mit erstellen

import UIKit

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

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

Seit iOS 10 und 11 unterstützt das iPad Slide Over und Split View. Um eine App in der Folien- und Splitansicht zu aktivieren, ist Requires full screenmust deaktiviert. Das heißt, die akzeptierte Antwort kann nicht verwendet werden if die App möchte Slide Over und Split View unterstützen. Erfahren Sie mehr über Apples Adopting Multitasking Enhancements auf dem iPadhier .

Ich habe eine Lösung, die (1) die Deaktivierung von Requires full screen, (2) nur eine Funktion in appDelegate ermöglicht (insbesondere wenn Sie die Ziel-View-Controller nicht ändern möchten/können) und (3) rekursive Aufrufe vermeiden . Keine Hilfsklasse oder Erweiterungen erforderlich.

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

Bearbeiten

@bmjohns weist darauf hin, dass diese Funktion auf dem iPad nicht aufgerufen wird. Ich verifizierte und ja es wurde nicht angerufen. Also habe ich ein wenig mehr getestet und einige Fakten herausgefunden:

  1. Ich habe Requires full screen nicht aktiviert, weil ich die Slide-Over- und Slide-Ansicht auf dem iPad aktivieren möchte. Dazu muss die App in Info.plist alle 4 Orientierungen für das iPad unterstützen: Supported interface orientations (iPad).

Meine App funktioniert auf die gleiche Weise wie Facebook: Auf dem iPhone ist die meiste Zeit im Hochformat gesperrt. Wenn Sie ein Bild im Vollbildmodus anzeigen, können Benutzer die Landschaft für eine bessere Ansicht drehen. Auf dem iPad können Benutzer in beliebigen Ansichtscontrollern in jede Ausrichtung drehen. So sieht die App schön aus, wenn das iPad auf Smart Cover steht (Querformat links).

  1. Damit das iPad application(_:supportedInterfaceOrientationsFor) aufruft, halten Sie in Info.plist nur Porträt für das iPad fest. Die App verliert die Slide Over- und Split View-Fähigkeit. Sie können jedoch die Ausrichtung für jeden Ansichtscontroller an nur einer Stelle sperren oder entsperren, ohne dass die ViewController-Klasse geändert werden muss.

  2. Schließlich wird diese Funktion im Lebenszyklus des View-Controllers aufgerufen, wenn die Ansicht angezeigt/entfernt wird. Wenn Ihre App die Ausrichtung zu einem anderen Zeitpunkt sperren/entsperren/ändern muss, funktioniert sie möglicherweise nicht

1
John Pang

Swift 3 & 4

Legen Sie die Eigenschaft supportedInterfaceOrientations für bestimmte UIViewController wie folgt fest:

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

Hier ist eine einfache Möglichkeit, die für mich mit Swift 4.2 (iOS 12.2) funktioniert. Fügen Sie diese in eine UIViewController ein, für die Sie shouldAutorotate deaktivieren möchten:

override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
    return .portrait
}

Der .portrait-Teil gibt an, in welcher (n) Ausrichtung (en) Sie verbleiben sollen. Sie können dies nach Belieben ändern. Zur Auswahl stehen: .portrait, .all, .allButUpsideDown, .landscape, .landscapeLeft, .landscapeRight, .portraitUpsideDown.

0
GregT

Danke an @bmjohns Antwort oben. Hier ist eine funktionierende Xamarin/C # Version des Codes dieser Antwort, um anderen die Zeit der Transkription zu ersparen:

AppDelegate.cs

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

Statische OrientationUtility.cs-Klasse:

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

View Controller:

    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

Tatsächlich getestete Lösung für dieses. In meinem Beispiel muss meine gesamte App im Hochformatmodus sein, aber nur die Ausrichtung eines Bildschirms sollte im Querformat sein  Make a Portrait orientation for app , by check only portrait mode

Code in AppDelegate wie oben beschrieben. 

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

Notieren Sie sich diesen Code, bevor der ViewController für die Landschaftsorientierung angezeigt wird/Push. 

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

Dann notieren Sie sich diesen Code im aktuellen Viewcontroller (Für Landschaftsansicht) 

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