18

How to get device current orientation in an App Extension, I have tried below two methods but no success.

  1. It always return UIDeviceOrientationUnknown

    [[UIDevice currentDevice] orientation]
    
  2. It shows red message that ‘sharedApplication’ is not available on iOS (App Extension)

    [[UIApplication sharedApplication] statusBarOrientation];
    
  3. I also add an observer but it not getting called.

    [[NSNotificationCenter defaultCenter] addObserver:self.view selector:@selector(notification_OrientationWillChange:) name:UIApplicationWillChangeStatusBarOrientationNotification object:nil];
    
    
    
    - (void)notification_OrientationWillChange:(NSNotification*)n
    {
       UIInterfaceOrientation orientation = (UIInterfaceOrientation)[[n.userInfo objectForKey:UIApplicationStatusBarOrientationUserInfoKey] intValue];
    
        if (orientation == UIInterfaceOrientationLandscapeLeft)
           [self.textDocumentProxy insertText:@"Left"];
        if (orientation == UIInterfaceOrientationLandscapeRight)
           [self.textDocumentProxy insertText:@"Right"];
    }
    

So now how can anyone get current device orientation.

Anand Suthar
  • 3,678
  • 2
  • 30
  • 52

11 Answers11

19

I got an idea!

extension UIScreen {

    var orientation: UIInterfaceOrientation {
        let point = coordinateSpace.convertPoint(CGPointZero, toCoordinateSpace: fixedCoordinateSpace)
        if point == CGPointZero {
            return .Portrait
        } else if point.x != 0 && point.y != 0 {
            return .PortraitUpsideDown
        } else if point.x == 0 && point.y != 0 {
            return .LandscapeLeft
        } else if point.x != 0 && point.y == 0 {
            return .LandscapeRight
        } else {
            return .Unknown
        }
    }

}

EDIT: On Swift 4 you can do:

extension UIScreen {
    var orientation: UIInterfaceOrientation {
        let point = coordinateSpace.convert(CGPoint.zero, to: fixedCoordinateSpace)
        switch (point.x, point.y) {
        case (0, 0):
            return .portrait
        case let (x, y) where x != 0 && y != 0:
            return .portraitUpsideDown
        case let (0, y) where y != 0:
            return .landscapeLeft
        case let (x, 0) where x != 0:
            return .landscapeRight
        default:
            return .unknown
        }
    }
}
Alex Machado
  • 1,171
  • 1
  • 12
  • 22
mmtootmm
  • 800
  • 6
  • 17
9

I found a way where we can calculate our device orientation like this way in (App Extension)

- (void)viewDidLayoutSubviews
{
   if(self.view.frame.size.width > self.view.frame.size.height)
      NSLog(@"Landscape");
   else
      NSLog(@"Portrait");
}

It gives me right orientation but still not get as device is LandscapeLeft or LandscapeRight as well as Portrait or PortraitUpsideDown.

Still need help.

Guillaume Algis
  • 10,705
  • 6
  • 44
  • 72
Anand Suthar
  • 3,678
  • 2
  • 30
  • 52
  • 1
    any one who vote me down, did you first try this practically. first check on this http://stackoverflow.com/questions/24166878/how-to-detect-orientation-change-in-custom-keyboard-extension-in-ios-8, and also explain here why you are voting me down. – Anand Suthar Sep 09 '14 at 09:58
  • 3
    You should use the screen bounds instead of the view frame because it's not always true, in my case the view width was always greater than the height but using the screen bounds worked for me CGRect r = [UIScreen mainScreen].bounds; BOOL isLandscape = r.size.width > r.size.height; – Chuy47 Jun 09 '15 at 17:43
7

In BroadcastExtension you can use sampleBuffer to understand orientation:

if let orientationAttachment =  CMGetAttachment(sampleBuffer, RPVideoSampleOrientationKey as CFString, nil) as? NSNumber 
{  
          let orientation = CGImagePropertyOrientation(rawValue: orientationAttachment.uint32Value)   
}
Bws Sluk
  • 478
  • 5
  • 11
6

The observer method will get called if you add this before: [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];

Edit: I use UIApplicationDidChangeStatusBarOrientationNotification for the observer

And in my method I check:

UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation]; BOOL isPortrait = UIDeviceOrientationIsPortrait(orientation);

Edit 2- Xcode 6.2 - iOS 7 & 8

It seems that if you want to use this on both iOS 7 & 8, the code above will give you wrong result on iOS 7.

So I use something else because on iOS 7 the mainScreen's bounds will never change, but on iOS 8 will change if the orientation changes.

I've got 3 macros that will give me the right size of the screen's width & height regardless of the iOS version:

#define IOS_VERSION_OLDER_THAN_8 ([[[UIDevice currentDevice] systemVersion] floatValue] < 8.0)

#define SCREEN_WIDTH_CALCULATED (IOS_VERSION_OLDER_THAN_8 ? (UIInterfaceOrientationIsPortrait([UIApplication sharedApplication].statusBarOrientation) ? [[UIScreen mainScreen] bounds].size.width : [[UIScreen mainScreen] bounds].size.height) : [[UIScreen mainScreen] bounds].size.width)

#define SCREEN_HEIGHT_CALCULATED (IOS_VERSION_OLDER_THAN_8 ? (UIInterfaceOrientationIsPortrait([UIApplication sharedApplication].statusBarOrientation) ? [[UIScreen mainScreen] bounds].size.height : [[UIScreen mainScreen] bounds].size.width) : [[UIScreen mainScreen] bounds].size.height)

Then, when the notification is fired, i check the orientation like this:

BOOL isPortrait = SCREEN_WIDTH_CALCULATED < SCREEN_HEIGHT_CALCULATED;

This will work on iOS 7 & iOS 8, but i didn't check for older versions of Xcode, only 6.2

This will return only if the device is on portrait or landscape, not all the 4 orientation types

Aura
  • 246
  • 1
  • 10
  • I edit my answer, maybe it will help you. I tested with Xcode 6.1 and is working. – Aura Nov 24 '14 at 10:45
  • to complete this answer, there's also `UIDeviceOrientationDidChangeNotification` – auco Mar 31 '15 at 22:33
  • I did this, but every time `UIApplicationDidChangeStatusBarOrientationNotification` is called, it seems to think it's in the same orientation; namely, UIDevice.currentDevice().orientation == 0. (`UIDeviceOrientationDidChangeNotification` didn't work at all for me) – Ruben Martinez Jr. Apr 02 '15 at 00:39
  • I tried this, but it always thinks its in landscape orientation. It doesn't work :( – Supertecnoboff Apr 27 '15 at 20:24
  • Device orientation events are not sent in app extensions – jjxtra Sep 09 '16 at 01:48
1

Use CoreMotion framework can get the device orientation.

func startMonitorDeviceOrientation() {
    if motionManager.isDeviceMotionAvailable {
        motionManager.deviceMotionUpdateInterval = 1.0
        let queue = OperationQueue()

        motionManager.startDeviceMotionUpdates(to: queue) { (deviceMotion, error) in
            guard let x = deviceMotion?.gravity.x,
            let y = deviceMotion?.gravity.y
            else {
                return
            }

            if fabs(y) >= fabs(x) {
                if y >= 0 {
                    // UIDeviceOrientationPortraitUpsideDown;
                    print("device orientation UIDeviceOrientationPortraitUpsideDown")
                } else {
                    // UIDeviceOrientationPortrait;
                    print("device orientation UIDeviceOrientationPortrait")
                }
            } else {
                if x >= 0 {
                    // UIDeviceOrientationLandscapeRight;
                    print("device orientation UIDeviceOrientationLandscapeRight")
                } else {
                    // UIDeviceOrientationLandscapeLeft;
                    print("device orientation UIDeviceOrientationLandscapeLeft")
                }
            }
        }
    } else {
        print("Device motion is not avaliable")
    }
}
Passaction
  • 389
  • 3
  • 5
0

You could take a look at this library and its forks. It uses CoreMotion to detect the device orientation so it may work on App Extension's restricted environment.

Rivera
  • 10,792
  • 3
  • 58
  • 102
0

I know it's late, but the mistake in this question was in this line:

[[NSNotificationCenter defaultCenter] addObserver:self.view selector:@selector(notification_OrientationWillChange:) name:UIApplicationWillChangeStatusBarOrientationNotification object:nil];

addObserver:self.view is wrong, the notification should be attached on self for being called. Work also on iOS8.

Benetz
  • 447
  • 5
  • 18
  • ok I will check this, and are you sure you are doing this in a "App Extension" – Anand Suthar Mar 24 '15 at 05:01
  • No you are right, i was looking for the standard app. By the way keep a look to this answer, maybe it work for App Extension too: http://stackoverflow.com/a/24467241/1171614 – Benetz Mar 24 '15 at 16:00
0

I haven't been been able to get it to work in an iMessage App Extension. Apple seems to have silently disabled it as far as I can tell. https://forums.developer.apple.com/thread/53981

Gandalf458
  • 2,139
  • 1
  • 21
  • 36
0

Handy extension method, just call UIInterfaceOrientation.current

extension UIInterfaceOrientation {
    static var current: UIInterfaceOrientation {
        return UIApplication.shared.windows.first?.windowScene?.interfaceOrientation ?? UIInterfaceOrientation.portrait
    }
}
p10ben
  • 425
  • 1
  • 6
  • 17
-2

This code won't give you the exact UIDeviceOrientation but you'll be able to know if it's in portrait or landscape mode

BOOL isLandScape = !(self.view.frame.size.width == ([[UIScreen mainScreen] bounds].size.width*([[UIScreen mainScreen] bounds].size.width<[[UIScreen mainScreen] bounds].size.height))+([[UIScreen mainScreen] bounds].size.height*([[UIScreen mainScreen] bounds].size.width>[[UIScreen mainScreen] bounds].size.height)));
tomercor
  • 15
  • 3
-2

I have done in my custom keyboard extension,Hope It will help you..

In order to update your custom keyboard when the orientation changes, override viewDidLayoutSubviews in the UIInputViewController. In another word, we can say that viewDidLayoutSubviews always called when rotation done.

In keyboard extension we're unable to use traditional flow as we usually used:

[UIApplication sharedApplication] statusBarOrientation]

So to detect current orientation, I used following code: In Objc :

if([UIScreen mainScreen].bounds.size.width < [UIScreen mainScreen].bounds.size.height){
// Portrait Orientation
}
else{
//Landscape Orientation
}

And in swift4, you can use this:

if UIScreen.main.bounds.size.width > UIScreen.main.bounds.size.height {
        //portrait Orientation
    }
    else
    {
        //landscape Orientation
    }
Anjali jariwala
  • 410
  • 5
  • 15