ViewController 被 Present
presentationController, presentingViewController, presentedViewController 区别
- UIPresentationController: 相当于一个框架, 用来管理所有的 presentedViewController, 控制从 present 到 dismiss 全过程
- presentedViewController: 要 modal 显示的视图控制器
- presentingViewController: 跳转前视图控制器
视图弹出的 Modal 分类及使用的动画效果
自 iOS 13 以后使用 present 方法弹出控制器然后关闭控制器后默认不会调用父控制器的 viewWillAppear() 等方法, 因为其默认的 modal 为 automatic(即 pagesheet). 使用此种 modal 时, 在弹出子控制器后, 父控制器不会被 disappear 掉.
Modal 分类
只要视图四周任一侧有留白情况, 在视图 dismiss 后不会呼叫 viewWillDisappear
和 viewDidDisappear
automatic
系统默认的, 一般会映射为 pageSheet. 有例外情况
fullScreen
全屏弹出,
iOS 13
之前系统默认的样式, 弹出的视图覆盖整个屏幕, 视图消失时会呼叫viewWillDisappear
和viewDidDisappear
pageSheet
视图顶部露出下方视图部分内容. 在垂直方向为
compact
的环境下显示的效果与fullScreen
相同formSheet
在水平和垂直都为
regular
的环境下会显示在屏幕中央, 四周留白并有dim
效果在垂直
reguler
但水平compact
的环境下顶部留白在垂直
vertical
环境下与fullScreen
的效果和功能相同currentContext
弹出上下文选单视图 (小视图)
custom
自定义弹出式图, 需配合
delegate
使用overFullscreen
覆盖整改屏幕. 下方视图在被覆盖后不会消失, 因此如果被弹出的视图是透明的, 那么下方被覆盖的内容仍可以被看到.
overCurrentContext
在一个视图的内容上弹出另一个小视图
popover
在水平
regular
的环境下, 背景被dim
, 四周留白, 点击空白处会dismiss
在水平
compact
的环境下, 与fullScreen
效果相同blurOverFullScreen
在全屏展示新内容视图之前模糊被覆盖的视图
弹出或关闭视图时的动画 - Transition
coverVertical
视图自底部弹出占据整个页面, 当
dismiss
时视图返回下方. 此为默认选项flipHorizontal
视图使用
3D
翻转, 像是从背面转过来, 当dismiss
时视图原路翻转回去crossDissolve
被弹出视图淡入的时候, 下方视图同时淡出, 当
dismiss
时顺序相反. 效果不明显, 不建议使用partialCurl
视图从右下角被掀起向上翻页, 露出被弹出的视图.
必须在下方视图和被弹出视图的
modal
都为fullScreen
时才能使用此功能, 否则崩溃swiftnewFoodVC.modalPresentationStyle = .fullScreen newFoodVC.modalTransitionStyle = .flipHorizontal present(newFoodVC, animated: true, completion: nil)
Modal 为 Automatic 时如何更新父控制器的数据并刷新界面
如果将 modalPresentationStyle
变更为 .fullScreen
, 可以再不做任何其他改变的情况下获得关闭子控制器默认调用父控制器的 viewWillAppear
等方法的结果
使用官方原生方法
使用协议 UIAdaptivePresentationControllerDelegate.
- 使需要更新数据与视图的父控制器遵守此协议
- 父控制器实现协议中的方法, 比如
presentationControllerDidDismiss(_:)
- 父控制器中设置父控制器为
presentedController
的代理 (也可以在presentedVC
中设置父控制器为presentedVC
的代理, 原理相同)
具体实现代码如下:
class Parent: UIViewController, UIAdaptivePresentationControllerDelegate {
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "mySegue" {
segue.destination.presentationController?.delegate = self;
}
}
// MARK: - UIAdaptivePresentationControllerDelegate
func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
// 需要自定义实现的内容
// ps: 只有在向下拖拽 VC 的时候触发 dismiss 的时候此方法才会被调用, 如果是自定义动作 dismiss 的话需要手动呼叫 presentationController.delegate?.presentationControllerDidDismiss
// 或者重写 dismiss 方法, 在 dismiss 方法中调用 presentationController.delegate?.presentationControllerDidDismiss
}
}
使用自定义协议传递方法
此方法原理是将父 VC 作为 presentedVC, 然后在需要需要关闭页面的地方呼叫代理的相应方法, 具体代码实现过程如下:
// 1. 自定义协议
protocol HLDelegate {
/// 在弹出的子视图 dismiss 后更新父视图
func updateListVC()
}
// 2. 设置父 VC 遵守协议, 实现协议的相应方法, 并将父 VC 设置为 childVC 的代理
class superVC: HLDelegate {
override func viewDidLoad() {
super.viewDidLoad()
childVC.delegate = self
}
func updateListVC() {
// 自定义想要实现的代码
}
}
// 3. childVC 中需要退出的地方调用代理的相应方法
class childVC {
var delegate: HLDelegate?
func exitAndUpdateSuperVC() {
delegate?.updateListVC()
}
}
present 与 dismiss 的关系
假设有 3 个 UIViewController, 分别是 A, B, C
- 如果 A 已经弹了 B, 这个时候你想想弹一个 C, 此时应该使用 B 弹 C(多层级弹窗应该由父控制器弹出子控制器)
- 此时
- A.presentingViewController (null)
- A.presentedViewController B
- B.presentingViewController A
- B.presentedViewController C
- C.presentingViewController B
- C.presentedViewController (null)
- dismiss 时, 遵守如下规则
- 父节点负责调用 dismiss 来关闭他弹出来的子节点, 也可以直接在子节点中调用 dismiss 方法, UIKit 会通知父节点去处理.
- 如果你连续弹出多个节点, 应当由最底层的父节点调用 dismiss 来一次性关闭所有子节点.
- 关闭多个子节点时, 只有最顶层的子节点会有动画效果, 下层的子节点会直接被移除, 不会有动画效果.
本博客文章采用 CC 4.0 协议,转载需注明出处和作者。