7

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.

Massimo Cafaro
  • 25,429
  • 15
  • 79
  • 93

4 Answers4

29

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.

Matteo Alessani
  • 10,264
  • 4
  • 40
  • 57
Corey Floyd
  • 25,929
  • 31
  • 126
  • 154
  • 1
    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. – Massimo Cafaro May 31 '09 at 07:04
  • this works very well. specially if you are using two different view controllers for two orientations . – Jinah Adam Feb 17 '11 at 04:52
  • The "checked" answer answers the question, but this response answers the question which (ideally) should have been asked. – DBD Sep 06 '11 at 17:32
7

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
  • 9,487
  • 2
  • 35
  • 29
  • 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. – Massimo Cafaro Apr 08 '09 at 18:50
  • 1
    Ok, I subclassed the tabBarController, adding shouldAutorotateToInterfaceOrientation and now everything works. Thank you very much for your help. Kind regards. – Massimo Cafaro Apr 08 '09 at 18:57
  • I prefer not using UITabBarController at all and instead using a UIViewController with a UITabBar. – mk12 Aug 17 '09 at 02:22
  • You should use the UIInterfaceOrientationIsPortrait function. – titaniumdecoy Jul 30 '10 at 23:18
3

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];
}
Kornel
  • 97,764
  • 37
  • 219
  • 309
1

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

Eric Petroelje
  • 59,820
  • 9
  • 127
  • 177