jszumski
jszumski

Reputation: 7416

How should I determine how to orient a view in landscape if the UIInterfaceOrientation landscape values are the same constant?

I want to present a view that responds to orientation changes but for various reasons can't use iOS's built-in autorotation. In viewDidLoad I use the current orientation to determine the initial layout of the view:

_originalOrientation = [UIDevice currentDevice].orientation;

// if we were launched flat or unknown, use the status bar orientation as a guide
if (_originalOrientation == UIDeviceOrientationUnknown ||
    _originalOrientation == UIDeviceOrientationFaceDown ||
    _originalOrientation == UIDeviceOrientationFaceUp) {

        _originalOrientation = UIDeviceOrientationFromInterfaceOrientation([UIApplication sharedApplication].statusBarOrientation);
}

As you can see from my comment, I need to handle the case where [UIDevice currentDevice].orientation is not a usable direction (i.e. the device is flat or in an unknown orientation). In this scenario, I attempt to use the statusBarOrientation to infer the device's orientation (using UIViewController's interfaceOrientation would be an alternative way to get the same information):

UIDeviceOrientation UIDeviceOrientationFromInterfaceOrientation(UIInterfaceOrientation interface) {
    // note: because UIInterfaceOrientationLandscapeLeft and UIInterfaceOrientationLandscapeRight have the same value, this conversion can't distinguish them

    if (interface == UIInterfaceOrientationLandscapeLeft) {
        return UIDeviceOrientationLandscapeLeft;

    } else if (interface == UIInterfaceOrientationLandscapeRight) {
        return UIDeviceOrientationLandscapeRight;

    } else if (interface == UIInterfaceOrientationPortraitUpsideDown) {
        return UIDeviceOrientationPortraitUpsideDown;

    } else {
        return UIDeviceOrientationPortrait;
    }
}

However my logic can't accurately discern the two landscape cases because they are defined UIApplication.h to be the same constant value:

// Note that UIInterfaceOrientationLandscapeLeft is equal to UIDeviceOrientationLandscapeRight (and vice versa).
// This is because rotating the device to the left requires rotating the content to the right.
typedef NS_ENUM(NSInteger, UIInterfaceOrientation) {
    UIInterfaceOrientationPortrait           = UIDeviceOrientationPortrait,
    UIInterfaceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown,
    UIInterfaceOrientationLandscapeLeft      = UIDeviceOrientationLandscapeRight,
    UIInterfaceOrientationLandscapeRight     = UIDeviceOrientationLandscapeLeft
};

Is there an alternative way I can distinguish between UIInterfaceOrientationLandscapeLeft and UIInterfaceOrientationLandscapeRight? Is there a better way for me to perform my "fallback" logic besides using the status bar orientation?

Upvotes: 3

Views: 3387

Answers (6)

Kalpesh Panchasara
Kalpesh Panchasara

Reputation: 1750

Had face the same issue. here is the answer, always go for UIIntefaceOrientation to check whether current orientation is Landscape or Portrait.

OBJECTIVE C :

if([[UIApplication sharedApplication] statusBarOrientation] == UIDeviceOrientationLandscapeLeft || [[UIApplication sharedApplication] statusBarOrientation] == UIDeviceOrientationLandscapeRight)
 {
         //Go Here for landscape
 }
    else if ([[UIApplication sharedApplication] statusBarOrientation] == UIDeviceOrientationPortrait || [[UIApplication sharedApplication] statusBarOrientation] == UIDeviceOrientationPortraitUpsideDown)
 {
        //Go Here for Portrait

 }

SWIFT :

let currentOrient : UIInterfaceOrientation = UIApplication.shared.statusBarOrientation
if currentOrient == UIInterfaceOrientation.landscapeLeft || currentOrient == UIInterfaceOrientation.landscapeRight
 {
            //Go Here for landscape
 }
else if currentOrient == UIInterfaceOrientation.portrait || currentOrient == UIInterfaceOrientation.portraitUpsideDown
 {
            //Go Here for Portrait
 }

Upvotes: 0

Dominic
Dominic

Reputation: 351

This is how I have done it in the pass

Create a global variable called previousOr. Then in your first viewController use the following code to determine the status bar position. I put it in the viewDidAppear

if ([UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationLandscapeLeft) {
    previousOr = [NSString stringWithFormat:@"L"];
}
else if ([UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationLandscapeRight) {
    previousOr = [NSString stringWithFormat:@"L"];
}
else if ([UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationPortrait) {
    previousOr = [NSString stringWithFormat:@"P"];
}
else if ([UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationPortraitUpsideDown) {
    previousOr = [NSString stringWithFormat:@"P"];
}

Then in your subsequent viewControllers you have the following in the ViewDidLoad methods, this will detect if the device is portrait or landscape or if the device is flat what the device was in before they placed it flat:

if (UIDeviceOrientationIsLandscape([UIDevice currentDevice].orientation)) {
    NSLog(@"landscape");
    [self switchToLandscape];
}
else if (UIDeviceOrientationIsPortrait([UIDevice currentDevice].orientation)) {
    NSLog(@"portrait");
    [self switchToPortrait];
}
else {
    if ([previousOr isEqualToString:@"L"]) {
        [self switchToLandscape];
    }
    else  {
        [self switchToPortrait];
    }
}

Then you also have the following to do the re-plotting of elements on the view

- (void)switchToLandscape {
    previousOr = [NSString stringWithFormat:@"L"];
    //do stuff like replot centers
}

- (void)switchToPortrait {
    previousOr = [NSString stringWithFormat:@"P"];
    //do stuff like replot centers
}

and then you also have the following to detect the change in orientation:

- (void) didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
if ([UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationLandscapeLeft) {
    [self switchToLandscape];
}
else if ([UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationLandscapeRight) {
    [self switchToLandscape];
}
else if ([UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationPortrait) {
    [self switchToPortrait];
}
else if ([UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationPortraitUpsideDown) {
    [self switchToPortrait];
}
}

-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return YES;
}

-(BOOL) shouldAutorotate {
    return YES;
}

NOTE; I put in both shouldAutorotateToInterfaceOrientation and shouldAutoRotate for backward compatibility.

Upvotes: 0

Tammo Freese
Tammo Freese

Reputation: 10754

You wrote:

However my logic can't accurately discern the two landscape cases because they are defined UIApplication.h to be the same constant value

I am pretty sure that they are not! The values for interface and device orientations are swapped. See UIApplication.h:

typedef NS_ENUM(NSInteger, UIInterfaceOrientation) {
    UIInterfaceOrientationPortrait           = UIDeviceOrientationPortrait,
    UIInterfaceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown,
    UIInterfaceOrientationLandscapeLeft      = UIDeviceOrientationLandscapeRight,
    UIInterfaceOrientationLandscapeRight     = UIDeviceOrientationLandscapeLeft
};

The interface orientation UIInterfaceOrientationLandscapeLeft is equal to the device orientation UIDeviceOrientationLandscapeRight, and vice versa. But:

  • UIInterfaceOrientationLandscapeLeft != UIInterfaceOrientationLandscapeRight
  • UIDeviceOrientationLandscapeLeft != UIDeviceOrientationLandscapeRight

Try this code, I assume that will fix the problem:

UIDeviceOrientation UIDeviceOrientationFromInterfaceOrientation(UIInterfaceOrientation interface) {
    // interface orientation left is device orientation right
    if (interface == UIInterfaceOrientationLandscapeLeft) {
        return UIDeviceOrientationLandscapeRight;

    // interface orientation right is device orientation left
    } else if (interface == UIInterfaceOrientationLandscapeRight) {
        return UIDeviceOrientationLandscapeLeft;

    } else if (interface == UIInterfaceOrientationPortraitUpsideDown) {
        return UIDeviceOrientationPortraitUpsideDown;

    } else {
        return UIDeviceOrientationPortrait;
    }
}

BTW, you should also consider to use an own prefix for your function instead of UI, otherwise it may be easy to mistakenly assume that UIDeviceOrientationFromInterfaceOrientation is an API function.

Upvotes: 4

Rubanov Pavel
Rubanov Pavel

Reputation: 1

You always can determine device orientation using gravity property of CMDeviceMotion class. Property return a normalized vector pointed to Earth center (aka real gravity vector). If you not interested in angles, you can test only signs and generate constants for your purpose.

Upvotes: 0

yujean
yujean

Reputation: 795

If you're showing what the camera is seeing, i would ignore all orientations and show the user a preview of what the devices sees in portrait. What they see will always be "correct", and never accidentally upside down.

Upvotes: 0

rdelmar
rdelmar

Reputation: 104092

The orientation property of UIDevice is an enum that does distinguish between landscape left and right (their values are 3 and 4 respectively), so you can check that value.

typedef enum {
   UIDeviceOrientationUnknown,
   UIDeviceOrientationPortrait,
   UIDeviceOrientationPortraitUpsideDown,
   UIDeviceOrientationLandscapeLeft,
   UIDeviceOrientationLandscapeRight,
   UIDeviceOrientationFaceUp,
   UIDeviceOrientationFaceDown
} UIDeviceOrientation;

Upvotes: 3

Related Questions