如何使用Swift锁定主视图的纵向方向?

47
我使用 Swift 创建了一个 iPhone 应用程序,其中有许多嵌入在导航控制器中的视图。我想锁定主视图为竖屏模式,并仅允许导航控制器的子视图以横屏模式锁定。以下是我所说的示例:
  • UINavigationController
    • UiViewController1(锁定为竖屏)初始视图控制器,其上有一个按钮放置在导航栏上,用户可以通过该按钮访问列表,选择其他视图。
    • UIViewController2(锁定为横屏)
    • UiViewController3(支持竖屏和横屏)
    • UiViewController4(支持竖屏和横屏)
    • ...
    • ...
如何实现这个需求呢?

1
请查看此链接:https://dev59.com/nV4b5IYBdhLWcg3wlihP - Pradeep Kashyap
1
看看这个:https://stackoverflow.com/a/45351231/2781088 它是用Objective C编写的,但你会有一个想法。 - Mohit Kumar
16个回答

53

根据 Swift Apple Docs 中有关 supportedInterfaceOrientations 的说明:

讨论

当用户更改设备方向时,系统会在填充窗口的根视图控制器或最上层的呈现视图控制器上调用此方法。如果视图控制器支持新方向,则窗口和视图控制器会旋转到新方向。仅当视图控制器的 shouldAutorotate 方法返回 true 时才会调用此方法。

您的导航控制器应该覆盖 shouldAutorotatesupportedInterfaceOrientations,如下所示。我在一个 UINavigationController 扩展中完成了这个过程以便操作:

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

    public override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
        return (visibleViewController?.supportedInterfaceOrientations())!
    }
}

你的主视图控制器(始终为纵向),应该具备:

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

然后,在您希望支持纵向或横向的子视图控制器中:

override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
    return UIInterfaceOrientationMask.All
}

编辑:已更新至iOS 9 :-)


4
当视图被嵌入到导航控制器中时,我遇到了一个问题。例如,如果我想在同一个导航控制器下有两个视图,一个是竖屏模式,另一个是横屏模式,那么代码就不能正常工作。请帮忙解决。 - Mono.WTF
4
简短提示:在最新的Swift中,toRaw()已被替换为rawValue - ndbroadbent
2
正如Regis上面所述:新的函数签名要求返回UIInterfaceOrientationMask值。这意味着正确的答案现在是:`override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask { return UIInterfaceOrientationMask.Portrait }` - charlieb
2
我认为它在iOS9上不起作用。你能确认一下吗? - Developer
2
Swift 3将这些从func更改为var getters。只需将func更改为var - imaCoden
显示剩余7条评论

24

这里还有一个[链接]提供了解答。

当你的视图层级比较复杂,例如有多个导航控制器和/或选项卡视图控制器时,情况可能会变得非常混乱。

这种实现方式是让各个视图控制器自行设置它们想要锁定方向的时间,而不是依赖于应用程序委托通过迭代子视图或依赖继承来查找它们。

Swift 3

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

在其他全局结构体或辅助类中,我创建了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")
    }

}

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

 override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(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)
}

3
请不要在多个问题中添加相同的答案,请回答最佳问题并将其余标记为重复。请参见是否可以在多个问题中添加重复的答案? - FelixSFD
1
这是我能够轻松找到的最佳答案。这是在Swift 3中对我有效的唯一答案。 - mythicalcoder
@NileshPol 我已经在 iPad 和 iPhone 应用程序中使其工作。你可能需要确保在 plist 中支持所有方向,同时在调试时方法是否按照你的预期被调用? - bmjohns
可能是iOS9和10的问题吗?我的iPad 3运行iOS9,这段代码可以工作,但在运行10.3.1的iPad mini 4上无法工作。 - gbryant
1
@NileshPol 前往“目标” => “常规”,并勾选“需要全屏幕”。它应该可以工作。 - Aximem
显示剩余3条评论

15

如果您的视图在故事板中嵌入了导航控制器,请设置导航控制器代理 UINavigationControllerDelegate 并添加以下方法。

class ViewController: UIViewController, UINavigationControllerDelegate {
    func navigationControllerSupportedInterfaceOrientations(navigationController: UINavigationController) -> UIInterfaceOrientationMask {
        return UIInterfaceOrientationMask.Portrait
    }
}

9

更新:如果在应用程序启动后设置方向时遇到问题,尝试使用ObjC而不是Swift,并使用class MyNavigationController: MyNavigationControllerBase

@implementation ABNavigationControllerBase
- (NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskPortrait;
}
@end

Swift 3:

class MyNavigationController: UINavigationController {
    override func viewDidLoad() {
        super.viewDidLoad()
        UIDevice.current.setValue(UIInterfaceOrientation.portrait.rawValue, forKey: "orientation")
    }
    override var shouldAutorotate: Bool {
        return false
    }
    override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        return .portrait
    }
    override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
        return .portrait
    }
}

唯一对我有效的Swift 3 / iOS 10解决方案。谢谢! - Fox5150

8

Swift 4.2+中回答与iOS 11兼容的相同问题(它可以正常工作)

1- 如下所示覆盖shouldAutorotate和supportedInterfaceOrientations。

extension UINavigationController {

open override var shouldAutorotate: Bool {
    return true
}

open override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
    return (visibleViewController?.supportedInterfaceOrientations)!
    }
}

2- 你的主视图控制器(始终为纵向),应该包含:

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

3- 在你想支持竖屏或横屏的子视图控制器中:

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

7
重要的是在AppDelegate中描述整个应用程序支持的界面方向。例如,如果要将所有视图锁定为竖屏,请执行以下操作:
  func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask{
    return UIInterfaceOrientationMask.portrait
}

很多人通过谷歌搜索来寻找这个答案,所以我希望你能原谅我。

适用于Swift 3.0


非常好!谢谢! - Gilad Brunfman

4

编辑为swift2.0:

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

override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
    return [.Portrait, .PortraitUpsideDown]
}

Swift 4使用 - 覆盖var supportedInterfaceOrientations:UIInterfaceOrientationMask - jpulikkottil

4
在主控制器中,您想使用纵向布局,请按以下步骤操作:
- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

self.orientation = UIInterfaceOrientationMaskPortrait;
//Or self.orientation = UIInterfaceOrientationPortraitUpsideDown
}

在需要横屏的子视图控制器中,使用以下代码:
  self.orientation =  UIInterfaceOrientationLandscapeLeft:
   self.orientation =  UIInterfaceOrientationLandscapeRight:

或者您可以重写这个方法

- (NSUInteger)supportedInterfaceOrientations
{
  return UIInterfaceOrientationMaskPortrait;
} 

这是我在iOS7中使用Obj-c实现的方法,我认为这段代码也适用于iOS8。


我无法访问self.orientation。 - Mono.WTF
我写这段代码时考虑了ios7,如果你正在使用Swift编程,那么一定有相应的等价物。如果是这种情况,请查阅苹果文档。 - Saheb Roy
我能理解你的问题,但是Swift只是Obj-c的包装类,请查看相应的Swift代码以解决这个Obj-c片段的问题。尝试在Swift文档中搜索“屏幕方向”。 - Saheb Roy

3

这里是Swift的更新内容:

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

你能为这个添加一些解释吗?比如在哪里,什么时候,怎么做等等? - user4886069
Swift 4使用 - override var supportedInterfaceOrientations: UIInterfaceOrientationMask - jpulikkottil

3
这是Swift 3(XCode 8.2 beta)的语法,其中这些方法被转换为属性:
extension UINavigationController {
    override open var shouldAutorotate: Bool {
        return false
    }
}

class ViewController: UIViewController {

    /*
            ...
    */

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

1
这会锁定所有屏幕为竖屏。如何仅锁定一个屏幕或特定的屏幕? - mythicalcoder
1
你尝试过为每个单独的ViewController覆盖此方法吗(您可以使用多个)? - nbloqs
1
不,我的问题是如何将上述解决方案适配到一个屏幕上? - mythicalcoder
1
这就是我想回答的:每个屏幕都可以有一个视图控制器,因此在应用程序中的每个视图控制器上以不同方式过载 supportedInterfaceOrientations(我没有尝试过)尝试一下。如果不起作用,您也可以发布一个新问题。 - nbloqs

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接