最近工作中遇到了这样一个需求,项目中大部分页面是只支持竖屏的,少量页面需要实现点击按钮旋转切换屏幕的方向,并且屏幕的方向不随设备的物理方向改变而改变。这里把实现的过程和中间的遇到的一些小问题记录一下。
三个属性
提到屏幕旋转,就离不开 UIViewController
的下面三个属性:
第一个属性
open var shouldAutorotate: Bool { get }
这个属性表明当前 ViewController 的内容是否可以旋转。只有返回值为
true
的时候下面的属性才会生效。
第二个属性
open var supportedInterfaceOrientations: UIInterfaceOrientationMask { get }
这个属性返回当前
ViewController
支持旋转的所有方向,至少返回一个方向。当用户改变设备的方向时,系统会调用rootViewController
的这个方法。
那么问题来了,我们应用中,window
的rootViewController
是UITabBarController
,UITabBarController
的rootViewController
是 UINavigationController
。前面说到了,系统自动调用的是 rootViewController
的 supportedInterfaceOrientations
方法,所以在需要旋转的页面的控制器重写了这个方法,也是不会生效的,因为根本不会调用。解决办法是:继承 UITabBarController
自己实现一个 BaseTabBarController
,重写 supportedInterfaceOrientations
如下:
override var supportedInterfaceOrientations: UIInterfaceOrientationMask{
return self.selectedViewController!.supportedInterfaceOrientations
}
这个时候会调用当前选中的 UINavigationController
的 supportedInterfaceOrientations
方法,同样的道理。我们需要继承 UINavigationController
实现一个 BaseNavigationController
,重写 supportedInterfaceOrientations
如下:
override var supportedInterfaceOrientations: UIInterfaceOrientationMask{
return self.topViewController!.supportedInterfaceOrientations
}
这样一来,在需要控制旋转的页面实现这个方法就会生效被调用了。
第三个属性
open var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation { get }
这个属性用于控制当前控制器页面第一次被展现时,
UI
界面的方向。
当控制器的页面被 present
或者 push
出来时,系统会自动调用这个方法。如果当前控制器支持两个或更多方向旋转,但是页面第一次展示有一个显示效果最佳的方向,那么可以重写这个方法,返回最佳的方向。如果实现了这个方法,页面第一次展示的会是之前设定返回的方向。当然,随后设备的方向果发生改变,系统会再次调用 supportedInterfaceOrientations
,根据返回值控制屏幕的旋转。这里需要特别注意的是:当前控制器支持旋转的方向不是完全由 supportedInterfaceOrientations
方法决定的。有三个地方可以设置屏幕支持的旋转方向:
-
Project
→Targets
→General
→Deployment Info
→Device Orientation
这里设置的支持旋转的方向是作用于整个
App
全局的。
-
AppDelegate
中的方法:
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
return .portrait
}
这个方法的作用和前面的通过第一个地方设置方向作用是一样的,如果实现了这个方法,
App
支持旋转的方向就是这个方法返回的方向集合。如果这个方法没有实现,系统就会读取Info.plist
中设置的方向集合,也就是第一个地方设置的方向合集。
-
UIViewController
中的supportedInterfaceOrientations
方法
实际上,当前控制器支持旋转的方向是这三个地方所设置得方向集合的交集。到这里,已经可以实现控制屏幕显示内容随设备旋转而旋转或者不旋转了。那么如何做到不依赖设备的旋转,手动控制屏幕旋转呢?
手动控制屏幕旋转
我们项目的需求是,只通过按钮点击来控制屏幕旋转,不响应设备的旋转事件。我的实现思路是这样的,在当前控制器中定义一个
Bool
类型的属性allowRotate
,重写shouldAutorotate
,将属性allowRotate
作为这个方法的返回值。在点击按钮的时候,将allowRotate
的值设为true
,然后调用
UIDevice.current.setValue(UIInterfaceOrientation.landscapeRight.rawValue, forKey: "orientation")
这里是以横屏 Home
键在右侧为例。
调用晚这个方法后,再将 allowRotate
设置为 false
.这样就实现了只通过按钮点击来控制屏幕的旋转了。
其他一些问题
按照以上方法可以控制屏幕旋转,但是会有一个问题,就是状态栏还是会自动旋转。这个问题可以通过这种方式解决:
将 Project
→ Targets
→ General
→ Deployment Info
→ Main Interface
框内的内容删掉。具体原因暂时不知道。
网友评论