IOS 6 force device orientation to landscape

2019-01-03 07:44发布

I gave an app with say 10 view controllers. I use navigation controller to load/unload them.

All but one are in portrait mode. Suppose the 7th VC is in landscape. I need it to be presented in landscape when it gets loaded.

Please suggest a way to force the orientation go from portrait to landscape in IOS 6 (and it will be good to work in IOS 5 as well).

Here is how I was doing it BEFORE 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);
}

Presenting and dismissing a modal VC was forcing the app to review its orientation, so shouldAutorotateToInterfaceOrientation was getting called.

What I have have tried in IOS 6:

- (BOOL)shouldAutorotate{
    return YES;
}
-(NSUInteger)supportedInterfaceOrientations{
    return UIInterfaceOrientationMaskLandscape;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{
    return UIInterfaceOrientationLandscapeLeft;
}

On load, the controller keeps staying in portrait. After rotating the device, the orientation changes just ok. But I need to make the controller to rotate automatically to landscape on load, thus the user will have to rotate the device to see the data correctly.

Another problem: after rotating the device back to portrait, the orientation goes to portrait, although I have specified in supportedInterfaceOrientations only UIInterfaceOrientationMaskLandscape. Why it happens?

Also, NONE of above 3 methods are getting called.

Some (useful) data:

  1. In my plist file I have specified 3 orientations - all but upside down.
  2. The project was started in Xcode 4.3 IOS 5. All classes including xibs were created before Xcode 4.5 IOS 6, now I use the last version.
  3. In plist file the status bar is set to visible.
  4. In xib file (the one I want to be in landscape) the status bar is "None", the orientation is set to landscape.

Any help is appreciated. Thanks.

13条回答
Summer. ? 凉城
2楼-- · 2019-01-03 08:24

Hey guys after tryng a lot of different possible solutions with no success i came out with the following solution hope it helps!.

I prepared a recipe :).

Problem: you need change orientation of viewcontrollers using navigationcontroller in ios 6.

Solution:

navigation suggested

step 1. one initial UIviewcontroler to trigger modal segues to landscape and portrait UInavigationControllers as picture shows....

more deeply in UIViewController1 we need 2 segues actions according to global variable at Appdelegate....

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

}

also we need a way back to portrait &| landscape....

- (IBAction)dimis:(id)sender {
    [globalDelegate setChangeOrientation:0];
    [self dismissViewControllerAnimated:NO completion:nil];
}

step 2. the first Pushed UiViewControllers at each NavigationController goes with...

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

-(BOOL)shouldAutorotate{
    return YES;
}

step 3. We overwrite supportedInterfaceOrientations method at subclass of UInavigationController....

in your customNavigationController we have .....

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

        return UIInterfaceOrientationMaskPortrait;
    }
    else{
        return UIInterfaceOrientationMaskLandscape;

    }

}

step 4. At storyboard or by code, set wantsFullScreenLayout flag to yes, to both portrait and landscape uinavigationcontrollers.

查看更多
Deceive 欺骗
3楼-- · 2019-01-03 08:24

As an alternative you can do the same using blocks:

UIViewController *viewController    = [[UIViewController alloc] init];
viewController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentViewController:viewController animated:NO completion:^{
    [self dismissViewControllerAnimated:NO completion:nil];
}];

Also, call it before pushing the new view.

查看更多
何必那么认真
4楼-- · 2019-01-03 08:25

I used the same method as OP pre-ios6 (present and dismiss a modal VC) to show a single view controller in landscape mode (all others in portrait). It broke in ios6 with the landscape VC showing in portrait.

To fix it, I just added the preferredInterfaceOrientationForPresentation method in the landscape VC. Seems to work fine for os 5 and os 6 now.

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

- (BOOL)shouldAutorotate
{
return NO;
}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{    
return UIInterfaceOrientationLandscapeLeft;
}
查看更多
戒情不戒烟
5楼-- · 2019-01-03 08:27

From the iOS 6 Release Notes:

Now, iOS containers (such as UINavigationController) do not consult their children to determine whether they should autorotate.

Does your rootViewController pass the shouldAutoRotate message down the ViewController hierarchy to your VC?

查看更多
我只想做你的唯一
6楼-- · 2019-01-03 08:29

I have used the following solution. In the one view controller that has a different orientation than all the others, I added an orientation check in the prepareForSegue method. If the destination view controller needs a different interface orientation than the current one displayed, then a message is sent that forces the interface to rotate during the seque.

#import <objc/message.h>

...

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if(UIDeviceOrientationIsLandscape(self.interfaceOrientation))
    {
        UIInterfaceOrientation destinationOrientation;

        if ([[segue destinationViewController] isKindOfClass:[UINavigationController class]])
        {
            UINavigationController *navController = (UINavigationController *)[segue destinationViewController];
            destinationOrientation = [navController.topViewController preferredInterfaceOrientationForPresentation];
        } else
        {
            destinationOrientation = [[segue destinationViewController] preferredInterfaceOrientationForPresentation];
        }

        if ( destinationOrientation == UIInterfaceOrientationPortrait )
        {
            if ([[UIDevice currentDevice] respondsToSelector:@selector(setOrientation:)])
            {
                objc_msgSend([UIDevice currentDevice], @selector(setOrientation:), UIInterfaceOrientationPortrait );
            }
        }
    }
}
查看更多
男人必须洒脱
7楼-- · 2019-01-03 08:31

Ok, folks, I will post my solution.

What I have:

  1. A view based application, with several view controllers. (It was navigation based, but I had to make it view based, due to orientation issues).
  2. All view controllers are portrait, except one - landscapeLeft.

Tasks:

  1. One of my view controllers must automatically rotate to landscape, no matter how the user holds the device. All other controllers must be portrait, and after leaving the landscape controller, the app must force rotate to portrait, no matter, again, how the user holds the device.
  2. This must work as on IOS 6.x as on IOS 5.x

Go!

(Update Removed the macros suggested by @Ivan Vučica)

In all your PORTRAIT view controllers override autorotation methods like this:

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

You can see the 2 approaches: one for IOS 5 and another For IOS 6.

The same for your LANDSCAPE view controller, with some additions and changes:

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

ATTENTION: to force autorotation in IOS 5 you should add this:

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

Analogically, after you leave the LANDSCAPE controller, whatever controller you load, you should force again autorotation for IOS 5, but now you will use UIDeviceOrientationPortrait, as you go to a PORTRAIT controller:

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

Now the last thing (and it's a bit weird) - you have to change the way you switch from a controller to another, depending on the IOS:

Make an NSObject class "Schalter" ("Switch" from German).

In Schalter.h say:

#import <Foundation/Foundation.h>

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

In Schalter.m say:

#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

NOW, this is the way you use Schalter ( suppose you go from Warehouse controller to Products controller ) :

#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

Of course you must release instance_to_products object:

- (void)dealloc{
     [instance_to_products release];
     [super dealloc];
}

Well, this is it. Don't hesitate to downvote, I don't care. This is for the ones who are looking for solutions, not for reputation. Cheers! Sava Mazare.

查看更多
登录 后发表回答