UIViewController有个属性modalPresentationStyle,它决定了当前控制器 present 出的下一控制器的展示方式。
官方文档对这些效果有比较详细的介绍,这里写个 demo 帮助理解,demo 在模拟器上运行可能会有一点卡顿,真机没有影响。
预备知识
presenting、presented viewController
presentingViewController指的是 present 出当前控制器的控制器。
presentedViewController指被当前控制器 present 出的控制器。
Size Class
对于各种常见情况的 Size Class 如下几张图片所示:



UIModalPresentationStyle
enum UIModalPresentationStyle : Int {
case fullScreen
case pageSheet
case formSheet
case currentContext
case custom
case overFullScreen
case overCurrentContext
case popover
case none
}
fullScreen
- 在各种 Size Class 情况下都是全屏展示
- 执行 present 操作的控制器的
view和它的subViews,在 present 完成后都会被从当前视图层级移除

对于水平方向为 compact 的情况,不管用哪种 style 推出其他控制器,presentedViewController都是以fullScreen方式展示。所以剩下的所有类型,都只针对水平方向为 regular 论述。
pageSheet
- 被推出视图部分的遮盖下层视图
- 其宽度总是为该设备竖屏时候的宽度(不可变),高度则为当前设备方向的屏幕高度(可变,其实还要去掉状态栏的高度)


formSheet
- 被推出视图大小比屏幕的小,且总是居中显示
- 在横屏时,如果弹出了键盘,视图位置会跟着上移
- 可以设置被推出视图的
preferredContentSize来设置它的大小


这里设置了preferredContentSize = CGSize(width: 200, height: 200)。
currentContext
- 可以用在 iPad
UISplitViewController中,指定单独覆盖屏幕单侧的控制器;popover方式展示的控制器,再用该方式 present 出下一视图 - 在执行 present 操作的控制器的控制器层级中往上查找,如果某个控制器的
definesPresentationContext == true则它来 present,假如没有一个为true,那么则由window.rootController来 present - 执行 present 操作的控制器的
view和它的subViews,在 present 完成后都会被从当前视图层级移除
definesPresentationContext默认为false,系统提供的一些像UINavigationController的控制器,其默认值为true。它的定义为:
A Boolean value that indicates whether this view controller's view is covered when the view controller or one of its descendants presents a view controller.
对于以currentContext方式推出的视图,如果它的presentedViewController是一个popover,那么推出该视图的modalTransitionStyle不能是partialCurl,否则会引起崩溃。



custom
A custom view presentation style that is managed by a custom presentation controller and one or more custom animator objects.
详细介绍在下篇文章中进行。
overFullScreen
基本和fullScreen一致。只是 present 完成后,不会移除执行 present 操作的控制器的view和它的subViews。如果presentedViewController.view是有透明度的,底层视图就可以得以显示。

overCurrentContext
基本和currentContext一致。只是 present 完成后,不会移除执行 present 操作的控制器的view和它的subViews。如果presentedViewController.view是有透明度的,底层视图就可以得以显示。

popover
- 在 iPad 上用
popover的方式显示;在 iPhone 上默认用fullScreen方式显示,但是在 iOS8 后可以设置成popover的方式,在none中会进行解释 - 默认情况下,点击灰色的背景
popover会直接消失,通过presentedViewController.popoverPresentationController?.passthroughViews可以配置灰色背景的哪些视图区域可以点击
UIPopoverPresentationController的定义:
An object that manages the display of content in a popover.
当视图被用popover方式 present 的时候,总有一个popoverPresentationController的实例来管理呈现的行为。

none
- 该枚举值不可以直接赋值给
modalPresentationStyle popoverPresentationController会调用它delegate的方法来配置popover的视图,none只能用在adaptivePresentationStyle(for:)代理方法中返回,告诉popoverPresentationController不要适配presentedViewController,这样在 iPhone 中也可以用popover的样式展示了
