如何确定iOS设备的方向是否*实际*更改?



我写这段代码是为了根据应用程序是处于横向模式还是纵向模式对其布局进行一些更改。我已经设置了应用程序,以获得方向更改的通知,如下所示:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receivedRotate:) name:UIDeviceOrientationDidChangeNotification object:NULL];

然后定义receivedRotate:

-(void) receivedRotate: (NSNotification*) notification {
    UIDeviceOrientation interfaceOrientation = [[UIDevice currentDevice] orientation];
    // ... do something
}

但是,只要设备稍微移动,就会调用UIDeviceOrientationDidChangeNotification。例如,UIDeviceOrientation可以是以下任意一种:

UIDeviceOrientationPortrait,
UIDeviceOrientationPortraitUpsideDown,
UIDeviceOrientationLandscapeLeft,
UIDeviceOrientationLandscapeRight,
UIDeviceOrientationFaceUp,
UIDeviceOrientationFaceDown

这很好,但我只想知道设备的方向是纵向还是横向,而且我只想在方向真正改变时运行此代码——从纵向变为横向,反之亦然。如果他们把手机一直转过来,它仍然处于横向模式——只是横向模式的一种不同形式。确保我的轮换代码只在必要时运行的理想方法是什么?

DeviceOrientation与ScreenSize与StatusBar.isLandscape

iOS 11、Swift 4和Xcode 9.X

无论是否使用AutoLayout,都有几种方法可以获得正确的设备方向,它们可以用于在使用应用程序时检测旋转变化,以及在应用程序启动时或从后台恢复后获得正确的方向。

此解决方案在iOS 11和Xcode 9.X 中运行良好

1.UIScreen.main.bounds.size:

如果你只想知道应用程序是处于横向模式还是纵向模式,最好在启动时在rootViewControllerviewDidLoad中启动,如果你想在应用程序处于后台时检测旋转变化,并且应该以正确的方向恢复UI,则在rootViewControllerviewWillTransition(toSize:)中启动。

let size = UIScreen.main.bounds.size
if size.width < size.height {
    print("Portrait: (size.width) X (size.height)")
} else {
    print("Landscape: (size.width) X (size.height)")
}

这也发生在app/viewController生命周期的早期。

2.通知中心

如果您需要获得实际的设备方向(包括面朝下、面朝上等)。您想按如下方式添加一个观察者(即使您在AppDelegate中的application:didFinishLaunchingWithOptions方法中这样做,第一个通知也可能在viewDidLoad执行后触发

device = UIDevice.current
device?.beginGeneratingDeviceOrientationNotifications()
notificationCenter = Notifi`enter code here`cationCenter.default
notificationCenter?.addObserver(self, selector: #selector(deviceOrientationChanged),
    name: Notification.Name("UIDeviceOrientationDidChangeNotification"),
    object: nil)

并按如下方式添加选择器。我把它分成两部分,以便能够在viewWillTransition(toSize:) 中运行inspectDeviceOrientation()

@objc func deviceOrientationChanged() {
    print("Orientation changed")
    inspectDeviceOrientation()
}
func inspectDeviceOrientation() {
    let orientation = UIDevice.current.orientation
    switch UIDevice.current.orientation {
    case .portrait:
        print("portrait")
    case .landscapeLeft:
        print("landscapeLeft")
    case .landscapeRight:
        print("landscapeRight")
    case .portraitUpsideDown:
        print("portraitUpsideDown")
    case .faceUp:
        print("faceUp")
    case .faceDown:
        print("faceDown")
    default: // .unknown
        print("unknown")
    }
    if orientation.isPortrait { print("isPortrait") }
    if orientation.isLandscape { print("isLandscape") }
    if orientation.isFlat { print("isFlat") }
}

请注意,UIDeviceOrientationDidChangeNotification可能在启动期间发布多次,在某些情况下可能是.unknown.。我所看到的是,在viewDidLoadviewWillAppear方法之后,并且就在viewDidAppear,甚至applicationDidBecomeActive 之前,收到了第一个正确的方向通知

方向对象将为您提供所有7种可能的场景(来自枚举UIDeviceOrientation定义):

public enum UIDeviceOrientation : Int {
    case unknown
    case portrait // Device oriented vertically, home button on the bottom
    case portraitUpsideDown // Device oriented vertically, home button on the top
    case landscapeLeft // Device oriented horizontally, home button on the right
    case landscapeRight // Device oriented horizontally, home button on the left
    case faceUp // Device oriented flat, face up
    case faceDown // Device oriented flat, face down
}

有趣的是,isPortrait只读Bool变量在UIDeviceOrientation的扩展中定义如下:

extension UIDeviceOrientation {
    public var isLandscape: Bool { get }
    public var isPortrait: Bool { get }
    public var isFlat: Bool { get }
    public var isValidInterfaceOrientation: Bool { get }
}

3.StatusBarOrientation

UIApplication.shared.statusBarOrientation.isLandscape

这也可以很好地确定方向是纵向还是横向,并给出与点1相同的结果。您可以在viewDidLoad(用于应用程序启动)和viewWillTransition(toSize:)(如果来自后台)中对其进行评估。但它不会为您提供通知的上/下、左/右、上/下的详细信息(第2点)

我相信您想要的是UIInterfaceOrientation,而不是UIDeviceOrientation。要使用此功能,只需在视图控制器中实现以下功能即可。

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
    UIInterfaceOrientation currentOrientation = self.interfaceOrientation;
    //
}

此枚举声明为以下

typedef enum : NSInteger {
   UIInterfaceOrientationPortrait           = UIDeviceOrientationPortrait,
   UIInterfaceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown,
   UIInterfaceOrientationLandscapeLeft      = UIDeviceOrientationLandscapeRight,
   UIInterfaceOrientationLandscapeRight     = UIDeviceOrientationLandscapeLeft
} UIInterfaceOrientation;

将上一个方向保存在属性中,并将其与当前方向进行比较。

@0x7ffffff和@Kunal Balani的答案是正确的,但我建议您查看UIViewController的文档,了解iOS 6+应用程序上的轮换处理与iOS 5及更早版本中的处理方式,因为您没有提及是否支持iOS 5(在撰写本答案时,iOS 5仍然支持)。这将有助于解决您今后可能遇到的任何问题。我特别建议查看willRotateToInterfaceOrientation:duration:/willAnimateRotationToInterfaceOrientation:duration:/didRotateFromInterfaceOrientation:,以及它们在循环生命周期中何时被调用。

您肯定希望存储其他答案中提到的以前的方向,这样您就不会多次运行代码。

至于您关于横向与纵向版本的具体问题,UIKit的函数参考包含许多相关的宏,特别是UIInterfaceOrientationIsPortrait()UIInterfaceOrientationIsLandscape()

相关内容

  • 没有找到相关文章

最新更新