有记录的方式设置iPhone方向吗?


94

我有一个应用,希望在某些视图中支持设备旋转,但在风景模式下,其他应用尤其没有意义,因此当我换掉视图时,我想强制将旋转设置为纵向。

UIDevice上有一个未记录的属性设置器,可以完成此操作,但显然会生成编译器警告,并且可能在以后的SDK版本中消失。

[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationPortrait];

是否有任何记录的方式来强制定位?

更新:我以为我将提供一个示例,因为我已经实现了该示例,因此我没有寻找shouldAutorotateToInterfaceOrientation。

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


11
我已经对此提交了一个错误,建议苹果在视图第一次加载时,而不是在手机旋转时,应公开上述API或采用YES,NOD应该从shouldRotate方法返回。
rustyshelf,

2
我提交了一个错误,要求公开setOrientation
Jessedc

1
[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationPortrait]; 此方法已被弃用,并且不再存在。
Hikmat Khan 2012年

Answers:


6

在更高版本的iPhone 3.1.2 SDK上,这不再是问题。现在,它似乎符合将视图推回堆栈时所要求的方向。这可能意味着您需要检测较旧的iPhone OS版本,并且仅在setOrientation早于最新版本时才应用。

目前尚不清楚Apple的静态分析是否可以理解您是否在解决旧的SDK限制。苹果本人已告诉我,在下一次更新时应删除方法调用,因此我不确定针对较旧设备的黑客攻击能否通过批准程序。


iPhone Simulator 3.3.2并非如此。它仍然无法正常工作。
鸭子2010年

4
@user在堆栈上推视图时如何请求方向?
progrmr 2010年

4
您可以绕过Apple的静态分析。通过使用performSelector并依靠Obj-C的出色动态性,我成功地使用了未公开的代码。
肯尼斯·巴伦内格

@KennethBallenegger我记得在准则和/或协议中读到,隐藏此类内容可能会使您的应用程序删除,甚至可能删除您的帐户。我不会冒险只使用未记录的API。
伊凡·武西卡(IvanVučica)2011年

101

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

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

展示和关闭香草视图控制器就足够了。

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


1
太好了,这个没有被标记为这篇文章的正确答案,但这解决了我问题stackoverflow.com/questions/5030315的一半。模型视图技巧“强制”纵向,您是否也知道强制横向?
epologee

5
哈哈哈!这太愚蠢了!但它起作用了!-当然可以!:D
hfossli 2011年

太棒了,为此苦苦挣扎了很长时间。谢谢!伴侣
Rahul Choudhary

3
这似乎对iOS5和iOS6均适用:}];
Inferis

1
这似乎可以在iOS 7和iOS 8上使用,但是有一个明显的黑屏,它闪烁进入视野(主要是iOS 8)。
levigroker 2014年

16

如果要强制其从纵向旋转为横向,请使用以下代码。请注意,您需要调整视图中心。我注意到我的视图没有放在正确的位置。否则,它会完美运行。谢谢你的提示。

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)?你得到这样的东西了吗?
NeverHopeless

使用进行此操作的最简单方法UIAlertView是将视图控制器设置为alertView的委托,然后在上- (void) didPresentAlertView:(UIAlertView *) alertView,根据current进行旋转[UIApplication sharedApplication].statusBarOrientation。如果您不设置动画,那么看起来就不会太怪异。
Michael Gaylord 2014年

16

据我所知,该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];

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


1
代替self.view,您应该旋转self.window.view。如果在视图顶部有UITabBar或其他控制器,这将很方便。
Borut Tomazin

7

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

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

利用这一点,我在我的一个应用程序中使用以下代码段:

- (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 :)。您是否找到了一种解决方法,可将视图强制为“横向”?
拉斐尔·诺布雷

有趣-对,我还没有这个问题。我会尝试按一下推和弹出按钮的顺序,很有可能您会提出一些建议。
Andrew Vilcsak 2010年

7

有一个简单的方法以编程方式迫使iPhone必要的方向-使用两个由已经提供了答案kdbdallas乔希

//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,我需要添加以下功能:(适用于模式Viewcontroller)

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

对我而言,它的工作方式并不尽如人意:导航栏和工具栏会缩小到其在“景观”视图中应具有的高度。删除并重新添加最上面的视图的方法可保持正确的高度,但也有一个缺点:我的导航栏向下移动了一点(就像启用了导航栏一样)
AlexWien 2012年

它对我来说适用于模态和推入式ViewController。但仅适用于iOS5。:(
Guntis Treulands

是的,我也可以在ios5中使用它。现在,在ios6中,一旦解散了模态景观视图控制器,导航栏的高度就会缩小
AlexWien

7

我一直在努力寻找一种好的解决方案。找到了可以解决这个问题的博客文章:从键中删除最外面的视图UIWindow,然后再次添加它,然后系统将重新从视图shouldAutorotateToInterfaceOrientation:控制器中查询方法,以强制应用正确的方向。看到它:iPhone强制uiview重新定向


此解决方案最适合我。重要的是,显示为空的modalViewController的修复会导致将ViewController推入堆栈的错误动画。此UIWindow方法不会遇到相同的问题。
里克·史密斯·翁纳

5

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

但是,我更喜欢发布“方向已更改,请更新UI”通知。当视图控制器收到此通知时,它将调用shouldAutorotateToInterfaceOrientation:,从而允许您通过返回YES所需的方向来设置任何方向。

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

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

希望能有所帮助。


3

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 ];
        }
    }
}

最后,注册UIDeviceOrientationDidChangeNotification的通知,initloadview像这样:

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

这是此页面上我唯一能够成功适应我的导航控制器代码的答案之一。一个小问题是,旋转的导航栏不会像通过普通机制旋转时那样在纵向和横向之间改变高度。
丹·代尔

@DanDyer您是否已解决导航条高度错误的问题?(特别是在ios6中,高度是错误的)
AlexWien

@AlexWien我最终设法说服了客户我们不应该这样做。对于iOS 6,您可能需要使用新shouldAutorotate方法做一些事情(请参阅stackoverflow.com/a/12586002/5171)。
Dan Dyer

@DanDyer昨天解决了它,一个简单的隐藏和ViewDidAppear导航和状态的显示解决it.Now所有作品(SDK iOS6的,目标的ios5)我要重新检测目标iOS6的
AlexWien

2

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

对我来说,目的是仅应对景观变化。

初始化方法:

[[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 ];
}

1

我有一个应用,希望在某些视图中支持设备旋转,但在风景模式下,其他应用尤其没有意义,因此当我换掉视图时,我想强制将旋转设置为纵向。

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

这很简单,但是像其他帖子一样,我希望该应用在返回上一屏幕时,无论当前设备的方向如何,都能自动返回纵向。

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

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

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

希望这可以帮助。


1

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

在需要保持横向(左或右)的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会被明确重置...

保持景观的这种方法唯一需要注意的是,固有的动画必须在必须发生的方向之间切换,而最好是使其看起来没有变化。


1

iOS 6解决方案:

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

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


0

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

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


-3

如果使用的是UIViewControllers,则有以下方法:

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation

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

更多信息在这里


1
这样就停止了旋转,如果用户在横向,我正在寻找的是强制旋转回纵向。我在上面添加了一个示例来说明我的意思更好。
Dave Verwer

-3

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


我最初发布的代码是有可能的,您只是永远不知道什么时候可能会停止工作;)
Dave Verwer

-3

这就是我用的。(您会收到一些编译警告,但它在Simulator和iPhone中均可使用)

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

我发现这足以使其在4.1中工作。但是,因为我的状态栏设置为隐藏,所以我在顶部获得了20像素的间隙:(
Anthony Main

setOrientation是私有api,使用它的应用程序被拒绝。
2011年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.