【问题标题】:How to allow only single UIViewController to rotate in both Landscape and Portrait direction?如何只允许单个 UIViewController 在横向和纵向方向上旋转?
【发布时间】:2013-07-02 05:00:53
【问题描述】:

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

我的整个应用只支持portrait 模式。但是有一个名为 "ChatView" 的视图,我想同时支持 landscapeportrait 模式。

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

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

-(BOOL)shouldAutorotate
{
    return YES;
}

-(NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskLandscape;
}

但它无法旋转该视图。

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

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

请帮帮我。

谢谢.....

【问题讨论】:

标签: iphone ios objective-c uiviewcontroller


【解决方案1】:

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

编辑

要子类化UINavigationController,请创建一个名称为例如的新文件。 CustomNavigationController 并使其成为 UINavigationController 的子类。

.h 文件

#import <UIKit/UIKit.h>

@interface CustomNavigationController : UINavigationController

@end

.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

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

【讨论】:

  • 感谢您的回复。但是,除了聊天视图,我怎样才能将所有视图控制器旋转冻结为纵向?
  • 它不起作用。视图仍然旋转到我只想要纵向的横向
  • @Rohan 我想知道为什么它不适合你。我已将其应用于我的控制器及其工作。你的控制器是嵌套在navigationcontroller 还是tabcontroller 中?如果是这样,则需要在父控制器中编写上述代码。
  • 您需要继承 UINavigationController 并在其中编写此代码,这样您就可以完全控制控制器的方向。
【解决方案2】:

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

- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskPortrait;
}

在 ChatView 中,应该是:

- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskAll;
}

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

- (void)viewWillLayoutSubviews

使用self.view.bounds 检查view 的当前大小,因为self.view.frame 在旋转后不会改变。

【讨论】:

  • @他也想要一个横向和纵向视图
  • 我已经试过你的代码了。但这无济于事。视图仍然旋转到我只想要纵向的横向
  • 我认为上面的答案是说ChatView,支持的界面方向是 UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscape
  • 感谢您的关注,我现在修好了。
【解决方案3】:

我也有同样的情况。所以我将 UINavigationController 子类化为 CustomNavigationController,在这个 CustomNavigationController 里面,我写了

#define IOS_OLDER_THAN_6 ( [ [ [ UIDevice currentDevice ] systemVersion ] floatValue ] < 6.0 )
#define IOS_NEWER_OR_EQUAL_TO_6 ( [ [ [ UIDevice currentDevice ] systemVersion ] floatValue ] >= 6.0 )


#pragma mark - Rotation

#ifdef IOS_OLDER_THAN_6
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation{
      return (toInterfaceOrientation == UIInterfaceOrientationPortrait);
}
#endif
#ifdef IOS_NEWER_OR_EQUAL_TO_6
-(BOOL)shouldAutorotate {
    return YES;
}
- (NSUInteger)supportedInterfaceOrientations {
    return UIInterfaceOrientationMaskPortrait;;
}
#endif

我使用了这个 CustomNavigationController 而不是现有的 NavigationController。

然后在你必须在 LandScape Orientation 中显示的视图控制器中说 LandScapeView,我写了

#pragma mark - Rotation

#ifdef IOS_OLDER_THAN_6

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation{
    return (toInterfaceOrientation == UIInterfaceOrientationLandscapeRight | toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft);
}

#endif

#ifdef IOS_NEWER_OR_EQUAL_TO_6

-(BOOL)shouldAutorotate {
    return YES;
}
- (NSUInteger)supportedInterfaceOrientations {
    return UIInterfaceOrientationMaskLandscapeRight | UIInterfaceOrientationMaskLandscapeLeft;
}

#endif

在 CustomNavigationController 中,我展示了这个视图控制器,而不是推入导航堆栈。于是 LandScapeView 出现在 LandScape Orientation 中。

LandScapeView *graph = [[LandScapeView alloc]init....];
[self presentViewController:graph animated:YES completion:nil];

我没有更改项目设置中支持的界面方向中的任何内容。

【讨论】:

    【解决方案4】:

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

    这是因为否则,进入和退出“特殊视图控制器”将无法纠正纵向方向,并且您将被卡在不受支持的方向。

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

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

    这里讨论的 VC 弹出问题 https://stackoverflow.com/a/15057537/1277350 在横向按下时甚至不会调用方向方法,因此您必须通过显示和关闭模式视图来稍微破解它。

    然后请记住,如果您希望 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:^{
                }];
            }];
        }
    }
    

    【讨论】:

      【解决方案5】:

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

      添加此方法:

      - (BOOL)canAutoRotate
      {
          return YES;
      }
      

      然后在你的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;
          }
      }
      

      【讨论】:

      • 当当前处于横向(启用所有方向)的 ViewController B 返回到 ViewController A 时。(仅纵向)在用户单击后退按钮后,supportInterfaceOrientationsForWindow 不会被调用并且 ViewController A 结束在景观中,即使它不应该能够。你如何处理这个问题?
      • 这很好用。如果您希望将启动屏幕的默认方向设置为一个方向,您可以在 Info.plist 中设置方向,并使用此技术覆盖特定视图控制器的 plist 值。
      • 它适用于 iOS 12,但在关闭旋转视图控制器后旋转保持不变。我在jairobjunior.com/blog/2016/03/05/… 找到了快速代码
      【解决方案6】:

      简单但效果很好。 IOS 7.1 和 8

      AppDelegate.h

      @property () BOOL restrictRotation;
      

      AppDelegate.m

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

      视图控制器

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

      viewDidLoad

      [self restrictRotation:YES]; or NO
      

      【讨论】:

      • 在从 ViewController A 到 ViewController B 时有效,但是当用户从 B(允许所有方向)返回到 A(仅纵向)时,A 以横向显示,即使它不应该是能够。
      • [[UIDevice currentDevice] setValue:[NSNumbernumberWithInteger: UIInterfaceOrientationPortrait] forKey:@"orientation"];或者你想要的。
      • 很遗憾(尽快)回答了您的问题。如果您自己无法找到解决方案!!!! ASAP 是贬义的!!!!!!!
      • 这拯救了我的一天!对于那些想知道何时将限制设置回旧值的人来说,在关闭模式视图之前完成它会很好地工作。
      • 试过这个方法。为我工作的视频视图控制器。但是有一个问题,如果视频以横向模式结束播放,那么整个应用程序将被转换为横向模式。
      【解决方案7】:

      像这样创建 UINavigationController 的子类:

      MyNavigationController.h

      #import <UIKit/UIKit.h>
      
      @interface MyNavigationController : UINavigationController
      
      @end
      

      MyNavigationController.m

      #import "MyNavigationController.h"
      #import "ServicesVC.h"
      
      @implementation MyNavigationController
      
      -(BOOL)shouldAutorotate{
      
          return YES;
      }
      
      -(NSUInteger)supportedInterfaceOrientations{
      
          if ([[self.viewControllers lastObject] isKindOfClass:[ServicesVC class]]) {
              return UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight;
          }
      
          return UIInterfaceOrientationMaskAll;
      }
      
      @end
      

      假设您的视图控制器名为:ServicesVC

      【讨论】:

        【解决方案8】:

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

        当当前处于横向的 ViewController B (All 方向已启用)返回到 ViewController A。(纵向 仅)在用户单击后退按钮后, supportedInterfaceOrientationsForWindow 不会被调用并且 ViewController A 以横向结束

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

        - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation;
        

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

        -(BOOL)shouldAutorotate{
        
            return YES;
        }
        

        因此,当您按下返回按钮时,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;
        }
        

        【讨论】:

          【解决方案9】:

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

          此外,您还需要在 AppDelegate.swift 中添加以下 sn-p:

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

          对于你想要允许所有旋转的 ViewControllers,添加这个函数:

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

          【讨论】:

            【解决方案10】:

            //将此方法粘贴到app deligate类中

            - (UIInterfaceOrientationMask)application:(UIApplication )application supportedInterfaceOrientationsForWindow:(UIWindow )window
            {
             if ([self.window.rootViewController.presentedViewController isKindOfClass: [_moviePlayerController class]])
              {
               if (self.window.rootViewController.presentedViewController)
                    return UIInterfaceOrientationMaskAll;
                else return UIInterfaceOrientationMaskPortrait;
              }
             else return UIInterfaceOrientationMaskPortrait;
            }   
            

            【讨论】:

              【解决方案11】:

              如果应用支持从 IOS7IOS9,请使用此代码进行定位:

              #if __IPHONE_OS_VERSION_MAX_ALLOWED < 90000
              - (NSUInteger)supportedInterfaceOrientations
              #else
              - (UIInterfaceOrientationMask)supportedInterfaceOrientations
              #endif
              {
                  if([AppDelegate isPad]) return UIInterfaceOrientationMaskAll;
                  else return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown;
              }
              

              【讨论】:

                【解决方案12】:

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

                 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
                }
                

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

                【讨论】:

                • 一个例外:如果您正在滚动自己的“全屏模式”,特别是对于框架,那么您下游的开发人员可以在仅纵向应用程序中使用它,但希望全屏仍然可以工作。
                【解决方案13】:

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

                假设你想要这个:

                1. 仅支持 iOS 7 及更新版本(包括 iOS 10)
                2. 一些视图控制器应该支持所有方向,其他的应该支持方向的子集。 我的意思的例子:一个视图控制器应该只支持纵向,而所有其他的应该支持所有方向
                3. 如果支持旋转,所有视图控制器都必须自动旋转(也就是说,您不希望在视图控制器中使用修复此问题的代码)
                4. 支持在 XIBs/NIBs/Storyboards 中添加 UINavigationControllers 而无需对其进行任何操作

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

                当我解决了这个问题后,我决定将我的第一个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
                        }
                    }
                

                【讨论】:

                  【解决方案14】:

                  Swift 3 犹太版

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

                  Apple's documentation for supportedInterfaceOrientations 说:

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

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

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

                  dump(UIApplication.shared.keyWindow?.rootViewController)
                  

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

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

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

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

                  class ChildViewController: UIViewController {
                  
                      override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
                          return .portrait
                      }
                  }
                  

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

                  override var shouldAutorotate: Bool {
                      return true
                  }
                  

                  否则supportedInterfaceOrientations 将不会被调用。

                  给你!

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

                  希望这会有所帮助。

                  【讨论】:

                    猜你喜欢
                    • 2014-10-19
                    • 2011-03-06
                    • 2011-05-30
                    • 1970-01-01
                    • 2023-03-05
                    • 1970-01-01
                    • 1970-01-01
                    • 2012-03-26
                    • 1970-01-01
                    相关资源
                    最近更新 更多