You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何监听控制器弹窗的弹出与关闭并接收通知?

解决方案

你可以通过以下几种方式实现对目标控制器模态弹窗的监听,不需要修改弹窗控制器的代码:

方法一: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 09:40:23