如何监听控制器弹窗的弹出与关闭并接收通知?
解决方案
你可以通过以下几种方式实现对目标控制器模态弹窗的监听,不需要修改弹窗控制器的代码:
方法一:KVO监听presentedViewController属性变化
这是最直接的方案,既然你已经通过vc.presentedViewController != nil判断弹窗状态,用KVO就能监听该属性的变更,区分弹窗的出现和关闭:
// 添加KVO监听 vc.addObserver(self, forKeyPath: "presentedViewController", options: [.old, .new], context: nil) // 实现监听回调 override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { guard keyPath == "presentedViewController", let targetVC = object as? MyUIViewController else { super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context) return } let oldPresented = change?[.oldKey] as? UIViewController let newPresented = change?[.newKey] as? UIViewController if oldPresented == nil && newPresented != nil { // 弹窗已完成呈现 print("弹窗已弹出") } else if oldPresented != nil && newPresented == nil { // 弹窗已关闭 print("弹窗已关闭") } } // 记得在合适时机移除监听,比如当前控制器销毁时 deinit { vc.removeObserver(self, forKeyPath: "presentedViewController") }
Objective-C写法逻辑一致,使用addObserver:forKeyPath:options:context:和observeValueForKeyPath:ofObject:change:context:方法即可。
方法二:监听全局ViewController通知
系统提供两个全局通知,对应模态控制器的呈现和关闭完成事件:
UIViewControllerDidPresentNotification:模态控制器呈现完成时发送UIViewControllerDidDismissNotification:模态控制器关闭完成时发送
注册通知后,通过过滤逻辑确保只处理目标vc弹出的弹窗:
// 注册通知 NotificationCenter.default.addObserver(self, selector: #selector(onModalPresented(_:)), name: UIViewControllerDidPresentNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(onModalDismissed(_:)), name: UIViewControllerDidDismissNotification, object: nil) // 处理呈现通知 @objc func onModalPresented(_ notification: Notification) { guard let presentedVC = notification.object as? UIViewController, presentedVC.presentingViewController === vc else { return } print("弹窗已弹出") } // 处理关闭通知 @objc func onModalDismissed(_ notification: Notification) { guard let dismissedVC = notification.object as? UIViewController, dismissedVC.presentingViewController === vc else { return } print("弹窗已关闭") } // 移除通知 deinit { NotificationCenter.default.removeObserver(self) }
这个方案的优势是可避免对目标vc的强引用,但过滤逻辑必须严谨,防止误处理其他控制器的弹窗事件。
方法三:Method Swizzling(方法交换)
如果需要监听弹窗开始呈现的瞬间(而非完成后),可以通过方法交换替换目标vc的present(_:animated:completion:)方法,在原始方法执行前后插入自定义逻辑:
extension MyUIViewController { static func swizzlePresentMethod() { let originalSelector = #selector(present(_:animated:completion:)) let swizzledSelector = #selector(swizzledPresent(_:animated:completion:)) guard let originalMethod = class_getInstanceMethod(self, originalSelector), let swizzledMethod = class_getInstanceMethod(self, swizzledSelector) else { return } method_exchangeImplementations(originalMethod, swizzledMethod) } @objc private func swizzledPresent(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) { // 弹窗开始呈现前的逻辑 print("弹窗开始弹出") // 调用原始present方法 self.swizzledPresent(viewControllerToPresent, animated: flag) { completion?() // 弹窗呈现完成后的可选逻辑 print("弹窗弹出完成") } } } // 在合适时机执行方法交换,比如App启动时或目标vc初始化后 MyUIViewController.swizzlePresentMethod()
同理,若要监听关闭事件,可交换dismiss(animated:completion:)方法,实现关闭前后的逻辑注入。注意:Method Swizzling需谨慎使用,确保仅针对目标类,避免影响其他控制器的正常行为。
内容的提问来源于stack exchange,提问作者Nick
相关产品推荐
相关产品推荐

