72

I have a question on how to detect the device orientation on iOS. I don't need to receive change notifications, just the current orientation itself. This seems to be a rather simple question, but I haven't been able to wrap my head around it. Below is what I have done so far:

UIDevice *myDevice = [UIDevice currentDevice] ;
[myDevice beginGeneratingDeviceOrientationNotifications];
UIDeviceOrientation deviceOrientation = myDevice.orientation;
BOOL isCurrentlyLandscapeView = UIDeviceOrientationIsLandscape(deviceOrientation);
[myDevice endGeneratingDeviceOrientationNotifications];

In my mind this should work. I enable the device to receive device orientation notices, then ask for what orientation it is in, but then it is not working and I don't know why.

Alexander Farber
  • 21,519
  • 75
  • 241
  • 416
JusmanX
  • 751
  • 1
  • 7
  • 5
  • This will help : http://jayprakashdubey.blogspot.in/2014/07/check-device-orientation.html – Jayprakash Dubey Jul 30 '14 at 10:38
  • Possible duplicate of [How to programmatically determine iPhone interface orientation?](http://stackoverflow.com/questions/634745/how-to-programmatically-determine-iphone-interface-orientation) – Suhaib Sep 19 '16 at 14:16

13 Answers13

120

Really old thread, but no real solution.

I Had the same problem, but found out that getting The UIDeviceOrientation isn't always consistent, so instead use this:

UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;

if(orientation == 0) //Default orientation 
    //UI is in Default (Portrait) -- this is really a just a failsafe. 
else if(orientation == UIInterfaceOrientationPortrait)
    //Do something if the orientation is in Portrait
else if(orientation == UIInterfaceOrientationLandscapeLeft)
    // Do something if Left
else if(orientation == UIInterfaceOrientationLandscapeRight)
    //Do something if right
Moe
  • 4,744
  • 7
  • 28
  • 37
  • 5
    This is the most correct answer if I may. This way, you can get the interface orientation at any time by simply checking this property. Note that it does not matter whether the status bar is hidden or not, the property will be updated no matter what! – ktolis Nov 07 '12 at 00:00
  • 3
    If you're picky and choose to not rely on the failsafe, don't forget `UIInterfaceOrientationPortraitUpsideDown`. – ArkReversed Feb 04 '13 at 14:46
  • This works for me, except it wasn't detecting portrait at all. I had to detect if it was right or left and then if it was neither, handle it as portrait. – Marcel Marino Aug 22 '13 at 15:06
  • 4
    This won't work if the Application's Orientation is restricted to either Portrait mode or Landscape Mode only. – Salman Khakwani Jun 23 '14 at 09:38
  • It doesn't work in my case, it always returns portrait. I changed the p-list to accept multiple orientations. Anyone knows why? – ZijunLost Apr 27 '17 at 05:34
  • `statusBarOrientation` is deprecated since iOS 9.0+ – Lal Krishna Sep 13 '17 at 10:46
76

if UIViewController:

if (UIDeviceOrientationIsLandscape(self.interfaceOrientation))
{
    // 
}

if UIView:

if (UIDeviceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation))
{
    //
}

UIDevice.h:

#define UIDeviceOrientationIsPortrait(orientation)  ((orientation) == UIDeviceOrientationPortrait || (orientation) == UIDeviceOrientationPortraitUpsideDown)
#define UIDeviceOrientationIsLandscape(orientation) ((orientation) == UIDeviceOrientationLandscapeLeft || (orientation) == UIDeviceOrientationLandscapeRight)

Updated:

add this code to xxx-Prefix.pch then you can use it anywhere:

// check device orientation
#define dDeviceOrientation [[UIDevice currentDevice] orientation]
#define isPortrait  UIDeviceOrientationIsPortrait(dDeviceOrientation)
#define isLandscape UIDeviceOrientationIsLandscape(dDeviceOrientation)
#define isFaceUp    dDeviceOrientation == UIDeviceOrientationFaceUp   ? YES : NO
#define isFaceDown  dDeviceOrientation == UIDeviceOrientationFaceDown ? YES : NO

usage:

if (isLandscape) { NSLog(@"Landscape"); }
TONy.W
  • 1,948
  • 19
  • 10
  • 1
    This is a very great answer. I've double-checked with documentation - more that it is a great answer, it is a correct one from the point of view of correctitude. Deserves to be marked as Checked. – Dumoko Apr 04 '13 at 10:03
  • 1
    This is all I ever wanted in a device orientation determination explanation. Thank you TONy. W – Morkrom Apr 05 '13 at 18:39
  • Interestingly enough only 'isLandscape' seems to fire correctly on my iPad simulator (all the others return NO regardless). Haven't had a chance yet to test on my real hardware ... but, I love your approach. Thx. – BonanzaDriver Jul 28 '13 at 03:44
  • 1
    `UIViewController::interfaceOrientation` returns `UIInterfaceOrientation`, while `UIDeviceOrientationIsLandscape` accepts a `UIDeviceOrientation`. This may work today, but it is not forwards compatible. – Pwner Jun 23 '14 at 22:11
  • 1
    'interfaceOrientation' is deprecated: first deprecated in iOS 8.0 – Bill Chan Jul 30 '16 at 21:00
  • use `[[UIDevice currentDevice] orientation]` in place of `self.interfaceOrientation` – Jason Oct 05 '19 at 00:18
22

For what You looking for first you have to Get Notification if Orientation Changed! You Can set This Thing in viewDidLoad like

[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(OrientationDidChange:) name:UIDeviceOrientationDidChangeNotification object:nil];

and whenever Orientation of your Device changed OrientationDidChange Called where You can do whatever You Want as Per Orientation

-(void)OrientationDidChange:(NSNotification*)notification
{
    UIDeviceOrientation Orientation=[[UIDevice currentDevice]orientation];

    if(Orientation==UIDeviceOrientationLandscapeLeft || Orientation==UIDeviceOrientationLandscapeRight)
    {
    }
    else if(Orientation==UIDeviceOrientationPortrait)
    {
    }
}
Saurabh Prajapati
  • 2,348
  • 1
  • 24
  • 42
  • 1
    This actually works perfectly for me because I need this information in only one ViewController, the rest of the app shouldn't change it orientation. So thank you! – CarmenA Jun 17 '15 at 15:26
22

If you want to get device orientation directly from accelerometer use [[UIDevice currentDevice] orientation]. But if you need current orientation of your application(interface orientation) use [[UIApplication sharedApplication] statusBarOrientation].

rafalkitta
  • 492
  • 6
  • 9
9

UIViewController has an interfaceOrientation property that you can access to find out the current orientation of a view controller.

As for your example, that should work. When you say it isn't working, what do you mean? What results does it give you versus what you expected?

Jasarien
  • 58,279
  • 31
  • 157
  • 188
  • 1
    I was debugging it in the iOS simulator, and regardless what the orientation of the device it always returns NO. When I put a breakpoint at it, I find out that the output of deviceOrientation = myDevice.orientation is always UIDeviceOrientationUnknown, so it doesn't look like it's tracking the orientation properly. – JusmanX Apr 06 '11 at 01:05
8

In Swift 3.0

to get device orientation.

/* return current device orientation.
   This will return UIDeviceOrientationUnknown unless device orientation notifications are being generated. 
*/
UIDevice.current.orientation

to get device orientation from your app

UIApplication.shared.statusBarOrientation
Ashok R
  • 19,892
  • 8
  • 68
  • 68
  • 2
    "'statusBarOrientation' was deprecated in iOS 13.0: Use the interfaceOrientation property of the window scene instead." Here is the reference https://stackoverflow.com/questions/25796545/getting-device-orientation-in-swift – Egel May 06 '20 at 13:01
4

Wasn't satisfied by "UIDeviceOrientation" because when a UIViewcontroller orientation is fixed to a specific orientation you don't get a pertinent information with the device orientation, so the right thing to do is using "UIInterfaceOrientation".

You can get the orientation from the UIViewController with a "self.interfaceOrientation", but when you are factorizing our code, you might need to do this kind of test outside a view controller, (custom view, a category…), so you still can access the information anywhere outside the controller by using the rootviewController:

if (UIInterfaceOrientationIsLandscape(view.window.rootViewController.interfaceOrientation)) {
}
Nicolas Lauquin
  • 1,517
  • 3
  • 14
  • 23
2

There's a way to achieve this whether the orientation lock is enabled or not by using data from CoreMotion. This is the code:

#import <CoreMotion/CoreMotion.h> 

    CMMotionManager *cm=[[CMMotionManager alloc] init];
    cm.deviceMotionUpdateInterval=0.2f;
    [cm startDeviceMotionUpdatesToQueue:[NSOperationQueue mainQueue]
                            withHandler:^(CMDeviceMotion *data, NSError *error) {

                            if(fabs(data.gravity.x)>fabs(data.gravity.y)){
                                    NSLog(@"LANSCAPE");
                                if(data.gravity.x>=0){
                                    NSLog(@"LEFT");
                                }
                                else{
                                    NSLog(@"RIGHT");
                                }

                        }
                        else{
                                NSLog(@"PORTRAIT");
                                if(data.gravity.y>=0){
                                    NSLog(@"DOWN");
                                }
                                else{

                                    NSLog(@"UP");
                                }

                            }

}];
FlySoFast
  • 1,854
  • 6
  • 26
  • 47
1

Here is some Swift variables to make detection easier:

let LANDSCAPE_RIGHT: Bool = UIDevice.currentDevice().orientation == UIDeviceOrientation.LandscapeRight
let LANDSCAPE_LEFT: Bool = UIDevice.currentDevice().orientation == UIDeviceOrientation.LandscapeLeft
let LANDSCAPE: Bool = LANDSCAPE_LEFT || LANDSCAPE_RIGHT
let PORTRAIT_NORMAL: Bool = UIDevice.currentDevice().orientation == UIDeviceOrientation.Portrait
let PORTRAIT_REVERSE: Bool = UIDevice.currentDevice().orientation == UIDeviceOrientation.PortraitUpsideDown
let PORTRAIT: Bool = PORTRAIT_REVERSE || PORTRAIT_NORMAL
Beninho85
  • 3,273
  • 27
  • 22
1

Have you unlocked the hardware lock for device orientation? There is one at the edge of my iPad 1.

Martin Lütke
  • 722
  • 7
  • 11
  • but it already doesn't work in the iOS simulator though...I'm not even putting it on the device yet. – JusmanX Apr 06 '11 at 01:00
  • @JusmanX There is a related question at SO: [uidevice-currentdevice-orientation-always-null](http://stackoverflow.com/questions/2461314/uidevice-currentdevice-orientation-always-null) – Martin Lütke Apr 06 '11 at 02:53
0

My current way of doing this:

+ (BOOL)isPortrait {
    let window = UIApplication.sharedApplication.delegate.window;
    if(window.rootViewController) {
        let orientation =
        window.rootViewController.interfaceOrientation;
        return UIInterfaceOrientationIsPortrait(orientation);
    } else {
        let orientation =
        UIApplication.sharedApplication.statusBarOrientation;
        return UIInterfaceOrientationIsPortrait(orientation);
    }
}

If there is for some reason no rootViewController yet fail safe to statusBarOrientation...

Renetik
  • 5,887
  • 1
  • 47
  • 66
0

And the best reliable way in swift :

public extension UIScreen {

    public class var isPortrait: Bool {
        UIApplication.shared.delegate?.window??.rootViewController?.interfaceOrientation.isPortrait ??
                UIApplication.shared.statusBarOrientation.isPortrait
    }

    public class var isLandscape: Bool { !isPortrait }
}
Renetik
  • 5,887
  • 1
  • 47
  • 66
0

This is my solution with Combine, which is quite easy to use with SwiftUI or regular Swift Object. A singleton object (static instance) is better than the "environment" for this kind of truly global object.

// Singleton object to keep the interface orientation (and any other global state)
class SceneContext: ObservableObject {
    @Published var interfaceOrientation = UIInterfaceOrientation.portrait
    static let shared = SceneContext()
}

class SceneDelegate: UIResponder, UIWindowSceneDelegate {
    ...
    func windowScene(_ windowScene: UIWindowScene, didUpdate previousCoordinateSpace: UICoordinateSpace, interfaceOrientation previousInterfaceOrientation: UIInterfaceOrientation, traitCollection previousTraitCollection: UITraitCollection) {
        SceneContext.shared.interfaceOrientation = windowScene.interfaceOrientation
    }
}

    // if you want to execute some code whenever the orientation changes in SwiftUI
    someView {
        ....
    }
    .onReceive(SceneContext.shared.$interfaceOrientation) { (orientation) in
        // do something with the new orientation
    }

    // if you want to execute some code whenever the orientation changes in a regular Swift object
    let pub = SceneContext.shared.$interfaceOrientation.sink(receiveValue: { (orientation) in
            // do something with the new orientation
            ...
        }) 

Satoshi Nakajima
  • 1,863
  • 18
  • 29