ios Swift - 如何检测方向变化
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/38894031/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Swift - How to detect orientation changes
提问by Raghuram
I want to add two images to single image view (i.e for landscape one image and for portrait another image)but i don't know how to detect orientation changes using swift languages.
我想将两个图像添加到单个图像视图中(即一张横向图像和另一张纵向图像),但我不知道如何使用 swift 语言检测方向变化。
I tried this answer but it takes only one image
我试过这个答案,但它只需要一张图片
override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
if UIDevice.currentDevice().orientation.isLandscape.boolValue {
print("Landscape")
} else {
print("Portrait")
}
}
I am new to iOS development,any advice would be greatly appreciated!
我是 iOS 开发的新手,任何建议将不胜感激!
回答by Rutvik Kanbargi
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)
}
}
回答by maslovsa
Using NotificationCenterand UIDevice's beginGeneratingDeviceOrientationNotifications
使用NotificationCenter和UIDevice的beginGeneratingDeviceOrientationNotifications
Swift 4.2+
斯威夫特 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")
}
}
Swift 3
斯威夫特 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")
}
}
回答by Yaroslav Bai
Swift 3Above code updated:
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")
}
}
回答by asetniop
Swift 4+:I was using this for soft keyboard design, and for some reason the UIDevice.current.orientation.isLandscape
method kept telling me it was Portrait
, so here's what I used instead:
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
}
}
回答by maslovsa
Swift 4.2, RxSwift
Swift 4.2,RxSwift
If we need to reload collectionView.
如果我们需要重新加载collectionView。
NotificationCenter.default.rx.notification(UIDevice.orientationDidChangeNotification)
.observeOn(MainScheduler.instance)
.map { _ in }
.bind(to: collectionView.rx.reloadData)
.disposed(by: bag)
Swift 4, RxSwift
Swift 4,RxSwift
If we need to reload collectionView.
如果我们需要重新加载collectionView。
NotificationCenter.default.rx.notification(NSNotification.Name.UIDeviceOrientationDidChange)
.observeOn(MainScheduler.instance)
.map { _ in }
.bind(to: collectionView.rx.reloadData)
.disposed(by: bag)
回答by Wilfried Josset
??Device Orientation != Interface Orientation??
??设备方向!=接口方向??
Swift 5.* iOS13 and below
Swift 5.* iOS13 及以下
You should really make a difference between:
你真的应该区分:
- Device Orientation => Indicates the orientation of the physical device
- Interface Orientation => Indicates the orientation of the interface displayed on the screen
- Device Orientation => 表示物理设备的方向
- Interface Orientation => 表示界面显示在屏幕上的方向
There are many scenarios where those 2 values are mismatching such as:
这两个值不匹配的情况有很多,例如:
- When you lock your screen orientation
- When you have your device flat
- 当您锁定屏幕方向时
- 当您将设备放平时
In most cases you would want to use the interface orientation and you can get it via the window:
在大多数情况下,您会希望使用界面方向,并且可以通过窗口获取它:
private var windowInterfaceOrientation: UIInterfaceOrientation? {
return UIApplication.shared.windows.first?.windowScene?.interfaceOrientation
}
In case you also want to support < iOS 13 (such as iOS 12) you would do the following:
如果您还想支持 < 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
}
}
Now you need to define where to react to the window interface orientation change. There are multiple ways to do that but the optimal solution is to do it within
willTransition(to newCollection: UITraitCollection
.
现在您需要定义对窗口界面方向变化做出反应的位置。有多种方法可以做到这一点,但最佳解决方案是在
willTransition(to newCollection: UITraitCollection
.
This inherited UIViewController method which can be overridden will be trigger every time the interface orientation will be change. Consequently you can do all your modifications in the latter.
这个可以被覆盖的继承的 UIViewController 方法将在每次界面方向改变时触发。因此,您可以在后者中进行所有修改。
Here is a solutionexample:
这是一个解决方案示例:
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
}
}
By implementing this method you'll then be able to react to any change of orientation to your interface. But keep in mind that it won't be triggered at the opening of the app so you will also have to manually update your interface in viewWillAppear()
.
通过实施此方法,您将能够对界面的任何方向变化做出反应。但请记住,它不会在应用程序打开时触发,因此您还必须手动更新viewWillAppear()
.
I've created a sample project which underlines the difference between device orientation and interface orientation. Additionally it will help you to understand the different behavior depending on which lifecycle step you decide to update your UI.
我创建了一个示例项目,它强调了设备方向和界面方向之间的区别。此外,它将帮助您了解不同的行为,具体取决于您决定更新 UI 的生命周期步骤。
Feel free to clone and run the following repository: https://github.com/wjosset/ReactToOrientation
随意克隆并运行以下存储库:https: //github.com/wjosset/ReactToOrientation
回答by RLoniello
Swift 4
斯威夫特 4
I've had some minor issues when updating the ViewControllers view using UIDevice.current.orientation
, such as updating constraints of tableview cells during rotation or animation of subviews.
我在使用 更新 ViewControllers 视图时遇到了一些小问题UIDevice.current.orientation
,例如在子视图的旋转或动画期间更新 tableview 单元格的约束。
Instead of the above methods I am currently comparing the transition size to the view controllers view size. This seems like the proper way to go since one has access to both at this point in code:
我目前将过渡大小与视图控制器视图大小进行比较,而不是上述方法。这似乎是正确的方法,因为此时可以在代码中访问两者:
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()
}
}
}
Output on a iPhone 6:
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)
回答by josuevhn
If your are using Swift version >= 3.0 there are some code updates you have to apply as others have already said. Just don't forget to call super:
如果您使用的是 Swift 版本 >= 3.0,那么您必须像其他人所说的那样应用一些代码更新。只是不要忘记调用超级:
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
// YOUR CODE OR FUNCTIONS CALL HERE
}
If you are thinking to use a StackView for your images be aware you can do something like the following:
如果您正在考虑为您的图像使用 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
}
If your are using Interface Builder don't forget to select the custom class for this UIStackView object, in the Identity Inspector section at the right panel. Then just create (also through Interface Builder) the IBOutlet reference to the custom UIStackView instance:
如果您使用的是 Interface Builder,请不要忘记在右侧面板的 Identity Inspector 部分中为此 UIStackView 对象选择自定义类。然后只需创建(也通过 Interface Builder)对自定义 UIStackView 实例的 IBOutlet 引用:
@IBOutlet weak var stackView: MyStackView!
Take the idea and adapt it to your needs. Hope this can help you!
采纳这个想法并使其适应您的需求。希望这可以帮到你!
回答by MH175
I believe the correct answer is actually a combination of both approaches: viewWIllTransition(toSize:)
and NotificationCenter
's UIDeviceOrientationDidChange
.
我认为正确的答案实际上是两者的结合方法:viewWIllTransition(toSize:)
和NotificationCenter
的UIDeviceOrientationDidChange
。
viewWillTransition(toSize:)
notifies you beforethe transition.
viewWillTransition(toSize:)
在转换之前通知您。
NotificationCenter
UIDeviceOrientationDidChange
notifies you after.
NotificationCenter
UIDeviceOrientationDidChange
通知你之后。
You have to be very careful. For example, in UISplitViewController
when the device rotates into certain orientations, the DetailViewController
gets popped off the UISplitViewController
's viewcontrollers
array, and pushed onto the master's UINavigationController
. If you go searching for the detail view controller before the rotation has finished, it may not exist and crash.
你必须非常小心。例如,UISplitViewController
当设备旋转到特定方向时,DetailViewController
会从UISplitViewController
'sviewcontrollers
数组中弹出,并推到主设备的UINavigationController
. 如果您在旋转完成之前搜索详细视图控制器,它可能不存在并崩溃。
回答by ingconti
All previous contributes are fine, but a little note:
以前的所有贡献都很好,但有一点要注意:
a) if orientation is set in plist, only portrait or example, You will be notnotified via viewWillTransition
a) 如果在 plist 中设置了方向,只有纵向或示例,您将不会通过 viewWillTransition 收到通知
b) if we anyway need to know if user has rotated device, (for example a game or similar..) we can only use:
b) 如果我们无论如何需要知道用户是否旋转了设备,(例如游戏或类似的......),我们只能使用:
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.rotated), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)
tested on Xcode8, iOS11
在 Xcode8、iOS11 上测试