Detecting iOS UIDevice orientation

IosObjective CSwiftOrientationUidevice

Ios Problem Overview


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.

Ios Solutions


Solution 1 - Ios

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.

Solution 2 - Ios

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

Solution 3 - Ios

  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 https://stackoverflow.com/questions/5559652/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
    }
}


    

Solution 4 - Ios

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.

Solution 5 - Ios

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
    }
}

Solution 6 - Ios

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.

Solution 7 - Ios

.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()
    }
}

Solution 8 - Ios

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 https://stackoverflow.com/questions/34452650/in-swift-how-to-get-the-device-orientation-correctly-right-after-its-launched/49058588#49058588

It covers how to correctly detect the orientation in Swift using 3 alternate ways to do it:

  • viewWillTransitionToSize()
  • Using Observer
  • Using the status bar

Solution 9 - Ios

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;
    }
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJace999View Question on Stackoverflow
Solution 1 - IosDavid M. SyzdekView Answer on Stackoverflow
Solution 2 - IosSuragchView Answer on Stackoverflow
Solution 3 - IosTsuneyuki OhsakiView Answer on Stackoverflow
Solution 4 - IosdrekkaView Answer on Stackoverflow
Solution 5 - IosB.KosmowskiView Answer on Stackoverflow
Solution 6 - IosPochiView Answer on Stackoverflow
Solution 7 - IosBartłomiej SemańczykView Answer on Stackoverflow
Solution 8 - Ioseharo2View Answer on Stackoverflow
Solution 9 - IossnibbeView Answer on Stackoverflow