Detecting iOS UIDevice orientation
Asked Answered
O

9

77

I need to detect when the device is in portrait orientation so that I can fire off a special animation. But I do not want my view to autorotate.

How do I override a view autorotating when the device is rotated to portrait? My app only needs to display it's view in landscape but it seems I need to support portrait also if I want to be able to detect a rotation to portrait.

Overmeasure answered 3/2, 2012 at 0:35 Comment(1)
Note. It's probably more modern to just use viewDidLayoutSubviews which will take care of all layout changes. (Recall that in the near future, users will be resizing apps in windows on devices.) Reactive layout is the norm now.Albaalbacete
M
181

Try doing the following when the application loads or when your view loads:

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

Then add the following method:

- (void) orientationChanged:(NSNotification *)note
{
   UIDevice * device = note.object;
   switch(device.orientation)
   {
       case UIDeviceOrientationPortrait:
       /* start special animation */
       break;

       case UIDeviceOrientationPortraitUpsideDown:
       /* start special animation */
       break;

       default:
       break;
   };
}

The above will allow you to register for orientation changes of the device without enabling the autorotate of your view.


Note

In all cases in iOS, when you add an observor, also remove it at appropriate times (possibly, but not always, when the view appears/disappears). You can only have "pairs" of observe/unobserve code. If you do not do this the app will crash. Choosing where to observe/unobserve is beyond the scope of this QA. However you must have an "unobserve" to match the "observe" code above.

Meddle answered 3/2, 2012 at 0:55 Comment(3)
there is something that you must take care in here. (int)device.orientation can be 1 or 2 in portrait, 3 or 4 in landscape. And there is a "5" which means that the device is simply standing on a desk, which returns as landscape if you are checking with UIDeviceOrientationIsLandscape(device.orientation). You can get this "landscape" answer even though your app/device is in the portrait mode.Dariadarian
UIDevice does not give orientation if the app starts and until the app changes orientation. Other possibility for iOS>5 [UIApplication sharedApplication].statusBarOrientationTailpipe
Don't forget to remove observer in a proper place (depending on where you added it) otherwise you will get exception..Harriot
G
21

If you came to this question looking for how to detect an orientation change (without necessarily wanting to disable the rotation), you should also be aware of viewWillTransitionToSize, which is available from iOS 8.

Swift example from here

override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {

    coordinator.animateAlongsideTransition({ (UIViewControllerTransitionCoordinatorContext) -> Void in

        let orient = UIApplication.sharedApplication().statusBarOrientation

        switch orient {
        case .Portrait:
            println("Portrait")
            // Do something
        default:
            println("Anything But Portrait")
            // Do something else
        }

        }, completion: { (UIViewControllerTransitionCoordinatorContext) -> Void in
            println("rotation completed")
    })

    super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)
}

And if you don't need to worry about the actual orientation:

override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {

    // do something

    super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)
}

Objective-C example from here

- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
{   
    [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context)
    {
        UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
        // do whatever
    } completion:^(id<UIViewControllerTransitionCoordinatorContext> context)
    { 

    }];

    [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
}

And if you don't need to worry about the actual orientation (taken from this answer):

- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
{
    // Do view manipulation here.
    [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
}

See also

Guanabara answered 10/9, 2015 at 5:30 Comment(1)
viewWillTransitionToSize will call even if you minimise the app or power button press.Sweetening
F
3

1) Swift version of David's answer 2) In case you still want to detect orientation when there's no orientation change (Swift vesion of Moe's answer to How Do I detect the orientation of the device on iOS?)

    // Initial device orientation
    let orientation: UIInterfaceOrientation = UIApplication.sharedApplication().statusBarOrientation
    if(orientation == UIInterfaceOrientation.Unknown){
        // code for Unknown
    }
    else if(orientation == UIInterfaceOrientation.Portrait){
        // code for Portrait
    }
    else if(orientation == UIInterfaceOrientation.PortraitUpsideDown){
        // code for Portrait
    }
    else if(orientation == UIInterfaceOrientation.LandscapeRight){
        // code for Landscape        
    }
    else if(orientation == UIInterfaceOrientation.LandscapeLeft){
        // ode for Landscape
    }

    // To detect device orientation change
    UIDevice.currentDevice().beginGeneratingDeviceOrientationNotifications()
    NSNotificationCenter.defaultCenter().addObserver(
        self,
        selector: "orientationChanged:",
        name: UIDeviceOrientationDidChangeNotification,
        object: UIDevice.currentDevice())

orientationChanged function

func orientationChanged(note: NSNotification)
{
    let device: UIDevice = note.object as! UIDevice
    switch(device.orientation)
    {
        case UIDeviceOrientation.Portrait:
        // code for Portrait
        break
        case UIDeviceOrientation.PortraitUpsideDown:
        // code for Portrait
        break
        case UIDeviceOrientation.LandscapeLeft:
        // code for Landscape
        break
        case UIDeviceOrientation.LandscapeRight:
        // code for Landscape
        break
        case UIDeviceOrientation.Unknown:
        // code for Unknown
        break
        default:
        break
    }
}
Frantic answered 19/7, 2015 at 22:33 Comment(0)
C
1

If I understand you correctly, your app is landscape only. You can simply specify in the apps setup that it is landscape only and therefore do not need to worry about rotation. The app will start in landscape and stay there regardless of how the iPad is orientated.

Cauda answered 3/2, 2012 at 0:55 Comment(1)
Let me explain it with a simple example. I have an app that is intended to be viewed landscape. My view shows a table with a ball on top. When the device is rotated to portrait the ball rolls off the table. Everything remains landscape, the table, the background etc.Overmeasure
D
1

If you do not want to create device object, you can also use

-(void) seObserverForOrientationChanging
{
    [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
    [[NSNotificationCenter defaultCenter]
     addObserver:self selector:@selector(orientationChanged:)
     name:UIDeviceOrientationDidChangeNotification
     object:[UIDevice currentDevice]];
}


- (void) orientationChanged:(NSNotification *)note
{
    if (UIDeviceOrientationIsLandscape([UIDevice currentDevice].orientation)){
        //Do something in landscape
    }
    else {
        //Do something in portrait
    }
}
Depside answered 29/8, 2016 at 7:57 Comment(0)
H
0

First disable all but the orientation you want (so that it doesnt rotate)

Then like David said just get the device current orientation:

https://developer.apple.com/library/ios/#documentation/EventHandling/Conceptual/EventHandlingiPhoneOS/MotionEvents/MotionEvents.html

Alternatively you can just use the accelerometer yourself (since its how it is done anyway) and check where the gravity is at to see what orientation it has. If you take this approach you can play with the values yourself to get different results.

Hedges answered 3/2, 2012 at 1:29 Comment(0)
O
0

.UIDeviceOrientationDidChange notification is called many times on iphone even when device did not rotate. Do not know the reason, but if you need it only when device really rotated, then do the following.

NotificationCenter.default.addObserver(self, selector: #selector(orientationChanged), name: .UIDeviceOrientationDidChange, object: nil)

The method called from observer should look like this:

func orientationChanged() {

    if traitCollection.isIphone {

        defer {
            self.previousTraitCollectionForIphone = traitCollection
        }

        guard let previousTraitCollectionForIphone = previousTraitCollectionForIphone else {

            updateView()
            return
        }

        if previousTraitCollectionForIphone != traitCollection {
            updateView()
        }

    } else {
        updateView()
    }
}
Oxide answered 30/4, 2017 at 8:10 Comment(1)
Nice solution. One thing: traitCollection.isIphone is not recognised in Xcode 8.3 - Swift 3.1. Use traitCollection.userInterfaceIdiom == .phoneFlight
V
0

In Swift 5.0.

DeviceOrientation vs. ScreenSize vs StatusBar.isLandscape?

If you want to detect changes of orientation in background, and also need to detect face up/down changes, check this link In Swift, how to get the device orientation correctly right after it's launched?

It covers how to correctly detect the orientation in Swift using 3 alternate ways to do it: - viewWillTransitionToSize() - Using Observer - Using the status bar

Varico answered 23/5, 2020 at 18:10 Comment(0)
M
0

Here is some additional code to avoid spurious UI resets or transitions:

- (void) orientationChanged:(NSNotification *)note
{
    UIDeviceOrientation newOrientation = ((UIDevice*)note.object).orientation;
    
    // don't do anything if device is flat...
    if (UIDeviceOrientationIsFlat(newOrientation) || newOrientation == UIDeviceOrientationUnknown)
        return;
           
    // ... and only if orientation has changed
    if (newOrientation != self->lastOrientation) {
        // do whatever you need to do to change UI
        
        self->lastOrientation = newOrientation;
    }
}
Magellan answered 16/6, 2021 at 18:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.