如何检测方向变化?

问题描述 投票:132回答:21

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

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

ios swift orientation-changes
21个回答
180
投票

以下是我如何使用它:

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

}

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

谢谢!


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之前,请确保停止观察通知流。

希望有人觉得这很有帮助。


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

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

}

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

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

3
投票

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

3
投票

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

当需要跨各种视图控制器进行检测时,许多答案都没有帮助。这个就行了。


3
投票

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

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
    }

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

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

168
投票
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")
    }
}

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

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


55
投票

需要在使用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)

17
投票

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

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

仍然缺少的是一种理解界面方向变化方向的方法,这在动画期间非常重要。 在WWDC 2014的会议中“查看iOS8中的控制器进展”,扬声器也提供了解决该问题的方法,使用替换-will/DidRotateToInterfaceOrientation的方法。

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

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

11
投票

我知道这个问题适用于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
}

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


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

7
投票

在目标C中

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

在迅速

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

覆盖此方法以检测方向更改。

© www.soinside.com 2019 - 2024. All rights reserved.