views:

352

answers:

0

I searched long and hard in an effort to figure this out. It took about 4-5 hours before I finally managed to reach a solution. I will be answering my own question in an effort to provide support to anyone who comes to the same problem.

So, I created the application Critical Mass Typer. It is a typing game that utilizes the keyboard as the main method of control. Words move towards the center of the screen where you have a nucleus. You type the word, and blow the word away before it reaches your nucleus. If 4 words make it to your nucleus, you reach critical mass, and explode (game over).

Now, I wanted to be able to support both landscape and portrait mode in the game. I thought about it ahead of time, and designed my classes such that it would be easy to implement. Setting up the game to do so was not a problem. Getting the views to rotate correctly was.

One thing that came up rather quickly was: any view that returns NO for the shouldAutorotateToInterfaceOrientation method of a view controller will cancel out the returns of its subviews.

So, my main view controller (CriticalMassViewController) controls the launch/menu of the game, and adds, as a subview, a view controller that runs a game mode (CMGameViewController). I only want my main view controller to be in landscape mode because if I rotate it, the animation/buttons all run off the side. I would have to create another view or alter the position of everything on the screen if I wanted it to be set up for portrait mode. However, my game view needs to be able to switch. To accomplish this, you set the return value to the specific mode you want your main view to be. Then tell your game view to not return any mode. After that, register the game view for notifications, then handle the rotation on your own.

For Ex: CriticalMassViewController.m:

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

CMGameViewController.m

- (void)viewDidLoad {
    [super viewDidLoad];
    /*GUI setup code was here*/

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didRotate:) name:UIDeviceOrientationDidChangeNotification object:nil];
}

-(void)didRotate:(NSNotification *)nsn_notification {
    UIInterfaceOrientation interfaceOrientation = [[UIDevice currentDevice] orientation];
    /*Rotation Code
    if(interfaceOrientation == UIInterfaceOrientationPortrait) {
        //Portrait setup
    } else if(interfaceOrientation == UIInterfaceOrientationLandscapeRight) {
        //Landscape setup
    }

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

Ok, so what's happening here? My main view controller only wants to be in LandscapeRight mode. So it stays like that since it never registers for any other orientation to auto rotate to. My game view controller needs to be able to rotate. If I use the following code for shouldAutorotateToInterfaceOrientation

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
        return (interfaceOrientation == UIInterfaceOrientationPortrait || interfaceOrientation == UIInterfaceOrientationLandscapeRight);
    }

IT WILL NOT ROTATE WHEN THE IPHONE CHANGES ORIENTATION. This is because its super view says it doesn't want to rotate. Once the super view returns no, the subview doesn't even get asked. So instead, we the game view controller just says No, i dont want to autorotate to any view. Then we register for the notification of an orientation change, and call our own rotate method.

These last bits of code are how you handle the rotation yourself. Call rotateView from inside didRotate. Note, I only want the user to be able to rotate after a difficulty is chosen, so I have logic inside my didRotate method to determine if it should even rotate. The comments in the code will explain why the certain things are happening.

-(void) rotateView:(UIInterfaceOrientation)uiio_orientation {

    /*This line is very important if you are using the keyboard. This was actually the
    largest problem I had during this entire ordeal. If you switch orientations while
    the keyboard is already being displayed, it will stay in landscape orientation. To
    get it to switch to portrait, we have to tell the status bar that its orientation
    has changed. Once you do this, the keyboard switches to portrait*/
    [UIApplication sharedApplication].statusBarOrientation = uiio_orientation;

    //Get the view you need to rotate
    UIView *portraitImageView = self.view;

    /*Check to make sure that you are in an orientation that you want to adjust to. If
    you don't check, you can make your view accidently rotate when the user orientates
    their phone to a position you aren't expecting*/
    if(uiio_orientation == UIInterfaceOrientationPortrait || uiio_orientation == UIInterfaceOrientationLandscapeRight) {
        if(uiio_orientation == UIInterfaceOrientationPortrait) {
                    //This transform rotates the view 90 degrees counter clock wise.
            portraitImageView.transform = CGAffineTransformMakeRotation(-M_PI/2);
        } else if(uiio_orientation == UIInterfaceOrientationLandscapeRight) {
                    //This transform rotates the view back to its original position
            portraitImageView.transform = CGAffineTransformMakeRotation(0);
        }

        CGRect frame = portraitImageView.frame;
        frame.origin.y = 0;
        frame.origin.x = 0;
        frame.size.width = portraitImageView.frame.size.height;
        frame.size.height = portraitImageView.frame.size.width;
        portraitImageView.frame = frame;
    }
}

I hope this helps someone, I know I had a very tough time with it, especially the keyboard going from landscape to portrait.