如何检测屏幕方向变化?

173

我正在使用Swift,当我旋转到横向模式时,我想要能够加载一个UIViewController,有人可以指点我一下吗?

我在网上找不到任何信息,对文档感到有点困惑。


1
我猜API没有改变,所以应该是“didRotateToOrientation”和“willRotateToOrientation”,类似这样的东西,请查看苹果文档。 - David Ansermot
1
嗨@mArm.ch,感谢您的快速回复!那么我该如何实现呢?(这是我的第一个应用程序...我对IOS非常陌生) :) - David
我将其转发为答案,供其他人参考。如果您觉得可以的话,您能否接受它? - David Ansermot
如果您想在应用程序启动时检查方向,请检查此内容。 https://dev59.com/clsW5IYBdhLWcg3w7KtL#49058588 - eharo2
24个回答

8

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

6
如果您想在旋转完成后执行某些操作,您可以使用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()
    }
}

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

最干净的答案。对我有用。 - Dorad
此功能已被弃用。 - azizj

6

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

我选择使用该实现是因为对于手机的正面朝上正面朝下方向对我很重要,并且我希望视图只有在我知道方向处于指定位置时才发生更改。

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. 然后,您需要打开设备方向,确保注意有时会出现未知方向。
  3. 像任何通知一样,在视图控制器被解除初始化之前,请务必停止观察通知流。
希望对某些人有所帮助。

谢谢,太棒了。 - Mohammad Razipour
所以我有点困惑,目前我的所有视图都会根据横屏或竖屏进行调整,但如果设备面朝上,它并不会改变?当设备面朝上时,我该如何使用您上面的代码来实现相同的效果呢? - Famic Tech
你能提供更多的上下文信息吗?我不确定你指的是哪种效果。这段代码可以简单地检测方向。如果你使用多种方法来检测方向,可能会遇到问题。 - Rob Norback

6

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

很多答案都不能帮助在不同的视图控制器中进行检测,但这个答案可以解决问题。

您还需要检查设备是否在视图控制器消失时旋转(如果其他视图控制器打开在当前视图控制器之上)。实际上,您可以跳过viewWillDisappear并在viewDidLoad中添加addObserver。iOS会自动取消订阅视图控制器。 - Alexander Volkov
你忘记了 UIDevice.current.orientation.isFlat - user924
@Joel,当手机方向锁定时,这个方法无效。有什么办法可以解决吗? - Khadija Daruwala

4

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

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

3
willRotate现在已过时,最好使用viewWillTransition - pre

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

}

3

我的方法与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
            }
    }

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

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