在 Swift 中获取设备方向

Posted

技术标签:

【中文标题】在 Swift 中获取设备方向【英文标题】:Getting device orientation in Swift 【发布时间】:2014-11-05 22:33:07 【问题描述】:

我想知道如何在 Swift 中获取当前设备方向?我知道有 Objective-C 的示例,但是我无法让它在 Swift 中运行。

我正在尝试获取设备方向并将其放入 if 语句中。

这是我遇到最多问题的一行:

[[UIApplication sharedApplication] statusBarOrientation]

【问题讨论】:

你的问题是什么?将其翻译成 Swift 或获得您期望的值? 设备方向不一定与您的 UI 方向匹配。案例点 - 将您的设备平放并测试您的代码! 【参考方案1】:

你可以使用:

override func didRotateFromInterfaceOrientation(fromInterfaceOrientation: UIInterfaceOrientation) 
    var text=""
    switch UIDevice.currentDevice().orientation
    case .Portrait:
        text="Portrait"
    case .PortraitUpsideDown:
        text="PortraitUpsideDown"
    case .LandscapeLeft:
        text="LandscapeLeft"
    case .LandscapeRight:
        text="LandscapeRight"
    default:
        text="Another"
    
    NSLog("You have moved: \(text)")        

SWIFT 3 更新

override func didRotate(from fromInterfaceOrientation: UIInterfaceOrientation) 
    var text=""
    switch UIDevice.current.orientation
    case .portrait:
        text="Portrait"
    case .portraitUpsideDown:
        text="PortraitUpsideDown"
    case .landscapeLeft:
        text="LandscapeLeft"
    case .landscapeRight:
        text="LandscapeRight"
    default:
        text="Another"
    
    NSLog("You have moved: \(text)")        

override func willRotateToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) 

您可以通过通知查看:ios8 Swift: How to detect orientation change?

注意: didRotateFromInterfaceOrientation 已弃用 viewWillTransitionToSize 适用于 iOS 2.0 及更高版本

在面朝上和面朝下的情况下,这将不起作用。 所以我们需要使用以下内容。

if UIApplication.shared.statusBarOrientation.isLandscape 
     // activate landscape changes
 else 
     // activate portrait changes

【讨论】:

感谢您的分享! 嗯...如果用户在旋转后导航到此视图,我们将不知道起始方向... 如果您仍在使用此功能,尽管它已被弃用。不要忘记在你的实现中调用它的超级方法。即:super.didRotate(from: fromInterfaceOrientation)。来自 Apple 文档:“您对此方法的实现必须在其执行期间的某个时间点调用 super。”【参考方案2】:

要像您拥有的 Objective-C 代码一样获得状态栏(以及 UI)方向,只需:

UIApplication.sharedApplication().statusBarOrientation

也可以使用 UIDevice 的orientation property:

UIDevice.currentDevice().orientation

但是,这可能与您的 UI 的方向不匹配。来自文档:

该属性的值是一个常数,表示当前 设备的方向。这个值代表物理 设备的方向,可能与当前不同 应用程序用户界面的方向。看 “UIDeviceOrientation”用于描述可能的值。

【讨论】:

UIApplication.sharedApplication().statusBarOrientation 仅在主线程上 感谢您的帮助! 我正在使用 uidevice.current.orientation.isportrait 检查设备方向并更改应用程序的用户界面,但即使将设备放在桌面上,它也会发生变化。但多亏了你,现在它工作得很好 在 swift 5.1 上更新 -> UIDevice.current.orientation 如果设备方向为面朝上或面朝下,UIDevice.current.orientation.isPortrait 不起作用。像这个答案一样检查状态栏一直是一致的并且对我有用。【参考方案3】:

Apple 最近摆脱了横向与纵向的概念,更喜欢我们使用屏幕尺寸。但是,这是可行的:

override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) 
    if UIDevice.currentDevice().orientation.isLandscape.boolValue 
        print("landscape")
     else 
        print("portrait")
    

【讨论】:

我会在您的解决方案中注意到苹果声明:“如果您在自定义视图控制器中覆盖此方法,请始终在您的实现中的某个时刻调用 super”【参考方案4】:
struct DeviceInfo 
struct Orientation 
    // indicate current device is in the LandScape orientation
    static var isLandscape: Bool 
        get 
            return UIDevice.current.orientation.isValidInterfaceOrientation
                ? UIDevice.current.orientation.isLandscape
                : UIApplication.shared.statusBarOrientation.isLandscape
        
    
    // indicate current device is in the Portrait orientation
    static var isPortrait: Bool 
        get 
            return UIDevice.current.orientation.isValidInterfaceOrientation
                ? UIDevice.current.orientation.isPortrait
                : UIApplication.shared.statusBarOrientation.isPortrait
        
    

swift4 答案: 我就是这样做的,

1.适用于各种视图控制器

2.当用户旋转应用时也可以工作

3.也是第一次安装应用

【讨论】:

完美答案!谢谢 很好,但为什么你写它拖时间而不是@objc class var isLandscape: Bool return !isPortrait 我没有得到重要的东西? ;) 哇,它工作得很好,因为我不会......非常感谢你:) 我怎么知道方向改变了?【参考方案5】:

斯威夫特 4:

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) 
        if UIDevice.current.orientation.isLandscape 
            print("landscape")
         else 
            print("portrait")
        
    

【讨论】:

isFlat 方向怎么样? 不要忘记在覆盖函数的某个位置调用 super.viewWillTransition(to: size, with: coordinator)。【参考方案6】:

要查找当前设备方向,只需使用以下代码:

UIApplication.sharedApplication().statusBarOrientation

对于 Swift 3.0

UIApplication.shared.statusBarOrientation

【讨论】:

iPad air 1&2 返回错误值,iPad 2 和 iPhone 正确。我真的很想找到一个适用于所有设备的设备,但苹果在这里搞得一团糟! :( UIDevice.current.orientation 不同,UIApplication.shared.statusBarOrientation 在初始加载时可用,而不是在屏幕旋转后才可用。【参考方案7】:

我在使用 InterfaceOrientation 时遇到了问题,它工作正常,只是它没有访问加载时的方向。所以我尝试了这个,它是一个守门员。这是因为 bounds.width 始终参考当前方向,而不是绝对的 nativeBounds.width

    if UIScreen.mainScreen().bounds.height > UIScreen.mainScreen().bounds.width 
        // do your portrait stuff
     else     // in landscape
        // do your landscape stuff
    

我从willRotateToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval)viewDidLoad 调用它,但它很灵活。

感谢 zSprawl 提供指向该方向的指针。我应该指出,这仅适用于 iOS 8 及更高版本。

【讨论】:

不适用于 iOS 9 & 10 iPad,只有初始值是正确的,以下所有值都是相反的方向。 @Steven.B 嗯...在我的 iPad Air、iPad 3 和 iOS 9 下的模拟器上运行良好。还与 beta 测试人员一起使用。也许您还使用了其他影响这一点的代码。 此代码可能仅在 UIViewcontroller 中使用时有效,我试图在自定义 UIView 类中获取方向和边界,但是当方向正确时,几乎我测试的每个设备都返回不同的值框架或边界仍然错误。【参考方案8】:

因此,如果 Apple 不赞成使用整个方向字符串(“portrait”、“landscape”),那么您所关心的只是宽度与高度的比率。 (有点像@bpedit 的回答)

当你将宽度除以高度时,如果结果小于 1,那么 mainScreen 或容器或任何处于“肖像”“模式”的东西。如果结果大于 1,则为“风景”画。 ;)

override func viewWillAppear(animated: Bool) 
    let size: CGSize = UIScreen.mainScreen().bounds.size
    if size.width / size.height > 1 
        print("landscape")
     else 
        print("portrait")
    

override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) 
    if size.width / size.height > 1 
        print("landscape")
     else 
        print("portrait")
    

(我猜如果您使用这种方法,那么您可能并不真正关心当比率恰好为 1、宽度和高度相等时的具体处理条件。)

【讨论】:

不错!对您的答案的小改进:不要忘记在您的覆盖函数中的某个时间点调用 super.viewWillAppear(animated)super.viewWillTransition(to: size, with: coordinator)【参考方案9】:

斯威夫特 3+

基本上:

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

@objc func didOrientationChange(_ notification: Notification) 
    //const_pickerBottom.constant = 394
    print("other")
    switch UIDevice.current.orientation 
        case .landscapeLeft, .landscapeRight:
            print("landscape")
        case .portrait, .portraitUpsideDown:
            print("portrait")
        default:
            print("other")
    

:)

【讨论】:

我喜欢这个答案。这对于像 iPad 这样的大屏幕非常有用,因为 traitcollection 将始终为 width.regularheight.regular,因此 traitCollection 不会在旋转时改变。在 Swift 4 中,通知已重命名为 UIDevice.orientationDidChangeNotification 这就是我要找的。对于 Swift 4 及更高版本,使用如下:let notificationCenter = NotificationCenter.default notificationCenter.addObserver(self, selector: #selector(self.didOrientationChange(_:)), name: UIDevice.orientationDidChangeNotification, object: nil)【参考方案10】:

statusBarOrientation弃用,因此不再可用像 在以上答案

在这段代码中可以得到方向而不用担心折旧。 斯威夫特 5 iOS 13.2 100% 测试

您的应用程序应该允许纵向和横向工作以使用以下代码,否则结果会有所不同

windows.first 是主窗口 windows.last 是您的当前窗口

struct Orientation 
    // indicate current device is in the LandScape orientation
    static var isLandscape: Bool 
        get 
            return UIDevice.current.orientation.isValidInterfaceOrientation
                ? UIDevice.current.orientation.isLandscape
                : (UIApplication.shared.windows.first?.windowScene?.interfaceOrientation.isLandscape)!
        
    
    // indicate current device is in the Portrait orientation
    static var isPortrait: Bool 
        get 
            return UIDevice.current.orientation.isValidInterfaceOrientation
                ? UIDevice.current.orientation.isPortrait
                : (UIApplication.shared.windows.first?.windowScene?.interfaceOrientation.isPortrait)!
        
    

【讨论】:

【参考方案11】:

Swift 3,基于 Rob 的回答

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) 
    if (size.width / size.height > 1) 
        print("landscape")
     else 
        print("portrait")
    

【讨论】:

不要忘记在覆盖函数中的某个时刻调用super.viewWillTransition(to: size, with: coordinator)【参考方案12】:

我发现 Swift 中的 Obj-C 代码的替代代码

if (UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation)) 

if UIApplication.shared.statusBarOrientation.isLandscape

注意:我们正在尝试查找状态栏方向是否为横向。如果是横向,则 if 语句为真。

【讨论】:

【参考方案13】:
   override func willRotateToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) 
    if (toInterfaceOrientation.isLandscape) 
        NSLog("Landscape");
    
    else 
        NSLog("Portrait");
    

【讨论】:

【参考方案14】:

Swift 5 – 解决方案:在应用启动和设备旋转期间检查方向:

// app start
override func viewDidAppear(_ animated: Bool) 
    super.viewWillAppear(animated)
    if let orientation = self.view.window?.windowScene?.interfaceOrientation 
        let landscape = orientation == .landscapeLeft || orientation == .landscapeRight
    


// on rotation
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) 
    super.viewWillTransition(to: size, with: coordinator)
    let landscape = UIDevice.current.orientation == .landscapeLeft || UIDevice.current.orientation == .landscapeRight

【讨论】:

这个答案消除了我在 OS 15 目标中的 windows 弃用警告 你调用的是 super.viewWillAppear 而不是 super.viewDidAppear【参考方案15】:

斯威夫特 5

适用于 SwiftUI 和基于故事板的应用程序。另外,检查旋转和特征处理程序

struct Orientation 
    
    /// true - if landscape orientation, false - else
    static var isLandscape: Bool 
        orientation?.isLandscape ?? window?.windowScene?.interfaceOrientation.isLandscape ?? false
    
    
    /// true - if portrait orientation, false - else
    static var isPortrait: Bool 
        orientation?.isPortrait ?? (window?.windowScene?.interfaceOrientation.isPortrait ?? false)
    
    
    /// true - if flat orientation, false - else
    static var isFlat: Bool 
        orientation?.isFlat ?? false
    
    
    /// valid orientation or nil
    static var orientation: UIDeviceOrientation? 
        UIDevice.current.orientation.isValidInterfaceOrientation ? UIDevice.current.orientation : nil
    
    
    /// Current window (for both SwiftUI and storyboard based app)
    static var window: UIWindow? 
        guard let scene = UIApplication.shared.connectedScenes.first,
              let windowSceneDelegate = scene.delegate as? UIWindowSceneDelegate,
              let window = windowSceneDelegate.window else 
            return UIApplication.shared.windows.first
        
        return window
    


class ViewController: UIViewController 
    
    override func viewWillAppear(_ animated: Bool) 
        super.viewWillAppear(animated)
        layoutAll()
    

    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) 
        super.viewWillTransition(to: size, with: coordinator)
        print("viewWillTransition")
        layoutAll()
    
    
    override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) 
        super.traitCollectionDidChange(previousTraitCollection)
        print("traitCollectionDidChange")
        layoutAll()
    
    
    /// Layout content depending on the orientation
    private func layoutAll() 
        // Layout as you need
        print("layoutAll: isLandscape=\(Orientation.isLandscape), isPortrait=\(Orientation.isPortrait), traitCollection=\(traitCollection)")
    

【讨论】:

【参考方案16】:

尝试使用horizontalSizeClass & verticalSizeClass

import SwiftUI

struct DemoView: View 
    
    @Environment(\.horizontalSizeClass) var hSizeClass
    @Environment(\.verticalSizeClass) var vSizeClass
    
    var body: some View 
        VStack 
            if hSizeClass == .compact && vSizeClass == .regular 
                VStack 
                    Text("Vertical View")
                
             else 
                HStack 
                    Text("Horizontal View")
                
            
        
    

在tutorial 中找到它。相关苹果的documentation。

【讨论】:

【参考方案17】:

对于任何看到过去 iOS 13 的人:

对我来说最可靠的方法现在已被弃用,尽管它(仍然)有效:

print(UIApplication.shared.statusBarOrientation.isPortrait)

现在看来要走的路:

if UIApplication.shared.windows.first?.
windowScene?.interfaceOrientation.isPortrait ?? true 
    print("Portrait")
 else 
    print("Landscape")

【讨论】:

【参考方案18】:

保持简单:

let orientation = UIApplication.shared.statusBarOrientation.isLandscape ? "landscape" : "portrait"

【讨论】:

以上是关于在 Swift 中获取设备方向的主要内容,如果未能解决你的问题,请参考以下文章

如何在纵向锁定设备时获取设备方向

如何在 Swift 中测量 iOS 设备上的抖动量?

如何使用 CoreMotion 在空间中获取设备方向

使用 Swift 获取用户面临的方向

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

Swift - 获取设备的 WIFI IP 地址