如何强制在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的应用程序。 共有三个控制器。
-
第一视图控制器 – 它应该支持iPad中的所有方向,并且只支持iPhone中的肖像(主屏幕button)。
-
第二个视图控制器 – 它应该只支持所有情况下的风景权
-
第三个视图控制器 – 它应该只支持所有情况下的风景
我们正在使用导航控制器进行页面导航。 从第一个视图控制器,在一个button点击动作,我们推动第二个堆栈。 所以,当第二个视图控制器到达时,不pipe设备的方向如何,应用程序只能locking在横向的权利。
下面是第二个和第三个视图控制器中的shouldAutorotate
和supportedInterfaceOrientations
方法。
-(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:
调用它即可。
如果您位于UINavigationController
或UITabBarController
则方向旋转稍微复杂一些。 问题是,如果一个视图控制器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 }
这对我来说是有效的:
在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,所以我不得不像这样添加它)
我的要求是
- 以纵向模式locking所有视图
- 使用
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。 我的应用程序设置只是支持肖像只。但是,一些屏幕到我的应用程序只需要横向。我通过有一个variablesisShouldRotate在AppDelegate修复它。 而在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分配isShouldRotate为true 。 不要忘了当popup/closures控制器分配isShouldRotate为false在viewWillDisappear 。
首先 – 这是一个坏主意,一般来说,你的应用程序架构出了问题,但是,如果是这样的话,你可以尝试做如下的事情:
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结合使用时,此更改才有效。