ios 如何只允许单个 UIViewController 在横向和纵向方向上旋转?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17466048/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 00:12:46  来源:igfitidea点击:

How to allow only single UIViewController to rotate in both Landscape and Portrait direction?

iphoneiosobjective-cuiviewcontroller

提问by Rohan

My app is only for iphonedevice (both iphone 4 and 5) and built to support only ios 6.

我的应用程序仅适用于iphone设备(iphone 4 和 5)并且仅支持ios 6.

My whole app only supports portraitmode. But there is one view called "ChatView" , which i want to support both landscapeand portraitmodes.

我的整个应用程序只支持portrait模式。但是有一种叫做 " ChatView" 的视图,我想同时支持landscapeportrait模式。

I have set the required device rotations as follows -

我已将所需的设备旋转设置如下 -

enter image description here

在此处输入图片说明

I have also tried following code to support rotation in "ChatView" -

我也尝试过以下代码来支持“ChatView”中的旋转 -

-(BOOL)shouldAutorotate
{
    return YES;
}

-(NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskLandscape;
}

But it could not rotate that view.

但它无法旋转该视图。

I have searched a lot for this , but could not be able to find the solution for my issue.

我为此搜索了很多,但无法找到解决我的问题的方法。

And also in "ChatView" there are some objects like buttons, textfields whose frames are set programmaticaly. So i want to know should i have to set frames of all those objects for landscape mode also?

而且在“ChatView”中还有一些对象,如按钮、文本字段,其框架以编程方式设置。所以我想知道我是否还必须为横向模式设置所有这些对象的框架?

Please help me.

请帮我。

Thanks.....

谢谢.....

采纳答案by NightFury

I think if you want to support just one viewcontroller rotation, it is not possible since application will follow orientations set by you in .plistfile. An alternate you can follow is to support your app for both landscape and portrait, freeze all viewcontrollers rotation to portrait except for chat view.

我认为如果您只想支持一个视图控制器旋转,这是不可能的,因为应用程序将遵循您在.plist文件中设置的方向。您可以遵循的替代方法是支持您的应用程序的横向和纵向,冻结所有视图控制器旋转到纵向,聊天视图除外。

EDIT

编辑

To subclass UINavigationController, create a new file with name e.g. CustomNavigationControllerand make it subclass of UINavigationController.

UINavigationController创建子类,请创建一个名为 eg 的新文件,CustomNavigationController并将其设为UINavigationController.

.h file

.h 文件

#import <UIKit/UIKit.h>

@interface CustomNavigationController : UINavigationController

@end

.m file

.m 文件

#import "CustomNavigationController.h"

@interface CustomNavigationController ()

@end


@implementation CustomNavigationController

-(BOOL)shouldAutorotate
{
    return NO;
}

-(UIInterfaceOrientationMask)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskAll;
}


- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return UIInterfaceOrientationIsPortrait(interfaceOrientation);
}

@end

Set the class of your UINavigationControllerin your main class xib as CustomNavigationController. Hope it helps ypu..

UINavigationController您的主类 xib 中的类设置为CustomNavigationController. 希望对你有帮助。。

回答by Alan10977

Simple but it work very fine. IOS 7.1 and 8

简单但它工作得很好。IOS 7.1 和 8

AppDelegate.h

AppDelegate.h

@property () BOOL restrictRotation;

AppDelegate.m

AppDelegate.m

-(NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
if(self.restrictRotation)
    return UIInterfaceOrientationMaskPortrait;
else
    return UIInterfaceOrientationMaskAll;
}

ViewController

视图控制器

-(void) restrictRotation:(BOOL) restriction
{
    AppDelegate* appDelegate = (AppDelegate*)[UIApplication sharedApplication].delegate;
    appDelegate.restrictRotation = restriction;
}

viewDidLoad

查看已加载

[self restrictRotation:YES]; or NO

回答by johnyu

Your view controller will never rotate to any position that is not supported by the app itself. You should enable all possible rotations and then in view controllers that are not supposed to rotate put the following lines

您的视图控制器永远不会旋转到应用程序本身不支持的任何位置。您应该启用所有可能的旋转,然后在不应该旋转的视图控制器中放置以下几行

- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskPortrait;
}

In ChatView, it should be:

在 ChatView 中,它应该是:

- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskAll;
}

If you need to change your layout after a rotation you should implement the appropriate changes to your subviews in

如果您需要在旋转后更改布局,您应该对子视图中的子视图进行适当的更改

- (void)viewWillLayoutSubviews

Use self.view.boundsto check the current size of the view, since self.view.framedoesn't change after rotations.

使用self.view.bounds检查的电流的大小view,因为self.view.frame旋转后也不会改变。

回答by Ted

for the specific viewcontroller.myou want to rotate

对于viewcontroller.m您要旋转的特定对象

add this method:

添加此方法:

- (BOOL)canAutoRotate
{
    return YES;
}

then inside your AppDelegate.m

然后在你的 AppDelegate.m

- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
    UIViewController *currentViewController = [self topViewController];

    if ([currentViewController respondsToSelector:@selector(canAutoRotate)]) {
        NSMethodSignature *signature = [currentViewController methodSignatureForSelector:@selector(canAutoRotate)];

        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];

        [invocation setSelector:@selector(canAutoRotate)];
        [invocation setTarget:currentViewController];

        [invocation invoke];

        BOOL canAutorotate = NO;
        [invocation getReturnValue:&canAutorotate];

        if (canAutorotate) {
            return UIInterfaceOrientationMaskAll;
        }
    }

    return UIInterfaceOrientationMaskPortrait;
}

- (UIViewController *)topViewController
{
    return [self topViewControllerWithRootViewController:[UIApplication sharedApplication].keyWindow.rootViewController];
}

- (UIViewController *)topViewControllerWithRootViewController:(UIViewController *)rootViewController
{
    if ([rootViewController isKindOfClass:[UITabBarController class]]) {
        UITabBarController* tabBarController = (UITabBarController*)rootViewController;
        return [self topViewControllerWithRootViewController:tabBarController.selectedViewController];
    } else if ([rootViewController isKindOfClass:[UINavigationController class]]) {
        UINavigationController* navigationController = (UINavigationController*)rootViewController;
        return [self topViewControllerWithRootViewController:navigationController.visibleViewController];
    } else if (rootViewController.presentedViewController) {
        UIViewController* presentedViewController = rootViewController.presentedViewController;
        return [self topViewControllerWithRootViewController:presentedViewController];
    } else {
        return rootViewController;
    }
}

回答by Charitha Basnayake

Ted's answer works well with the issue mentioned by Alexander of Norway. But I figured that issue is not happening the way Alexander explained,

Ted 的回答很适合挪威的 Alexander 提到的问题。但我认为这个问题并没有像亚历山大解释的那样发生,

When ViewController B which currently is in landscape (All orientations enabled) returns back to ViewController A. (Portrait only) after the user clicks on the back button, supportedInterfaceOrientationsForWindow doesn't get called and ViewController A ends up in landscape

当当前处于横向(启用所有方向)的 ViewController B 返回到 ViewController A 时。(仅限纵向)在用户单击后退按钮后,supportedInterfaceOrientationsForWindow 不会被调用并且 ViewController A 最终处于横向

Actually when ViewController B which currently is in landscape (All orientations enabled) returns back to ViewController A (Portrait only) after the user clicks on the back button, Appdelegate

实际上,当用户单击后退按钮后,当前处于横向(启用所有方向)的 ViewController B 返回到 ViewController A(仅限纵向)时,Appdelegate

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation;

is getting called. But still root view controller is ViewController B (that rotation enabled view controller), ViewController A is not getting back to portrait orientation, since ViewController B is still returning

正在被调用。但是根视图控制器仍然是 ViewController B(启用旋转的视图控制器),ViewController A 没有回到纵向,因为 ViewController B 仍在返回

-(BOOL)shouldAutorotate{

    return YES;
}

So when you press back button return, "shouldAutorotate -> NO" in ViewController B. Then ViewController A will come to Portrait orientation. This is what I did

因此,当您按下后退按钮返回时,ViewController B 中的“shouldAutorotate -> NO”。然后 ViewController A 将变为纵向。这就是我所做的

@property (nonatomic, assign) BOOL canAutoRotate;

#pragma mark - Public methods
- (BOOL)canAutoRotate
{
    return _canAutoRotate;
}

#pragma mark - Button actions
- (void)backButtonPressed:(UIButton *)sender {
    _canAutoRotate = NO;
   (...)
}

#pragma mark - Init
- (id)init{
    if(self=[super init]) {
        _canAutoRotate = YES;
    }
    return self;
}

回答by Artem Kirillov

Swift 3 Kosher Version

Swift 3 犹太版

I've left this here just for a case somebody has the problem.

我把这个留在这里只是为了以防万一有人遇到问题。

Apple's documentationfor supportedInterfaceOrientationssays:

苹果的文档supportedInterfaceOrientations说:

When the user changes the device orientation, the system calls this method on the root view controller or the topmost presented view controller that fills the window. If the view controller supports the new orientation, the window and view controller are rotated to the new orientation. This method is only called if the view controller's shouldAutorotate method returns true.

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

In few words you have to override supportedInterfaceOrientationsin root view controller so that it returns the value for its top child view controller and default value otherwise.

简而言之,您必须supportedInterfaceOrientations在根视图控制器中进行覆盖,以便它返回其顶部子视图控制器的值,否则返回默认值。

What you should do is checking if app supports all modes (go to Deployment info in targets General settings or Info.plist), find out the class of your root view controller. It can be generic UIViewController, UINavigationController, UITabBarController or some custom class. You can check it out this way:

您应该做的是检查应用程序是否支持所有模式(转到目标常规设置或 Info.plist 中的部署信息),找出根视图控制器的类。它可以是通用的 UIViewController、UINavigationController、UITabBarController 或一些自定义类。你可以这样检查:

dump(UIApplication.shared.keyWindow?.rootViewController)

Or any other way you like.

或者你喜欢的任何其他方式。

Let it be some CustomNavigationController. So you should override supportedInterfaceOrientationslike this:

让它成为一些CustomNavigationController。所以你应该supportedInterfaceOrientations像这样覆盖:

class CustomNavigationController: UINavigationController {

    override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        return topViewController?.supportedInterfaceOrientations ?? .allButUpsideDown
    }
}

In any view controller which should support only portrait orientation for instance override supportedInterfaceOrientationsthis way:

在任何只支持纵向的视图控制器中,例如以supportedInterfaceOrientations这种方式覆盖:

class ChildViewController: UIViewController {

    override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        return .portrait
    }
}

Then don't forget to check if shouldAutorotateof you root view controller and topmost presented view controller already returns true. If not, add this to classes definitions:

然后不要忘记检查shouldAutorotate您的根视图控制器和最顶层的视图控制器是否已经返回true。如果没有,请将其添加到类定义中:

override var shouldAutorotate: Bool {
    return true
}

Otherwise supportedInterfaceOrientationswill nor be called at all.

否则supportedInterfaceOrientations根本不会被调用。

Here you go!

干得好!

If you need to fix the opposite problem when only one view controller should support a bunch of orientations and others don't, make this changes to every view controller but this one.

如果当只有一个视图控制器应该支持一堆方向而其他视图控制器不支持时,您需要解决相反的问题,请对每个视图控制器进行更改,但除了这个。

Hope this will help.

希望这会有所帮助。

回答by xaphod

I'm not sure about the history of this issue (now = iOS 10 timeframe), but the easiest solution was missing as I posted thisin Oct. 2016.

我不确定这个问题的历史(现在 = iOS 10 时间范围),但最简单的解决方案丢失了,因为我在 2016 年 10 月发布了这个

Assuming you want this:

假设你想要这个:

  1. Supporting iOS 7and newer only (including iOS 10)
  2. Some view controllers should support all orientations, others should support a subset of orientations. Example of what I mean: one view controller should only support portrait, while all others should support all orientations
  3. All view controllers must auto-rotate if they support rotation (meaning, you don't want the code that fixes this issue in your view controllers)
  4. Support adding UINavigationControllers in XIBs/NIBs/Storyboards without having to do anything to them
  1. 仅支持iOS 7及更新版本(包括 iOS 10)
  2. 一些视图控制器应该支持所有方向,其他视图控制器应该支持方向的子集。我的意思的例子:一个视图控制器应该只支持纵向,而所有其他视图控制器应该支持所有方向
  3. 如果视图控制器支持旋转,则所有视图控制器都必须自动旋转(这意味着,您不希望在视图控制器中修复此问题的代码)
  4. 支持UINavigationController在 XIBs/NIBs/Storyboards 中添加s 而无需对它们做任何事情

... then (IMO) the easiest solution is to make a UINavigationControllerDelegate, NOT sub-class UINavigationController (which violates assumption 4 above).

...然后(IMO)最简单的解决方案是创建一个 UINavigationControllerDelegate,而不是子类 UINavigationController (这违反了上面的假设 4)。

When I solved this, I decided to make my first ViewControllera UINavigationControllerDelegate. This view controller sets itself as the navigation controller's delegate, and returns which orientations are allowed. In my case the default is that all orientations are allowed, with portrait preferred, but in one particular case only portrait is allowed. Code below is from Swift 3 / XCode 8:

当我解决这个问题时,我决定制作我的第ViewController一个UINavigationControllerDelegate. 此视图控制器将自身设置为导航控制器的委托,并返回允许的方向。在我的情况下,默认情况下允许所有方向,首选纵向,但在一种特定情况下只允许纵向。下面的代码来自 Swift 3 / XCode 8:

    class iPhoneStartViewController: UIViewController {

        var navInterfaceOrientationMask: UIInterfaceOrientationMask?
        var navInterfaceOrientationPreferred: UIInterfaceOrientation! = .portrait

        override func viewDidLoad() {
            super.viewDidLoad()
            self.navigationController?.delegate = self
        }

        @IBAction func cameraButtonPressed(_ sender: AnyObject) {
            if PermissionsHelper.singleton().photosPermissionGranted() == false {
                self.navInterfaceOrientationMask = nil   // default is: all orientations supported
                self.performSegue(withIdentifier: "segueToPhotoAccess", sender: self)
            } else {
                self.navInterfaceOrientationMask = .portrait // this stops the next view controller from being to rotate away from portrait
                self.performSegue(withIdentifier: "segueToCamera", sender: self)
            }
        }
     }

     // lock orientation to portrait in certain cases only. Default is: all orientations supported
    extension iPhoneStartViewController : UINavigationControllerDelegate {
        public func navigationControllerSupportedInterfaceOrientations(_ navigationController: UINavigationController) -> UIInterfaceOrientationMask {
            if let mask = self.navInterfaceOrientationMask {
                return mask
            } else {
                return .all
            }
        }

        public func navigationControllerPreferredInterfaceOrientationForPresentation(_ navigationController: UINavigationController) -> UIInterfaceOrientation {
            return self.navInterfaceOrientationPreferred
        }
    }

回答by Paul Lehn

I know this questions is very old but It needs an updated answer. The easiest and most correct way to achieve this result is to enable Portrait and Landscape in your app settings. Then add this code to your app delegate:

我知道这个问题很老了,但它需要一个更新的答案。实现此结果的最简单和最正确的方法是在您的应用设置中启用纵向和横向。然后将此代码添加到您的应用程序委托中:

 func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask {

    if let navigationController = self.window?.rootViewController as? UINavigationController {

        if navigationController.visibleViewController is INSERTYOURVIEWCONTROLLERHERE  {
            return UIInterfaceOrientationMask.All
        }

        else {
            return UIInterfaceOrientationMask.Portrait
        }
    }

    return UIInterfaceOrientationMask.Portrait
}

Dont forget to replace "INSERTYOURVIEWCONTROLLERHERE" with your view controller.

不要忘记用您的视图控制器替换“INSERTYOURVIEWCONTROLLERHERE”。

回答by egsemsem

Here is the answer of Alexander (https://stackoverflow.com/posts/25507963/revisions) in Swift:

这是 Alexander ( https://stackoverflow.com/posts/25507963/revisions) 在 Swift 中的回答:

func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> Int {

    var currentViewController: UIViewController? = self.topViewController()
    if currentViewController != nil && currentViewController!.canAutoRotate() {
        return Int(UIInterfaceOrientationMask.All.rawValue)
    }
    return Int(UIInterfaceOrientationMask.Portrait.rawValue)


}

func topViewController() -> UIViewController? {
    if UIApplication.sharedApplication().keyWindow != nil
    {
        return self.topViewControllerWithRootViewController(UIApplication.sharedApplication().keyWindow!.rootViewController!)
    }
    return nil
}

func topViewControllerWithRootViewController(rootViewController: UIViewController?) -> UIViewController? {
    if rootViewController == nil {
        return nil
    }
    if rootViewController!.isKindOfClass(UITabBarController) {
        var tabBarController: UITabBarController = (rootViewController as? UITabBarController)!
        return self.topViewControllerWithRootViewController(tabBarController.selectedViewController)
    }
    else {
        if rootViewController!.isKindOfClass(UINavigationController) {
            var navigationController: UINavigationController = (rootViewController as? UINavigationController)!
            return self.topViewControllerWithRootViewController(navigationController.visibleViewController)
        }
        else {
            if (rootViewController!.presentedViewController != nil) {
                var presentedViewController: UIViewController = rootViewController!.presentedViewController!
                return self.topViewControllerWithRootViewController(presentedViewController)
            }
            else {
                return rootViewController
            }
        }
    }
}

In addition, you will need to add the following snippet in AppDelegate.swift:

此外,您需要在 AppDelegate.swift 中添加以下代码段:

extension UIViewController {
func canAutoRotate() -> Bool {
    return false
}}

And for ViewControllers for which you want to allow all rotations, add this function:

对于您希望允许所有旋转的 ViewController,请添加以下功能:

override func canAutoRotate() -> Bool {
    return true
}

回答by LordParsley

Based on @iAnum's answer, I've enabled autorotate and UIViewController class detection.

根据@iAnum 的回答,我启用了自动旋转和 UIViewController 类检测。

This is because otherwise transitioning into and out of the "special view controller" won't correct to portrait orientation, and you'll be stuck in an unsupported orientation.

这是因为以其他方式进出“特殊视图控制器”将不会更正为纵向,并且您将陷入不受支持的方向。

I only had one view supporting landscape, so I just hard-coded it in the custom navigation view controller:

我只有一个支持横向的视图,所以我只是在自定义导航视图控制器中对其进行了硬编码:

-(BOOL)shouldAutorotate
{
    return YES;
}

-(NSUInteger)supportedInterfaceOrientations
{
    //Access the current top object.
    UIViewController *viewController = [self.viewControllers lastObject];
    //Is it one of the landscape supported ones?
    if ([viewController isMemberOfClass:[SpecialViewController class]]) {
        return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight;
    } else
        return UIInterfaceOrientationMaskPortrait;
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    //Access the current top object.
    UIViewController *viewController = [self.viewControllers lastObject];
    //Is it one of the landscape supported ones?
    if ([viewController isMemberOfClass:[SpecialViewController class]]) {
        return interfaceOrientation;
    } else
        return UIInterfaceOrientationIsPortrait(interfaceOrientation);
}

There's a problem popping VCs discussed here https://stackoverflow.com/a/15057537/1277350where pressing back while in landscape won't even call the orientation methods, so you've got to hack it a bit by showing and dismissing a modal view.

此处讨论的 VC 存在弹出问题https://stackoverflow.com/a/15057537/1277350,其中在横向按下时甚至不会调用方向方法,因此您必须通过显示和关闭模态视图。

And then just remember that if you want willShowViewController to fire, you need to set self.delegate = self and add the UINavigationControllerDelegate to your custom navigation controller along with the code below.

然后请记住,如果您希望 willShowViewController 触发,您需要设置 self.delegate = self 并将 UINavigationControllerDelegate 与下面的代码一起添加到您的自定义导航控制器。

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
    return UIInterfaceOrientationPortrait;
}

- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    UIApplication* application = [UIApplication sharedApplication];
    if (application.statusBarOrientation != UIInterfaceOrientationPortrait)
    {
        UIViewController *c = [[UIViewController alloc]init];
        [c.view setBackgroundColor:[UIColor clearColor]];
        [navigationController presentViewController:c animated:NO completion:^{
            [self dismissViewControllerAnimated:YES completion:^{
            }];
        }];
    }
}