如何检测方向变化?

时间:2014-09-04 13:03:09

标签: ios swift orientation-changes

我正在使用Swift,我希望能够在旋转到横向时加载UIViewController,任何人都可以指向正确的方向吗?

我在网上找不到任何东西,文档也有点混淆。

24 个答案:

答案 0 :(得分:178)

以下是我的工作原理:

在我AppDelegate.swift函数内的didFinishLaunchingWithOptions中:

NotificationCenter.default.addObserver(self, selector: #selector(AppDelegate.rotated), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)

然后在AppDelegate类中我放了以下函数:

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

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

}

希望这有助于其他任何人!

谢谢!

答案 1 :(得分:166)

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

答案 2 :(得分:53)

使用AVFoundation相机时需要检测旋转,并发现didRotate现已弃用)& willTransition方法对我的需求不可靠。使用David发布的通知确实有效,但对于Swift 3.x / 4.x来说不是最新的。

Swift 4.2 通知名称已更改。

闭包值与Swift 4.0保持一致:

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

设置Swift 4.2的通知

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

要删除Swift 4.2的通知

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

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

此处的所有内容都已淘汰,如XCode 10& iOS 4.2

Swift 4.0 使用Swift 4.0,Apple鼓励我们避免使用#selector,因此这种方法现在使用完成块。这种方法也向后兼容Swift 3.x&将是推荐的方法。

如果由于#selector推断的弃用而使用@objc函数,这是您将在Swift 4.x项目中收到的编译器警告:

enter image description here

Entry in swift-evolution on this change

设置回调:

// If you do not use the notification var in your callback, 
// you can safely replace it with _
    var didRotate: (Notification) -> Void = { notification in
        switch UIDevice.current.orientation {
        case .landscapeLeft, .landscapeRight:
            print("landscape")
        case .portrait, .portraitUpsideDown:
            print("Portrait")
        default:
            print("other")
        }
    }

设置通知:

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

撕下来:

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

答案 3 :(得分:17)

使用-orientation的{​​{1}}属性不正确(即使它可以在大多数情况下都有效)并且可能会导致一些错误,例如UIDevice也要考虑如果设备面朝上或朝下,则UIDeviceOrientation枚举中没有这些值的直接对 此外,如果您将应用程序锁定在某个特定方向,UIDevice将为您提供设备方向,而不考虑这一点 另一方面,iOS8已弃用UIInterfaceOrientation类的interfaceOrientation属性。
有两个选项可用于检测界面方向:

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

仍然缺少一种理解界面方向变化方向的方法,这在动画制作过程中非常重要。
在WWDC 2014的会议中"查看iOS8中的控制器进展"说话者也使用替换UIViewController的方法提供了解决该问题的方法。

此处提出的解决方案部分实施,更多信息here

-will/DidRotateToInterfaceOrientation

答案 4 :(得分:11)

很简单,这适用于iOS8和9 / Swift 2 / Xcode7,只需将此代码放入viewcontroller.swift即可。它会在每个方向更改时打印屏幕尺寸,您可以改为使用自己的代码:

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

答案 5 :(得分:10)

我知道此问题适用于Swift,但由于它是Google搜索的顶级链接之一,并且您在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
}

答案 6 :(得分:9)

答案 7 :(得分:8)

斯威夫特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")
}

答案 8 :(得分:7)

目标C

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

在swift中

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

重写此方法以检测方向更改。

答案 9 :(得分:5)

如何在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之前,请务必停止观察通知流。
  4. 希望有人觉得这很有帮助。

答案 10 :(得分:5)

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

答案 11 :(得分:4)

检查轮换是否已更改为: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")
    }

}

答案 12 :(得分:4)

以下是检测设备方向的简便方法:( 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
        }
    }

答案 13 :(得分:4)

如果您想在轮换完成后执行某些操作,可以像这样使用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()
    }
}

答案 14 :(得分:3)

我喜欢检查方向通知,因为您可以在任何类中添加此功能,不需要是视图或视图控制器。即使在你的app delegate。

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
    }

答案 15 :(得分:2)

我使用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...
     }
}

答案 16 :(得分:2)

我的方法类似于上面显示的bpedit,但重点是iOS 9+。我希望在视图旋转时更改FSCalendar的范围。

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

答案 17 :(得分:2)

从iOS 8开始,这是正确的方法。

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

答案 18 :(得分:2)

快捷键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
    }
}

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

答案 19 :(得分:1)

适用于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
    }
}

答案 20 :(得分:1)

快速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
        }
    }
}

答案 21 :(得分:0)

- (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的方向

答案 22 :(得分:0)

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... 

}

答案 23 :(得分:0)

在iOS 13.1.2中,方向始终返回0,直到旋转设备为止。我需要在发生任何旋转事件之前调用UIDevice.current.beginGeneratingDeviceOrientationNotifications()以获得实际旋转。