如何检测方向变化?

我正在使用 Swift,我想能够加载一个 UIViewController 当我旋转到横向,有人能指出我在正确的方向吗?

我在网上找不到任何东西,而且对文档有点困惑。

180970 次浏览

我是这么做的:

AppDelegate.swift中的 didFinishLaunchingWithOptions函数中,我放入:

NotificationCenter.default.addObserver(self, selector: #selector(AppDelegate.rotated), name: UIDevice.orientationDidChangeNotification, object: nil)

然后,我将下面的函数放到 App臀代表类中:

func rotated() {
if UIDeviceOrientationIsLandscape(UIDevice.current.orientation) {
print("Landscape")
}


if UIDeviceOrientationIsPortrait(UIDevice.current.orientation) {
print("Portrait")
}
}

希望这对其他人有帮助!

谢谢!

根据 苹果文档:

当视图控制器的视图的大小被其父视图更改时(例如,当根视图控制器的窗口旋转或调整大小时) ,将调用此方法。

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
    

if UIDevice.current.orientation.isLandscape {
print("Landscape")
}
if UIDevice.current.orientation.isFlat {
print("Flat")
} else {
print("Portrait")
}
}

我知道这个问题是针对 Swift的,但是因为它是谷歌搜索的顶级链接之一,如果你在 Objective-C中寻找相同的代码:

// add the observer
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(rotated:) name:UIDeviceOrientationDidChangeNotification object:nil];


// remove the observer
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil];


// method signature
- (void)rotated:(NSNotification *)notification {
// do stuff here
}

很简单,这在 iOS8和9/Swift 2/Xcode7中都可以工作,只需要把这段代码放到 viewcontroller 中。它会打印屏幕尺寸与每个方向的变化,你可以把你自己的代码代替:

override func didRotateFromInterfaceOrientation(fromInterfaceOrientation: UIInterfaceOrientation) {
getScreenSize()
}
var screenWidth:CGFloat=0
var screenHeight:CGFloat=0
func getScreenSize(){
screenWidth=UIScreen.mainScreen().bounds.width
screenHeight=UIScreen.mainScreen().bounds.height
print("SCREEN RESOLUTION: "+screenWidth.description+" x "+screenHeight.description)
}

我的方法类似于上面的 拜拜,但是以 iOS9 + 为焦点。我想在视图旋转时更改 食物及衞生局日历的作用域。

override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)


coordinator.animateAlongsideTransition({ (context) in
if size.height < size.width {
self.calendar.setScope(.Week, animated: true)
self.calendar.appearance.cellShape = .Rectangle
}
else {
self.calendar.appearance.cellShape = .Circle
self.calendar.setScope(.Month, animated: true)


}


}, completion: nil)
}

下面这个方法奏效了,但我感到有点不好意思:)

coordinator.animateAlongsideTransition({ (context) in
if size.height < size.width {
self.calendar.scope = .Week
self.calendar.appearance.cellShape = .Rectangle
}
}) { (context) in
if size.height > size.width {
self.calendar.scope = .Month
self.calendar.appearance.cellShape = .Circle
}
}

目标 C

-(void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator

迅速

func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator)

重写此方法以检测方向变化。

使用 UIDevice-orientation属性是不正确的(即使它可以在大多数情况下工作) ,并可能导致一些错误,例如 UIDeviceOrientation也考虑设备的方向,如果它是正面朝上或向下,没有直接对在 UIInterfaceOrientation枚举为这些值。
此外,如果你锁定你的应用程序在某个特定的方向,UIDevice 会给你的设备方向没有考虑到这一点。
另一方面,iOS8已经废弃了 UIViewController类上的 interfaceOrientation属性。
有两个选项可用于检测界面方向:

  • 使用状态栏方向
  • 在 iPhone 上使用 size 类,如果它们没有被覆盖,它们可以让你理解当前界面的方向

仍然缺少的是理解界面方向变化的方向,这在动画中非常重要。在 WWDC 2014“ iOS8中查看控制器的进展”的会议上,演讲者也提供了一个解决这个问题的方案,使用的方法取代了 -will/DidRotateToInterfaceOrientation。< br >

这里提出的解决方案部分实现,更多信息 给你:

func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
let orientation = orientationFromTransform(coordinator.targetTransform())
let oldOrientation = UIApplication.sharedApplication().statusBarOrientation
myWillRotateToInterfaceOrientation(orientation,duration: duration)
coordinator.animateAlongsideTransition({ (ctx) in
self.myWillAnimateRotationToInterfaceOrientation(orientation,
duration:duration)
}) { (ctx) in
self.myDidAnimateFromInterfaceOrientation(oldOrientation)
}
}

我喜欢检查方向通知,因为你可以在任何类中添加这个特性,不需要视图或视图控制器。即使在你的应用程序代表。

SWIFT 5:

    //ask the system to start notifying when interface change
UIDevice.current.beginGeneratingDeviceOrientationNotifications()
//add the observer
NotificationCenter.default.addObserver(
self,
selector: #selector(orientationChanged(notification:)),
name: UIDevice.orientationDidChangeNotification,
object: nil)

而不是缓存通知

    @objc func orientationChanged(notification : NSNotification) {
//your code there
}
override func didRotate(from fromInterfaceOrientation: UIInterfaceOrientation) {
//swift 3
getScreenSize()
}




func getScreenSize(){
let screenWidth = UIScreen.main.bounds.width
let  screenHeight = UIScreen.main.bounds.height
print("SCREEN RESOLUTION: \(screenWidth.description) x \(screenHeight.description)")
}

我需要检测旋转时,使用与 AVFoundation相机,发现 didRotate(现已废弃)和 willTransition的方法是不可靠的,我的需要。使用 David 发布的通知确实有效,但是对于 Swift 3.x 及以上版本来说并不是最新的。

下面的例子利用了一个结束,这似乎是苹果公司未来的优先选择。

var didRotate: (Notification) -> Void = { notification in
switch UIDevice.current.orientation {
case .landscapeLeft, .landscapeRight:
print("landscape")
case .portrait, .portraitUpsideDown:
print("Portrait")
default:
print("other (such as face up & down)")
}
}

设置通知 :

NotificationCenter.default.addObserver(forName: UIDevice.orientationDidChangeNotification,
object: nil,
queue: .main,
using: didRotate)

要删除通知 :

NotificationCenter.default.removeObserver(self,
name: UIDevice.orientationDidChangeNotification,
object: nil)

关于弃权声明 ,我最初的评论有误导性,所以我想更新一下。如前所述,@objc推理的使用已被弃用,而使用 #selector则需要使用 @objc推理。通过使用闭包,可以避免这种情况,现在您有了一个应该可以避免由于调用无效选择器而导致崩溃的解决方案。

为了 Swift 3

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
if UIDevice.current.orientation.isLandscape {
//Landscape
}
else if UIDevice.current.orientation.isFlat {
//isFlat
}
else {
//Portrait
}
}

Swift 3.0中如何检测方向变化的完整实现。

我之所以选择使用这个实现,是因为 face upface down的手机方向对我来说很重要,而且我希望只有在我知道方向在指定位置时,视图才会改变。

import UIKit


class ViewController: UIViewController {


override func viewDidLoad() {
super.viewDidLoad()
//1
NotificationCenter.default.addObserver(self, selector: #selector(deviceOrientationDidChange), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)


}


deinit {
//3
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)
}


func deviceOrientationDidChange() {
//2
switch UIDevice.current.orientation {
case .faceDown:
print("Face down")
case .faceUp:
print("Face up")
case .unknown:
print("Unknown")
case .landscapeLeft:
print("Landscape left")
case .landscapeRight:
print("Landscape right")
case .portrait:
print("Portrait")
case .portraitUpsideDown:
print("Portrait upside down")
}
}


}

需要注意的重点是:

  1. 您将监听 DeviceOrientationDidChange 通知流,并将其绑定到函数 deviceOrientationDidChange
  2. 然后打开设备方向,请注意有时会有 unknown方向。
  3. 像任何通知一样,在 viewController 被反初始化之前,确保停止观察通知流。

希望有人觉得这个有用。

我使用 UIUserInterfaceSizeClass来检测 UIViewController类中方向的改变,就像这样:

override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) {


let isiPadLandscapePortrait = newCollection.horizontalSizeClass == .regular && newCollection.verticalSizeClass == .regular
let isiPhonePlustLandscape = newCollection.horizontalSizeClass == .regular && newCollection.verticalSizeClass == .compact
let isiPhonePortrait = newCollection.horizontalSizeClass == .compact && newCollection.verticalSizeClass == .regular
let isiPhoneLandscape = newCollection.horizontalSizeClass == .compact && newCollection.verticalSizeClass == .compact


if isiPhonePortrait {
// do something...
}
}

Swift 3 | UIDeviceOrientationDidChange 通知观察得太频繁

下面的代码将在每次设备在3D 空间中改变方向时打印“ deviceDidRotate”——不管从纵向到横向方向的变化如何。例如,如果您以纵向方向握住手机,并将其向前倾斜,然后向后倾斜-deviceDidRotate ()将被重复调用。

override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(
self,
selector:  #selector(deviceDidRotate),
name: .UIDeviceOrientationDidChange,
object: nil
)
}


func deviceDidRotate() {
print("deviceDidRotate")
}

为了解决这个问题,您可以保持以前的设备方向并检查 deviceDidRotate ()中的更改。

var previousDeviceOrientation: UIDeviceOrientation = UIDevice.current.orientation


override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(
self,
selector:  #selector(deviceDidRotate),
name: .UIDeviceOrientationDidChange,
object: nil
)
}


func deviceDidRotate() {
if UIDevice.current.orientation == previousDeviceOrientation { return }
previousDeviceOrientation = UIDevice.current.orientation
print("deviceDidRotate")
}

或者您可以使用不同的通知,只有当设备从横向更改为纵向时才会调用该通知。在这种情况下,您希望使用 UIApplicationDidChangeStatusBarOrientation通知。

override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(
self,
selector:  #selector(deviceDidRotate),
name: .UIApplicationDidChangeStatusBarOrientation,
object: nil
)
}


func deviceDidRotate() {
print("deviceDidRotate")
}

检查旋转是否改变为: viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator)

使用 coordinator.animateAlongsideTransition(nil) { (UIViewControllerTransitionCoordinatorContext)可以检查转换是否完成。

见下面的代码:

override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {


super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)


coordinator.animateAlongsideTransition(nil) { (UIViewControllerTransitionCoordinatorContext) in
// if you want to execute code after transition finished
print("Transition finished")
}


if size.height < size.width {
// Landscape
print("Landscape")
} else {
// Portrait
print("Portrait")
}


}

这里有一种检测设备方向的简单方法: (Swift 3)

override func willRotate(to toInterfaceOrientation: UIInterfaceOrientation, duration: TimeInterval) {
handleViewRotaion(orientation: toInterfaceOrientation)
}


//MARK: - Rotation controls
func handleViewRotaion(orientation:UIInterfaceOrientation) -> Void {
switch orientation {
case .portrait :
print("portrait view")
break
case .portraitUpsideDown :
print("portraitUpsideDown view")
break
case .landscapeLeft :
print("landscapeLeft view")
break
case .landscapeRight :
print("landscapeRight view")
break
case .unknown :
break
}
}
- (void)viewDidLoad {
[super viewDidLoad];
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(OrientationDidChange:) name:UIDeviceOrientationDidChangeNotification object:nil];
}


-(void)OrientationDidChange:(NSNotification*)notification {
UIDeviceOrientation Orientation=[[UIDevice currentDevice]orientation];


if(Orientation==UIDeviceOrientationLandscapeLeft || Orientation==UIDeviceOrientationLandscapeRight) {
NSLog(@"Landscape");
} else if(Orientation==UIDeviceOrientationPortrait) {
NSLog(@"Potrait Mode");
}
}

注意: 只需使用此代码确定 UIViewController 的方向

如果您想在旋转完成之后执行某些操作,您可以像下面这样使用 UIViewControllerTransitionCoordinator完成处理程序

public override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)


// Hook in to the rotation animation completion handler
coordinator.animate(alongsideTransition: nil) { (_) in
// Updates to your UI...
self.tableView.reloadData()
}
}

从 iOS8开始,这就是正确的做法。

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)


coordinator.animate(alongsideTransition: { context in
// This is called during the animation
}, completion: { context in
// This is called after the rotation is finished. Equal to deprecated `didRotate`
})
}

迅捷4:

override func viewWillAppear(_ animated: Bool) {
NotificationCenter.default.addObserver(self, selector: #selector(deviceRotated), name: UIDevice.orientationDidChangeNotification, object: nil)
}


override func viewWillDisappear(_ animated: Bool) {
NotificationCenter.default.removeObserver(self, name: UIDevice.orientationDidChangeNotification, object: nil)
}


@objc func deviceRotated(){
if UIDevice.current.orientation.isLandscape {
//Code here
} else {
//Code here
}
}

当需要跨各种视图控制器进行检测时,许多答案都无济于事。

override func viewDidLoad() {
NotificationCenter.default.addObserver(self, selector: #selector(MyController.rotated), name: UIDevice.orientationDidChangeNotification, object: nil)
//...
}


@objc
private func rotated() {
if UIDevice.current.orientation.isLandscape {


} else if UIDevice.current.orientation.isPortrait {


}


//or you can check orientation separately UIDevice.current.orientation
//portrait, portraitUpsideDown, landscapeLeft, landscapeRight...


}

Swift 5

设置类以接收设备方向更改通知:

class MyClass {


...


init (...) {


...


super.init(...)


// subscribe to device orientation change notifications
UIDevice.current.beginGeneratingDeviceOrientationNotifications()
NotificationCenter.default.addObserver(self, selector: #selector(orientationChanged), name: UIDevice.orientationDidChangeNotification, object: nil)


...


}


...


}

安装处理程序代码:

@objc extension MyClass {
func orientationChanged(_ notification: NSNotification) {
let device = notification.object as! UIDevice
let deviceOrientation = device.orientation


switch deviceOrientation {
case .landscapeLeft:   //do something for landscape left
case .landscapeRight:  //do something for landscape right
case .portrait:        //do something for portrait
case .portraitUpsideDown: //do something for portrait upside-down
case .faceDown:        //do something for face down
case .faceUp:          //do something for face up
case .unknown:         //handle unknown
@unknown default:      //handle unknown default
}
}
}

在 iOS13.1.2中,在设备旋转之前,方向总是返回0。在任何旋转事件发生之前,我需要调用 UIDvice.current.startGeneratingDeviceOrientationNotification ()来获得实际的旋转。