如何在 iOS 8 中强制视图控制器方向?

Posted

技术标签:

【中文标题】如何在 iOS 8 中强制视图控制器方向?【英文标题】:How to force view controller orientation in iOS 8? 【发布时间】:2014-12-09 00:45:34 【问题描述】:

ios 8 之前,我们将以下代码与 supportedInterfaceOrientationsshouldAutoRotate 委托方法结合使用,以强制应用程序方向为任何特定方向。我使用下面的代码 sn-p 以编程方式将应用程序旋转到所需的方向。首先,我正在更改状态栏方向。然后只需呈现并立即关闭模态视图即可将视图旋转到所需的方向。

[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeRight animated:YES];
UIViewController *c = [[UIViewController alloc]init];
[self presentViewController:vc animated:NO completion:nil];
[self dismissViewControllerAnimated:NO completion:nil];

但这在 iOS 8 中失败了。另外,我在堆栈溢出中看到了一些答案,人们建议我们应该从 iOS 8 开始避免这种方法。

更具体地说,我的应用程序是一种通用类型的应用程序。一共有三个控制器。

    First View 控制器- 它应该支持 iPad 中的所有方向,并且只支持 iPhone 中的纵向(主页按钮向下)。

    第二视图控制器- 它应该在所有情况下都只支持横向

    第三视图控制器- 它应该在所有条件下仅支持横向

我们正在使用导航控制器进行页面导航。从第一个视图控制器,在按钮单击操作中,我们将第二个压入堆栈。因此,当第二个视图控制器到达时,无论设备方向如何,应用都应该只锁定横向。

下面是我在第二个和第三个视图控制器中的shouldAutorotatesupportedInterfaceOrientations 方法。

-(NSUInteger)supportedInterfaceOrientations
    return UIInterfaceOrientationMaskLandscapeRight;


-(BOOL)shouldAutorotate 
    return NO;

是否有任何解决方案或任何更好的方法来锁定 iOS 8 的特定方向的视图控制器。请帮助!!

【问题讨论】:

实现您在介绍的 VC 中提到的方法通常应该可以工作(至少这是我在iOS 8 中的经验)。也许您的特定设置会导致问题? 也许我可以让这个问题更清楚一点。我会稍微修改一下这个问题。 @VladimirGritsenko:请立即查看。我已经编辑过了。 问题中的代码不使用导航控制器的堆栈,而是使用模态表示,所以仍然不清楚你在做什么。我会说,在我们的代码中,从 shouldAutoRotate 返回 YES 并从 presented VC 中的 supportedInterfaceOrientations 返回所需的方向正确地定位了该 VC。 嗯,这并不是真正的失败,这只是概念上的重大变化。是否是好的改变完全是另一个话题。 【参考方案1】:

对于 iOS 7 - 10:

目标-C:

[[UIDevice currentDevice] setValue:@(UIInterfaceOrientationLandscapeLeft) forKey:@"orientation"];
[UINavigationController attemptRotationToDeviceOrientation];

斯威夫特 3:

let value = UIInterfaceOrientation.landscapeLeft.rawValue
UIDevice.current.setValue(value, forKey: "orientation")
UINavigationController.attemptRotationToDeviceOrientation()

只需在呈现的视图控制器的- viewDidAppear: 中调用它即可。

【讨论】:

这是不是私有 API。这是对 API 的不安全使用。您没有使用任何私有方法,而是使用“内部事物”。如果 Apple 更改“方向”的值,它会失败。更糟糕的是:如果 Apple 改变了“orientation”值的含义,你不知道你在改变什么硬设置值。 要禁用动画,请将[UIView setAnimationsEnabled:NO]; 放入viewWillAppear[UIView setAnimationsEnabled:YES]; 放入viewDidAppear,相关:***.com/a/6292506/88597 不错的 hack,但问题在于,在设置新的方向值后没有触发 didRotateFromInterfaceOrientation(叹气) 在 iOS 9.2 中,这只是改变方向而不是锁定。 对于每个人在 90% 的时间里表现良好而在另外 10% 的时间里有问题,请在设置方向键后调用:[UINavigationController attemptRotationToDeviceOrientation];【参考方案2】:

如果您在UINavigationControllerUITabBarController 内,方向旋转会稍微复杂一些。问题在于,如果视图控制器嵌入在这些控制器之一中,则导航或标签栏控制器具有优先权,并决定自动旋转和支持的方向。

我在 UINavigationController 和 UITabBarController 上使用了以下 2 个扩展,以便嵌入在其中一个控制器中的视图控制器做出决定。

赋予视图控制器权力!

斯威夫特 2.3

extension UINavigationController 
    public override func supportedInterfaceOrientations() -> Int 
        return visibleViewController.supportedInterfaceOrientations()
    
    public override func shouldAutorotate() -> Bool 
        return visibleViewController.shouldAutorotate()
    


extension UITabBarController 
    public override func supportedInterfaceOrientations() -> Int 
        if let selected = selectedViewController 
            return selected.supportedInterfaceOrientations()
        
        return super.supportedInterfaceOrientations()
    
    public override func shouldAutorotate() -> Bool 
        if let selected = selectedViewController 
            return selected.shouldAutorotate()
        
        return super.shouldAutorotate()
    

斯威夫特 3

extension UINavigationController 
    open override var supportedInterfaceOrientations: UIInterfaceOrientationMask 
        return visibleViewController?.supportedInterfaceOrientations ?? super.supportedInterfaceOrientations
    

    open override var shouldAutorotate: Bool 
        return visibleViewController?.shouldAutorotate ?? super.shouldAutorotate
    


extension UITabBarController 
    open override var supportedInterfaceOrientations: UIInterfaceOrientationMask 
        if let selected = selectedViewController 
            return selected.supportedInterfaceOrientations
        
        return super.supportedInterfaceOrientations
    

    open override var shouldAutorotate: Bool 
        if let selected = selectedViewController 
            return selected.shouldAutorotate
        
        return super.shouldAutorotate
    

现在您可以覆盖supportedInterfaceOrientations 方法,或者您可以在要锁定的视图控制器中覆盖shouldAutoRotate,否则您可以省略其他视图控制器中的覆盖,您希望继承在中指定的默认方向行为您应用的 plist

禁用旋转

class ViewController: UIViewController 
    override func shouldAutorotate() -> Bool 
        return false
    

锁定特定方向

class ViewController: UIViewController 
    override func supportedInterfaceOrientations() -> Int 
        return Int(UIInterfaceOrientationMask.Landscape.rawValue)
    

理论上这应该适用于所有复杂的视图控制器层次结构,但我注意到 UITabBarController 存在问题。出于某种原因,它想使用默认方向值。如果您有兴趣了解如何解决某些问题,请参阅以下博客文章:

Lock Screen Rotation

【讨论】:

我知道这是一篇很老的帖子,但你会把扩展代码放在哪里? 在 Objective-C 中会是什么样子? 这是 Swift 和 ObjC 的一个很好的解决方案。不明白为什么人们拒绝投票。你明白了,然后编写代码很容易。为什么抱怨? @Krodak ,在扩展中尝试将“-> Int”更改为“-> UIInterfaceOrientationMask”。对我有用。 这个想法和代码是完美的,我喜欢它,但是在使用 UINavigationController 时存在一个问题,从横向显示的 UIViewController 回到必须以纵向显示的 UIViewController。有什么想法吗?【参考方案3】:

这对我有用:

https://developer.apple.com/library//ios/documentation/UIKit/Reference/UIViewController_Class/index.html#//apple_ref/occ/clm/UIViewController/attemptRotationToDeviceOrientation

在你的 viewDidAppear: 方法中调用它。

- (void) viewDidAppear:(BOOL)animated

    [super viewDidAppear:animated];

    [UIViewController attemptRotationToDeviceOrientation];

【讨论】:

非常有用,如果设备已经旋转并且您需要旋转视图控制器 很好的答案。当与接受的答案结合使用时,这对我每次在 Swift 和 iOS 9+ 中都很有效。 虽然我认为这不是问题的答案,但它确实帮助了我。【参考方案4】:

我发现如果它是一个呈现的视图控制器,你可以覆盖preferredInterfaceOrientationForPresentation

斯威夫特:

override func supportedInterfaceOrientations() -> Int 
  return Int(UIInterfaceOrientationMask.Landscape.rawValue)


override func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation 
  return UIInterfaceOrientation.LandscapeLeft


override func shouldAutorotate() -> Bool 
  return false

【讨论】:

似乎接受的答案只是触发了横向方向的更改。您在此处展示的是如何强制 视图控制器 处于横向状态,而不影响呈现视图控制器(这正是我所需要的)。谢谢。 @CliftonLabrum 你有没有做其他事情来强制only横向?使用相同的代码,视图控制器仍然能够旋转到另一个方向。 我后来意识到——你是对的。我还没有找到解决办法。 这对我有用。通过使用此代码(将横向替换为纵向),我能够将单个视图控制器保持在纵向模式。 +1 是的!这在 Xcode 7.3 和 Swift 2.2 中也适用于我。但仍需在 AppDelegate 中为supportedInterfaceOrientationsForWindow 定义func 应用程序。【参考方案5】:

这种方式适用于 Swift 2 iOS 8.x:

PS(此方法不需要覆盖每个 viewController 上的方向函数,如 shouldautorotate,只需 AppDelegate 上的一个方法)

选中项目常规信息中的“需要全屏”。

所以,在 AppDelegate.swift 上创建一个变量:

var enableAllOrientation = false

所以,把这个函数也放进去:

func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask 
        if (enableAllOrientation == true)
            return UIInterfaceOrientationMask.All
        
        return UIInterfaceOrientationMask.Portrait

因此,在您项目的每个类中,您都可以在 viewWillAppear 中设置此 var:

override func viewWillAppear(animated: Bool)

        super.viewWillAppear(animated)
        let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
        appDelegate.enableAllOrientation = true

如果您需要根据设备类型做出选择,您可以这样做:

override func viewWillAppear(animated: Bool)
    
        super.viewWillAppear(animated)
        let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
        switch UIDevice.currentDevice().userInterfaceIdiom 
        case .Phone:
        // It's an iPhone
           print(" - Only portrait mode to iPhone")
           appDelegate.enableAllOrientation = false
        case .Pad:
        // It's an iPad
           print(" - All orientation mode enabled on iPad")
           appDelegate.enableAllOrientation = true
        case .Unspecified:
        // Uh, oh! What could it be?
           appDelegate.enableAllOrientation = false
        
    

【讨论】:

很好的解释【参考方案6】:

首先 - 这是一个坏主意,一般来说,你的应用架构出了点问题,但是,sh..t happens,如果是这样,你可以尝试像下面这样:

final class OrientationController 

    static private (set) var allowedOrientation:UIInterfaceOrientationMask = [.all]

    // MARK: - Public

    class func lockOrientation(_ orientationIdiom: UIInterfaceOrientationMask) 
        OrientationController.allowedOrientation = [orientationIdiom]
    

    class func forceLockOrientation(_ orientation: UIInterfaceOrientation) 
        var mask:UIInterfaceOrientationMask = []
        switch orientation 
            case .unknown:
                mask = [.all]
            case .portrait:
                mask = [.portrait]
            case .portraitUpsideDown:
                mask = [.portraitUpsideDown]
            case .landscapeLeft:
                mask = [.landscapeLeft]
            case .landscapeRight:
                mask = [.landscapeRight]
        

        OrientationController.lockOrientation(mask)

        UIDevice.current.setValue(orientation.rawValue, forKey: "orientation")
    

比,AppDelegate

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool 
    // do stuff
    OrientationController.lockOrientation(.portrait)
    return true


// MARK: - Orientation

func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask 
    return OrientationController.allowedOrientation

每当您想改变方向时,请执行以下操作:

OrientationController.forceLockOrientation(.landscapeRight)

注意:有时,设备可能无法从此类调用中更新,因此您可能需要执行以下操作

OrientationController.forceLockOrientation(.portrait)
OrientationController.forceLockOrientation(.landscapeRight)

就是这样

【讨论】:

【参考方案7】:

这应该适用于 iOS 6 以上,但我只在 iOS 8 上测试过。子类 UINavigationController 并覆盖以下方法:

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation 
    return UIInterfaceOrientationLandscapeRight;


- (BOOL)shouldAutorotate 
    return NO;

或者询问可见的视图控制器

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation 
    return self.visibleViewController.preferredInterfaceOrientationForPresentation;


- (BOOL)shouldAutorotate 
    return self.visibleViewController.shouldAutorotate;

并在那里实现方法。

【讨论】:

我刚刚在运行 iOS 10.0 的 Xcode 8.2.1 中的应用程序上对此进行了测试,它可以正常工作。我的游戏菜单是第一个屏幕并且不会旋转;所有其他视图旋转。完美的!给它一个。我只需要 Mojo66 提供的 'preferredInterfaceOrientationForPresentation' 函数。【参考方案8】:

这是对Sid Shah's answer 中的 cmets 的反馈,关于如何使用以下方法禁用动画:

[UIView setAnimationsEnabled:enabled];

代码:

- (void)viewWillAppear:(BOOL)animated 
    [super viewWillAppear:NO];
    [UIView setAnimationsEnabled:NO];

    // *** #26357162 to force orientation
    NSNumber *value = [NSNumber numberWithInt:UIInterfaceOrientationLandscapeRight];
    [[UIDevice currentDevice] setValue:value forKey:@"orientation"];


- (void)viewDidAppear:(BOOL)animated 
    [super viewDidAppear:NO];
    [UIView setAnimationsEnabled:YES];

【讨论】:

如何将其用于肖像。【参考方案9】:

如果您使用的是 navigationViewController,您应该为此创建自己的超类并覆盖:

- (BOOL)shouldAutorotate 
  id currentViewController = self.topViewController;

  if ([currentViewController isKindOfClass:[SecondViewController class]])
    return NO;

  return YES;

这将禁用 SecondViewController 中的旋转,但如果您在设备处于纵向时按下 SecondViewController,那么您的 SecondViewController 将纵向显示模式。

假设您正在使用情节提要。您必须创建手动 segue (How to) 并在您的“onClick”方法中:

- (IBAction)onPlayButtonClicked:(UIBarButtonItem *)sender 
  NSNumber *value = [NSNumber numberWithInt:UIInterfaceOrientationLandscapeLeft];
  [[UIDevice currentDevice] setValue:value forKey:@"orientation"];
  [self performSegueWithIdentifier:@"PushPlayerViewController" sender:self];

这将在您的超类禁用自动旋转功能之前强制横向。

【讨论】:

【参考方案10】:

Xcode 8 上,方法将转换为属性,因此以下适用于Swift

override public var supportedInterfaceOrientations: UIInterfaceOrientationMask 
    return UIInterfaceOrientationMask.portrait


override public var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation 
    return UIInterfaceOrientation.portrait


override public var shouldAutorotate: Bool 
    return true

【讨论】:

这会锁定方向吗? @bnussey 我想防止自动旋转并始终处于portrait 模式,无论 iPad 上的方向如何,shouldAutorotate 返回 true 我实现了这一点。【参考方案11】:

我尝试了很多解决方案,但最有效的解决方案如下:

在 ios 8 和 9 中无需编辑 info.plist

- (BOOL) shouldAutorotate 
    return NO;
   

- (UIInterfaceOrientationMask)supportedInterfaceOrientations 
    return (UIInterfaceOrientationPortrait | UIInterfaceOrientationPortraitUpsideDown);

Possible orientations from the Apple Documentation:

UIInterfaceOrientationUnknown

无法确定设备的方向。

UIInterfaceOrientationPortrait

设备处于纵向模式,设备直立, 底部的主页按钮。

UIInterfaceOrientationPortraitUpsideDown

设备处于纵向模式但上下颠倒,设备被握住 直立和顶部的主页按钮。

UIInterfaceOrientationLandscapeLeft

设备处于横向模式,设备保持直立, 左侧的主页按钮。

UIInterfaceOrientationLandscapeRight

设备处于横向模式,设备保持直立, 右侧的主页按钮。

【讨论】:

【参考方案12】:

[iOS9+] 如果有人因为上述解决方案都不起作用而一直拖到这里, 如果你展示你想要改变方向的视图 通过segue,您可能想检查一下。

检查您的 segue 的演示文稿类型。 我的是“在当前情况下”。 当我将其更改为全屏时,它起作用了。

感谢@GabLeRoux,我找到了这个解决方案。

此更改仅在与上述解决方案结合使用时才有效。

【讨论】:

【参考方案13】:

Sids 和 Koreys 的答案组合对我有用。

扩展导航控制器:

extension UINavigationController 
    public override func shouldAutorotate() -> Bool 
        return visibleViewController.shouldAutorotate()
    

然后在单个视图上禁用旋转

class ViewController: UIViewController 
    override func shouldAutorotate() -> Bool 
        return false
    

在segue之前旋转到合适的方向

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) 
    if (segue.identifier == "SomeSegue")
    
        let value = UIInterfaceOrientation.Portrait.rawValue;
        UIDevice.currentDevice().setValue(value, forKey: "orientation")
    

【讨论】:

你知道这是否有助于解决我的问题,即显示的视图控制器在旋转到纵向之前处于横向状态一秒钟,就像它应该总是那样?问题在这里:***.com/questions/30693964/…【参考方案14】:

上面的***解决方案:

let value = UIInterfaceOrientation.LandscapeLeft.rawValue
UIDevice.currentDevice().setValue(value, forKey: "orientation")

当我在呈现的视图控制器的 viewDidAppear 中调用它时,它没有为我工作。但是,当我在呈现视图控制器的 preparForSegue 中调用它时,它确实起作用了。

(对不起,没有足够的声誉点来评论该解决方案,所以我不得不这样添加)

【讨论】:

我已经完成了这项工作,但是当从横向视图控制器转到纵向视图控制器时,应该以纵向显示的视图控制器在自行旋转之前简要地以横向显示。如果有人知道如何帮助我的问题在这里:***.com/questions/30693964/… @dwinnbrown at viewDidAppear 视图控制器已经可见。也许尝试从 viewDidLoad 代替? @Crashalot 我现在已经解决了这个问题。请查看我标记为正确的答案。 @dwinnbrown 您问题的链接已失效。更新?谢谢! @Crashalot 社区删除了它,但我已投票取消删除它。我会尽快通知你【参考方案15】:

我的要求是

    以纵向模式锁定所有视图 使用AVPlayerViewController播放视频

播放视频时,如果是横向,则允许屏幕向右横向旋转和向左横向旋转。如果是纵向,则仅将视图锁定为纵向模式。

首先,在AppDelegate.swift中定义supportedInterfaceOrientationsForWindow

var portrait = true
func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask 
    if portrait 
        return .Portrait
     else 
        return .Landscape
    

其次,在您的主视图控制器中,定义以下函数

override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask 
    print("\(#function)")
    return .Portrait


override func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation 
    return .Portrait


override func shouldAutorotate() -> Bool 
    return false

那么,你需要继承AVPlayerViewController

class MyPlayerViewController: AVPlayerViewController 

    var size: CGSize?

    var supportedOrientationMask: UIInterfaceOrientationMask?
    var preferredOrientation: UIInterfaceOrientation?

    override func viewDidLoad() 
        super.viewDidLoad()

        if let size = size 
            if size.width > size.height 
                self.supportedOrientationMask =[.LandscapeLeft,.LandscapeRight]
                self.preferredOrientation =.LandscapeRight
             else 
                self.supportedOrientationMask =.Portrait
                self.preferredOrientation =.Portrait
            
        
    

MyPlayerViewController.swift中重写这三个函数

override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask 
    return self.supportedOrientationMask!


override func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation 
    return self.preferredOrientation!

由于用户可能会向左或向右旋转设备横向,我们需要将自动旋转设置为 true

override func shouldAutorotate() -> Bool 
    return true

最后,在您的视图控制器中创建MyPlayerViewController 实例并设置属性大小值。

let playerViewController = MyPlayerViewController()

// Get the thumbnail  
let thumbnail = MyAlbumFileManager.sharedManager().getThumbnailFromMyVideoMedia(......)

let size = thumbnail?.size
playerViewController.size = size

使用正确的videoUrl 启动您的播放器,然后将您的播放器分配给playerViewController。编码愉快!

【讨论】:

【参考方案16】:
- (void)viewDidAppear:(BOOL)animated

    [super viewDidAppear:animated];
    [UIViewController attemptRotationToDeviceOrientation];

【讨论】:

【参考方案17】:

似乎仍然存在一些关于如何最好地完成这项任务的争论,所以我想我会分享我的(工作)方法。在您的 UIViewController 实现中添加以下代码:

- (void) viewWillAppear:(BOOL)animated

    [UIViewController attemptRotationToDeviceOrientation];


- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation

    return (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft);


-(BOOL)shouldAutorotate

    return NO;


- (UIInterfaceOrientationMask)supportedInterfaceOrientations

    return UIInterfaceOrientationMaskLandscapeLeft;

对于此示例,您还需要在项目设置中(或直接在info.plist)中将允许的设备方向设置为“横向左”。如果您想要LandscapeLeft.以外的其他内容,只需更改您想要强制的特定方向

对我来说,关键是 viewWillAppear 中的 attemptRotationToDeviceOrientation 调用 - 如果没有实际旋转设备,视图将无法正确旋转。

【讨论】:

不推荐使用的方法。【参考方案18】:

根据Korey Hinton's answer

斯威夫特 2.2:

extension UINavigationController 
    public override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask 
        return visibleViewController!.supportedInterfaceOrientations()
    
    public override func shouldAutorotate() -> Bool 
        return visibleViewController!.shouldAutorotate()
    



extension UITabBarController 
    public override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask 
        if let selected = selectedViewController 
            return selected.supportedInterfaceOrientations()
        
        return super.supportedInterfaceOrientations()
    
    public override func shouldAutorotate() -> Bool 
        if let selected = selectedViewController 
            return selected.shouldAutorotate()
        
        return super.shouldAutorotate()
    

禁用旋转

override func shouldAutorotate() -> Bool 
    return false

锁定到特定方向

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

【讨论】:

【参考方案19】:

我在这里尝试了一些解决方案,重要的是要理解的是根视图控制器将确定它是否会旋转。

我创建了以下 objective-c 项目github.com/GabLeRoux/RotationLockInTabbedViewChild,其中一个工作示例为TabbedViewController,其中一个子视图允许旋转,另一个子视图锁定为纵向。

它并不完美,但它可以工作,同样的想法应该适用于其他类型的根视图,例如NavigationViewController。 :)

【讨论】:

【参考方案20】:

根据@sid-sha 显示的解决方案,您必须将所有内容放入viewDidAppear: 方法中,否则您将不会触发didRotateFromInterfaceOrientation:,因此类似于:

- (void)viewDidAppear:(BOOL)animated 
    [super viewDidAppear:animated];
    UIInterfaceOrientation interfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation];
    if (interfaceOrientation == UIInterfaceOrientationLandscapeLeft ||
        interfaceOrientation == UIInterfaceOrientationLandscapeRight) 
        NSNumber *value = [NSNumber numberWithInt:interfaceOrientation];
        [[UIDevice currentDevice] setValue:value forKey:@"orientation"];
    

【讨论】:

【参考方案21】:

我的解决方案

AppDelegate:

func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask 
    if let topController = UIViewController.topMostViewController() 
        if topController is XXViewController 
            return [.Portrait, .LandscapeLeft]
        
    
    return [.Portrait]

XXViewController 是您要支持横向模式的 ViewController。

那么Sunny Shah's solution 可以在你的XXViewController 在任何iOS 版本上工作:

let value = UIInterfaceOrientation.LandscapeLeft.rawValue
UIDevice.currentDevice().setValue(value, forKey: "orientation")

这是查找最顶层 ViewController 的实用函数。

extension UIViewController 

    /// Returns the current application's top most view controller.
    public class func topMostViewController() -> UIViewController? 
        let rootViewController = UIApplication.sharedApplication().windows.first?.rootViewController
        return self.topMostViewControllerOfViewController(rootViewController)
    



    /// Returns the top most view controller from given view controller's stack.
    class func topMostViewControllerOfViewController(viewController: UIViewController?) -> UIViewController? 
        // UITabBarController
        if let tabBarController = viewController as? UITabBarController,
           let selectedViewController = tabBarController.selectedViewController 
            return self.topMostViewControllerOfViewController(selectedViewController)
        

        // UINavigationController
        if let navigationController = viewController as? UINavigationController,
           let visibleViewController = navigationController.visibleViewController 
            return self.topMostViewControllerOfViewController(visibleViewController)
        

        // presented view controller
        if let presentedViewController = viewController?.presentedViewController 
            return self.topMostViewControllerOfViewController(presentedViewController)
        

        // child view controller
        for subview in viewController?.view?.subviews ?? [] 
            if let childViewController = subview.nextResponder() as? UIViewController 
                return self.topMostViewControllerOfViewController(childViewController)
            
        

        return viewController
    


【讨论】:

【参考方案22】:

使用它来锁定视图控制器方向,在 IOS 9 上测试:

// 锁定方向为横向

-(UIInterfaceOrientationMask)supportedInterfaceOrientations 
    return UIInterfaceOrientationMaskLandscapeRight;


-(NSUInteger)navigationControllerSupportedInterfaceOrientations:(UINavigationController *)navigationController 
    return UIInterfaceOrientationMaskLandscapeRight;

【讨论】:

【参考方案23】:

我也有同样的问题,为此浪费了很多时间。所以现在我有了我的解决方案。我的应用设置仅支持纵向。但是,我的应用中的某些屏幕只需要横向。我通过在 AppDelegate 处设置一个变量 isShouldRotate 来修复它。 AppDelegate 处的函数:

func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> Int 
    if isShouldRotate == true 
        return Int(UIInterfaceOrientationMask.Landscape.rawValue)
    
    return Int(UIInterfaceOrientationMask.Portrait.rawValue)

最后当 ViewControllerA 需要横向状态时。只需这样做:在推送/呈现之前ViewControllerAisShouldRotate 分配给 true。不要忘记在 viewWillDisappear 处弹出/关闭控制器将 isShouldRotate 分配给 false

【讨论】:

【参考方案24】:

对我来说,顶层 VC 需要实现方向覆盖。如果顶部的 VC 没有实现,则使用堆栈中的 VC 将无效。

VC-main
    |
    -> VC 2
        |
        -> VC 3

基本上在我的测试中,只听 VC-Main。

【讨论】:

【参考方案25】:

看起来即使你在这里也有很多答案,没有一个人能满足我。我想强制定向,然后返回设备方向,但 [UIViewController attemptRotationToDeviceOrientation]; 只是没有工作。使整个事情变得复杂的是,我根据一些答案将 shouldAutorotate 添加到 false 并且无法在所有情况下都获得正确旋转回来的预期效果。

这就是我所做的:

在他的 init 构造函数中调用控制器之前:

_userOrientation = UIDevice.currentDevice.orientation;
[UIDevice.currentDevice setValue:@(UIInterfaceOrientationPortrait) forKey:@"orientation"];
[self addNotificationCenterObserver:@selector(rotated:)
                               name:UIDeviceOrientationDidChangeNotification];

所以我保存最后一个设备方向并注册方向更改事件。方向改变事件很简单:

- (void)rotated:(NSNotification*)notification 
    _userOrientation = UIDevice.currentDevice.orientation;

在视图中我只是强制回到我作为 userOreintation 的任何方向:

- (void)onViewDismissing 
    super.onViewDismissing;
    [UIDevice.currentDevice setValue:@(_userOrientation) forKey:@"orientation"];
    [UIViewController attemptRotationToDeviceOrientation];

这也必须存在:

- (BOOL)shouldAutorotate 
    return true;


- (UIInterfaceOrientationMask)supportedInterfaceOrientations 
    return UIInterfaceOrientationMaskPortrait;

导航控制器也必须委托给 shouldAutorotate 和 supportedInterfaceOrientations,但我相信大多数人已经拥有了。

PS:对不起,我使用了一些扩展和基类,但名称很有意义,所以概念是可以理解的,会做更多的扩展,因为它现在不太漂亮。

【讨论】:

以上是关于如何在 iOS 8 中强制视图控制器方向?的主要内容,如果未能解决你的问题,请参考以下文章

在 Objective-C 中的 iOS 6 上强制横向方向

iOS 6 中的模态视图控制器强制横向方向

在一个模式视图控制器上强制 iOS 方向为横向

如何在 iOS 7 和 iOS 8 中锁定设备方向

如何在 iOS 7/8 的单个视图上禁用横向

在 ios cordova 插件中,对于视图控制器,如何确定是不是应该发生方向更改