Swift - 如何检测方向变化

Rag*_*ram 72 ios swift

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

我试过这个答案,但它只需要一张图片

override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
    if UIDevice.currentDevice().orientation.isLandscape.boolValue {
        print("Landscape")
    } else {
        print("Portrait")
    }
}
Run Code Online (Sandbox Code Playgroud)

我是iOS开发的新手,任何建议都将不胜感激!

小智 86

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)
        }
    }
Run Code Online (Sandbox Code Playgroud)

  • 不要忘记在覆盖方法中调用super.viewWillTransitionToSize (3认同)
  • 还有另一种方向类型:`.isFlat`。如果你只想捕捉 `portrait`,我建议你将 else 子句更改为 `else if UIDevice.current.orientation.isPortrait`。注意:`.isFlat` 可以出现在竖屏和横屏中,只有 else {} 它将始终默认出现在那里(即使它是平面横屏)。 (2认同)

Yar*_*Bai 60

Swift 3 以上代码更新:

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

  • 请注意,界面方向和设备方向之间存在差异 (2认同)

mas*_*vsa 56

Swift 3 使用NotificationCenter

override func viewDidLoad() {
    super.viewDidLoad()        

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

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

func rotated() {
    if UIDevice.current.orientation.isLandscape {
        print("Landscape")
    } else {
        print("Portrait")
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这个答案中有趣的是,即使视图控制器的`shouldAutorotate`设置为`false`,这也会给出当前的方向.这对于像Camera应用程序的主屏幕这样的屏幕很方便 - 方向被锁定但按钮可以旋转. (4认同)
  • @Michael因为`viewWillTransition:`预计在更改完成后调用`UIDeviceOrientationDidChange`时会发生更改. (2认同)
  • 从 iOS 9 开始,您甚至不需要显式调用 deinit。您可以在此处阅读更多相关信息:https://useyourloaf.com/blog/unregistering-nsnotificationcenter-observers-in-ios-9/ (2认同)

Wil*_*set 47

??设备方向!=接口方向??

Swift 5.* iOS14 及以下

你真的应该区分:

  • Device Orientation => 表示物理设备的方向
  • Interface Orientation => 表示界面显示在屏幕上的方向

这两个值不匹配的情况有很多,例如:

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

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

private var windowInterfaceOrientation: UIInterfaceOrientation? {
    return UIApplication.shared.windows.first?.windowScene?.interfaceOrientation
}
Run Code Online (Sandbox Code Playgroud)

如果您还想支持 < iOS 13(例如 iOS 12),您可以执行以下操作:

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

现在您需要定义对窗口界面方向变化做出反应的位置。有多种方法可以做到这一点,但最佳解决方案是在 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
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

  • 我发现这个答案是最好的、信息最丰富的。但在 iPadOS13.5 上测试时,使用建议的“func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator)”不起作用。我已经使用 func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator)` 使其工作。 (2认同)

Nic*_* S. 10

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

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)
    }
}
Run Code Online (Sandbox Code Playgroud)


ase*_*iop 9

Swift 3: 我用它来进行软键盘设计,出于某种原因,UIDevice.current.orientation.isLandscape方法一直告诉我它是"Portrait",所以这就是我用过的东西:

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    if(size.width > self.view.frame.size.width){
        //Landscape
    }
    else{
        //Portrait
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @asetniop,不。您在上面编写的viewWillTransitionToSize:withCoordinator:方法中注入的`size`参数。这将反映过渡后视图的确切大小。 (3认同)
  • 如果您使用子视图控制器,这不是一个好的解决方案,无论方向如何,容器的大小可能始终是正方形。 (2认同)

mas*_*vsa 7

Swift 4.2,RxSwift

如果需要重新加载collectionView。

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

迅捷4,RxSwift

如果需要重新加载collectionView。

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


MH1*_*175 5

我认为正确的答案实际上是两者的结合方法:viewWIllTransition(toSize:)NotificationCenterUIDeviceOrientationDidChange

viewWillTransition(toSize:)在转换之前通知您。

NotificationCenter UIDeviceOrientationDidChange通知你之后

你必须非常小心。例如,UISplitViewController当设备旋转到特定方向时,DetailViewController会从UISplitViewController'sviewcontrollers数组中弹出,并推到主设备的UINavigationController. 如果您在旋转完成之前搜索详细视图控制器,它可能不存在并崩溃。


jos*_*vhn 5

如果您使用的是Swift版本> = 3.0,则必须执行某些代码更新,就像其他人已经说过的那样。只是不要忘了调用super:

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

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

   // YOUR CODE OR FUNCTIONS CALL HERE

}
Run Code Online (Sandbox Code Playgroud)

如果您想对图像使用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

}
Run Code Online (Sandbox Code Playgroud)

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

@IBOutlet weak var stackView: MyStackView!
Run Code Online (Sandbox Code Playgroud)

采纳想法并使其适应您的需求。希望这可以帮到你!


RLo*_*llo 5

斯威夫特4

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

代替上面的方法,我目前正在将过渡大小与视图控制器的视图大小进行比较。这似乎是正确的方法,因为在此刻可以访问两个代码:

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


}
Run Code Online (Sandbox Code Playgroud)

在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)
Run Code Online (Sandbox Code Playgroud)


小智 5

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

protocol CanReceiveTransitionEvents {
    func viewWillTransition(to size: CGSize)
    func interfaceOrientationChanged(to orientation: UIInterfaceOrientation)
}
Run Code Online (Sandbox Code Playgroud)
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)
    }
Run Code Online (Sandbox Code Playgroud)

您可以在这些函数中设置断点,并观察始终以更新的方向interfaceOrientationChanged(to orientation: UIInterfaceOrientation)调用该断点。viewWillTransition(to size: CGSize)