[英]Swift - How to detect orientation changes
我想將兩個圖像添加到單個圖像視圖(即橫向一個圖像和縱向另一個圖像),但我不知道如何使用 swift 語言檢測方向變化。
我試過這個答案,但它只需要一張圖片
override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
if UIDevice.currentDevice().orientation.isLandscape.boolValue {
print("Landscape")
} else {
print("Portrait")
}
}
我是 iOS 開發的新手,任何建議將不勝感激!
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)
}
}
使用NotificationCenter和UIDevice的beginGeneratingDeviceOrientationNotifications
斯威夫特 4.2+
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")
}
}
斯威夫特 3
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")
}
}
Swift 3以上代碼更新:
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
if UIDevice.current.orientation.isLandscape {
print("Landscape")
} else {
print("Portrait")
}
}
你真的應該區分:
這兩個值不匹配的情況有很多,例如:
在大多數情況下,您會希望使用界面方向,並且可以通過窗口獲取它:
private var windowInterfaceOrientation: UIInterfaceOrientation? {
return UIApplication.shared.windows.first?.windowScene?.interfaceOrientation
}
如果您還想支持 < 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
}
}
現在您需要定義對窗口界面方向變化做出反應的位置。 有多種方法可以做到這一點,但最佳解決方案是在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
Swift 4+:我將它用於軟鍵盤設計,出於某種原因UIDevice.current.orientation.isLandscape
方法一直告訴我它是Portrait
,所以這是我使用的:
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
if(size.width > self.view.frame.size.width){
//Landscape
}
else{
//Portrait
}
}
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)
如果您使用的是 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,請不要忘記在右側面板的 Identity Inspector 部分中為此 UIStackView 對象選擇自定義類。 然后只需創建(也通過 Interface Builder)對自定義 UIStackView 實例的 IBOutlet 引用:
@IBOutlet weak var stackView: MyStackView!
采納這個想法並使其適應您的需求。 希望這可以幫助你!
我相信正確的答案實際上是兩種方法的組合: viewWIllTransition(toSize:)
和NotificationCenter
的UIDeviceOrientationDidChange
。
viewWillTransition(toSize:)
在過渡之前通知您。
NotificationCenter
UIDeviceOrientationDidChange
在之后通知您。
你必須非常小心。 例如,在UISplitViewController
當設備旋轉到特定方向時, DetailViewController
會從UISplitViewController
的viewcontrollers
數組中彈出,並推送到 master 的UINavigationController
。 如果您在旋轉完成之前搜索詳細視圖控制器,它可能不存在並崩潰。
斯威夫特 4
我在使用UIDevice.current.orientation
更新 ViewControllers 視圖時遇到了一些小問題,例如在子視圖的旋轉或動畫期間更新 tableview 單元格的約束。
我目前將過渡大小與視圖控制器視圖大小進行比較,而不是上述方法。 這似乎是正確的方法,因為此時可以在代碼中訪問兩者:
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)
您可以使用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)
總是在viewWillTransition(to size: CGSize)
之后調用更新的方向。
以前的所有貢獻都很好,但有一點要注意:
a) 如果在 plist 中設置了方向,只有縱向或示例,您將不會通過 viewWillTransition 收到通知
b) 如果我們無論如何需要知道用戶是否旋轉了設備,(例如游戲或類似的......),我們只能使用:
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.rotated), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)
在 Xcode8、iOS11 上測試
要在應用程序啟動時獲得正確的方向,您必須在viewDidLayoutSubviews()
檢查它。 此處描述的其他方法將不起作用。
以下是如何執行此操作的示例:
var mFirstStart = true
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if (mFirstStart) {
mFirstStart = false
detectOrientation()
}
}
func detectOrientation() {
if UIDevice.current.orientation.isLandscape {
print("Landscape")
// do your stuff here for landscape
} else {
print("Portrait")
// do your stuff here for portrait
}
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
detectOrientation()
}
這將始終有效,在應用程序首次啟動時,如果在應用程序運行時旋轉。
這是一個現代的組合解決方案:
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)
}
}
我的應用程序在 iOS 15 上運行,我只在 iPhone/iPad 上進行了檢查,所以我不能說所有用例,但是我使用以下環境變量:
@Environment(\.verticalSizeClass) private var verticalSizeClass
然后使用以下命令檢查其值: verticalSizeClass ==.compact
是水平的verticalSizeClass ==.regular
是垂直的
https://developer.apple.com/documentation/swiftui/environmentvalues/verticalsizeclass
另一種檢測設備方向的方法是使用 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)
//...
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.