tags:

views:

4639

answers:

4

Hi all,

I would like to implement the following. When the user rotates the iPhone, I want to instantiate a new UIViewController (automatically upon rotation, not clicking a button or performing a similar action) and show to the user a view handled by this new UIViewController in landscape orientation. How to do this properly ?

I tried to instantiate the new controller in the methods willRotateToInterfaceOrientation and didRotateFromInterfaceOrientation, but none of this methods gets called!. I suspect this is because the current controller is pushed in by a navigation controller which is itself handled by a tabBarController. Any clue? A simple code snippet would be greatly appreciated.

Thank you in advance.

+1  A: 

Do you implement shouldAutorotateToInterfaceOrientation? If not, then that might be why you aren't getting the messages.

Eric Petroelje
Yes I do. Even simply returning YES in shouldAutorotateToInterfaceOrientation does not work.
unforgiven
+5  A: 

Hey,

I've implemented this exact type of behavior on an app and the key is to make sure that any parent controllers implement shouldAutorotateToInterfaceOrientation and that your current view controller also implements it. In my case I am using a tab controller which intercepts the call to shouldAutorotateToInterfaceOrientation and I had to override the tab controller to get the call to fall through. The default behavior of view controllers is to display in portrait mode only.

So you need to force them to allow all orientation changes through: -(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { return YES; }

Then in order to load a new view controller you should respond to:

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
  if((fromInterfaceOrientation == UIInterfaceOrientationPortrait) ||
     (fromInterfaceOrientation == UIInterfaceOrientationPortraitUpsideDown))
  {    
    // Load the view controller you want to display in landscape mode...
  }
}

and can also use:

-(void)willAnimateFirstHalfOfRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration

To detect that the orientation change is coming.

In my case I am using didRotateFromInterfaceOrientation to check if the last orientation was portrait and if so load the view I want displayed in landscape mode.

I then implemented the same methods in the viewcontroller I am loading and it is responsible for detecting when the orientation changes back to portrait and dismissing itself from the view stack.

Hope that helps a little.

Paul

paulthenerd
Do I need to subclass the initial tabBarController to add the shouldAutorotateToInterfaceOrientation method to it? This tabBar Controller has been setup using IB and a few lines a code in the application delegate. I have followed all of the steps, but didRotateFromInterfaceOrientation is not called.
unforgiven
Ok, I subclassed the tabBarController, adding shouldAutorotateToInterfaceOrientation and now everything works. Thank you very much for your help. Kind regards.
unforgiven
I prefer not using UITabBarController at all and instead using a UIViewController with a UITabBar.
Mk12
You should use the UIInterfaceOrientationIsPortrait function.
titaniumdecoy
+15  A: 

A cleaner way is NOT to use UIInterfaceOrientation for the rotation.

Why, because then your interface or view 1 actually rotates. This means you have to rotate it back to redisplay it after view 2 is removed.

To compensate for this, I simple subscribe to UIDeviceOrientation notifications. Subtly different. View 1 does NOT have to support autorotation to make this method work. In viewDidLoad enter the following code:

[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(detectOrientation) name:@"UIDeviceOrientationDidChangeNotification" object:nil];

Then just define the method detectOrientation:

-(void) detectOrientation {

     if (([[UIDevice currentDevice] orientation] == UIDeviceOrientationLandscapeLeft) || 
      ([[UIDevice currentDevice] orientation] == UIDeviceOrientationLandscapeRight)) {

     //load view 2


    } else if ([[UIDevice currentDevice] orientation] == UIDeviceOrientationPortrait) {

     // load view 1

    } 
}

Now neither of your views need support autoratation! You will however need to perform a transform on view 2 before loading:

-(void) transformView2ToLandscape {

NSInteger rotationDirection;
UIDeviceOrientation currentOrientation = [[UIDevice currentDevice] orientation];

if(currentOrientation == UIDeviceOrientationLandscapeLeft){
 rotationDirection = 1;
}else {
 rotationDirection = -1;
}

CGRect myFrame = CGRectMake(0, 0, 480, 300);
CGAffineTransform transform = [[self view2] transform];
transform = CGAffineTransformRotate(transform, degreesToRadians(rotationDirection * 90));
[[self view2] setFrame: myFrame];
CGPoint center = CGPointMake(myFrame.size.height/2.0, myFrame.size.width/2.0);
[[self view2] setTransform: transform];
[[self view2] setCenter: center];

}

Thats how I swap views o rotation without supporting autorotation in my views.

Corey Floyd
This looks really interesting. I will give it a try to understand the actual performance, i.e., how much faster a notification is delivered and how much does it take to apply the transform.Thank you very much.
unforgiven
+1  A: 

Indeed only top-level controller is notified. You're responsible for notifying nested controllers.

Add this to your tab bar controller:

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
    [self.selectedViewController didRotateFromInterfaceOrientation:fromInterfaceOrientation];
}
porneL
Thank you, I will add this to my code.
unforgiven
You shouldn't be subclassing UITabBarController.
Mk12
@Mk12: Why? Can such small override cause problems?
porneL