iOS 6 强制设备横屏显示方向

57

我的应用程序中有10个视图控制器,我使用导航控制器来加载/卸载它们。

除了一个视图控制器以外,其余的所有视图控制器都是竖屏模式。假设第7个视图控制器是横屏模式,当它被加载时我需要它以横屏模式呈现。

请建议一种方法来强制在IOS 6中从纵向模式转换为横向模式(最好在IOS 5中也适用)。

这是我在IOS 6之前的操作方法:

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    UIViewController *c = [[[UIViewController alloc]init] autorelease];
    [self presentModalViewController:c animated:NO];
    [self dismissModalViewControllerAnimated:NO];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation{
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}

弹出和关闭模态视图控制器会迫使应用程序重新审视其方向,因此shouldAutorotateToInterfaceOrientation方法会被调用。

我在iOS 6中尝试的内容:

- (BOOL)shouldAutorotate{
    return YES;
}
-(NSUInteger)supportedInterfaceOrientations{
    return UIInterfaceOrientationMaskLandscape;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{
    return UIInterfaceOrientationLandscapeLeft;
}
加载时,控制器一直呈现为横屏状态。在旋转设备后,方向就会正常改变。但是我需要让控制器在加载时自动旋转到横屏状态,这样用户就必须旋转设备才能正确查看数据。
另一个问题:在将设备旋转回纵向后,虽然我已经在supportedInterfaceOrientations中指定了UIInterfaceOrientationMaskLandscape,但方向仍然保持为纵向。为什么会这样呢?
此外,以上3种方法都没有被调用。 一些有用的数据:
  1. 在我的plist文件中,我指定了除倒置之外的3个方向。
  2. 该项目是在Xcode 4.3 IOS 5中启动的。所有类(包括xib文件)都是在Xcode 4.5 IOS 6之前创建的,现在我使用的是最新版本。
  3. 在plist文件中,状态栏设置为可见。
  4. 在要在横屏中显示的xib文件中,状态栏为“无”,方向设置为横屏。
感谢任何帮助。谢谢。

我有和你一样的问题,你解决了吗? - aneuryzm
我讨厌新的iOS 6。我必须更改所有我的应用程序,以使方向正确工作。 - Bagusflyer
5
嗨,@bagusflyer,这就是程序员的生活啊))) - John Smith
1
这个问题和我的问题很像,我已经解决了。解决方案在这里:http://stackoverflow.com/questions/14658268/why-cant-i-force-landscape-orientation-when-use-uinavigationcontroller - Bob
你的问题对我来说是一个解决方案。 我也想要同样的结果,我刚刚写了你的ios6代码,它可以自动加载横向视图。 - Shilpa
这是我针对你在iOS 7中的问题提供的解决方案:https://dev59.com/T33aa4cB1Zd3GeqPhcDg#22491787 - Fede Cugliandolo
13个回答

43

好的,大家好,我将发布我的解决方案。

我的情况如下:

  1. 一个基于视图的应用程序,有几个视图控制器。(它是基于导航的,但由于方向问题,我不得不使它基于视图)。
  2. 所有视图控制器都是竖屏的,除了一个——横屏左侧。

任务:

  1. 我的一个视图控制器必须自动旋转到横屏,无论用户如何持握设备。所有其他控制器必须是竖屏的,并且在离开横屏控制器后,应用程序必须强制旋转到竖屏,无论用户如何持握设备。
  2. 这必须在IOS 6.x和IOS 5.x上都能正常工作。

开始吧!

(更新 删除了@Ivan Vučica建议的宏)

在您所有的竖屏视图控制器中重写自动旋转方法,像这样:

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation{
    return (toInterfaceOrientation == UIInterfaceOrientationPortrait);
}
-(BOOL)shouldAutorotate {
    return YES;
}
- (NSUInteger)supportedInterfaceOrientations {
    return UIInterfaceOrientationMaskPortrait;
}

你可以看到两种方法:一种适用于IOS 5,另一种适用于IOS 6。
同样适用于你的LANDSCAPE视图控制器,其中有一些添加和更改:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation{
    [image_signature setImage:[self resizeImage:image_signature.image]];
    return (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft);
}
-(BOOL)shouldAutorotate {
    return YES;
}
- (NSUInteger)supportedInterfaceOrientations {
    [image_signature setImage:[self resizeImage:image_signature.image]];
    return UIInterfaceOrientationMaskLandscapeLeft;
}

注意:如果要在iOS 5中强制自动旋转,请添加以下内容:

- (void)viewDidLoad{
    [super viewDidLoad];
    if ([[[UIDevice currentDevice] systemVersion] floatValue] < 6.0)
        [[UIApplication sharedApplication] setStatusBarOrientation:UIDeviceOrientationLandscapeLeft animated:NO];    
}

类比地说,当你离开LANDSCAPE控制器后,无论你加载哪个控制器,你都应该强制再次进行IOS 5的自动旋转,但现在你将使用UIDeviceOrientationPortrait,因为你进入了一个PORTRAIT控制器:
- (void)viewDidLoad{
        [super viewDidLoad];
        if ([[[UIDevice currentDevice] systemVersion] floatValue] < 6.0)
            [[UIApplication sharedApplication] setStatusBarOrientation:UIDeviceOrientationPortrait animated:NO];    
    }

现在最后一件事情(有点奇怪)——您必须根据IOS更改从一个控制器切换到另一个控制器的方式:

创建一个NSObject类“Schalter”(德语中的“Switch”)。

在Schalter.h文件中添加以下内容:

#import <Foundation/Foundation.h>

@interface Schalter : NSObject
+ (void)loadController:(UIViewController*)VControllerToLoad andRelease:(UIViewController*)VControllerToRelease;
@end

在Schalter.m中说:
#import "Schalter.h"
#import "AppDelegate.h"

@implementation Schalter
+ (void)loadController:(UIViewController*)VControllerToLoad andRelease:(UIViewController*)VControllerToRelease{

    //adjust the frame of the new controller
    CGRect statusBarFrame = [[UIApplication sharedApplication] statusBarFrame];
    CGRect windowFrame = [[UIScreen mainScreen] bounds];
    CGRect firstViewFrame = CGRectMake(statusBarFrame.origin.x, statusBarFrame.size.height, windowFrame.size.width, windowFrame.size.height - statusBarFrame.size.height);
    VControllerToLoad.view.frame = firstViewFrame;

    //check version and go
    if (IOS_OLDER_THAN_6)
        [((AppDelegate*)[UIApplication sharedApplication].delegate).window addSubview:VControllerToLoad.view];
    else
        [((AppDelegate*)[UIApplication sharedApplication].delegate).window setRootViewController:VControllerToLoad];

    //kill the previous view controller
    [VControllerToRelease.view removeFromSuperview];
}
@end

现在,这是您使用Schalter的方式(假设您从Warehouse控制器转到Products控制器):
#import "Warehouse.h"
#import "Products.h"

@implementation Warehouse
Products *instance_to_products;

- (void)goToProducts{
    instance_to_products = [[Products alloc] init];
    [Schalter loadController:instance_to_products andRelease:self];
}

bla-bla-bla your methods

@end

当然,你必须释放 instance_to_products 对象:
- (void)dealloc{
     [instance_to_products release];
     [super dealloc];
}

好的,这就是全部内容。不要犹豫地点踩,我不在意。这是为那些寻找解决方案而不是声誉的人准备的。 干杯! Sava Mazare。


7
由于您显然无条件定义了宏,因此那些 #ifdef IOS_OLDER_THAN_6 行没有任何作用。即使它们有作用,它们也只会进行编译时检查。您后来对这些宏的使用是正确的。 - Ivan Vučica
3
这些宏(例如 IOS_OLDER_THAN_6)不应用于检查 iOS 版本。 systemVersion 不是浮点值(例如 5.1.1 不是浮点数),有时可能会导致错误结果。请参阅此处以了解正确的实现方式 - Nate
1
[[UIApplication sharedApplication] setStatusBarOrientation:UIDeviceOrientationPortrait animated:NO];我也尝试了这个方法在iOS 6上,它可以工作。我不必做你所说的奇怪的事情 :) 你认为怎么样?请给予建议。顺便说一下,我遇到了你在下面答案中详细解释的完全相同的情况。 - hasan
实际上,Hasan的状态栏方向技巧对我很有用 :) - AWrightIV
更好的解决方案:https://dev59.com/T33aa4cB1Zd3GeqPhcDg#22491787 - Fede Cugliandolo
显示剩余3条评论

34

这应该可行,与 iOS 6 之前的版本类似,但使用了 UINavigationController

UIViewController *portraitViewController = [[UIViewController alloc] init];
UINavigationController* nc = [[UINavigationController alloc] initWithRootViewController:portraitViewController];
[self.navigationController presentModalViewController:nc animated:NO];
[self.navigationController dismissModalViewControllerAnimated:NO];

在我推入下一个UIViewController之前,我调用了这个。它将强制下一个被推入的UIViewController以纵向模式显示,即使当前的UIViewController是横向的(对于从纵向到横向也应该有效)。这对我来说在iOS 4+5+6上工作。


不是很准确,@Chunkylover53,被采纳的答案是我找到的最接近解决方案的答案。我很快会发布它。 - John Smith
2
你能详细说明一下在什么情况下应该使用这个吗?我遇到了同样的问题,当我的其它视图控制器是竖屏时,我需要下一个推出的视图控制器是横屏的。我正在使用UINavigationController,但不确定这段代码应该放在哪里。我尝试在推出横屏视图控制器之前加入这段代码,但那行不通。 - Ken Woo
我是否错过了这个告诉呈现的视图控制器是横向还是纵向的地方/方法?这个想法是因为它是根视图控制器,所以方向方法会被调用吗? - jesses.co.tt
我是新手...这段代码应该放在哪里?我把它放在执行segue之前,但目标视图仍然旋转到纵向?谢谢。 - maddog
将segue样式更改为模态使其对我起作用。 - maddog
显示剩余3条评论

14

我认为最好的解决方案是遵循官方的苹果文档。因此,根据文档,我使用以下方法,在iOS 5和6上一切正常。 在我的VC中,我重写了以下方法:

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    // Return YES for supported orientations
    return UIInterfaceOrientationIsPortrait(interfaceOrientation);
}

iOS 6的方法,第一种方法返回支持的方向掩码(如它们的名称所示)

-(NSUInteger)supportedInterfaceOrientations{

    return UIInterfaceOrientationMaskPortrait;
}

第二个告诉您的VC,在将VC显示时首选的界面方向是什么。

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
    return UIInterfaceOrientationPortrait;
}

只需将肖像模式更改为所需的方向即可;) 此解决方案运行顺畅,我不喜欢创建宏和其他绕过这个简单解决方案的东西的想法。 希望这可以帮助...


7

我有同样的问题,在我的应用程序中有27个视图,其中26个是竖屏,只有一个是所有方向(一个图像查看器 :))。 在每个类中添加宏并更换导航不是我感到舒适的解决方案...

所以,我想在我的应用程序中保持UINavigationController机制,而不是用其他代码替换它。

该怎么办:

@1 在应用程序委托中的方法didFinishLaunchingWithOptions

if ([[UIDevice currentDevice].systemVersion floatValue] < 6.0)
{
    // how the view was configured before IOS6
    [self.window addSubview: navigationController.view];
    [self.window makeKeyAndVisible];
}
else
{
    // this is the code that will start the interface to rotate once again
    [self.window setRootViewController: self.navigationController];
}

@2 因为导航控制器只会以YES的方式响应自动旋转,所以我们需要添加一些限制:

扩展UINavigationController -> YourNavigationController并在Interface Builder中链接它。

@3 重写导航控制器的“烦人的新方法”。

由于该类仅适用于此应用程序,因此它可以负责其控制器并代替它们进行响应。

-(BOOL)shouldAutorotate {

    if ([self.viewControllers firstObject] == YourObject)
    {
         return YES;
    }
    return NO;
}
- (NSUInteger)supportedInterfaceOrientations {
     if ([self.viewControllers firstObject] == YourObject)
    {
         return UIINterfaceOrientationMaskLandscape;
    }
    return UIInterfaceOrientationMaskPortrait;
}

我希望这能对你有所帮助,


谢谢您,setRootViewController解决了我遇到的问题! - Meta-Knight
我有点困惑于:[self.viewControllers firstObject],因为NSArray没有名为“firstObject”的方法,无论如何,我不确定你是否想查看视图控制器列表中的第一个对象。也许你是指self.topViewController? - RefuX
UIViewController* vc = (UIViewController*)[self.viewControllers lastObject];如果 ([NSStringFromClass([vc class]) isEqualToString:@"YourControllerClassName"]) { 返回 UIInterfaceOrientationMaskAll; } 返回 UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown; - Vlad Cioaba

3

根据iOS 6发布说明

现在,iOS容器(例如UINavigationController)不会查询其子级以确定它们是否应自动旋转。

你的rootViewController是否将shouldAutoRotate消息传递到ViewController层次结构中的VC?


嗯...你说的传递 shouldAutoRotate 是什么意思?在 applicationDidFinishLaunching 中,我使用 [self.window setRootViewController:navigationController](navigationController 是 AppDelegate 中 UINavigationController 的一个实例)来设置根视图控制器。 - John Smith
如上述发布说明所述,仅向rootViewController询问哪些方向是有效的。如果您的仅支持横向的视图控制器不是rootViewController,那么它将不会被询问 - UINavigationController将返回其认为有效的任何方向。 - sam-w
但是如果它不应该是根视图控制器,我该如何使其成为根视图控制器呢——假设它是导航控制器堆栈中的第七个。 - John Smith
根据我的经验,回答自己的问题会导致被踩。但是等我有空了,我会发布一些代码,因为我现在正在工作。 - John Smith
1
请查看解决方案,@sjwarner,我已经发布为“回答自己的问题”。 - John Smith
显示剩余4条评论

2

我使用了与iOS6之前相同的方法(显示和关闭模态视图控制器)来在横向模式下显示单个视图控制器(所有其他视图控制器都在纵向模式下)。但在iOS6中,横向视图控制器显示为纵向。

为了解决这个问题,我只需在横向视图控制器中添加preferredInterfaceOrientationForPresentation方法。现在似乎在iOS5和iOS6上都能正常工作了。

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationLandscapeLeft);
}

- (BOOL)shouldAutorotate
{
return NO;
}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{    
return UIInterfaceOrientationLandscapeLeft;
}

这是我找到的唯一一个关于这个问题不令人困惑的答案。 - zakdances

2

大家好,我尝试了许多不同的解决方案都没有成功,最后找到了下面的解决方案,希望能帮到你们!

我做了一个详细的说明 :)

问题: 你需要在ios 6中使用navigationcontroller更改视图控制器的方向。

解决方法:

推荐导航

步骤1:首先有一个初始的UIViewController触发模态segue到横向和纵向的UInavigationControllers,如图所示...

更深入地说,在UIViewController1中我们需要根据AppDelegate中的全局变量设置2个segue操作...

-(void)viewDidAppear:(BOOL)animated{
    if([globalDelegate changeOrientation]==0){
        [self performSegueWithIdentifier:@"p" sender:self];
    }
    else{
        [self performSegueWithIdentifier:@"l" sender:self];
    }

}

同时,我们需要一种可以在竖屏和横屏之间切换的方法...
- (IBAction)dimis:(id)sender {
    [globalDelegate setChangeOrientation:0];
    [self dismissViewControllerAnimated:NO completion:nil];
}

第二步。每个导航控制器中第一个推出的 UiViewControllers 会随着...

-(NSUInteger)supportedInterfaceOrientations{
    return [self.navigationController supportedInterfaceOrientations];
}

-(BOOL)shouldAutorotate{
    return YES;
}

步骤三。 我们在UInavigationController的子类中重写supportedInterfaceOrientations方法...

在您的customNavigationController中,我们有......

-(NSUInteger)supportedInterfaceOrientations{
    if([self.visibleViewController isKindOfClass:[ViewController2 class]]){

        return UIInterfaceOrientationMaskPortrait;
    }
    else{
        return UIInterfaceOrientationMaskLandscape;

    }

}

步骤4:在故事板或代码中,将wantsFullScreenLayout标志设置为“是”,同时适用于纵向和横向的uinavigationcontrollers。


1
作为替代方案,您可以使用块来完成相同的操作:
UIViewController *viewController    = [[UIViewController alloc] init];
viewController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentViewController:viewController animated:NO completion:^{
    [self dismissViewControllerAnimated:NO completion:nil];
}];

此外,在推送新视图之前调用它。

1
尝试使用类别或子类化指定所需方向的UINavigationController,然后转换到所需的VC。在这里阅读更多信息。

0

我曾经遇到过同样的问题。如果你想强制一个特定的视图控制器以横向模式出现,那么在将其推入导航栈之前进行设置。

UIInterfaceOrientation currentOrientation = [[UIApplication sharedApplication] statusBarOrientation];
        if (currentOrientation == UIInterfaceOrientationPortrait ||
            currentOrientation == UIInterfaceOrientationPortraitUpsideDown)
            [[UIDevice currentDevice] setOrientation:UIInterfaceOrientationLandscapeLeft];

        UIViewController *vc = [[UIViewController alloc] init];
        [self.navigationController pushViewController:vc animated:YES];
        [vc release];

1
[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationLandscapeLeft]; 在 iOS6 上不起作用。 - dev

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接