Frank Rupprecht
Frank Rupprecht

Reputation: 10408

How to get initial device orientation on app start in iOS 7/8?

I'm implementing a photo/video picking interface similar to Apple's Camera app. I need to do manual interface rotation since I don't want the preview view to rotate, but I do want to rotate the controls accordingly. Therefore I have the following code in my view controller (which is the root view controller of the app):

- (NSUInteger)supportedInterfaceOrientations {
    return UIInterfaceOrientationMaskPortrait;
}

To detect orientation changes I registered my VC for the according notifications in viewDidLoad:

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

This works as expected except when I start the app in an orientation other then portrait. My callback gets called; however, the current device orientation is always UIDeviceOrientationPortrait, even when I hold my device sideways when starting the app. The same holds for the status bar orientation.

How can I figure out the device orientation right after app start? I know it's possible because Apple is doing it in their Camera app. The question is, is there any official way, or do they use some magic internal APIs?

Update

Anna's answer turns out to be correct, but you should also read the comments. TL;DR: The effect occurs on iPhone 6 (Plus) when the app is not optimized for them.

Upvotes: 0

Views: 1193

Answers (1)

Anna Dickinson
Anna Dickinson

Reputation: 3347

First of all, you don't want to look at the UIDevice orientation (see this post).

I think you're getting a spurious result before the UI is done configuring itself. In other words, it reports Portrait because it's just not ready at that point in the startup process. Why is it sending notifications at all? Not sure; but, you definitely don't want to use them for your task.

I don't quite understand what you're using the initial orientation for, but I have an alternate way to solve your problem: you can keep a view static by counter-rotating it. When your view controller receives a rotation message (I.e. viewDidRotate, pre-iOS 8), you change the transform on your static view to rotate in the direction opposite that rotation.

There are probably some clever ways to do it with autolayout as well, but I haven't figured them out yet... :-p

EDIT:

Some code...

YMMV -- this works, but I haven't touched it in a while! You might have to play with it a little... and contrary to what I said in the comments, it looks like I am animating the rotation. But, it really doesn't move! I think the counter-rotation animation gets cancelled out by the system's rotation of the whole display... or something.

simViewControllerContainer is the view I'm preventing from rotating.

- (void)viewDidLoad
{
    [super viewDidLoad];

    // Set simView container's orientation if the device started in landscape
    if ( self.interfaceOrientation == UIInterfaceOrientationLandscapeRight ) {
        simViewControllerContainer.center = (CGPoint) {self.view.bounds.size.height/2, self.view.bounds.size.width/2};
        simViewControllerContainer.transform = CGAffineTransformMakeRotation(-M_PI/2);
    }
    else if ( self.interfaceOrientation == UIInterfaceOrientationLandscapeLeft ) {
        simViewControllerContainer.center = (CGPoint) {self.view.bounds.size.height/2, self.view.bounds.size.width/2};
        simViewControllerContainer.transform = CGAffineTransformMakeRotation(M_PI/2);
    }
}

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

    BOOL doubleRotation =
    (UIInterfaceOrientationIsPortrait(self.interfaceOrientation) && UIInterfaceOrientationIsPortrait(toInterfaceOrientation)) ||
    (UIInterfaceOrientationIsLandscape(self.interfaceOrientation) && UIInterfaceOrientationIsLandscape(toInterfaceOrientation));

    if ( toInterfaceOrientation == UIInterfaceOrientationLandscapeRight ) {
        [UIView animateWithDuration:duration animations:^{
            if ( !doubleRotation ) {
                // For a double-rotation, we don't want to change the center -- it's the same:
                // a double-rotation is landscape to landscape or portrait to portrait -- same center.
                simViewControllerContainer.center = (CGPoint) {self.view.bounds.size.height/2, self.view.bounds.size.width/2};
            }
            simViewControllerContainer.transform = CGAffineTransformMakeRotation(-M_PI/2);
        }];
    }
    else if ( toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft ) {
        [UIView animateWithDuration:duration animations:^{
            if ( !doubleRotation ) {
                simViewControllerContainer.center = (CGPoint) {self.view.bounds.size.height/2, self.view.bounds.size.width/2};
            }
            simViewControllerContainer.transform = CGAffineTransformMakeRotation(M_PI/2);
        }];

    }
    else if ( toInterfaceOrientation == UIInterfaceOrientationPortraitUpsideDown ) {
        [UIView animateWithDuration:duration animations:^{
            if ( !doubleRotation ) {
                simViewControllerContainer.center = (CGPoint) {self.view.bounds.size.height/2, self.view.bounds.size.width/2};
            }
            simViewControllerContainer.transform = CGAffineTransformIdentity;
        }];

    }
    else if ( [UIDevice currentDevice].orientation == UIDeviceOrientationPortrait ) {
        [UIView animateWithDuration:duration animations:^{
            if ( !doubleRotation ) {
                simViewControllerContainer.center = (CGPoint) {self.view.bounds.size.height/2, self.view.bounds.size.width/2};
            }
            simViewControllerContainer.transform = CGAffineTransformIdentity;
        }];
    }
}

Upvotes: 1

Related Questions