【问题标题】:Is there a documented way to set the iPhone orientation?是否有记录的方法来设置 iPhone 方向?
【发布时间】:2010-09-15 22:58:06
【问题描述】:

我有一个应用程序,我希望在某些视图中支持设备旋转,但其他视图在横向模式下并不特别有意义,因此当我交换视图时,我想强制将旋转设置为纵向。

UIDevice 上有一个未记录的属性设置器可以解决问题,但显然会生成编译器警告,并且可能会在 SDK 的未来版本中消失。

[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationPortrait];

是否有任何记录在案的方法来强制定向?

更新:我想我会提供一个示例,因为我不是在寻找 shouldAutorotateToInterfaceOrientation,因为我已经实现了它。

我希望我的应用在视图 1 中支持横向和纵向,但在视图 2 中仅支持纵向。我已经为所有视图实现了 shouldAutorotateToInterfaceOrientation 但如果用户在视图 1 中处于横向模式然后切换到视图 2,我想要强制手机旋转回纵向。

【问题讨论】:

  • 我对此提出了一个错误,建议 Apple 要么公开上述 API,要么尊重 shouldRotate 方法返回的 YES,NO,当视图首次加载时,而不仅仅是手机旋转时.
  • 我提交了一个错误,要求公开 setOrientation
  • [[UIDevice currentDevice] setOrientation:UIInterfaceOrientationPortrait];此方法已弃用,不再存在。

标签: iphone objective-c cocoa-touch


【解决方案1】:

如果你使用的是 UIViewControllers,有这个方法:

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation

为包含您不想旋转的视图的视图控制器返回 NO

More info here

【讨论】:

  • 停止旋转,我正在寻找的是如果用户处于横向状态,则强制旋转回纵向。我在上面添加了一个例子来更好地表达我的意思。
【解决方案2】:

我认为这在运行时是不可能的,尽管您当然可以对您的 UI 应用 90 度变换。

【讨论】:

  • 我最初发布的代码是可能的,你永远不知道它什么时候会停止工作;)
【解决方案3】:

这是我使用的。 (您会收到一些编译警告,但它在模拟器和 iPhone 中都可以使用)

[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeRight];
[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationLandscapeRight];

【讨论】:

  • 我发现这足以让它在 4.1 中也能正常工作。但是,由于我的状态栏设置为隐藏,我在顶部获得 20 像素间隙:(
  • setOrientation 是私有 api,使用它的应用程序被拒绝。
【解决方案4】:

据我所知,setOrientation: 方法不起作用(或者可能不再起作用)。这是我正在做的事情:

首先,将此定义放在文件顶部,就在您的#imports 下方:

#define degreesToRadian(x) (M_PI * (x) / 180.0)

那么,在viewWillAppear: 方法中

[[UIApplication sharedApplication] setStatusBarHidden:YES animated:NO];     
if (self.interfaceOrientation == UIInterfaceOrientationPortrait) {  
    self.view.transform = CGAffineTransformIdentity;
    self.view.transform = CGAffineTransformMakeRotation(degreesToRadian(90));
    self.view.bounds = CGRectMake(0.0, 0.0, 480, 320);
}

如果你想让它被动画化,那么你可以将整个东西包装在一个动画块中,如下所示:

[UIView beginAnimations:@"View Flip" context:nil];
[UIView setAnimationDuration:1.25];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];

[[UIApplication sharedApplication] setStatusBarHidden:YES animated:NO];     
if (self.interfaceOrientation == UIInterfaceOrientationPortrait) {  
    self.view.transform = CGAffineTransformIdentity;
    self.view.transform = CGAffineTransformMakeRotation(degreesToRadian(90));
    self.view.bounds = CGRectMake(0.0, 0.0, 480, 320);
}
[UIView commitAnimations];

然后,在您的纵向模式控制器中,您可以执行相反的操作 - 检查其当前是否为横向,如果是,请将其旋转回纵向。

【讨论】:

  • 你应该旋转 self.window.view 而不是 self.view。如果您的视图顶部有 UITabBar 或其他控制器,这将非常方便。
【解决方案5】:

如果你想强制它从纵向旋转到横向,这里是代码。请注意,您需要调整视图的中心。我注意到我的视图没有放在正确的位置。否则,它工作得很好。感谢您的提示。

if(UIInterfaceOrientationIsLandscape(self.interfaceOrientation)){

        [UIView beginAnimations:@"View Flip" context:nil];
        [UIView setAnimationDuration:0.5f];
        [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];

        self.view.transform = CGAffineTransformIdentity;
        self.view.transform = CGAffineTransformMakeRotation(degreesToRadian(90));
        self.view.bounds = CGRectMake(0.0f, 0.0f, 480.0f, 320.0f);
        self.view.center = CGPointMake(160.0f, 240.0f);

        [UIView commitAnimations];
}

【讨论】:

  • 如果我添加了 UIAlertView 也可以吗?我试过CGAffineTransformMakeRotation 视图被旋转但 UIAlertView 仍然根据状态栏方向,即 -degreeToRadian(angle) ?你有得到这样的东西吗?
  • 使用UIAlertView 执行此操作的最简单方法是将视图控制器设置为alertView 的委托,然后在- (void) didPresentAlertView:(UIAlertView *) alertView 上,根据当前[UIApplication sharedApplication].statusBarOrientation 执行旋转。如果你不为它制作动画,看起来应该不会太奇怪。
【解决方案6】:

这不再是后来的 iPhone 3.1.2 SDK 的问题。它现在似乎尊重被推回堆栈的视图的请求方向。这可能意味着您需要检测较旧的 iPhone OS 版本,并且仅在最新版本之前应用 setOrientation。

目前尚不清楚 Apple 的静态分析是否会理解您正在解决旧的 SDK 限制。 Apple 已告知我个人在下一次更新时删除方法调用,因此我还不确定对旧设备进行 hack 是否会通过审批流程。

【讨论】:

  • iPhone Simulator 3.3.2 并非如此。它仍然无法正常工作。
  • @user 当你在堆栈中推送视图时如何请求方向?
  • 你可以绕过苹果的静态分析。我通过使用 performSelector 并依靠 Obj-C 的强大活力成功地使用了未记录的代码。
  • @KennethBallenegger 我记得在指南和/或协议中读到,隐藏此类内容可能会导致您的应用被删除,甚至您的帐户也可能被删除。我不会冒险仅仅使用未记录的 API。
【解决方案7】:

我遇到了一个问题,我在屏幕上有一个UIViewController,在一个UINavigationController,横向。但是,当在流程中推送下一个视图控制器时,我需要设备返回纵向。

我注意到,shouldAutorotateToInterfaceOrientation: 方法不会在将新视图控制器压入堆栈时调用,但会在视图控制器从堆栈弹出时调用

利用这一点,我在我的一个应用程序中使用了这段 sn-p 代码:

- (void)selectHostingAtIndex:(int)hostingIndex {

    self.transitioning = YES;

    UIViewController *garbageController = [[[UIViewController alloc] init] autorelease];
    [self.navigationController pushViewController:garbageController animated:NO];
    [self.navigationController popViewControllerAnimated:NO];

    BBHostingController *hostingController = [[BBHostingController alloc] init];
    hostingController.hosting = [self.hostings objectAtIndex:hostingIndex];
    [self.navigationController pushViewController:hostingController animated:YES];
    [hostingController release];

    self.transitioning = NO;
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
    if (self.transitioning)
        return (toInterfaceOrientation == UIInterfaceOrientationPortrait);
    else
        return YES;
}

基本上,通过创建一个空视图控制器,将其压入堆栈并立即将其弹出,可以使界面恢复到纵向位置。弹出控制器后,我只需推动我最初打算推动的控制器。从视觉上看,它看起来很棒 - 用户永远不会看到空的、任意的视图控制器。

【讨论】:

  • 嗨,我刚刚实现了你的方法,在推动 VC 之前强制它处于正确的方向,但我发现它只在强制纵向方向时有效(如果当前视图是纵向并且你希望强制下一个进入横向,它不会调用 shouldAutorotateToInterfaceOrientation: 弹出时)。您是否找到一种解决方法来强制查看横向?
  • 有趣 - 我没有遇到过这个问题,抱歉。我会尝试玩一下推和弹出的顺序,你很有可能会想出一些东西。
【解决方案8】:

我找到了一个解决方案并用法语写了一些东西(但代码是英文的)。 here

方法是将控制器添加到窗口视图中(控制器必须具有良好的shouldRotate....函数实现)。

【讨论】:

    【解决方案9】:

    我一直在挖掘和挖掘寻找一个好的解决方案。发现这篇博文可以解决问题:从键 UIWindow 中删除最外层视图并再次添加,然后系统将从视图控制器中重新查询 shouldAutorotateToInterfaceOrientation: 方法,强制应用正确的方向。 看到它:iphone forcing uiview to reorientate

    【讨论】:

    • 这个解决方案最适合我。重要的是,呈现空 modalViewController 的修复会导致将视图控制器推入堆栈的动画不正确。这个 UIWindow 方法不会遇到同样的问题。
    【解决方案10】:

    FWIW,这是我手动设置方向的实现(进入应用的根视图控制器,natch):

    -(void)rotateInterfaceToOrientation:(UIDeviceOrientation)orientation{
    
        CGRect bounds = [[ UIScreen mainScreen ] bounds ];
        CGAffineTransform t;
        CGFloat r = 0;
        switch ( orientation ) {
            case UIDeviceOrientationLandscapeRight:
                r = -(M_PI / 2);
                break;
            case UIDeviceOrientationLandscapeLeft:
                r  = M_PI / 2;
                break;
        }
        if( r != 0 ){
            CGSize sz = bounds.size;
            bounds.size.width = sz.height;
            bounds.size.height = sz.width;
        }
        t = CGAffineTransformMakeRotation( r );
    
        UIApplication *application = [ UIApplication sharedApplication ];
    
        [ UIView beginAnimations:@"InterfaceOrientation" context: nil ];
        [ UIView setAnimationDuration: [ application statusBarOrientationAnimationDuration ] ];
        self.view.transform = t;
        self.view.bounds = bounds;
        [ UIView commitAnimations ];
    
        [ application setStatusBarOrientation: orientation animated: YES ];     
    }
    

    结合以下UINavigationControllerDelegate 方法(假设您使用的是UINavigationController):

    -(void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated{
        // rotate interface, if we need to
        UIDeviceOrientation orientation = [[ UIDevice currentDevice ] orientation ];
        BOOL bViewControllerDoesSupportCurrentOrientation = [ viewController shouldAutorotateToInterfaceOrientation: orientation ];
        if( !bViewControllerDoesSupportCurrentOrientation ){
            [ self rotateInterfaceToOrientation: UIDeviceOrientationPortrait ];
        }
    }
    

    它负责根据传入的UIViewController 是否支持当前设备方向来旋转根视图。最后,您需要将rotateInterfaceToOrientation 连接到实际的设备方向更改,以模仿标准的 iOS 功能。将此事件处理程序添加到同一个根视图控制器:

    -(void)onUIDeviceOrientationDidChangeNotification:(NSNotification*)notification{
        UIViewController *tvc = self.rootNavigationController.topViewController;
        UIDeviceOrientation orientation = [[ UIDevice currentDevice ] orientation ];
        // only switch if we need to (seem to get multiple notifications on device)
        if( orientation != [[ UIApplication sharedApplication ] statusBarOrientation ] ){
            if( [ tvc shouldAutorotateToInterfaceOrientation: orientation ] ){
                [ self rotateInterfaceToOrientation: orientation ];
            }
        }
    }
    

    最后,在initloadview 中注册UIDeviceOrientationDidChangeNotification 通知,如下所示:

    [[ NSNotificationCenter defaultCenter ] addObserver: self
                                               selector: @selector(onUIDeviceOrientationDidChangeNotification:)
                                                   name: UIDeviceOrientationDidChangeNotification
                                                 object: nil ];
    [[ UIDevice currentDevice ] beginGeneratingDeviceOrientationNotifications ];
    

    【讨论】:

    • 这是此页面上唯一一个我能够成功适应我的导航控制器代码的答案。一个小问题是旋转后的导航栏不会像通过正常机制旋转时那样在纵向和横向之间改变高度。
    • @DanDyer 你解决了导航栏高度错误的问题吗? (特别是在ios6中高度是错误的)
    • @AlexWien 我最终设法说服客户我们不应该这样做。对于 iOS 6,您可能需要使用新的 shouldAutorotate 方法(请参阅 stackoverflow.com/a/12586002/5171)。
    • @DanDyer 昨天解决了它,ViewDidAppear 中导航和状态栏的简单隐藏和显示解决了它。现在一切正常(SDK ios6,目标 ios5)我必须用目标 ios6 重新测试
    【解决方案11】:

    这是很久以后的事,但以防万一有人不使用导航控制器和/或不希望使用未记录的方法:

    UIViewController *c = [[UIViewController alloc]init];
    [self presentModalViewController:c animated:NO];
    [self dismissModalViewControllerAnimated:NO];
    [c release];
    

    展示和关闭一个普通视图控制器就足够了。

    显然,您仍然需要在覆盖 shouldAutorotateToInterfaceOrientation 时确认或拒绝方向。但这会导致 shouldAutorotate... 被系统再次调用。

    【讨论】:

    • 太棒了,这个没有标记为这篇文章的正确答案,但这解决了我的问题stackoverflow.com/questions/5030315的一半。模型视图技巧“强制”纵向,您是否也知道强制横向?
    • 哈哈哈!!这太愚蠢了!但它有效! - 当然可以! :D
    • 太棒了,为此苦苦挣扎了很长时间。谢谢!伴侣
    • 这似乎适用于 iOS5 和 iOS6:[self presentViewController:[UIViewController new] animated:NO completion:^{ [self dismissViewControllerAnimated:NO completion:nil]; }];
    • 这似乎适用于 iOS 7 和 iOS 8,但有一个明显的黑屏闪烁进入视图(主要是 iOS 8)。
    【解决方案12】:

    这对我有用(谢谢亨利·库克):

    我的目标是只处理横向方向的变化。

    初始化方法:

    [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(orientationChanged:)
                                                 name:UIDeviceOrientationDidChangeNotification
                                               object:nil];
    

    - (void)orientationChanged:(NSNotification *)notification {    
        //[[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];
        UIDeviceOrientation orientation = [UIDevice currentDevice].orientation;
        CGRect bounds = [[ UIScreen mainScreen ] bounds ];
        CGAffineTransform t;
        CGFloat r = 0;
        switch ( orientation ) {
            case UIDeviceOrientationLandscapeRight:
                r = 0;
                NSLog(@"Right");
                break;
            case UIDeviceOrientationLandscapeLeft:
                r  = M_PI;
                NSLog(@"Left");
                break;
            default:return;
        }
    
        t = CGAffineTransformMakeRotation( r );
    
        UIApplication *application = [ UIApplication sharedApplication ];
        [ UIView beginAnimations:@"InterfaceOrientation" context: nil ];
        [ UIView setAnimationDuration: [ application statusBarOrientationAnimationDuration ] ];
        self.view.transform = t;
        self.view.bounds = bounds;
        [ UIView commitAnimations ];
    
        [ application setStatusBarOrientation: orientation animated: YES ];
    }
    

    【讨论】:

      【解决方案13】:

      乔希的回答对我来说很好。

      但是,我更喜欢发布 “方向确实发生了变化,请更新 UI” 通知。当视图控制器收到此通知时,它会调用shouldAutorotateToInterfaceOrientation:,允许您通过返回YES 来设置任何方向。

      [[NSNotificationCenter defaultCenter] postNotificationName:UIDeviceOrientationDidChangeNotification object:nil];
      

      唯一的问题是这会强制重新定向没有动画。您需要在 beginAnimations:commitAnimations 之间换行以实现平滑过渡。

      希望对您有所帮助。

      【讨论】:

        【解决方案14】:

        我有一个应用程序,我希望在某些视图中支持设备旋转,但其他在横向模式下并不特别有意义,因此当我交换视图时,我想强制将旋转设置为纵向。

        我意识到这个帖子中的上述原始帖子现在已经很老了,但我遇到了类似的问题 - 即。我的应用程序中的所有屏幕都是纵向的,除了一个屏幕,用户可以在横向和纵向之间旋转。

        这很简单,但与其他帖子一样,我希望应用程序在返回上一屏幕时自动返回纵向,而不管当前设备方向如何。

        我实施的解决方案是在横向模式下隐藏导航栏,这意味着用户只能在纵向模式下返回之前的屏幕。因此,所有其他屏幕只能是纵向的。

        - (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)pInterfaceOrientation {
            BOOL lHideNavBar = self.interfaceOrientation == UIInterfaceOrientationPortrait ? NO : YES;
            [self.navigationController setNavigationBarHidden:lHideNavBar animated:YES];
        }
        

        这对我的应用程序还有一个额外的好处,那就是在横向模式下有更多可用的屏幕空间。这很有用,因为相关屏幕用于显示 PDF 文件。

        希望这会有所帮助。

        【讨论】:

          【解决方案15】:

          有一种简单的方法可以以编程方式强制 iPhone 进入必要的方向 - 使用 kdbdallasJosh 已经提供的两个答案:

          //will rotate status bar
          [[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeRight];
          
          
          //will re-rotate view according to statusbar
          UIViewController *c = [[UIViewController alloc]init];
          [self presentModalViewController:c animated:NO];
          [self dismissModalViewControllerAnimated:NO];
          [c release];
          

          像魅力一样工作:)

          编辑:

          对于 iOS 6,我需要添加此功能: (适用于模态视图控制器)

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

          【讨论】:

          • 对我来说,它不像一个魅力:导航栏和工具栏缩小到它们在横向视图中的高度。删除和重新添加最顶层视图的方法保持正确的高度,但也有一个缺点:我的导航栏向下移动了一点(就像启用了thering bar一样)
          • 它适用于我,无论是在模态还是推送视图控制器上。但仅在 iOS 5 上。:(
          • 是的,我也让它在 ios5 中工作。现在在 ios6 中,一旦模式横向视图控制器被解除,导航栏的高度就会缩小
          【解决方案16】:

          最后我很容易解决了这个问题。我尝试了上面的每一个建议,但仍然失败,所以这是我的解决方案:

          在需要保持横向(左或右)的 ViewController 中,我监听方向变化:

              [[NSNotificationCenter defaultCenter] addObserver:self
                                                   selector:@selector(didRotate:)
                                                       name:UIDeviceOrientationDidChangeNotification object:nil];
          

          然后在 didRotate:

          - (void) didRotate:(NSNotification *)notification
          {   if (orientationa == UIDeviceOrientationPortrait) 
              {
                  if (hasRotated == NO) 
                  {
                      NSLog(@"Rotating to portait");
                      hasRotated = YES;
                      [UIView beginAnimations: @"" context:nil];
                      [UIView setAnimationDuration: 0];
                      self.view.transform = CGAffineTransformIdentity;
                      self.view.transform = CGAffineTransformMakeRotation(DEGREES_TO_RADIANS(-90));
                      self.view.bounds = CGRectMake(0.0f, 0.0f, 480.0f, 320.0f);
                      self.view.frame = CGRectMake(0.0f, 0.0f, 480.0f, 320.0f);
                      [UIView commitAnimations];
          
              }
          }
          else if (UIDeviceOrientationIsLandscape( orientationa))
          {
              if (hasRotated) 
              {
                  NSLog(@"Rotating to lands");
                  hasRotated = NO;
                  [UIView beginAnimations: @"" context:nil];
                  [UIView setAnimationDuration: 0];
                  self.view.transform = CGAffineTransformIdentity;
                  self.view.transform = CGAffineTransformMakeRotation(DEGREES_TO_RADIANS(0));
                  self.view.bounds = CGRectMake(0.0f, 0.0f, 320.0f, 480.0f);
                  self.view.frame = CGRectMake(0.0f, 0.0f, 320.0f, 480.0f);
                  [UIView commitAnimations];
          
              }
          }
          

          请记住任何使用自动调整大小的超级视图/子视图,因为 view.bounds/frame 正在被显式重置...

          这种保持视图横向的方法的唯一警告是,必须发生方向之间的固有动画切换,而最好让它看起来没有变化。

          【讨论】:

            【解决方案17】:

            iOS 6 解决方案:

            [[[self window] rootViewController] presentViewController:[[UIViewController alloc] init] animated:NO completion:^{
                [[[self window] rootViewController] dismissViewControllerAnimated:NO completion:nil];
            }];
            

            确切的代码取决于每个应用程序以及您放置它的位置(我在我的 AppDelegate 中使用它)。将[[self window] rootViewController] 替换为您使用的内容。我使用的是UITabBarController

            【讨论】:

              猜你喜欢
              • 2011-09-25
              • 1970-01-01
              • 1970-01-01
              • 2017-08-28
              • 1970-01-01
              • 1970-01-01
              • 2011-07-28
              • 1970-01-01
              • 2020-09-19
              相关资源
              最近更新 更多