95

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

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

[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationPortrait];

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

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

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

4

17 回答 17

101

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

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

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

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

于 2011-02-06T18:56:02.303 回答
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];

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

于 2009-01-21T04:07:47.293 回答
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];
}
于 2009-04-27T12:32:27.920 回答
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;
}

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

于 2010-08-19T21:24:54.933 回答
7

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

于 2010-10-27T15:54:37.480 回答
7

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

//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);
}
于 2012-05-09T12:00:43.897 回答
6

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

目前尚不清楚 Apple 的静态分析是否会理解您正在解决较旧的 SDK 限制。我个人被 Apple 告知要在我的下一次更新中删除方法调用,所以我还不确定对旧设备进行黑客攻击是否会通过审批流程。

于 2009-12-05T00:03:27.007 回答
5

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

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

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

唯一的问题是这会在没有动画的情况下强制重新定向。您需要在beginAnimations:和之间包装这条线commitAnimations以实现平滑过渡。

希望有帮助。

于 2011-06-13T15:20:23.500 回答
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方法(假设您使用的是 a 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 ];
于 2010-12-07T07:11:16.763 回答
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 ];
}
于 2011-04-18T16:26:57.163 回答
1

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

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

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

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

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

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

希望这可以帮助。

于 2011-10-03T15:45:37.447 回答
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 正在被显式重置......

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

于 2012-07-05T03:55:31.040 回答
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.

于 2013-01-13T00:42:41.503 回答
0

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

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

于 2010-09-10T14:26:59.163 回答
-3

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

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation

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

更多信息在这里

于 2008-10-08T15:21:14.957 回答
-3

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

于 2008-10-08T20:09:48.680 回答
-3

这就是我使用的。(您会收到一些编译警告,但它适用于模拟器和 iPhone)

[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeRight];
[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationLandscapeRight];
于 2008-10-10T06:58:18.433 回答