在 Swift 中获取设备定位

我想知道如何在 Swift 中获得当前的设备定位?我知道有一些 Objective-C 的例子,但是我还没有能够在 Swift 中使用它。

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

这是我遇到的最大问题:

[[UIApplication sharedApplication] statusBarOrientation]
104423 次浏览

To get the status bar (and therefor UI) orientation like the Objective-C code you have, it's simply:

UIApplication.sharedApplication().statusBarOrientation

You can also use the orientation property of UIDevice:

UIDevice.currentDevice().orientation

However, that may not match what orientation your UI is in. From the docs:

The value of the property is a constant that indicates the current orientation of the device. This value represents the physical orientation of the device and may be different from the current orientation of your application’s user interface. See “UIDeviceOrientation” for descriptions of the possible values.

you can use:

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 UPDATE

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

or

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

with Notification you can check: IOS8 Swift: How to detect orientation change?

NOTE : didRotateFromInterfaceOrientation is Deprecated Use viewWillTransitionToSize for iOS 2.0 and later

In case of Face up and Face Down this will not work. So we need to use the following.

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

Apple recently got rid of the idea of Landscape vs. Portrait and prefers we use screen size. However, this works:

override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
if UIDevice.currentDevice().orientation.isLandscape.boolValue {
print("landscape")
} else {
print("portrait")
}
}
   override func willRotateToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) {
if (toInterfaceOrientation.isLandscape) {
NSLog("Landscape");
}
else {
NSLog("Portrait");
}
}

I had issues with using InterfaceOrientation, it worked OK except it wasn't accessing the orientation on loading. So I tried this and it's a keeper. This works because the bounds.width is always in reference to the current orientation as opposed to nativeBounds.width which is absolute.

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

I call this from willRotateToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) and from viewDidLoad but it flexible.

Thanks to zSprawl for the pointer in that direction. I should point out that this is only good for iOS 8 and later.

So, if Apple is deprecating the whole orientation string thing ("portrait","landscape"), then all you care about is the ratio of width to height. (kinda like @bpedit's answer)

When you divide the width by the height, if the result is less than 1, then the mainScreen or container or whatever is in "portrait" "mode". If the result is greater than 1, it's a "landscape" painting. ;)

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

(I'm guessing that if you use this approach then you probably don't really care about specifically handling the condition when the ratio is exactly 1, equal width and height.)

To find current device orientation simply use this code:

UIApplication.sharedApplication().statusBarOrientation

for swift 3.0

UIApplication.shared.statusBarOrientation

Swift 3, based on Rob's answer

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
if (size.width / size.height > 1) {
print("landscape")
} else {
print("portrait")
}
}
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 answer: this is how I do it,

1.works for all kinds of view controller

2.also work when the user rotates the app

3.also for the first time install the app

Swift 4:

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

Swift 3+

Basically:

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

:)

I found that the alternative code in Swift for the Obj-C code

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

is

if UIApplication.shared.statusBarOrientation.isLandscape

Note: we are trying to find the status bar orientation is landscape or not. If it is landscape then the if statement is true.

statusBarOrientation is deprecated, so no longer available to use like in above answers

In this code can get orientation without worrying about depreciation. Swift 5 ioS 13.2 Tested 100%

Your application should allow working in both portrait and landscape to use the below code, otherwise, results will be different

windows.first is main window windows.last is your current window

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

Try to use 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")
}
}
}
}
}

Found it in this tutorial. Related Apple's documentation.

For anyone seeing this past iOS 13:

The most reliable way to me is deprecated now, though it is (still) working:

print(UIApplication.shared.statusBarOrientation.isPortrait)

What seems to be the way to go now:

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

Swift 5 – Solution: Check orientation on app start & during device rotation:

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

Swift 5

Works in SwiftUI and storyboard based app. Also, check rotation and trait handlers:

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

Keeping it simple:

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

I think the best way to do this in modern Objective C and accounting for now deprecated functions is...

UIDeviceOrientationIsLandscape([[UIDevice currentDevice] orientation]);

In swift, that would be.

UIDevice.current.orientation.isLandscape