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
相关产品推荐
相关产品推荐

