在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中失败了。此外,我在堆栈溢出中看到了一些答案,人们建议我们应该始终从iOS 8开始避免这种方法。
更具体地说,我的应用程序是一种通用类型的应用程序。总共有三个控制器。
第一个视图控制器 - 它应该支持iPad中的所有方向,并且只支持iPhone中的纵向(主页按钮)。
第二个视图控制器 - 它应该只支持所有条件下的横向
第三视图控制器 - 它应该只支持所有条件下的横向
我们正在使用导航控制器进行页面导航。从第一个视图控制器,在按钮单击操作上,我们在堆栈上推送第二个。因此,当第二个视图控制器到达时,无论设备方向如何,应用程序都应仅锁定横向。
以下是第二和第三个视图控制器中的shouldAutorotate
和supportedInterfaceOrientations
方法。
-(NSUInteger)supportedInterfaceOrientations{
return UIInterfaceOrientationMaskLandscapeRight;
}
-(BOOL)shouldAutorotate {
return NO;
}
对于这种或任何更好的方法来锁定iOS 8的特定方向的视图控制器是否有任何解决方案。请帮助!!
答案 0 :(得分:298)
对于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:
中调用它即可。
答案 1 :(得分:91)
如果您位于UINavigationController
或UITabBarController
内,方向旋转会更复杂一些。问题是如果视图控制器嵌入其中一个控制器中,导航或标签栏控制器优先,并决定自动旋转和支持的方向。
我在UINavigationController和UITabBarController上使用以下2个扩展,以便嵌入其中一个控制器的视图控制器可以做出决定。
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
方法,或者可以在要锁定的视图控制器中覆盖shouldAutoRotate
,否则可以忽略要继承默认值的其他视图控制器中的覆盖应用程序的plist中指定的方向行为
class ViewController: UIViewController {
override func shouldAutorotate() -> Bool {
return false
}
}
class ViewController: UIViewController {
override func supportedInterfaceOrientations() -> Int {
return Int(UIInterfaceOrientationMask.Landscape.rawValue)
}
}
理论上这适用于所有复杂的视图控制器层次结构,但我注意到UITabBarController存在问题。出于某种原因,它想要使用默认方向值。如果您有兴趣了解如何解决某些问题,请参阅以下博客文章:
答案 2 :(得分:23)
这对我有用:
在viewDidAppear:方法中调用它。
- (void) viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[UIViewController attemptRotationToDeviceOrientation];
}
答案 3 :(得分:20)
我发现如果它是一个呈现的视图控制器,您可以覆盖preferredInterfaceOrientationForPresentation
夫特:
override func supportedInterfaceOrientations() -> Int {
return Int(UIInterfaceOrientationMask.Landscape.rawValue)
}
override func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation {
return UIInterfaceOrientation.LandscapeLeft
}
override func shouldAutorotate() -> Bool {
return false
}
答案 4 :(得分:15)
这种方式适用于 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
}
}
答案 5 :(得分:9)
这应该可以从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;
}
并在那里实施这些方法。
答案 6 :(得分:9)
这是对Sid Shah's answer中评论的反馈,关于如何使用以下方式禁用动画:
[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];
}
答案 7 :(得分:9)
首先 - 这是一个坏主意,一般来说,您的应用架构出现问题,但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)
全部
答案 8 :(得分:6)
在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
}
答案 9 :(得分:6)
如果您正在使用navigationViewController,则应为此创建自己的超类并覆盖:
- (BOOL)shouldAutorotate {
id currentViewController = self.topViewController;
if ([currentViewController isKindOfClass:[SecondViewController class]])
return NO;
return YES;
}
这将禁用 SecondViewController 中的旋转,但如果您在设备处于纵向方向时推动 SecondViewController ,则 SecondViewController 将以纵向显示模式。
假设您正在使用故事板。您必须在“onClick”方法中创建手动segue(How to):
- (IBAction)onPlayButtonClicked:(UIBarButtonItem *)sender {
NSNumber *value = [NSNumber numberWithInt:UIInterfaceOrientationLandscapeLeft];
[[UIDevice currentDevice] setValue:value forKey:@"orientation"];
[self performSegueWithIdentifier:@"PushPlayerViewController" sender:self];
}
这会在超类禁用自动旋转功能之前强制横向显示。
答案 10 :(得分:4)
我尝试过很多解决方案,但有效的方法如下:
无需编辑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 强>
设备处于横向模式,设备保持直立状态 右侧的主页按钮。
答案 11 :(得分:3)
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")
}
}
答案 12 :(得分:2)
[iOS9 +] 如果有人一直拖到这里,因为以上解决方案都没有工作, 如果您提供视图,则需要更改方向 通过segue,您可能想检查一下。
检查您的segue的演示文稿类型。 我的目前背景是“我的”。 当我将其更改为全屏时,它有效。
感谢@GabLeRoux,我找到了这个解决方案。
答案 13 :(得分:2)
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[UIViewController attemptRotationToDeviceOrientation];
}
答案 14 :(得分:2)
上面的最佳解决方案:
SELECT ta.topcallid,
CASE
WHEN ta.e_created < Trans.e_created AND ta.e_terminated > Trans.e_terminated THEN 'Consult (Stage ' + CAST(TA.EmpNum AS VARCHAR(3)) + ' To ' + CAST(Trans.EmpNum AS VARCHAR(3)) + ')'
WHEN ta.e_terminated < Trans.e_created THEN 'Cold Transfer (Stage ' + CAST(TA.EmpNum AS VARCHAR(3)) + ' To ' + CAST(Trans.EmpNum AS VARCHAR(3)) + ')'
WHEN ta.e_terminated > Trans.e_created THEN 'Warm Transfer (Stage ' + CAST(TA.EmpNum AS VARCHAR(3)) + ' To ' + CAST(Trans.EmpNum AS VARCHAR(3)) + ')'
END TransStatus
FROM [TransferTypeAnalysis] TA
JOIN [TransferTypeAnalysis] Trans
ON TA.topcallid = Trans.topcallid
AND ta.empnum < Trans.empnum
ORDER BY
TA.topcallid,
ta.empnum
当我在所呈现的视图控制器的viewDidAppear中调用它时,对我没有用。但是,当我在呈现视图控制器中的preparForSegue中调用它时,它确实有效。
(抱歉,没有足够的声誉可以评论该解决方案,所以我必须像这样添加它)
答案 15 :(得分:1)
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
}
锁定具体方向
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
return UIInterfaceOrientationMask.Portrait
}
答案 16 :(得分:1)
我在这里尝试了一些解决方案,重要的是要理解它是根视图控制器,它将确定它是否会旋转。
我使用TabbedViewController
的工作示例创建了以下 objective-c 项目github.com/GabLeRoux/RotationLockInTabbedViewChild,其中一个子视图允许旋转,另一个子视图被锁定在纵向
它并不完美但它有效并且同样的想法应该适用于其他类型的根视图,例如NavigationViewController
。 :)
答案 17 :(得分:0)
关于如何最好地完成这项任务似乎还存在争议,所以我想我会分享我的(工作)方法。在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.
我的关键是attemptRotationToDeviceOrientation
中的viewWillAppear
来电 - 如果没有物理旋转设备,视图将无法正常旋转。
答案 18 :(得分:0)
使用此方法锁定视图控制器方向,在IOS 9上测试:
//将方向锁定为横向右侧
-(UIInterfaceOrientationMask)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskLandscapeRight;
}
-(NSUInteger)navigationControllerSupportedInterfaceOrientations:(UINavigationController *)navigationController {
return UIInterfaceOrientationMaskLandscapeRight;
}
答案 19 :(得分:0)
在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适用于任何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
}
}
答案 20 :(得分:0)
根据@ 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 :(得分:0)
我有同样的问题,浪费了很多时间。所以现在我有了解决方案。我的应用程序设置仅支持纵向。但是,我的应用程序中的某些屏幕只需要横向。我通过在 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 需要横向状态时。只需这样做: 在推送/呈现之前 到 ViewControllerA 将 isShouldRotate 指定为 true 。不要忘记当控制器在 viewWillDisappear 处将 isShouldRotate 分配给 false 时弹出/关闭。
答案 22 :(得分:0)
对我来说,顶级VC需要实现方向覆盖。如果顶级VC未实现,则使用VC向下堆栈将无效。
VC-main
|
-> VC 2
|
-> VC 3
只收听VC-Main,主要是在我的测试中。
答案 23 :(得分:0)
看来,即使您在这里有这么多答案,也没有人对我足够。我想强制定位,然后再回到设备定位,但是[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:抱歉,我使用了一些扩展名和基类,但是名称很有意义,因此概念是可以理解的,它将进行更多的扩展,因为现在还不太好。