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

iOS开发:如何禁用全局NSNotification通知并按需恢复?

解决全局禁用UIApplicationDidBecomeActive通知的问题

这个问题我之前也遇到过,你用NotificationCenter.default.removeObserver(self)没用的原因很明确——它只移除当前ViewController自己注册的观察者,其他地方(比如别的VC、单例类)注册的UIApplicationDidBecomeActive通知依然会正常触发。要实现全局禁用这个通知再恢复,有两种靠谱的方案,看你需求选:

方案一:全局开关控制回调执行(推荐,无Runtime风险)

这个思路是给所有监听UIApplicationDidBecomeActive的回调加一个“开关判断”,当你的目标VC(vc)显示时,关闭开关,所有回调都不执行逻辑;VC消失时再打开开关。

步骤1:定义全局开关

你可以把开关放在AppDelegate里,或者单独的配置类中,方便全局访问:

// 在AppDelegate中添加全局变量
class AppDelegate: UIResponder, UIApplicationDelegate {
    static let shared = AppDelegate()
    var shouldSuppressDidBecomeActive = false // 全局开关
    
    // ... 其他原有代码
}

步骤2:修改所有通知回调

找到项目中所有注册UIApplicationDidBecomeActive通知的地方,在回调方法开头加上开关判断:

// 示例:某个类中的通知回调
@objc private func handleAppDidBecomeActive(_ notification: Notification) {
    // 先判断开关,如果禁用就直接返回
    guard !AppDelegate.shared.shouldSuppressDidBecomeActive else { return }
    
    // 原来的业务逻辑代码
    print("应用激活,执行原有逻辑")
}

步骤3:在目标VC中控制开关

在你的vc类中,在视图显示和消失时切换开关状态:

class YourViewController: UIViewController {
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        // 显示时禁用全局通知
        AppDelegate.shared.shouldSuppressDidBecomeActive = true
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        // 消失时恢复全局通知
        AppDelegate.shared.shouldSuppressDidBecomeActive = false
    }
}

方案二:Runtime拦截通知发送(无需修改现有回调)

如果你的项目中监听这个通知的地方太多,不想逐个修改回调,可以用Method Swizzling拦截NotificationCenter的post方法,直接阻止UIApplicationDidBecomeActive通知的发送。

步骤1:给NotificationCenter添加Swizzling扩展

extension NotificationCenter {
    // 执行Swizzling的方法,只需要调用一次
    static let swizzlePostNotification: Void = {
        let originalSelector = #selector(post(name:object:userInfo:))
        let swizzledSelector = #selector(swizzled_post(name:object:userInfo:))
        
        guard let originalMethod = class_getInstanceMethod(self, originalSelector),
              let swizzledMethod = class_getInstanceMethod(self, swizzledSelector) else { return }
        
        // 交换方法实现
        method_exchangeImplementations(originalMethod, swizzledMethod)
    }()
    
    // 替换后的post方法
    @objc private func swizzled_post(name aName: NSNotification.Name, object anObject: Any?, userInfo aUserInfo: [AnyHashable : Any]? = nil) {
        // 判断是否是要拦截的通知,并且开关处于禁用状态
        if aName == UIApplication.didBecomeActiveNotification && AppDelegate.shared.shouldSuppressDidBecomeActive {
            return // 直接不发送通知
        }
        // 调用原来的post实现(因为方法已经交换,这里调用swizzled_post其实是调用原方法)
        swizzled_post(name: aName, object: anObject, userInfo: aUserInfo)
    }
}

步骤2:启动时执行Swizzling

在AppDelegate的启动方法中调用一次Swizzling:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // 执行通知拦截的Swizzling
    NotificationCenter.swizzlePostNotification
    return true
}

步骤3:同样用开关控制拦截状态

和方案一一样,在你的vc类中控制shouldSuppressDidBecomeActive开关即可,无需修改任何现有回调。

注意事项

  • 如果你的vc是模态弹出的,viewWillDisappear会在被dismiss时触发,此时设置开关为false是正确的;如果是push到下一个VC,同样会触发,也符合需求。
  • 如果多个VC都需要这个功能,可以把开关控制逻辑封装成一个协议或者基类ViewController,避免重复代码。
  • 方案二的Swizzling要注意线程安全,确保只执行一次(上面的实现已经用静态变量保证只执行一次),避免出现意外问题。

内容的提问来源于stack exchange,提问作者tara tandel

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 09:00:01