如何仅在Swift中将一个视图控制器的方向锁定为纵向模式

时间:2015-03-09 09:29:26

标签: swift uiviewcontroller portrait device-orientation

由于我的应用程序获得了所有方向的支持。我想只将肖像模式锁定到特定的UIViewController。

  

(例如,假设它是Tabbed应用程序,当SignIn View以模态方式出现时,我只希望SignIn View只能进入纵向模式   无论用户如何旋转设备或当前设备如何   方向将是)

16 个答案:

答案 0 :(得分:186)

当您拥有复杂的视图层次结构时,例如拥有多个导航控制器和/或制表视图控制器,事情会变得非常混乱。

此实现将它放在各个视图控制器上,以设置何时锁定方向,而不是依靠App Delegate通过迭代子视图来查找它们。

Swift 3& 4

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

在其他一些全局struct或helper类中,我在这里创建了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()
    }

}

然后在想要的ViewController中锁定方向:

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

如果是iPad或通用应用

确保"需要全屏"在目标设置中检查 - >一般 - >部署信息如果未选中,supportedInterfaceOrientationsFor委托将不会被调用。 enter image description here

答案 1 :(得分:16)

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

您的ViewController 如果您只需要纵向方向,请添加以下行。你必须将它应用于所有需要显示肖像模式的ViewController。

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

和 这将根据设备的物理方向为其他ViewController制作屏幕方向。

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

    }

答案 2 :(得分:9)

对于Swift的新版本,请尝试

override var shouldAutorotate: Bool {
    return false
}

override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
    return UIInterfaceOrientationMask.portrait
}

override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
    return UIInterfaceOrientation.portrait
}

答案 3 :(得分:8)

添加此代码以强制纵向并锁定它:

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
}

在AppDelegate中 - 将supportedInterfaceOrientationsForWindow设置为您希望整个应用程序支持的任何方向:

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

答案 4 :(得分:5)

这是针对您的问题和其他相关问题的通用解决方案。

<强> 1。创建辅助类UIHelper并使用以下方法:

    /**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。根据您的愿望行为创建一个协议,因为您的具体情况将是肖像。

protocol orientationIsOnlyPortrait {}

Nota:如果需要,请将其添加到UIHelper类的顶部。

第3。扩展视图控制器

在你的情况下:

class Any_ViewController: UIViewController,orientationIsOnlyPortrait {

   ....

}

<强> 4。在app delegate类中添加此方法:

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

最终备注:

  • 如果您有更多课程处于纵向模式,请延长该课程 协议
  • 如果您希望其他行为来自视图控制器,请创建其他协议并遵循相同的结构。
  • 此示例解决了推送后方向更改的问题 查看控制器

答案 5 :(得分:4)

这个帖子中有很多很棒的答案,但没有一个能满足我的需求。我有一个带标签的应用程序,每个选项卡中都有导航控制器,需要旋转一个视图,而其他视图需要以纵向锁定。由于某种原因,导航控制器没有正确调整其子视图的大小。通过结合this答案找到解决方案(在Swift 3中),布局问题消失了。按@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")
    }
} 

然后继承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
    }
}

不要忘记将故事板中TabBarController的类更改为新创建的子类。

答案 6 :(得分:2)

将横向设置为应用的所有视图&amp;只允许一个视图到所有方向(例如,能够添加相机胶卷):

在AppDelegate.swift中:

var adaptOrientation = false

In:didFinishLaunchingWithOptions

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

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

然后在视图中,您希望能够拥有所有方向:

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

最后一件事是勾选设备方向: - 肖像 - 左侧风景 - 风景正确

答案 7 :(得分:2)

快速3和4

设置特定UIViewController的supportedInterfaceOrientations属性,如下所示:

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

    //...
}

答案 8 :(得分:1)

bmjohns - &gt;你是我生命的救星。这是唯一可行的解​​决方案(使用AppUtility结构)

我创建了这个类:

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

    }
}

并按照您的说明操作,一切都适用于Swift 3 - &gt; xcode版本8.2.1

答案 9 :(得分:1)

从iOS 10和11开始,iPad支持Slide Over和Split View。要在幻灯片放映和拆分视图中启用应用,必须取消选中Requires full screen 。这意味着如果应用程序想要支持幻灯片放映和拆分视图,则无法使用接受的答案。了解更多Apple的在iPad上采用多任务处理增强功能 here

我有一个解决方案,允许(1)取消选中Requires full screen,(2)只需在appDelegate中实施一项功能(特别是如果你不想/可以&#39; t修改目标视图控制器),以及(3)避免递归调用。不需要帮助类也不需要扩展。

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

修改

@bmjohns指出在iPad上没有调用此功能。我验证了,是的,它没有被调用。所以,我做了一些测试,发现了一些事实:

  1. 我取消选中Requires full screen,因为我想在iPad上启用幻灯片放映和幻灯片放映。这要求应用程序在Info.plist中支持iPad的所有4个方向:Supported interface orientations (iPad)
  2. 我的应用程序与Facebook的工作方式相同:在iPhone上,大部分时间它都被锁定为肖像。在全屏查看图像时,允许用户旋转横向以获得更好的视图。在iPad上,用户可以在任何视图控制器中旋转到任何方向。所以,当iPad站在Smart Cover上时,应用程序看起来很不错(左图)。

    1. 要让iPad拨打application(_:supportedInterfaceOrientationsFor),请在Info.plist中保留iPad的肖像。该应用程序将失去Slide Over + Split View功能。但是您可以在一个地方锁定或解锁任何视图控制器的方向,而无需修改ViewController类。

    2. 最后,当显示/删除视图时,在视图控制器的生命周期中调用此函数。如果您的应用需要在其他时间锁定/解锁/更改方向,则可能无效

答案 10 :(得分:1)

使用

创建新扩展程序
import UIKit

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

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

答案 11 :(得分:0)

感谢@ bmjohn的回答。这是一个有效的 Xamarin / C#版本的答案代码,以节省其他人的转录时间:

<强> AppDelegate.cs

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

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

查看控制器:

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

答案 12 :(得分:0)

为此目的经过实际测试的解决方案。在我的示例中,我需要整个应用程序都处于纵向模式,但只有一个屏幕的方向应处于横向模式。 Make a Portrait orientation for app , by check only portrait mode

如上所述的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")
}  
}

然后记下此代码,然后再显示/推动您的横向视图控制器。

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

然后在实际的viewcontroller(用于横向视图)中写下此代码

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

答案 13 :(得分:0)

这是一种适用于我的Swift 4.2(iOS 12.2)的简单方法,将其放在您要禁用shouldAutorotate的UIViewController中:

override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
    return .portrait
}

.portrait部分告诉它要保留的方向,您可以根据需要进行更改。选项包括:.portrait.all.allButUpsideDown.landscape.landscapeLeft.landscapeRight.portraitUpsideDown

答案 14 :(得分:0)

我做了一些实验,设法找到了解决此问题的解决方案。 该方法基于通过view-> tag

进行的视图标记

在目标ViewController中,只需将标签分配给根视图即可,如以下代码示例所示:

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

最后在AppDelegate.swift中,检查当前显示的视图是否是我们标记的视图:

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

这种方法已经在我的模拟器上进行了测试,似乎效果很好。

注意:如果当前MVC与导航堆栈中的某些子级ViewController重叠,也会找到标记的视图。

答案 15 :(得分:0)

锁定和更改人像和风景方向的最佳解决方案:

在YouTube上观看此视频:

https://m.youtube.com/watch?v=4vRrHdBowyo

本教程是最佳和简单的。

或使用以下代码:

See this picture

// 1-在第二个视图控制器中,我们设置了landscapeleft,在第一个视图控制器中,我们设置了portrat:

// 2-如果使用NavigationController,则应添加扩展名

import UIKit

class SecondViewController: UIViewController {


    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        UIDevice.current.setValue(UIInterfaceOrientation.landscapeLeft.rawValue, forKey: "orientation")
    }

    override open var shouldAutorotate: Bool {
        return false
    }

    override open var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        return .landscapeLeft
    }

    override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
        return .landscapeLeft
    }
    
    

    override func viewDidLoad() {
        super.viewDidLoad()
    }

//write The rest of your code in here


}

//if you use NavigationController, you should add this extension

extension UINavigationController {
    override open var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        return topViewController?.supportedInterfaceOrientations ?? .allButUpsideDown
    
    }
}