获取设备当前方向(应用扩展)

18

如何在应用扩展中获取设备的当前方向,我尝试了以下两种方法但未成功。

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

那么现在如何获取当前设备方向呢?


你使用的是哪个iOS版本? - angerboy
Xcode6 beta4 与 iOS8。 - Anand Suthar
2
从 iOS 8 开始,苹果不再使用设备方向。请查看大小类。 - angerboy
我发现这个答案很有帮助: https://dev59.com/0l0b5IYBdhLWcg3wLOc2 - Sagi Mann
11个回答

19

我有一个想法!

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

}

编辑:在 Swift 4 中,你可以这样做:

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

1
这是我见过的最好的答案。在扩展上下文和常规应用程序上都能完美运行,并且它能区分左横屏和右横屏。干得好! - lintmachine
1
太棒了!像魔法一样顺利运行。 - Alex Machado
1
这总是在所有方向上都给我纵向。 - Saqibdb

9

我发现一种在应用扩展中计算设备方向的方法,如下:

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

它为我提供了正确的定位,但仍然无法获取设备是LandscapeLeft(左横屏)LandscapeRight(右横屏)Portrait(竖屏)还是PortraitUpsideDown(倒立竖屏)

仍需要帮助。


1
任何一个给我投反对票的人,你们有没有实际尝试过这个方法?首先请在此链接中查看:https://dev59.com/HWAf5IYBdhLWcg3w52E_,并且在这里解释一下为什么要给我投反对票。 - Anand Suthar
3
应该使用屏幕边界而不是视图框架,因为这并非总是正确的。在我的情况下,视图宽度始终大于高度,但使用屏幕边界适用于我。CGRect r = [UIScreen mainScreen].bounds; BOOL isLandscape = r.size.width > r.size.height; - Chuy47

7
在BroadcastExtension中,您可以使用sampleBuffer来了解方向:
if let orientationAttachment =  CMGetAttachment(sampleBuffer, RPVideoSampleOrientationKey as CFString, nil) as? NSNumber 
{  
          let orientation = CGImagePropertyOrientation(rawValue: orientationAttachment.uint32Value)   
}

6
观察者方法将在添加以下代码后调用: [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; 编辑:我使用UIApplicationDidChangeStatusBarOrientationNotification作为观察者
在我的方法中,我检查: UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation]; BOOL isPortrait = UIDeviceOrientationIsPortrait(orientation); 编辑2 - Xcode 6.2 - iOS 7和8
如果您想在iOS 7和8上同时使用此功能,则以上代码在iOS 7上会给出错误结果。
因此,我使用其他方法,因为在iOS 7上,mainScreen的边界永远不会改变,但在iOS 8上,如果方向发生变化,它将发生变化。
我有3个宏,无论iOS版本如何,都可以给我正确的屏幕宽度和高度大小:
#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)

然后,当通知被触发时,我会像这样检查方向:
BOOL isPortrait = SCREEN_WIDTH_CALCULATED < SCREEN_HEIGHT_CALCULATED;

这将适用于iOS 7和iOS 8,但我没有检查过旧版本的Xcode,仅限于6.2版。

只有当设备处于纵向或横向模式时,才会返回结果,而不是所有4种方向类型。


我编辑了我的回答,也许能帮到你。我使用 Xcode 6.1 进行了测试,它可以正常工作。 - Aura
为了完善这个答案,还有UIDeviceOrientationDidChangeNotification - auco
我已经尝试过这个方法,但每次调用 UIApplicationDidChangeStatusBarOrientationNotification 时,它似乎都认为设备处于相同的方向;即 UIDevice.currentDevice().orientation == 0。(对我来说,UIDeviceOrientationDidChangeNotification 根本不起作用) - Ruben Martinez Jr.
我尝试过这个方法,但它总是认为它处于横向方向。它无效 :( - Supertecnoboff
设备方向事件在应用扩展中不会被发送。 - jjxtra

1
使用CoreMotion框架可以获取设备的方向。
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")
    }
}

0
我知道现在有点晚了,但这个问题的错误在这一行:
[[NSNotificationCenter defaultCenter] addObserver:self.view selector:@selector(notification_OrientationWillChange:) name:UIApplicationWillChangeStatusBarOrientationNotification object:nil];

addObserver:self.view 是错误的,通知应该附加在 self 上以便被调用。同样适用于 iOS8。


好的,我会检查一下。你确定你是在“应用扩展”中进行操作吗? - Anand Suthar
不,你是对的,我正在寻找标准应用程序。顺便说一下,请注意这个答案,也许它也适用于应用程序扩展:https://dev59.com/HWAf5IYBdhLWcg3w52E_#24467241 - Benetz

0

0
你可以看一下this库及其分支。它使用CoreMotion来检测设备方向,因此可以在应用扩展的受限环境中工作。

0

这是一个方便的扩展方法,只需调用UIInterfaceOrientation.current

extension UIInterfaceOrientation {
    static var current: UIInterfaceOrientation {
        return UIApplication.shared.windows.first?.windowScene?.interfaceOrientation ?? UIInterfaceOrientation.portrait
    }
}

-2

我已经在我的自定义键盘扩展中完成了,希望它能帮到你。

为了在方向改变时更新您的自定义键盘,在UIInputViewController中覆盖viewDidLayoutSubviews。换句话说,我们可以说当旋转完成时,viewDidLayoutSubviews总是被调用。

在键盘扩展中,我们无法像通常使用的那样使用传统的流程:

[UIApplication sharedApplication] statusBarOrientation]

为了检测当前方向,我使用了以下代码: 在Objc中:
if([UIScreen mainScreen].bounds.size.width < [UIScreen mainScreen].bounds.size.height){
// Portrait Orientation
}
else{
//Landscape Orientation
}

在Swift4中,您可以使用以下代码:

if UIScreen.main.bounds.size.width > UIScreen.main.bounds.size.height {
        //portrait Orientation
    }
    else
    {
        //landscape Orientation
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接