在Swift中获取设备方向

96

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

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

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

[[UIApplication sharedApplication] statusBarOrientation]

你的问题是什么?是将其翻译成Swift还是获得您期望的值? - David Rönnqvist
1
设备方向不一定与您的UI方向匹配。举个例子 - 将您的设备平放并测试您的代码! - Patrick
20个回答

77

你可以使用:

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:如何检测方向更改?

注意:didRotateFromInterfaceOrientation已弃用,请在iOS 2.0及更高版本中使用viewWillTransitionToSize

如果面朝上或面朝下则无法使用此方法。因此我们需要使用以下方法。

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

嗯...如果用户在旋转后导航到此视图,我们将不知道起始方向... - ScottyBlades
如果您仍在使用此函数,尽管它已被弃用,请不要忘记在您的实现中调用其超级方法,例如:super.didRotate(from: fromInterfaceOrientation)。根据苹果文档:“您必须在执行过程中的某个时刻调用此方法的超级方法。” - Bocaxica

64

要像Objective-C代码一样获取状态栏(因此UI)方向,只需执行以下操作:

UIApplication.sharedApplication().statusBarOrientation

您还可以使用UIDevice的orientation属性:

UIDevice.currentDevice().orientation

然而,这可能与您的用户界面(UI)的方向不匹配。从文档中可以看到:

该属性的值是指示设备当前方向的常量。此值表示设备的物理方向,可能与应用程序用户界面的当前方向不同。请参见“UIDeviceOrientation”以了解可能值的描述。


UIApplication.sharedApplication().statusBarOrientation 只能在主线程上使用。 - Yair hadad
1
我之前使用uidevice.current.orientation.isportrait来检测设备方向并且改变应用的用户界面,但是即使我把设备放在桌子上它也会改变。但是现在感谢你,一切都完美正常了。 - Gulfam Khan
更新于Swift 5.1 -> UIDevice.current.orientation - Ronaldo Albertini
UIDevice.current.orientation.isPortrait在设备方向为面朝上或面朝下时无法正常工作。像这个答案中所示,检查状态栏一直是一种可靠且有效的方法。 - Austin
对于那些无法使其工作的人:UIDevice.current.orientation 也可以使用。 - Mustafa Demir

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

Swift 4的答案:这就是我做的方式,

1.适用于所有类型的视图控制器

2.当用户旋转应用程序时也适用

3.也适用于第一次安装应用程序


1
完美的答案!谢谢 - torap
很好,但为什么你写了两次而不是这样:@objc class var isLandscape: Bool { return !isPortrait }我可能没理解到关键点? ;) - Renetik
哇,它完美地运行了,就像我想要的一样。非常感谢你 :) - Yogesh Patel
如何知道方向已更改? - user5306470

28

苹果最近放弃了横屏和竖屏的概念,而更喜欢我们使用屏幕尺寸。不过,以下方式仍然有效:

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

8
你提供的解决方案中,苹果公司声明:“如果你在自定义视图控制器中覆盖此方法,请始终在实现过程中某个时刻调用 super。” - Popmedic

17

Swift 4:

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

isFlat方向是什么? - Michał Ziobro
4
别忘了在你的重写函数中某个时刻调用super.viewWillTransition(to: size, with: coordinator)。 - Bocaxica

16

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

UIApplication.sharedApplication().statusBarOrientation

对于Swift 3.0:

UIApplication.shared.statusBarOrientation


iPad Air 1和2返回了错误的值,而iPad 2和iPhone则是正确的。我真的很想找到一个在所有设备上都能正常工作的解决方案,但苹果在这里搞砸了!:( - Steven B.
1
UIDevice.current.orientation不同,UIApplication.shared.statusBarOrientation在初始加载时就可用,而不仅仅是在屏幕旋转后。 - ewizard

13

statusBarOrientation已经被弃用,所以不能再像之前的答案那样使用它了。

此代码可获取方向而无需担心弃用问题。Swift 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)!
        }
    }
}

12

Swift 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这样的大屏幕非常有用,因为您无法使用func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?),因为traitcollection始终是width.regularheight.regular,因此在旋转时traitCollection不会改变。在Swift 4中,通知已重命名为UIDevice.orientationDidChangeNotification - Bocaxica
1
这正是我在寻找的。对于Swift 4及以上版本,使用以下方式:let notificationCenter = NotificationCenter.default notificationCenter.addObserver(self, selector: #selector(self.didOrientationChange(_:)), name: UIDevice.orientationDidChangeNotification, object: nil) - GeekyDeveloper

12

我使用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.
@Steven.B 嗯...在我的iPad Air、iPad 3和iOS 9模拟器上都能正常工作。而且也经过了测试人员的验证。也许你同时使用了其他影响它的代码。 - bpedit
这段代码可能只在UIViewcontroller中使用时有效,我试图在自定义的UIView类中获取方向和边界,但几乎每个设备返回不同的值,即使方向正确,框架或边界仍然是错误的。 - Steven B.

12

因此,如果苹果公司已经弃用了整个方向字符串的概念("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时的特定处理条件,即宽度和高度相等。

2
不错!对你的回答进行了小改进:别忘了在你的重写函数中某个时刻调用 super.viewWillAppear(animated)super.viewWillTransition(to: size, with: coordinator) - Bocaxica

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