1

这是我的代码。

 SomeController *analyticsViewController = [[SomeController alloc] init];

 MTNavigaionLandscapeViewController *analyticsNavigaionObject =  [[MTNavigaionLandscapeViewController alloc] initWithRootViewController:analyticsViewController];

 [analyticsNavigaionObject setNavigationBarHidden:YES];

 if ([self respondsToSelector:@selector(presentModalViewController:animated:completion:)]) {
      [self presentViewController:analyticsNavigaionObject animated:YES completion:nil];
  } else {
          [self presentModalViewController:analyticsNavigaionObject animated:YES];
  }
  [analyticsViewController release];
  [analyticsNavigaionObject release];

这是 SomeController.m 中支持的方向

- (NSUInteger)supportedInterfaceOrientations
{
 return UIInterfaceOrientationMaskLandscape;
}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
  return UIInterfaceOrientationLandscapeRight;
}

以及 MTNavigaionLandscapeViewController 中支持的方向(UINavigationController 的子类)

- (NSUInteger)supportedInterfaceOrientations
{
 return UIInterfaceOrientationMaskLandscape;
}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
 return UIInterfaceOrientationLandscapeRight;
}

最后,如果我只提供 SomeController,它可以正常工作。我的问题是,当我提供 Navigationcontroller 时,就会出现问题。请帮我

4

2 回答 2

2

如何强制 iPhone 旋转到某个方向。

问题

iPhone 和 iPad 应用程序可以支持纵向或横向或两者。有时,一个或少数几个视图需要仅以纵向或横向显示,或者以其余应用程序不支持的方向显示。iOS 或 cocoa 框架分别提供方法来为每个视图单独定义,支持哪些方向。但是,除非任何限制适用于所有应用程序并因此分别在项目或目标级别上定义,否则必须执行轮换的始终是用户。应用程序可以决定它是否支持某个方向,但它不能强制系统实际旋转。

本教程展示了如何克服这种不足。

给出的示例是一个 iPhone 应用程序,它以纵向模式呈现除一个视图控制器之外的所有视图控制器。出于良好的设计和可用性原因,其中一个视图控制器仅适用于横向。

到目前为止,这已在 iPhone iOS 6 上得到证明。

旋转模态呈现的视图控制器

当视图以模态方式呈现时,此任务很容易。对于模态呈现的视图控制器,呈现的视图控制器应仅支持横向,并且在模态呈现视图之前,视图控制器的方向应设置为横向。

[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeLeft animated:YES];
[self presentModalViewController:landscapeVC animated:YES];

这样做,视图控制器landscapeVC将以横向显示。

但是要保留导航栏等。我想将其推送到导航控制器的堆栈中。不幸的是,除非用户实际旋转设备,否则设备不会旋转其方向。

此外,我的应用程序有两个目标。其中一个的导航基于标签栏,另一个目标的导航(功能减少免费版本)是普通的。因此,我的解决方案需要同时适用于带有标签栏的应用程序和不带标签栏的应用程序。

因此,对于只有标签栏或根本没有标签栏的应用程序,此解决方案可能会过大。但它在任何情况下都有效。

标签栏控制器的方向问题

当标签栏显示其视图控制器时,控制方向的是标签栏控制器。实际上,这意味着其中呈现的所有视图都应该支持所有相同的方向。为了克服这个问题,我们需要子类UITabBar化并引入一些控制标签栏是否支持横向的功能。

对应用程序委托的更改

在我的例子中,我向应用程序委托添加了一个布尔变量。这是因为使用标签栏或不使用标签栏运行的双重性。如果您只有一个标签栏,您可能会将该变量作为属性添加到您的标签栏子类中。对于书外的 OOP,单例会很好,但我认为对于单个布尔开关来说有点过大。

AppDelegate.h:

@property BOOL  isLandscapePreferred;

它是自动合成的。它的默认值为NO. 但是,您可能希望在您的应用程序委托中NO明确设置。application:didFinishLoadingWithOptions:

对于没有标签栏的目标,我所有的视图控制器都需要响应该设置。基本上,呈现横向视图的必须实现以下内容。但是,在整个应用程序中使用它并没有什么坏处,尤其是当您的应用程序中有多个视图需要以横向显示时。

对 UIApplication 的更改

根据我在网上找到的一些建议,我决定继承 UIApplication 并覆盖supportedInterfaceOrientations那里。但是,我仍然在 xcode 的目标摘要窗格中将所有方向标记为支持,尽管该设置应该被 UIApplication 覆盖。

MyApp.h 和 MyApp.m:

#import <UIKit/UIKit.h>

@interface MyApp : UIApplication

@end

#import "MyApp.h"
#import "AppDelegate.h"

@implementation MyApp

- (NSUInteger)supportedInterfaceOrientationsForWindow:(UIWindow *)window {

    if ([(AppDelegate*) [[UIApplication sharedApplication] delegate] isLandscapePreferred]) {
        return (UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeLeft);
        NSLog(@"PhotocollApp: Landscape");
    } else {
        return (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown);
        NSLog(@"PhotocollApp: Portrait");
   }

}

@end

从 main 调用 MyApp

接下来,我们需要确保我们的子类应用程序对象在应用程序启动时被实例化。这需要更改 main.m。

#import <UIKit/UIKit.h>

#import "AppDelegate.h"
#import "PhotocollApp.h"

int main(int argc, char *argv[])
{
    @autoreleasepool {
        return UIApplicationMain(argc, argv, NSStringFromClass([MyApp class]), NSStringFromClass([AppDelegate class]));
    }
}

UIApplicationMain传递了两个接收到的参数argcargv在前两个参数中,后跟两个NSString对象,代表应用程序类的名称和一个应用程序委托类。我没有更改应用程序委托的标准名称,但我们需要在这里标识我们自己的应用程序类。

在所有视图控制器中覆盖supportedInterfaceOrientations

UIViewController我已经介绍了和的“抽象”子类UITableViewController。我所有的视图控制器都继承自它们:

MyRotatingViewController.h 和 .m:

#import <UIKit/UIKit.h>

@interface MyRotatingViewController : UIViewController

@end

#import "MyRotatingViewController.h"
#import "AppDelegate.h"

@implementation MyRotatingViewController

// … 

#pragma mark - Rotation Management

- (BOOL) shouldAutorotate {

    return NO;
}

- (NSUInteger)supportedInterfaceOrientations {

    if ([(AppDelegate*) [[UIApplication sharedApplication] delegate] isLandscapePreferred]) {
        return (UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeLeft);
    } else {
        return (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown);
    }

}

@end

对于MyRotatingTableViewController. 唯一的区别在于 .h 文件。自然它继承自UITableViewController.

#import <UIKit/UIKit.h>

@interface MyRotatingTableViewController : UIViewTableController

@end

确保所有(受影响的)视图控制器都继承自MyRotatingViewControllerMyRotatingTableViewController分别继承。除了实现这个“抽象”类,你当然可以shouldAutorotatesupportedInterfaceOrientations所有相关的视图控制器中实现。

自定义标签栏类

如上所述,在标签栏驱动的应用程序中,是标签栏的方法控制方向设置,而不是标签栏呈现的视图控制器的方法。

//  MyTabBarController.h
#import <UIKit/UIKit.h>

@interface MyTabBarController : UITabBarController

@end

//  MyTabBarController.m

#import "MyTabBarController.h"

#import "AppDelegate.h"

@interface MyTabBarController ()

@end

@implementation MyTabBarController

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
}

#pragma mark - Rotation Management

- (BOOL) shouldAutorotate {

    return YES;
}

- (NSUInteger)supportedInterfaceOrientations {


    if ([(AppDelegate*) [[UIApplication sharedApplication] delegate] isLandscapePreferred]) {
        return (UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeLeft);
    } else {
        return (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown);
    }

}
@end

当我使用情节提要时,我只需要MyTabBarController在 IB 中唯一的选项卡栏对象的属性窗格中进行设置。如果您以编程方式创建它,那么只需实例化MyTabBarController而不是UITabBarController.

调用视图控制器强制横向

根据 80/20 法则,我们只为 20% 的实际工作做好了 80% 的准备。 现在真正的工作来了……

这是视图控制器的方法的中间,横向视图被推送。就我而言,这是在一个tableView:didSelectRowAtIndexPath:实现中。它当然可以在IBAction方法内或prepareForSegue:sender:方法内。如果您这样做,prepareForSegue那么您可能会在运行时在错误控制台上出现警告。一方面我没有看到任何故障,但另一方面我不知道当应用程序提交到商店时这是否会通过或导致拒绝。

诀窍是将状态栏的方向设置为横向,然后以模态方式呈现视图控制器。现在设备处于横向模式。到目前为止,用户不应该看到任何东西。即使,裸视图控制器没有视图。根本不会显示零视图。接下来这个最近呈现的视图控制器被解除。之后,真正的视图控制器将被推送到导航堆栈。

这是代码:

//set the landscape switch for the tab bar, view controllers and application
[(AppDelegate*) [[UIApplication sharedApplication] delegate] setIsLandscapePreferred:YES];

//set statusbar to the desired rotation position
[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeLeft animated:YES];

// Create any view controller. UIViewController will do. 
// Present it modally and directly after that dismiss it again. 
UIViewController *anyVC = [[UIViewController alloc] init];
[self presentModalViewController: anyVC animated:NO];
[self dismissModalViewControllerAnimated:NO];
// The user should not have noticed anything but how the device is in landscape orientation

//Frankly I am not sure whether the next statement must be included or does not need to be. 
//While working on “my” solution I came across a number of tricks and hints on several places on the web and this was amongst them.
//At least it does not do any harm. 
if ([UIViewController respondsToSelector:@selector(attemptRotationToDeviceOrientation)]) {
    // this ensures that the view will be presented in the orientation of the device
    // This method is only supported on iOS 5.0.  iOS 4.3 users may get a little dizzy.
    [UIViewController attemptRotationToDeviceOrientation];
}

// Get the storyboard named secondStoryBoard from the main bundle:
UIStoryboard *storyBoard = [UIStoryboard storyboardWithName:@"MainStoryboard_iPhone" bundle:nil];

// I am using storyboard in this project. If you don’t do that then you could just instantiate the view controller the usual way with alloc/init or by loading from nib. 
LandscapeVC *landscapeVC = (landscapeVC *)[storyBoard instantiateViewControllerWithIdentifier:@"LandscapeVC"];


// Then push the new view controller in the usual way:
[self.navigationController pushViewController:paintingVC animated:YES];

横向视图控制器

我们快到了。现在,新的视图控制器LandscapeVC在标签栏应用程序上以横向模式呈现。对于没有标签栏的应用程序,我们必须对LandscapeVC 视图控制器本身进行一些更改。当然,我们需要确保在关闭横向模式的视图控制器时设备旋转回纵向。

LandscapeVC.m 的片段

#pragma mark - actions

// certain tasks need to be performed before the view controller is dismissed. This could be done within the viewWillDisappar. 
// Again, if you do that in viewWillDisappear: then you risk some warnings on the console. I have decided to overlay the “Back” button (leftBarButtonItem) with a custom button that invokes the following action. 
- (IBAction)done :(id)sender{

    //set the landscape switch back to off/NO/portrait
    [(AppDelegate*) [[UIApplication sharedApplication] delegate] setIsLandscapePreferred:NO];

    //set statusbar to the desired rotation position
    [[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationPortrait animated:YES];

    //present/dismiss viewcontroller in order to activate rotating.
    UIViewController *mVC = [[UIViewController alloc] init];
    [self presentModalViewController:mVC animated:NO];
    [self dismissModalViewControllerAnimated:NO];

    [self.navigationController popViewControllerAnimated:YES];
    return;
}

#pragma mark - Rotation
// Rotation

-(BOOL)hidesBottomBarWhenPushed{
// This one is just for my design. I like to use the full screen of the iPhone for my landscape view controller. 
// In the end it is the size of the pane in my case which motivates me to break with UI guidelines and force the user to use this single view controller in landscape only. 
// Besides this design issue this method is not required for the rotation itself. 
// What it does: It overwrites the getter of the UIViewController property hidesBottomBarWhenPushed. 
// By returning YES as constant the bottom bar (= tab bar in my case) will be hidden. 
// However, it remains hidden in the event that any more view controllers are pushed on top of the navigation stack. 
// So if you plan to drill further down in your navigation from here, it may not be a good idea to hide the bottom bar. 
// You will not get it back until the user returns to the calling view controller. 
    return YES;
}

// Well, if all of your view controllers inherit from MyRotatingViewController or MyRotatingTableViewController
// respectively then the following is redundant. 
// While writing this “tutorial” for stack overflow I noticed that this
// very view controller does not. Don’t ask me why. I may fix it later. 
// However, I want to show to you what is proven to work fine. Therefore I owe you these methods. 
-(BOOL)shouldAutorotate{
    return NO;
}

- (NSInteger)supportedInterfaceOrientations{
    return (UIInterfaceOrientationLandscapeRight | UIInterfaceOrientationLandscapeLeft);
}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{
    return (UIInterfaceOrientationLandscapeLeft);
}

就是这样。很直接,不是吗?

于 2013-03-20T16:08:34.263 回答
1

在您将要展示的 ViewController 中编写这些方法:

- (BOOL)shouldAutorotate
{
    AppDelegate *mainDelegate = (AppDelegate*)[[UIApplication sharedApplication]delegate];
    mainDelegate.shouldRotate = YES;
    return YES;
}
- (NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskLandscapeRight;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
    return YES;
}

在你的 AppDelegate.m 中粘贴这个

- (NSUInteger) application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
    if(self.shouldRotate)
    {
        self.shouldRotate = NO;
        return UIInterfaceOrientationMaskLandscapeRight;
    }
    return UIInterfaceOrientationMaskPortrait;
}

在你的 AppDelegate.h 中粘贴这个

@property (assign, nonatomic) BOOL shouldRotate;

在 ViewWillAppear 中的 presentingViewController 中粘贴:

[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeRight animated:NO];
于 2013-03-20T09:30:43.027 回答