在Swift中,如何在设备启动后正确获取设备方向?

12

我使用以下代码来查看设备是否处于横屏模式:

UIDevice.currentDevice().orientation.isLandscape.boolValue

如果我在应用程序启动之前将我的设备放入横向模式,并在viewDidLoad之后调用此行代码,则它有效,但它总是返回false

如果我改用这个:

interfaceOrientation.isLandscape

它返回true,这是正确的,但编译器正在显示一个警告,interfaceOrientation在iOS 8.0中已被弃用

在应用程序启动后获得设备方向的正确方法是什么?


你的应用程序只支持一个方向吗? - aaisataev
interfaceOrientation和deviceOrientation是两个不同的东西。使用UIDeviceOrientationDidChangeNotification来获取设备方向变化时的通知。 - san
1
UIApplication.sharedApplication().statusBarOrientation.isLandscape 是关于什么的? - aaisataev
@aaisataev UIApplication.sharedApplication().statusBarOrientation 可以工作,但我想知道为什么在应用程序启动时 UIDevice.currentDevice().orientation 不起作用。 - Joe Huang
@san在你最后的陈述中,你是不是指的是statusBarOrientation - Joe Huang
显示剩余4条评论
5个回答

13

DeviceOrientation vs. ScreenSize vs StatusBar.isLandscape? --- NEW --- vs. SwuftUI GeometryReader?

SwiftUI 更新 - Xcode 12 和 iOS 16

如果您只想处理横向/纵向之间的变化,另一种 SwiftUI 的替代方案是在 ContentView 中拦截 geometryProxy,然后将值保存在全局变量中,以便在整个应用程序中使用。每次重新渲染 contentView 时,该值都会更新。这将使您避免观察 UIDevice 通知。

var content: some View {
        GeometryReader { geometry in
            processDeviceOrientation(with: geometry)
            myContentView()
        }
    }
}

func processDeviceOrientation(with geometry: GeometryProxy) {
    let orientation: DeviceOrientation = geometry.size.width > geometry.size.height ? .landscape : .portrait
        if orientation != deviceOrientation {
            deviceOrientation = orientation
        }
    }
}

var deviceOrientation: DeviceOrientation = .unknown

enum DeviceOrientation {
    case landscape, portrait, unknown
}

iOS 11,Swift 4和Xcode 9.X

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

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

1. UIScreen.main.bounds.size: 如果您只想知道应用程序是否处于横向或纵向模式,则最好从viewDidLoad中的rootViewController开始,在启动时,并且在rootViewController中的viewWillTransition(toSize:)中,如果要检测应用程序在后台的旋转更改,并且应在正确的方向上恢复UI。

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

这也发生在应用程序/视图控制器生命周期的早期阶段。

2. NotificationCenter

如果您需要获取实际设备方向(包括面朝下,面朝上等),您可以添加一个观察者如下(即使您在AppDelegate中的application:didFinishLaunchingWithOptions方法中执行此操作,第一个通知也可能在执行viewDidLoad之后触发)。

device = UIDevice.current
device?.beginGeneratingDeviceOrientationNotifications()
notificationCenter = NotificationCenter.default
notificationCenter?.addObserver(self, selector: #selector(deviceOrientationChanged),
    name: Notification.Name("UIDeviceOrientationDidChangeNotification"),
    object: nil)

然后将选择器添加如下。我将其分为两部分,以便在viewWillTransition中运行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 种可能的情况(从 enum 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
}

有趣的是,只读的Bool变量isPortrait在扩展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 

这个方法同样适用于确定设备为纵向还是横向,并且与第一条方法得出的结果相同。您可以在viewDidLoad中(应用启动时)进行评估,以及在从后台返回时在viewWillTransition(toSize:)中进行评估。但是它不会像通知(Point 2)那样给你有关顶部/底部、左侧/右侧、上下的详细信息。

2
#3 对我很有效 - 我想要两个不同的状态,即横屏和竖屏,以便进行一些非常特定的 UI 更新。使用设备方向会在设备变平时出现问题。 - brindy

5

对我来说,这是避免方向“朝上或平放”的最佳方法。 - HomiFox
不工作对我来说不行。 - undefined

3

isValidInterfaceOrientation 应该在检查方向 isLandscape 之前被识别。当平面消息具有任何 isLandscape 的值时,不要使用 isValidInterfaceOrientation == false 进行处理。

在仔细阅读主题后,我才明白这一点。考虑到 isValidInterfaceOrientation ,它可以正常工作。

@objc func rotated() {
    if (UIDevice.current.orientation.isValidInterfaceOrientation) {
        if (UIDevice.current.orientation.isLandscape) { 
            if(!bLandscape) {
                bLandscape = true
                setupTabBar() // Repaint the app
            }
        } else { // Portait 
            if(bLandscape) {
                bLandscape = false
                setupTabBar() // Repaint the app 
            }
        }
    }
}

当手机方向被锁定时,这个解决方案对我不起作用。我想在手机方向被锁定时检测方向变化。有可能吗? - Khadija Daruwala

2

我已经进行了多次有关方向的测试,所以总结了一些经验。

在所有的iPhone设备中,除了iPhone6(s) plus,唯一的界面方向是 .portrait 。如果应用程序在横屏模式下启动,必须更改方向。将收到 UIDeviceOrientationDidChangeNotification 。这是获取方向的合适时间。

关于iPhone6在横屏模式下启动时,启动后的方向会改变一次:

enter image description here

在iPhone6 plus上,在横屏模式下启动后,方向永远不会改变:

enter image description here

同一个应用程序的两个不同屏幕截图:

enter image description here

因此,在应用程序更改方向之前,方向仍然像在主页中一样。

viewDidLoad中,方向尚未改变,日志将是错误的方向。


0

我曾经遇到一个问题,无法检测出 isFlat 之前的方向,所以我将以下代码放在我的视图控制器中

let orientation = UIDevice.current.orientation

override open var supportedInterfaceOrientations: UIInterfaceOrientationMask {
    if  orientation.isPortrait {
        return .portrait
    } else if orientation.isFlat{
        if UIScreen.main.bounds.width < UIScreen.main.bounds.height{
            return .portrait
        } else {
            return .landscape
        }
    } else {
        return .landscape
    }
}

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