iOS 横竖屏切换问题
首先介绍一下大的背景,我们的应用同时支持 iPad 和 iPhone 的 Universal App。在 iPhone 下我们的应用是一竖屏应用。在 iPad 自然是横竖屏都支持的。
最近开发的一个小页面要求是横屏的,所以在 iPhone 上我们应用主要是竖屏但是有一些页面要求是横屏的。
iPad 上支持横竖屏,和iPhone 限定竖屏的实现
首先我们应用是一个同时支持 iPad 和 iPhone 应用。但是在 iPhone 是只支持竖屏的。所以基本的配置是这样做的。
- 在
Info.plist
的 Deployment Info 下的 Device Orientation 是这样配置的。
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
- 在
AppDelegate
中实现下面的接口
optional func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask
此接口的说明如下:
This method returns the total set of interface orientations supported by the app. When determining whether to rotate a particular view controller, the orientations returned by this method are intersected with the orientations supported by the root view controller or topmost presented view controller. The app and view controller must agree before the rotation is allowed.
If you do not implement this method, the app uses the values in the UIInterfaceOrientation key of the app’s Info.plist as the default interface orientations.
具体实现如下:
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
let device = UIDevice.current
switch device.userInterfaceIdiom {
case .phone:
return [.portrait]
case .pad:
return UIInterfaceOrientationMask.all
default:
return [.portrait]
}
}
iPhone 上部分页面支持横屏的实现
首先这个需要横屏的页面(MyLandscapeViewController)是通过 present
的方式来程现的。
- 重写 需要横屏的
UIViewController
的supportedInterfaceOrientations
属性。 同时也明确的将shouldAutorotate
返回true
。虽然此方法默认返回
true
,因为只有此方法返回true
,supportedInterfaceOrientations
方法才会调用。即:
override var supportedInterfaceOrientations: UIInterfaceOrientationMask{
if UIDevice.isIpad{
return super.supportedInterfaceOrientations
}else{
return UIInterfaceOrientationMask.landscape
}
}
override var shouldAutorotate: Bool{
return true
}
- 修改
application:supportedInterfaceOrientationsFor:
接口 在 iPhone 上的返回。
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
let device = UIDevice.current
switch device.userInterfaceIdiom {
case .phone:
var presentedVC = application.keyWindow?.rootViewController
while let pVC = presentedVC?.presentedViewController{
presentedVC = pVC
}
let fullscreenSupportedVC = ["MPInlineVideoFullscreenViewController", "MPMoviePlayerViewController", "AVFullScreenViewController","NSKVONotifying_AVFullScreenPlaybackControlsViewController","UIAlertController"
]
if let pVC = presentedVC{
let vcName = simpleClassName(pVC.classForCoder)
if pVC is MyLandscapeViewController{
return UIInterfaceOrientationMask.allButUpsideDown
}else if fullscreenSupportedVC.contains(vcName){
return UIInterfaceOrientationMask.allButUpsideDown
}else{
return [.portrait]
}
}
return [.portrait]
case .pad:
return UIInterfaceOrientationMask.all
default:
return [.portrait]
}
}
系统在显示MyLandscapeViewController
时会通过取 Info.plist
或 上面
AppDelegate 的 返回的结果取交集来确定需要 VC 需要的方向及是否需要旋转。显然他们的交集在 iPhone 上是 landscape
。 于是在 iPhone 上,进入MyLandscapeViewController
页面时会自动旋转到横屏。
常见问题的处理
- UIApplicationInvalidInterfaceOrientation
Terminating app due to uncaught exception 'UIApplicationInvalidInterfaceOrientation', reason: 'Supported orientations has no common orientation with the application, and [PokerFury.NiuNiuGameWebViewController shouldAutorotate] is returning YES'
比如在上面的横屏页面再打开其他竖屏的页面可能就会导致上面的错误。
比如在此使用系统分享会打开 UIActivityViewController
就会报错。那么一种办法就是将可能需要打开的 ViewController 的名称的列表添加时上面支持的列表。
然后上面的 fullscreenSupportedVC
列表修正如下:
let fullscreenSupportedVC = ["MPInlineVideoFullscreenViewController", "MPMoviePlayerViewController", "AVFullScreenViewController","NSKVONotifying_AVFullScreenPlaybackControlsViewController",
"UIAlertController","UIActivityViewController","SLComposeViewController"
]
还有一个优化方向就是,因为可能不方便穷举上面的这种 VC,那么可以在横屏VC 实例化时设置一个标志变量(在 dealloc 时重置),如果此标志变量为 true
那就返回UIInterfaceOrientationMask.allButUpsideDown
。
- 从横屏页面退出应用没有切换回竖屏的问题。
一般可以通过在应用的 RootVC 中实现下面的代码来处理。
override var shouldAutorotate: Bool{
return true
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask{
if UIDevice.isIpad{
return super.supportedInterfaceOrientations
}else{
return [.portrait]
}
}
附 UIViewController. supportedInterfaceOrientations
接口的说明如下:
When the user changes the device orientation, the system calls this method on the root view controller or the topmost presented view controller that fills the window. If the view controller supports the new orientation, the window and view controller are rotated to the new orientation. This method is only called if the view controller's
shouldAutorotate
method returnstrue
.Override this method to report all of the orientations that the view controller supports. The default values for a view controller's supported interface orientations is set to
all
for the iPad idiom andallButUpsideDown
for the iPhone idiom.The system intersects the view controller's supported orientations with the app's supported orientations (as determined by the Info.plist file or the app delegate's
application(_:supportedInterfaceOrientationsFor:)
method) to determine whether to rotate.