【问题标题】:How do I programmatically set device orientation in iOS 7?如何在 iOS 7 中以编程方式设置设备方向?
【发布时间】:2014-01-26 01:22:58
【问题描述】:

我正在开发一个 iPad 应用程序,使用 AutoLayout,如果用户启用某种模式(“抬头”模式),我只想支持纵向(或纵向倒置)方向,此外,如果设备处于横向模式,我想自动切换到纵向模式。

在顶视图控制器中,我有以下内容:

- (NSUInteger) supportedInterfaceOrientations {
    if (self.modeHeadsUp) {
        return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown;
    } else {
        return UIInterfaceOrientationMaskAll;
    }
}

- (BOOL) shouldAutorotate {
    return TRUE;
}

根据我在这里其他地方看到的答案,答案似乎是我应该使用“应用程序 setStatusBarOrientation”。因此,在用户选择“heads-up”模式的方法中,我加入了:

    UIApplication *application = [UIApplication sharedApplication];
    [application setStatusBarOrientation:UIInterfaceOrientationPortrait
                                animated:YES];

但是,这似乎没有任何作用。虽然我可以通过物理方式移动设备以使其旋转为纵向,但它不会自动旋转。

实际上,在运行上述代码以尝试以编程方式设置方向后处于横向模式时,当我使用以下代码查询应用程序“statusBarOrientation”时,横向保持在“4”:

UIApplication *application = [UIApplication sharedApplication];
int orientation = [application statusBarOrientation];
self.movesTextView.text = [NSString stringWithFormat:@"ORIENTATION %d", orientation];

似乎 setStatusBarOrientation 没有触发自动布局,所以我尝试在之后添加此代码,但没有效果:

    [super updateViewConstraints];
    [self.view updateConstraints];

我意识到 Apple 希望将设备方向留给用户。但是,我希望能够在不处于“单挑”模式时支持横向模式。

我是否遗漏了一些能够强制改变方向的东西?

【问题讨论】:

    标签: ios xcode ios7 autolayout autorotate


    【解决方案1】:

    2020 年 Swift 5:

    override var supportedInterfaceOrientations:UIInterfaceOrientationMask {
        return .portrait
    }
    

    【讨论】:

      【解决方案2】:

      对于像我这样努力让@Sunny Shah 接受在 iPad 上工作的答案的人。您需要在项目设置中设置“需要全屏”复选框。请注意,这将阻止您的应用在可能或不可接受的多任务模式下工作。

      【讨论】:

        【解决方案3】:

        对于 iOS 7 和 8:

        目标-C:

        NSNumber *value = [NSNumber numberWithInt:UIInterfaceOrientationLandscapeLeft];
        [[UIDevice currentDevice] setValue:value forKey:@"orientation"];
        

        斯威夫特 3+:

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

        我叫它- viewDidAppear:

        【讨论】:

        • 这行得通!现在,我担心这会产生编译器错误,人们似乎对 objc_msgSend 皱眉头(请参阅stackoverflow.com/questions/17263354/…),但这有效!我将尝试看看是否有办法更干净地做到这一点。
        • 当我尝试这个时,它只适用于 iPad。 iPad Mini (iOS 7.1) 和 iPhone 5s (iOS 7.1) 都不能工作。适用于所有设备的模拟器工作
        • @Jeef 确保您已为应用程序设置所有方向模式,
        • 有什么方法可以让我在没有动画的情况下让它工作?
        • 这是黄金,尤其是当您使用 UIViewController.attemptRotationToDeviceOrientation() 和 stackoverflow.com/questions/24928057/… 时,您可以获得任何您想要的视图设置!
        【解决方案4】:

        这对我很有效....

        NSNumber *value = [NSNumber numberWithInt:UIDeviceOrientationPortrait];
        [[UIDevice currentDevice] setValue:value forKey:@"orientation"];
        

        【讨论】:

          【解决方案5】:

          这里是 iOS 7、8、9、10 的完整示例,如何将应用方向更改为当前相反

          Objective-C

          - (void)flipOrientation
          {
              NSNumber *value;
              UIInterfaceOrientation currentOrientation = [[UIApplication sharedApplication] statusBarOrientation];
              if(UIInterfaceOrientationIsPortrait(currentOrientation))
              {
                  if(currentOrientation == UIInterfaceOrientationPortrait)
                  {
                      value = [NSNumber numberWithInt:UIInterfaceOrientationPortraitUpsideDown];
                  }
                  else //if(currentOrientation == UIInterfaceOrientationPortraitUpsideDown)
                  {
                      value = [NSNumber numberWithInt:UIInterfaceOrientationPortrait];
                  }
              }
              else
              {
                  if(currentOrientation == UIInterfaceOrientationLandscapeRight)
                  {
                      value = [NSNumber numberWithInt:UIInterfaceOrientationLandscapeLeft];
                  }
                  else //if(currentOrientation == UIInterfaceOrientationLandscapeLeft)
                  {
                      value = [NSNumber numberWithInt:UIInterfaceOrientationLandscapeRight];
                  }
              }
              [[UIDevice currentDevice] setValue:value forKey:@"orientation"];
              [UIViewController attemptRotationToDeviceOrientation];
          }
          

          斯威夫特 3

          func flipOrientation() -> Void
          {
              let currentOrientation : UIInterfaceOrientation = UIApplication.shared.statusBarOrientation
              var value : Int = 0;
              if(UIInterfaceOrientationIsPortrait(currentOrientation))
              {
                  if(currentOrientation == UIInterfaceOrientation.portrait)
                  {
                      value = UIInterfaceOrientation.portraitUpsideDown.rawValue
                  }
                  else //if(currentOrientation == UIInterfaceOrientation.portraitUpsideDown)
                  {
                      value = UIInterfaceOrientation.portrait.rawValue
                  }
              }
              else
              {
                  if(currentOrientation == UIInterfaceOrientation.landscapeRight)
                  {
                      value = UIInterfaceOrientation.landscapeLeft.rawValue
                  }
                  else //if(currentOrientation == UIInterfaceOrientation.landscapeLeft)
                  {
                      value = UIInterfaceOrientation.landscapeRight.rawValue
                  }
              }
              UIDevice.current.setValue(value, forKey: "orientation")
              UIViewController.attemptRotationToDeviceOrientation()
          }
          

          【讨论】:

          • 这个解决方案使用反射技术,因为iOS没有/显示任何公共方法来实现这个目标,所以我们必须通过反射来完成
          • 好吧,最终我们正在修改 Apple 不希望我们做的事情:UIDeviceorientation 属性是现成的。您是否曾经因为此代码而被拒绝过应用?
          • 抱歉,我无法回答,因为我使用这段代码的应用程序是在 iTunes 之外发布的,您是否尝试过提交这样的代码?
          【解决方案6】:

          此解决方案允许您通过临时覆盖 UIDevice.current.orientation 的值,然后要求系统旋转界面以匹配设备的旋转来强制某个界面方向:

          重要提示:这是一个 hack,可能随时停止工作

          在应用的根视图控制器中添加以下内容:

          class RootViewController : UIViewController {
              private var _interfaceOrientation: UIInterfaceOrientation = .portrait
              override var supportedInterfaceOrientations: UIInterfaceOrientationMask { return UIInterfaceOrientationMask(from: _interfaceOrientation) }
              override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation { return _interfaceOrientation }
          
              override func viewDidLoad() {
                  super.viewDidLoad()
                  // Register for notifications
                  NotificationCenter.default.addObserver(self, selector: #selector(RootViewController.handleInterfaceOrientationChangeRequestedNotification(_:)), name: .interfaceOrientationChangeRequested, object: nil)
              }
          
              deinit { NotificationCenter.default.removeObserver(self) }
          
              func handleInterfaceOrientationChangeRequestedNotification(_ notification: Notification) {
                  guard let interfaceOrientation = notification.object as? UIInterfaceOrientation else { return }
                  _interfaceOrientation = interfaceOrientation
                  // Set device orientation
                  // Important:
                  // • Passing a UIDeviceOrientation here doesn't work, but passing a UIInterfaceOrientation does
                  // • This is a hack, and could stop working at any moment
                  UIDevice.current.setValue(interfaceOrientation.rawValue, forKey: "orientation")
                  // Rotate the interface to the device orientation we just set
                  UIViewController.attemptRotationToDeviceOrientation()
              }
          }
          
          private extension UIInterfaceOrientationMask {
          
              init(from interfaceOrientation: UIInterfaceOrientation) {
                  switch interfaceOrientation {
                  case .portrait: self = .portrait
                  case .landscapeLeft: self = .landscapeLeft
                  case .landscapeRight: self = .landscapeRight
                  case .portraitUpsideDown: self = .portraitUpsideDown
                  case .unknown: self = .portrait
                  }
              }
          }
          
          extension Notification.Name {
              static let interfaceOrientationChangeRequested = Notification.Name(rawValue: "interfaceOrientationChangeRequested")
          }
          

          确保在“部署信息”下检查所有界面方向:

          请求在您需要的地方更改界面方向:

          NotificationCenter.default.post(name: .interfaceOrientationChangeRequested, object: UIInterfaceOrientation.landscapeRight)
          

          【讨论】:

            【解决方案7】:

            如果您有一个应该保持纵向模式的 UIViewController,只需添加此覆盖即可。

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

            最好的部分是显示此视图时没有动画,它只是已经处于正确的方向。

            【讨论】:

              【解决方案8】:
              1. 将此语句添加到AppDelegate.h

                //whether to allow cross screen marker 
                @property (nonatomic, assign) allowRotation BOOL;
                
              2. 把这段代码写到AppDelegate.m

                - (UIInterfaceOrientationMask) application: (UIApplication *) supportedInterfaceOrientationsForWindow: application (UIWindow *) window {
                    If (self.allowRotation) {
                        UIInterfaceOrientationMaskAll return;
                    }
                    UIInterfaceOrientationMaskPortrait return;
                }
                
              3. 更改代理应用的allowRotation属性

              【讨论】:

              • 欢迎来到 StackOverflow。能否请您更好地格式化代码以使其更具可读性?
              【解决方案9】:

              我遇到了和你类似的问题。我需要为某些屏幕(如登录)锁定设备方向并允许在其他屏幕上旋转。

              经过一些更改并遵循以下一些答案,我做到了:

              • 启用项目 Info.plist 中的所有方向。

              • 在我需要设备不旋转的那些 ViewController 中禁用方向,例如在我的情况下的登录屏幕中。我需要在这个 VC 中重写 shouldAutorotate 方法:

              -(BOOL)shouldAutorotate{ return NO; }

              希望这对你有用。

              【讨论】:

                【解决方案10】:
                NSNumber *value = [NSNumber numberWithInt:UIInterfaceOrientationLandscapeLeft]; [[UIDevice currentDevice] setValue:value forKey:@"orientation"];
                

                确实有效,但您必须在视图控制器中返回 shouldAutorotate 并选择 YES

                - (BOOL)shouldAutorotate
                {
                    return self.shouldAutoRotate;
                }
                

                但如果你这样做,如果用户旋转设备,你的 VC 将自动旋转...... 所以我把它改成了:

                @property (nonatomic, assign) BOOL shouldAutoRotate;
                
                - (BOOL)shouldAutorotate
                {
                    return self.shouldAutoRotate;
                }
                

                我打电话给

                - (void)swithInterfaceOrientation:(UIInterfaceOrientation)orientation
                {
                    self.rootVC.shouldAutoRotate = YES;
                
                    NSNumber *value = [NSNumber numberWithInt: orientation];
                    [[UIDevice currentDevice] setValue:value forKey:@"orientation"];
                }
                

                通过单击按钮强制新方向。 要将 shouldAutoRotate 设置为 NO,我添加到我的 rootVC

                - (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
                {
                    self.shouldAutoRotate = NO;
                }
                

                PS:这种解决方法也适用于所有模拟器。

                【讨论】:

                • Funny.. 我正在寻找类似的东西,我发现了什么? iOS 11 的唯一解决方案,我在 1.5 年前自己编写了它。当我想为解决方案 +1 时,我意识到了这一点:D
                • 哇...先生,您值得一票,这是迄今为止我见过的最干净的解决方案
                【解决方案11】:

                如果您只想要纵向模式,在 iOS 9 (Xcode 7) 中,您可以:

                • 转到 Info.plist
                • 选择“支持的界面方向”项
                • 删除“横向(左主页按钮)”和“横向(右主页按钮)”

                【讨论】:

                  【解决方案12】:

                  基础 UINavigationController 应该有以下回调,以便子项可以决定他们想要的方向。

                  -(NSUInteger)supportedInterfaceOrientations {
                      UIViewController *topVC = self.topViewController;
                      return topVC.supportedInterfaceOrientations;
                  }
                  
                  -(BOOL)shouldAutorotate {
                     UIViewController *topVC = self.topViewController;
                     return [topVC shouldAutorotate];
                  }
                  

                  【讨论】:

                    【解决方案13】:

                    这适用于Xcode 6 和 5。

                    - (BOOL)shouldAutorotate {
                        return YES;
                    }
                    - (NSUInteger)supportedInterfaceOrientations {
                        return (UIInterfaceOrientationMaskPortrait);
                    }
                    

                    【讨论】:

                      【解决方案14】:

                      使用这个。完美解决方向问题..ios7及更早版本

                      [[UIDevice currentDevice] setValue:
                          [NSNumber numberWithInteger: UIInterfaceOrientationPortrait]
                              forKey:@"orientation"];
                      

                      【讨论】:

                      • 甚至可以在 iOS 8 上使用。完美。
                      • 这已被 Apple app review 团队(目前)接受,但我不会说它“被 Apple 接受”。也就是说,使用键值编码是一种 hack,并且可能在以后的任何 iOS 版本中停止工作。
                      • @Arpan 应该在哪里添加此代码,我已在 viewwillapear 添加但无法正常工作,请解释。
                      • @arpan_techisavy:我已经尝试了所有,didload,willapear,didapear。由于方向问题,我的一个项目卡在了 ios8 中。我想要所有的纵向视图,但一个视图(视频播放器)具有横向模式,我已经在情节提要中检查了纵向和左侧横向模式。我尝试了所有代码但没有工作,我想我错过了一些东西。请指教,如果你有演示,请野兔。
                      • @Josef Rysanek:我已经尝试了所有,didload,willapear,didapear。由于方向问题,我的一个项目卡在 IOS-8 中。我想要所有的纵向视图,但一个视图(视频播放器)具有横向模式,我已经在情节提要中检查了纵向和左侧横向模式。我尝试了所有代码但没有工作,我想我错过了一些东西。请指教,如果你有演示,请野兔。
                      【解决方案15】:

                      与您的代码一起尝试。

                      -(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation  
                      
                      -(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
                      

                      一旦用户选择任何选项,然后调用此方法,因为用户可以处于横向模式,然后他可以在同一视图控制器中仅设置纵向模式,因此视图应自动移动到纵向模式,因此在该按钮动作中调用此方法

                      -(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
                      

                      【讨论】:

                      • 我不太确定您的建议是什么,但在 iOS7 中,shouldAutorotateToInterfaceOrientation 似乎已被弃用,取而代之的是我已经实现的 shouldAutorotate。
                      • 对不起,我的错误。仅使用 shouldAutorotate 和 -(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
                      • 我在选择单挑模式的方法中添加了[self willRotateToInterfaceOrientation:UIInterfaceOrientationPortrait duration:ROTATE_VIEW_DELAY];,替换了上面 Sunny Shah 的 objc_msgSend 答案。唉,对显示仍然没有影响。据我了解,当显示器即将旋转时,会调用“willrotate”,以响应用户物理移动设备。
                      • @John Stewart 我们可以独立调用这个方法...但是告诉我你在这个方法中写的代码是什么。如果可能的话,你能用你为方向编写的所有代码(你使用的所有方法)更新你的问题
                      • Charan Giri - 我创建了一个简单的测试项目来演示:github.com/johnstewart/RotateTest。该项目是一个简单的 iPad 应用程序,带有一个 ImageView,以及一个用于打开和关闭“仅纵向”的开关。我从一个空白的仅查看项目开始,并添加了这个属性:@property (nonatomic) bool modePortraitOnly;
                      【解决方案16】:

                      对我有用的唯一方法是呈现虚拟模式视图控制器。

                      UIViewController* dummyVC = [[UIViewController alloc] init];
                      dummyVC.view = [[UIView alloc] init];
                      [self presentModalViewController:dummyVC animated:NO];
                      [self dismissModalViewControllerAnimated:NO];
                      

                      当模式视图控制器被关闭时,您的 VC 将被要求更新界面方向。

                      奇怪的是 UINavigationController 在推送/弹出具有不同支持的界面方向的子视图控制器时正是这样做的(在 iOS 6.1、7.0 上测试)。

                      【讨论】:

                      • 正如其他地方提到的,这不再适用于 iOS 8。
                      【解决方案17】:

                      如果您想将应用的主视图锁定为纵向,但想以横向打开弹出视图,并且您像我一样使用 tabBarController 作为 rootViewController,您可以在 AppDelegate 上使用此代码。

                      AppDelegate.h

                      @interface AppDelegate : UIResponder <UIApplicationDelegate, UITabBarControllerDelegate>
                      
                      @property (strong, nonatomic) UIWindow *window;
                      @property (strong, nonatomic) UITabBarController *tabBarController;
                      
                      @end
                      

                      AppDelegate.m

                      - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
                      {
                          self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
                      
                          // Create a tab bar and set it as root view for the application
                          self.tabBarController = [[UITabBarController alloc] init];
                          self.tabBarController.delegate = self;
                          self.window.rootViewController = self.tabBarController;
                      
                          ...
                      }
                      
                      - (NSUInteger)tabBarControllerSupportedInterfaceOrientations:(UITabBarController *)tabBarController
                      {
                          return UIInterfaceOrientationMaskPortrait;
                      }
                      
                      - (UIInterfaceOrientation)tabBarControllerPreferredInterfaceOrientationForPresentation:(UITabBarController *)tabBarController
                      {
                          return UIInterfaceOrientationPortrait;
                      }
                      

                      效果很好。

                      在您想要以横向显示的 viewController 中,您只需使用以下内容:

                      - (NSUInteger)supportedInterfaceOrientations {
                          return UIInterfaceOrientationMaskLandscape;
                      }
                      
                      - (BOOL)shouldAutorotate {
                          return YES;
                      }
                      

                      【讨论】:

                      • 有趣。当我重组我的应用程序时,我会解决这个问题。目前没有容器视图控制器,但我一直打算将它包装成一个。谢谢。
                      • 对我来说最后一段代码运行良好!但我也没有集成任何UINavigationControllerUITabBarController,所以可能会有一点不同。
                      【解决方案18】:

                      当条件发生变化时,你需要调用attemptRotationToDeviceOrientation (UIViewController)让系统调用你的supportedInterfaceOrientations

                      【讨论】:

                      • 在我的代码中,我尝试按照您的建议将应用程序 setStatusBarOrientation 方法替换为 [UIViewController attemptRotationToDeviceOrientation]。不过好像没什么效果,唉。
                      • 我也发现了这个帖子,虽然你的建议似乎是“正确答案”,但它不起作用:stackoverflow.com/questions/19125263/…
                      • 这仅在设备方向与UIViewController 的方向不同时才有效
                      猜你喜欢
                      • 2011-12-19
                      • 2013-10-01
                      • 1970-01-01
                      • 2013-06-02
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2012-09-20
                      相关资源
                      最近更新 更多