viewWillDisappear 是 UIViewController 的一个方法,UIKit 在屏幕即将从用户显示器上消失之前调用它。根据 Apple Developer Documentation,此方法接收 animated 参数并在 push、pop、present、dismiss 以及切换标签时触发。viewWillDisappear 是保存状态和正确清理资源的主要位置。
要点
viewWillDisappear 是 UIViewController 的一个方法,UIKit 在控制器的 View 开始从屏幕消失之前立即调用它。此时,屏幕对用户仍然可见,但过渡已经开始:NavigationController 已启动 push/pop 动画,模态窗口开始关闭,或 TabBar 已开始切换到其他标签。开发人员重写此方法以执行那些需要屏幕仍然可用但已准备隐藏的操作。
与屏幕隐藏后触发的 viewDidDisappear 不同,viewWillDisappear 提供了最后的机会在用户仍能看到界面时保存数据和释放资源。这对用户体验至关重要 — 保存草稿或停止计时器必须在用户切换到另一个屏幕之前发生。
此方法接收 animated 参数,指示消失是否伴随动画。true 值表示 UIKit 执行带动画的过渡,false — 屏幕立即消失,例如在无动画的 dismiss 或编程从层次结构中删除时。
viewWillDisappear 在当前屏幕停止活动的所有场景中被调用。我们来看一下 iOS 开发特有的主要情况。
当 UINavigationController 推送新控制器时,当前控制器的 viewWillDisappear 在过渡动画开始时被调用。此时,当前屏幕在新滑入的控制器下方仍然可见。这是 viewWillDisappear 以 animated = true 触发的标准场景。
当用户按下返回按钮或执行交互式向后滑动手势时,当前控制器的 viewWillDisappear 被调用。在交互式手势中,如果用户改变主意并将屏幕恢复原位,此调用可能会被取消。这是设计状态保存时需要考虑的重要特性。
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
saveDraftData()
NotificationCenter.default.removeObserver(self)
}
关闭模态窗口时,被关闭的控制器在 dismiss 动画开始时调用 viewWillDisappear。此时可以通过委托或闭包将结果返回,因为呈现模态窗口的控制器尚未获得控制权。
UITabBarController 在用户触摸其他标签后立即在离开的标签控制器上调用 viewWillDisappear。如果当前标签有活动进程 — 媒体播放、文件加载、计时器 — 在此处暂停或停止它们。
viewWillDisappear 解决资源管理和状态管理的具体任务。让我们通过代码示例来看一下关键场景。
viewWillDisappear 最重要的任务 — 保存用户在当前屏幕输入或更改的数据。消息草稿、编辑过的表单字段、选中的设置 — 所有这些都必须在屏幕消失前保存。使用 Core Data、UserDefaults 或文件存储来实现持久性。
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
guard hasUnsavedChanges else { return }
draftStorage.save(currentDraft)
}
在 viewWillAppear 或 viewDidLoad 中订阅的 NotificationCenter、KVO 和 Combine 发布者必须在 viewWillDisappear 中取消。否则,通知将发送到隐藏的屏幕,导致用户看不到的 UI 更新,或更糟 — 由于引用已释放的对象而导致崩溃。
在 viewDidAppear 中启动的 UIView 动画和通过 Timer 或 DispatchSource 运行的计时器必须在 viewWillDisappear 中停止。在隐藏屏幕上继续运行的动画消耗 GPU 和电池而对用户毫无益处。显式停止它们 — 对计时器调用 invalidate,对图层调用 removeAllAnimations。
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
countdownTimer?.invalidate()
countdownTimer = nil
loadingIndicator.layer.removeAllAnimations()
}
如果控制器是为了获取结果而打开的 — 选择元素、输入文本、确认操作 — viewWillDisappear 是原始控制器仍存在于堆栈中并可以接收数据的最后时刻。在调用 deinit 之前调用委托或闭包。
屏幕状态的可靠保存是 iOS 开发中最困难的任务之一。viewWillDisappear 是策略中重要但非唯一的元素。让我们来看一下综合方法。
第 1 层 — 在 viewWillDisappear 中保存。快速保存返回后立即可用的轻量数据。适用于 UI 状态:滚动位置、选中的段、输入框中的文本。问题:在已取消的交互式 pop 手势中,即使留在屏幕上,保存也会发生 — 数据被不必要地覆盖。
第 2 层 — 在 viewDidDisappear 中保存。重复第一层的保存,但仅在屏幕被保证隐藏后触发。这是针对取消的手势的保险。但是,如果在 viewWillDisappear 中您已经取消订阅了通知,viewDidDisappear 可能无法访问某些数据。
第 3 层 — 通过应用程序通知保存。UIApplication.willResignActiveNotification 和 UIApplication.didEnterBackgroundNotification 拦截应用程序最小化。如果用户最小化了应用程序,viewWillDisappear 可能没有被调用 — 但这些通知的保存保证了会话结束时的数据完整性。
| 层级 | 方法/通知 | 可靠性 | 用途 |
|---|---|---|---|
| 1 | viewWillDisappear | 高 | UI 状态、草稿 |
| 2 | viewDidDisappear | 非常高 | 关键数据 |
| 3 | willResignActive | 最高 | 最小化时 |
建议:对关键用户数据使用所有三个层次的组合。对于非关键状态 — 第一层就足够了。重要的是不要重复覆盖相同的数据 — 使用 dirty 标记指示自上次保存以来数据已更改。
应特别注意 CRUD 屏幕的策略,用户在其中输入数据。在此类屏幕上,不建议在 viewWillDisappear 中保存每次按键 — 这是多余的。使用通过 Timer 的带延迟(防抖)自动保存,并且仅在存在未保存更改时将 viewWillDisappear 用于最终强制保存。这种方法在性能和安全性之间取得平衡。
对于使用 Core Data 的应用程序,额外措施是仅在托管对象上下文中存在实际更改时在 viewWillDisappear 中调用 saveContext。保存前的 context.hasChanges 检查可防止对 persistent store 的不必要写入并延长设备电池寿命。将此检查与 applicationDidEnterBackground 中的全局保存相结合。
viewWillDisappear 的不正确使用可能导致数据丢失、内存泄漏和应用程序行为不稳定。让我们看一下 iOS 开发人员的常见错误。
第一个错误 — 仅在 viewWillDisappear 中保存数据。如上所述,在交互式 pop 手势中即使屏幕没有消失也会调用该方法。如果保存有副作用 — 将数据发送到服务器、更改状态 — 这可能导致错误触发。添加 isBeingDismissed 或 isMovingFromParent 检查。
第二个错误 — 未从 NotificationCenter 取消订阅。这是 iOS 中最常见的内存泄漏之一。如果您在 viewWillAppear 中订阅了 UIResponder.keyboardWillShowNotification 但未在 viewWillDisappear 中取消订阅,闭包将继续被调用。当控制器 deinit 时,闭包将引用已释放的对象 — 应用程序崩溃是必然的。
第三个错误 — 执行繁重的同步操作。在 viewWillDisappear 中保存大量数据、写入 Core Data 或文件系统会阻塞主线程。如果操作持续时间超过过渡动画,UIKit 会暂停线程并且界面冻结。将繁重保存移至后台队列。
第四个错误 — 忘记调用 super。不调用 super.viewWillDisappear 可能会破坏 UINavigationController 和 UITabBarController 的工作,它们使用此方法进行内部状态管理。始终按照 Apple 文档首先或最后调用 super。
这个问题在具有活跃多任务和应用切换的 iOS 上更加严重。第五个错误 — 在 viewWillDisappear 中保存后使用 DispatchQueue.main.async。如果在调用 super.viewWillDisappear 后异步向主队列发送块,无法保证控制器在块执行时仍然存在。始终在闭包内使用弱引用 [weak self] 以防止引用已释放的内存并防止应用程序崩溃。
常见问题
viewWillDisappear 在消失开始时被调用,此时屏幕仍然可见。viewDidDisappear — 在屏幕完全隐藏且动画完成后被调用。
使用 viewDidDisappear 确认保存或在 viewWillDisappear 中检查 isMovingFromParent 和 isBeingDismissed 属性以确定屏幕是否真的会消失。
是的,必须,如果您使用带 self 的块或选择器。ARC 不管理 NotificationCenter 的订阅。在 iOS 9+ 中,对于块使用弱引用并在 viewWillDisappear 中取消订阅。
无法做到 — 强制退出不调用 Lifecycle 方法。为保证应用程序结束时的保存,使用 UIApplication.willTerminateNotification 或在数据变化时实时保存。
会,在交互式 pop 手势中,UIKit 在手势开始后立即调用 viewWillDisappear。如果用户取消手势,屏幕保持可见,但该方法已触发。始终检查 isMovingFromParent。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。