Swift - 如何检测方向变化

时间:2016-08-11 10:38:57

标签: ios swift

我想将两个图像添加到单个图像视图(即横向一个图像和纵向另一个图像),但我不知道如何使用快速语言检测方向变化。

我尝试了这个答案,但只需要一张图片

{{1}}

我是iOS开发的新手,非常感谢任何建议!

14 个答案:

答案 0 :(得分:71)

let const = "Background" //image name
let const2 = "GreyBackground" // image name
    @IBOutlet weak var imageView: UIImageView!
    override func viewDidLoad() {
        super.viewDidLoad()

        imageView.image = UIImage(named: const)
        // Do any additional setup after loading the view.
    }

    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        super.viewWillTransition(to: size, with: coordinator)
        if UIDevice.current.orientation.isLandscape {
            print("Landscape")
            imageView.image = UIImage(named: const2)
        } else {
            print("Portrait")
            imageView.image = UIImage(named: const)
        }
    }

答案 1 :(得分:49)

Swift 3 上面的代码已更新:

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

答案 2 :(得分:46)

Swift 3 使用 NotificationCenter

override func viewDidLoad() {
    super.viewDidLoad()        

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

deinit {
     NotificationCenter.default.removeObserver(self)
}

func rotated() {
    if UIDevice.current.orientation.isLandscape {
        print("Landscape")
    } else {
        print("Portrait")
    }
}

答案 3 :(得分:28)

⚠️设备方向!=接口方向⚠️

Swift 5. * iOS14及以下版本

您真的应该在以下方面有所作为:

  • 设备方向=>指示物理设备的方向
  • 界面方向=>指示屏幕上显示的界面的方向

在许多情况下,这两个值不匹配,例如:

  • 锁定屏幕方向时
  • 将设备放平时

在大多数情况下,您希望使用界面方向,并且可以通过窗口获取它:

private var windowInterfaceOrientation: UIInterfaceOrientation? {
    return UIApplication.shared.windows.first?.windowScene?.interfaceOrientation
}

如果您还想支持

private var windowInterfaceOrientation: UIInterfaceOrientation? {
    if #available(iOS 13.0, *) {
        return UIApplication.shared.windows.first?.windowScene?.interfaceOrientation
    } else {
        return UIApplication.shared.statusBarOrientation
    }
}

现在,您需要定义对窗口界面方向变化做出反应的位置。有多种方法可以做到这一点,但最佳解决方案是在 willTransition(to newCollection: UITraitCollection

此继承的UIViewController方法可以被覆盖,每次更改界面方向时都会触发此方法。因此,您可以在后者中进行所有修改。

这是一个解决方案示例:

class ViewController: UIViewController {
    override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) {
        super.willTransition(to: newCollection, with: coordinator)
        
        coordinator.animate(alongsideTransition: { (context) in
            guard let windowInterfaceOrientation = self.windowInterfaceOrientation else { return }
            
            if windowInterfaceOrientation.isLandscape {
                // activate landscape changes
            } else {
                // activate portrait changes
            }
        })
    }
    
    private var windowInterfaceOrientation: UIInterfaceOrientation? {
        return UIApplication.shared.windows.first?.windowScene?.interfaceOrientation
    }
}

通过实现此方法,您将能够对界面方向的任何更改做出反应。但是请记住,它不会在应用程序打开时触发,因此您还必须在viewWillAppear()中手动更新界面。

我创建了一个示例项目,该项目强调了设备方向和界面方向之间的差异。此外,它将帮助您了解不同的行为,具体取决于您决定更新UI的生命周期步骤。

随意克隆并运行以下存储库: https://github.com/wjosset/ReactToOrientation

答案 4 :(得分:7)

斯威夫特3: 我用它来进行软键盘设计,出于某种原因,UIDevice.current.orientation.isLandscape方法一直告诉我它是“肖像”,所以这就是我用过的东西:

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

    let screenSize = UIScreen.main.bounds
    let screenWidth = screenSize.width
    let screenHeight = screenSize.height

    if(screenWidth > screenHeight){
        //Landscape
    }
    else{
        //Portrait
    }

}

答案 5 :(得分:5)

如果您使用的是Swift版本> = 3.0,则必须应用一些代码更新,正如其他人已经说过的那样。只是不要忘记打电话给超级:

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

   super.viewWillTransition(to: size, with: coordinator)

   // YOUR CODE OR FUNCTIONS CALL HERE

}

如果您打算使用StackView,请注意您可以执行以下操作:

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

   super.viewWillTransition(to: size, with: coordinator)

   if UIDevice.current.orientation.isLandscape {

      stackView.axis = .horizontal

   } else {

      stackView.axis = .vertical

   } // else

}

如果您正在使用Interface Builder,请不要忘记为此UIStackView对象选择自定义类,请在右侧面板的Identity Inspector部分中。然后只需创建(也通过Interface Builder)对自定义UIStackView实例的IBOutlet引用:

@IBOutlet weak var stackView: MyStackView!

采纳这个想法并根据您的需求进行调整。希望这可以帮到你!

答案 6 :(得分:5)

快捷键4

在使用UIDevice.current.orientation更新ViewControllers视图时,我遇到了一些小问题,例如在旋转或子视图动画期间更新表视图单元的约束。

目前,我不是在上面的方法中比较过渡大小和视图控制器的视图大小。这似乎是正确的方法,因为在此刻,一个人可以访问两者:

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransition(to: size, with: coordinator)
    print("Will Transition to size \(size) from super view size \(self.view.frame.size)")

    if (size.width > self.view.frame.size.width) {
        print("Landscape")
    } else {
        print("Portrait")
    }

    if (size.width != self.view.frame.size.width) {
        // Reload TableView to update cell's constraints.
    // Ensuring no dequeued cells have old constraints.
        DispatchQueue.main.async {
            self.tableView.reloadData()
        }
    }


}

在iPhone 6上的输出:

Will Transition to size (667.0, 375.0) from super view size (375.0, 667.0) 
Will Transition to size (375.0, 667.0) from super view size (667.0, 375.0)

答案 7 :(得分:4)

Swift 4.2,RxSwift

如果我们需要重新加载collectionView。

NotificationCenter.default.rx.notification(UIDevice.orientationDidChangeNotification)
    .observeOn(MainScheduler.instance)
    .map { _ in }            
    .bind(to: collectionView.rx.reloadData)
    .disposed(by: bag)

Swift 4,RxSwift

如果我们需要重新加载collectionView。

NotificationCenter.default.rx.notification(NSNotification.Name.UIDeviceOrientationDidChange)
    .observeOn(MainScheduler.instance)
    .map { _ in }            
    .bind(to: collectionView.rx.reloadData)
    .disposed(by: bag)

答案 8 :(得分:3)

之前的所有贡献都很好,但有一点注意事项:

a)如果在plist中设置了方向,只有纵向或示例,您将通过viewWillTransition 通知

b)如果我们无论如何都需要知道用户是否已经旋转了设备(例如游戏或类似的......),我们只能使用:

myrespheader

在Xcode8,iOS11上测试

答案 9 :(得分:3)

我认为正确答案实际上是两种方法的组合:viewWIllTransition(toSize:)NotificationCenter' s UIDeviceOrientationDidChange

viewWillTransition(toSize:)通知之前过渡。

NotificationCenter UIDeviceOrientationDidChange在之后通知您

你必须非常小心。例如,在UISplitViewController设备旋转到某些方向时,DetailViewController会弹出UISplitViewController的{​​{1}}数组,然后推送到主设备上&# 39; s viewcontrollers。如果您在轮换完成之前搜索详细视图控制器,它可能不存在并崩溃。

答案 10 :(得分:3)

您可以使用viewWillTransition(to:with:)并点击animate(alongsideTransition:completion:)以在转换完成后获得界面方向。您只需定义并实现与此协议类似的协议即可利用该事件。 请注意,此代码用于SpriteKit游戏,您的具体实现可能有所不同。

protocol CanReceiveTransitionEvents {
    func viewWillTransition(to size: CGSize)
    func interfaceOrientationChanged(to orientation: UIInterfaceOrientation)
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        super.viewWillTransition(to: size, with: coordinator)

        guard
            let skView = self.view as? SKView,
            let canReceiveRotationEvents = skView.scene as? CanReceiveTransitionEvents else { return }

        coordinator.animate(alongsideTransition: nil) { _ in
            if let interfaceOrientation = UIApplication.shared.windows.first?.windowScene?.interfaceOrientation {
                canReceiveRotationEvents.interfaceOrientationChanged(to: interfaceOrientation)
            }
        }

        canReceiveRotationEvents.viewWillTransition(to: size)
    }

您可以在这些函数中设置断点,并观察到interfaceOrientationChanged(to orientation: UIInterfaceOrientation)总是在viewWillTransition(to size: CGSize)之后以更新的方向被调用。

答案 11 :(得分:1)

要在 app start 上获得正确的方向,您必须在dfm.withColumn( "config_mapped", map_from_entries(expr("transform(config, k -> struct(k, cmap[k]))")) ).show // +-----+---+---+---+------+--------------------+----------------+ // |group| a| b| c|config| cmap| config_mapped| // +-----+---+---+---+------+--------------------+----------------+ // | a| 1| 2| 3| [a]|[a -> 1, b -> 2, ...| [a -> 1]| // | b| 2| 3| 4|[a, b]|[a -> 2, b -> 3, ...|[a -> 2, b -> 3]| // +-----+---+---+---+------+--------------------+----------------+ 中进行检查。这里介绍的其他方法无效。

下面是一个示例:

viewDidLayoutSubviews()

应用首次启动以及在应用运行时旋转 的情况下,这将始终有效。

答案 12 :(得分:0)

检测设备方向的另一种方法是使用traitCollectionDidChange(_ :)函数。当iOS界面环境更改时,系统会调用此方法。

override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?)
{
    super.traitCollectionDidChange(previousTraitCollection)
    //...
}

此外,您可以使用willTransition(to:with :)函数(在traitCollectionDidChange(_ :)之前调用)来获取信息,然后再应用方向。

 override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator)
{
    super.willTransition(to: newCollection, with: coordinator)
    //...
}

答案 13 :(得分:0)

这是一个现代的组合解决方案:

import UIKit
import Combine

class MyClass: UIViewController {

     private var subscriptions = Set<AnyCancellable>()

     override func viewDidLoad() {
         super.viewDidLoad()
    
         NotificationCenter
             .default
             .publisher(for: UIDevice.orientationDidChangeNotification)
             .sink { [weak self] _ in
            
                 let orientation = UIDevice.current.orientation
                 print("Landscape: \(orientation.isLandscape)")
         }
         .store(in: &subscriptions)
    }
}