iOS 6:如何限制一些视图的肖像,并允许他人旋转?

我有一个iPhone应用程序,它使用UINavigationController来呈现一个向下钻取的界面:第一个视图,然后是另一个视图,深达四层。 我希望将前三个视图限制为纵向,只允许最后一个视图旋转到横向。 当从第四个视图返回到第三个视图,并且第四个视图处于横向时,我希望所有的东西都旋转回肖像。

在iOS 5中,我简单地定义了shouldAutorotateToInterfaceOrientation:在我的每个视图控制器中为允许的方向返回YES。 一切工作如上所述,包括即使从视图控制器#4到#3返回时横向保持设备也返回肖像。

在iOS 6中,所有的视图控制器旋转到横向,打破那些不意味着。 iOS 6发行说明说

更多的责任是转移到应用程序和应用程序委托。 现在,iOS容器(如UINavigationController )不会咨询他们的孩子,以确定他们是否应该自动旋转。 […]系统每当设备旋转时或每当视图控制器呈现全屏模式演示风格时,都会询问其支持的接口方向的最顶级全屏视图控制器(通常是根视图控制器)。 此外,只有当这个视图控制器从其shouldAutorotate方法返回YES时,才会检索支持的方向。 […]系统通过将应用程序的supportedInterfaceOrientationsForWindow:方法返回的值与最顶级的全屏控制器的supportedInterfaceOrientations方法返回的值相交来确定是否支持方向。

所以我subclassed UINavigationController ,给了我的MainNavigationController布尔属性landscapeOK并用它来返回在supportedInterfaceOrientations允许的方向。 然后在我的每个视图控制器的viewWillAppear:方法,我有这样的一行

  [(MainNavigationController*)[self navigationController] setLandscapeOK:YES]; 

告诉我的MainNavigationController所需的行为。

问题来了:如果我现在在纵向模式下导航到我的第四个视图,并翻转手机,它会旋转到横向。 现在我按下后退button返回到我的第三个视图,这应该只是工作肖像。 但是它不会回转。 我该如何做到这一点?

我试过了

  [[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationPortrait] 

在我的第三个视图控制器的viewWillAppear方法,但它什么都不做。 这是错误的方法来调用,或者错误的地方来调用它,或者我应该以完全不同的方式来实现整个事情吗?

我遇到了同样的问题,并find了适合我的解决scheme。 为了使其工作,在你的UINavigationController中实现- (NSUInteger)supportedInterfaceOrientations是不够的。 你也需要在你的控制器#3中实现这个方法,这是第一个在popup控制器#4之后才是肖像的方法。 所以,我在我的UINavigationController中有以下代码:

 - (BOOL)shouldAutorotate { return YES; } - (NSUInteger)supportedInterfaceOrientations { if (self.isLandscapeOK) { // for iPhone, you could also return UIInterfaceOrientationMaskAllButUpsideDown return UIInterfaceOrientationMaskAll; } return UIInterfaceOrientationMaskPortrait; } 

在视图控制器#3中添加以下内容:

 - (NSUInteger)supportedInterfaceOrientations { return UIInterfaceOrientationMaskPortrait; } 

你不需要添加任何东西到你的视图控制器#1,#2和#4。 这对我有用,我希望它能帮助你。

添加一个CustomNavigationController

重写这些方法:

 -(BOOL)shouldAutorotate { return [[self.viewControllers lastObject] shouldAutorotate]; } -(NSUInteger)supportedInterfaceOrientations { return [[self.viewControllers lastObject] supportedInterfaceOrientations]; } - (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation { return [[self.viewControllers lastObject] preferredInterfaceOrientationForPresentation]; } 

现在在plist中添加所有的方向

在这里输入图像说明

在视图控制器中只添加需要的:

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

这些方法覆盖导航控制器方法

在对所有关于SO的类似问题的每一个答案进行审查后,没有任何答案为我工作,但他们给了我一些想法。 以下是我最终解决问题的方法:

首先,确保项目目标中支持的界面方向包含所需的旋转视图的所有方向。

在这里输入图像说明

接下来,做一个UINavigationController的类别(因为苹果说不要inheritance它):

 @implementation UINavigationController (iOS6AutorotationFix) -(BOOL)shouldAutorotate { return [self.topViewController shouldAutorotate]; } @end 

将您想要旋转的类别和视图控制器(我将调用RotatingViewController )导入到最高级别的视图控制器,该视图控制器应该包含您的导航控制器。 在该视图控制器中,执行如下所示的操作。 请注意,这不应该是你想要旋转的视图控制器。

 -(BOOL)shouldAutorotate { BOOL shouldRotate = NO; if ([navigationController.topViewController isMemberOfClass:[RotatingViewController class]] ) { shouldRotate = [navigationController.topViewController shouldAutorotate]; } return shouldRotate; } 

最后,在您的RotatingViewController ,执行shouldAutorotatesupportedInterfaceOrientations ,如下所示:

 -(BOOL)shouldAutorotate { // Preparations to rotate view go here return YES; } -(NSUInteger)supportedInterfaceOrientations { return UIInterfaceOrientationMaskAllButUpsideDown; // or however you want to rotate } 

你需要这样做的原因是因为iOS 6控制旋转到根视图控制器而不是顶视图控制器。 如果您希望单个视图的旋转与堆栈中其他视图的行为不同,则需要在根视图控制器中为其编写特定的案例。

我没有足够的声望评论@ Brian的答案,所以我会在这里添加我的笔记。

Brian提到iOS6给了rootViewController旋转控制 – 这不仅可以是上面提到的UINavigationController,也可以是UITabBarController,这对我来说也是如此。 我的结构如下所示:

  • 的UITabBarController
    • UINavigationController的
      • UIViewControllers …
    • UINavigationController的
      • UIViewControllers …

所以我首先在自定义的UITabBarController中添加了方法,然后在自定义的UINavigationController中添加了方法,最后在特定的UIViewController中添加了方法。

来自UITabBarController和UINavigationController的示例:

 - (BOOL)shouldAutorotate { return [self.viewControllers.lastObject shouldAutorotate]; } - (NSUInteger)supportedInterfaceOrientations { return [self.viewControllers.lastObject supportedInterfaceOrientations]; } - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation { return [self.viewControllers.lastObject shouldAutorotateToInterfaceOrientation:toInterfaceOrientation]; } - (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation { return [self.viewControllers.lastObject preferredInterfaceOrientationForPresentation]; } 

我想对自己的问题给出部分答案。 我发现下面一行代码,在我的第三个UIViewControllerviewWillAppear方法中使用:

 [[UIDevice currentDevice] performSelector:NSSelectorFromString(@"setOrientation:") withObject:(id)UIInterfaceOrientationPortrait]; 

但我不太喜欢这个解决scheme。 它使用一个技巧来分配一个只读属性,根据Apple文档,该属性表示设备的物理方向。 这就像告诉iPhone跳到用户手中正确的方向。

我很想把它放在我的应用程序,因为它只是工作。 但是感觉不对,所以我想留下一个干净的解决scheme。

这是我在ios 6.0中用于定向支持

 -(BOOL)shouldAutorotate{ return YES; } - (NSUInteger)supportedInterfaceOrientations{ return UIInterfaceOrientationMaskAll; } - (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{ return UIInterfaceOrientationPortrait; } 

因为这是一个高度看待的线程。 我想我会添加我认为是最简单的答案。 这适用于ios8及以上

 -(BOOL)shouldAutorotate { return YES; } 

 -(UIInterfaceOrientationMask)supportedInterfaceOrientations{ return UIInterfaceOrientationMaskPortrait; } 

而已。 请享用!

哦,我的ViewControllers被embedded到一个导航控制器中,我不需要任何子类或configuration。

这可能不适用于所有人,但对我来说这很好。 而不是实施…

 [(MainNavigationController*)[self navigationController] setLandscapeOK:YES]; 

在viewWillAppear在我所有的控制器中,我决定通过重写UINavigationControllerDelegate方法navigationController:willShowViewController:animated:来集中这个过程在我的UINavigationController子类中navigationController:willShowViewController:animated:

 - (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated { self.previousVCLandscapeOK = self.isLandscapeOK; // Store the current VC's orientation preference before pushing on the new VC so we can set this again from within the custom "back" method self.isLandscapeOK = NO; // Set NO as default for all VC's if ([viewController isKindOfClass:[YourViewController class]]) { self.isLandscapeOK = YES; } } 

我发现这个代理方法不会被调用时,从导航堆栈popup一个VC。 这对我来说不是问题,因为我在我的UINavigationController子类中处理后面的function,这样我就可以为特定的VC设置正确的导航栏button和动作,像这样…

 if ([viewController isKindOfClass:[ShareViewController class]]) { UIButton* backButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 57, 30)]; [backButton setImage:[UIImage imageNamed:@"back-arrow"] forState:UIControlStateNormal]; [backButton addTarget:self action:@selector(back) forControlEvents:UIControlEventTouchUpInside]; UIBarButtonItem* backButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton]; viewController.navigationItem.leftBarButtonItem = backButtonItem; UIImageView* shareTitle = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"share-title"]]; [shareTitle setContentMode:UIViewContentModeScaleAspectFit]; [shareTitle setFrame:CGRectMake(0, 0, shareTitle.frame.size.width - 10, shareTitle.frame.size.height - 10)]; viewController.navigationItem.titleView = shareTitle; } else if(...) { ... } 

这是我的back方法看起来像处理从堆栈中popupVC并设置适当的旋转首选项…

 - (void)back { self.isLandscapeOK = self.previousVCLandscapeOK; self.previousVCLandscapeOK = NO; [self popViewControllerAnimated:YES]; } 

所以,正如你所看到的,基本上所有发生的事情是我第一次设定我的两个属性…

 @property (nonatomic) BOOL isLandscapeOK; @property (nonatomic) BOOL previousVCLandscapeOK; 

navigationController:willShowViewController:animated:这将决定什么样的支持的方向是即将展示的VC内。 当popup一个VC时,我调用了自定义的“后退”方法,然后将isLandscapeOK设置为通过之前的VCLandscapeOK值存储的内容。

正如我所说的,这可能不适用于每个人,但它对我来说很好,我不必担心向我的每个视图控制器添加代码,我可以将它全部集中在UINavigationController子类中。

希望这可以帮助像我这样的人。 谢谢,杰里米。

转到Info.plist文件并进行更改 在这里输入图像说明

你想强制iOS 6的应用程序的肖像只有那么你可以添加到UIViewController子类下方的方法

 - (BOOL)shouldAutorotate { if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { return YES; } else { return NO; } } - (NSUInteger)supportedInterfaceOrientations { if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { return UIInterfaceOrientationMaskAll; } else { return UIInterfaceOrientationMaskPortrait; } } 

除了一个,我想让所有的VC都locking为纵向。 这是为我工作。

  1. 在plist文件中添加对所有方向的支持。
  2. 在根视图控制器中,检测窗口顶部的视图控制器的types,并相应地在supportedInterfaceOrientations方法中设置应用的方向。 例如,我需要我的应用程序只有当webview在堆栈顶部时才能旋转。 以下是我在rootVC中添加的内容:

     -(NSUInteger)supportedInterfaceOrientations { UIViewController *topMostViewController = [[Utils getAppDelegate] appNavigationController].topViewController; if ([topMostViewController isKindOfClass:[SVWebViewController class]]) { return UIInterfaceOrientationMaskAllButUpsideDown; } return UIInterfaceOrientationMaskPortrait; } 

我没有足够的声望回答@Ram S在@micmdk答复下的问题,所以我会在这里添加我的笔记。

当您使用UITabbarController时 ,请尝试将@ micmdk的代码中的self.viewControllers.lastObject更改为self.selectedViewController,如下所示:

 - (BOOL)shouldAutorotate { return [self.selectedViewController shouldAutorotate]; } - (NSUInteger)supportedInterfaceOrientations { return [self.selectedViewController supportedInterfaceOrientations]; } - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation { return [self.selectedViewController shouldAutorotateToInterfaceOrientation:toInterfaceOrientation]; } - (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation { return [self.selectedViewController preferredInterfaceOrientationForPresentation]; } 

我面临同样的问题。 解决了我的问题,如下所示。

如果您使用UINavigationController来推视图控制器,那么你必须设置下面的方法。

扩展UINavigationController {

 override open var shouldAutorotate: Bool { if topViewController != nil && (topViewController?.isKind(of: LogInViewController.self))! { return true } return false } override open var supportedInterfaceOrientations: UIInterfaceOrientationMask { if topViewController != nil && (topViewController?.isKind(of: LogInViewController.self))! { return .portrait } return .landscapeRight } override open var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation { if topViewController != nil && (topViewController?.isKind(of: LogInViewController.self))! { return .portrait } return .landscapeRight } 

}

代替LoginViewController使用你想要显示的UIViewController 。 在我的情况下,我想在Portrait模式下以Portrait模式显示其他ViewControllers中的LoginViewController

请使用下面的方法来解决这个问题

 - (NSUInteger)supportedInterfaceOrientations { return UIInterfaceOrientationMaskPortrait; } 

只返回你想要的方向!

Interesting Posts