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

在iOS 8之前,我们将下面的代码与supportedInterfaceOrientations结合使用,并使用shouldAutoRotate委托方法强制应用程序定位到任何特定的方向。 我使用下面的代码片段以编程方式将应用程序旋转到所需的方向。 首先,我正在改变状态栏的方向。 然后只是提出并立即解散模态视图将视图旋转到所需的方向。

[[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中是失败的。另外,我在堆栈溢出中看到了一些答案,人们build议我们应该始终避免从iOS 8开始使用这种方法。

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

  1. 第一视图控制器 – 它应该支持iPad中的所有方向,并且只支持iPhone中的肖像(主屏幕button)。

  2. 第二个视图控制器 – 它应该只支持所有情况下的风景权

  3. 第三个视图控制器 – 它应该只支持所有情况下的风景

我们正在使用导航控制器进行页面导航。 从第一个视图控制器,在一个button点击动作,我们推动第二个堆栈。 所以,当第二个视图控制器到达时,不pipe设备的方向如何,应用程序只能locking在横向的权利。

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

 -(NSUInteger)supportedInterfaceOrientations{ return UIInterfaceOrientationMaskLandscapeRight; } -(BOOL)shouldAutorotate { return NO; } 

有没有什么解决scheme这个或任何更好的方式locking视图控制器在iOS 8的特定方向。请帮助!

对于iOS 7-10:

Objective-C的:

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

Swift 3:

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

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

如果您位于UINavigationControllerUITabBarController则方向旋转稍微复杂一些。 问题是,如果一个视图控制器embedded在这些控制器中的一个中,导航或标签栏控制器优先并且作出自动旋转和支持的方向的决定。

我在UINavigationController和UITabBarController上使用以下两个扩展,以便embedded在这些控制器之一中的视图控制器可以做出决定。

给视图控制器的权力!

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

Swift 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方法,或者您可以覆盖要locking的视图控制器中的shouldAutoRotate ,否则可以忽略其他视图控制器中的覆盖,这些视图控制器要inheritance应用程序的plist中指定的默认方向行为

禁用旋转

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

locking具体的方向

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

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

锁屏旋转

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

迅速:

 override func supportedInterfaceOrientations() -> Int { return Int(UIInterfaceOrientationMask.Landscape.rawValue) } override func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation { return UIInterfaceOrientation.LandscapeLeft } override func shouldAutorotate() -> Bool { return false } 

这对我来说是有效的:

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 2 iOS 8.x中适用于我:

PS(这个方法不需要重写像每个viewController shouldautorotate方向函数,只是在AppDelegate上的一个方法)

在项目一般信息中检查“需要全屏”。 在这里输入图像说明

所以,在AppDelegate.swift上做一个variables:

 var enableAllOrientation = false 

所以,也把这个function:

 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 } 

如果您需要根据设备types进行select,则可以这样做:

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

这应该从iOS 6起,但我只在iOS 8上testing过。子类UINavigationController并覆盖下列方法:

 - (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation { return UIInterfaceOrientationLandscapeRight; } - (BOOL)shouldAutorotate { return NO; } 

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

 - (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation { return self.visibleViewController.preferredInterfaceOrientationForPresentation; } - (BOOL)shouldAutorotate { return self.visibleViewController.shouldAutorotate; } 

并执行那里的方法。

这是对Sid Shah的回答 ,如何使用以下方法禁用animation:

 [UIView setAnimationsEnabled:enabled]; 

码:

 - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:NO]; [UIView setAnimationsEnabled:NO]; // Stackoverflow #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]; } 

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 } 

如果您正在使用navigationViewController,则应该为此创build自己的超类,并覆盖:

 - (BOOL)shouldAutorotate { id currentViewController = self.topViewController; if ([currentViewController isKindOfClass:[SecondViewController class]]) return NO; return YES; } 

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

假设你正在使用故事板。 你必须在你的“onClick”方法中创build手动search( 如何 ):

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

这将强制在超类禁用自动旋转function之前进行横向取向。

Sids和Koreys答案的结合为我工作。

扩展导航控制器:

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

然后禁用单个视图的旋转

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

并在赛格之前旋转到适当的方向

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

上面的顶部解决scheme:

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

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

(对不起,没有足够的声望评论该解决scheme,所以我不得不像这样添加它)

我的要求是

  1. 以纵向模式locking所有视图
  2. 使用AVPlayerViewController播放video

在播放video时,如果是横屏,则允许屏幕左右旋转横屏,左横屏。 如果是肖像,那么只能在肖像模式下locking视图。

首先,在AppDelegate.swift定义supportedInterfaceOrientationsForWindow

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

其次,在你的主视图控制器中,定义以下function

 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 } 

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

 let playerViewController = MyPlayerViewController() // Get the thumbnail let thumbnail = MyAlbumFileManager.sharedManager().getThumbnailFromMyVideoMedia(......) let size = thumbnail?.size playerViewController.size = size 

用适当的videoUrl启动你的播放器,然后把你的播放器分配给playerViewController 。 快乐的编码!

我已经尝试了很多解决scheme,但是有效的解决scheme如下:

不需要编辑ios 8和9中的info.plist

 - (BOOL) shouldAutorotate { return NO; } - (UIInterfaceOrientationMask)supportedInterfaceOrientations { return (UIInterfaceOrientationPortrait | UIInterfaceOrientationPortraitUpsideDown); } 

Apple文档可能的方向 :

UIInterfaceOrientationUnknown

设备的方向不能确定。

UIInterfaceOrientationPortrait

该设备处于纵向模式,设备保持直立,底部的主页button。

UIInterfaceOrientationPortraitUpsideDown

该设备是纵向模式,但倒挂,设备保持直立,顶部的主页button。

UIInterfaceOrientationLandscapeLeft

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

UIInterfaceOrientationLandscapeRight

设备处于横向模式,设备直立,主页button位于右侧。

 - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; [UIViewController attemptRotationToDeviceOrientation]; } 

根据Korey Hinton的回答

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

locking具体的方向

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

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

我创build了下面的Objective-C项目github.com/GabLeRoux/RotationLockInTabbedViewChild ,其中有一个TabbedViewController实例,其中一个子视图被允许旋转,另一个子视图被locking在纵向上。

这不是完美的,但它的工作原理和相同的想法应该适用于其他types的根视图,如NavigationViewController 。 🙂

子视图锁定父方向

根据@ sid-sha显示的解决scheme,您必须将所有内容放在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"]; } } 

我的解决scheme

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的解决scheme可以在任何iOS版本的XXViewController上运行:

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

使用这个来locking视图控制器的方向,在IOS 9上testing:

//将方向locking到右侧

 -(UIInterfaceOrientationMask)supportedInterfaceOrientations { return UIInterfaceOrientationMaskLandscapeRight; } -(NSUInteger)navigationControllerSupportedInterfaceOrientations:(UINavigationController *)navigationController { return UIInterfaceOrientationMaskLandscapeRight; } 

关于如何最好地完成这个任务似乎还有一些争论,所以我想我会分享我的(工作)方法。 在你的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调用 – 没有这个视图将不会正确地旋转,没有物理旋转设备。

我有同样的问题,浪费了很多时间。 所以现在我有我的解决scheme。 我的应用程序设置只是支持肖像只。但是,一些屏幕到我的应用程序只需要横向。我通过有一个variablesisShouldRotateAppDelegate修复它。 而在AppDelegate的function:

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

最后,当一个ViewControllerA需要横向状态。 只要做到这一点: 在推送/呈现ViewControllerA分配isShouldRotatetrue 。 不要忘了当popup/closures控制器分配isShouldRotatefalseviewWillDisappear

首先 – 这是一个坏主意,一般来说,你的应用程序架构出了问题,但是,如果是这样的话,你可以尝试做如下的事情:

 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) 

Thant的一切

[iOS9 +]如果任何人在这里一直拖下去,因为没有上述解决scheme的工作,如果你提出的观点,你想改变方向segue,你可能想检查这一点。

检查您的segue的演示文稿types。 矿是“过度的情况下”。 当我将其更改为全屏时,它工作。

感谢@GabLeRoux,我find了这个解决scheme。

  • 与上述解决scheme结合使用时,此更改才有效。