Friday, October 26, 2012

How To Determine iPad Launch Orientation?

I've got an iPhone app with a little animation at startup. The desired effect is a fade/zoom of the Default.png. How I accomplish this is adding an image view in the app delegate that has the same image, then I do a UIViewAnimation block on it. Works just fine.

Enter iPad, and the conversion process to make this a universal app.

I know about Default-Landscape.png and Default-Portrait.png and those work just fine. But since it could be either image, I need to know which one to put in my image view for the animation.

The problem I'm encountering is that everything I know to ask is reporting portrait, even if the device/simulator launches landscape. Checking the frame of the UIWindow gives portrait dimensions, as does [UIScreen mainScreen]. Asking UIDevice for orientation reports portrait. Since this is the app delegate, I don't have a view controller yet to ask for orientation.

I found a little nugget on the Apple dev forums that says that view controllers are always created portrait, and then rotated without animation if necessary before being put on the screen. So I change my approach and use a view controller instead of just the image view.

This always goes portrait:
- (void)viewWillAppear:(BOOL)animated
{
 NSLog(@\">>> Entering %s <<<\", __PRETTY_FUNCTION__);
 
 [super viewWillAppear:animated];
 
 if ( ([self interfaceOrientation] == UIInterfaceOrientationLandscapeLeft) || ([self interfaceOrientation] == UIInterfaceOrientationLandscapeRight) )
 {
  NSLog(@\"Using landscape image\");
  [[self mainImageView] setImage:[UIImage imageNamed:@\"Default-Landscape.png\"]];
 }
 else
 {
  NSLog(@\"Using portrait image\");
  [[self mainImageView] setImage:[UIImage imageNamed:@\"Default-Portrait.png\"]];
 }
 
 NSLog(@\"<<< Leaving %s >>>\", __PRETTY_FUNCTION__);
}


I got this to work, but it seems a tad unnecessary. And it only works on the device, it does not work in the simulator.


- (void)viewWillAppear:(BOOL)animated
{
 NSLog(@\">>> Entering %s <<<\", __PRETTY_FUNCTION__);
 
 [super viewWillAppear:animated];
 
 [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
 
 if ( ([[UIDevice currentDevice] orientation] == UIDeviceOrientationLandscapeLeft) || ([[UIDevice currentDevice] orientation] == UIDeviceOrientationLandscapeRight) )
 {
  NSLog(@\"Using landscape image\");
  [[self mainImageView] setImage:[UIImage imageNamed:@\"Default-Landscape.png\"]];
 }
 else
 {
  NSLog(@\"Using portrait image\");
  [[self mainImageView] setImage:[UIImage imageNamed:@\"Default-Portrait.png\"]];
 }
 
 [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];
 
 NSLog(@\"<<< Leaving %s >>>\", __PRETTY_FUNCTION__);
}


I feel like I'm missing something obvious, but nothing else I've tried is working. Surely there has to be something easier, and something that would work on the simulator too. Any tips?

It's not obvious, but but this
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
UIInterfaceOrientation orientation = [UIDevice currentDevice].orientation;

works correctly for me on the device (haven't tried it in the simulator, though). I got stuck on this, too, when trying to ask UIDevice for the orientation inside didFinishLaunching, because I didn't realize at first that I had to explicitly turn on orientation notifications.

According to the UIDevice reference:
The value of this property always returns 0 unless orientation notifications have been enabled by calling beginGeneratingDeviceOrientationNotifications.
I had initially assumed that this property contained the current orientation at all times, but not so, apparently. I guess that turning on notifications is being handled for us behind the scenes in other situations where the orientation property is typically accessed, so it wasn't obvious that this needs to be done manually inside the app delegate.

I believe this works.


if (([[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationLandscapeLeft) ||
([[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationLandscapeRight)) {
set up your landscape view here
} else {
set up your portrait view here

I have a tab bar controller and 4 view controllers, and use this in each view controller at ViewDidLoad, ViewDidAppear, when it comes back from background processing, and after returning from reviewing an iAd. Since it follows the status bar it seems to always be in sync with it. Haven't checked to see if it works in the simulator.

I had the same problem, 

Basically I solved writing 

- (void)viewWillAppear:(BOOL)animated
{

   [self repositionUIWithInterfaceOrientation:self.interfaceOrientation];

   [super viewWillAppear:animated];

}

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {

   [self repositionUIWithInterfaceOrientation:toInterfaceOrientation];
 
}


So when i launch the app on the iPad (that is in landscape), viewWillAppear say "portrait" but after that is called willRotateToInterfaceOrientation that say "landscape", and when i launch the app with the iPad (that is in portrait), viewWillAppear say "portrait" and willRotateToInterfaceOrientation is not called.

No comments: