iOS如何通过NotificationCenter监听深浅模式切换替代代理方法
问题原因
你原有代码无法触发回调的核心原因是:UITraitCollection 没有对外公开可被监听的系统通知,你传入name: nil的写法虽然会监听所有通知,但 UIKit 触发 trait 变化时并不会发送以UIScreen.main.traitCollection为发送对象的公开通知,因此回调方法永远不会被触发。
正确实现方案
我们可以通过系统允许的UIWindow/UIWindowScene的trait变化回调来触发全局通知,实现无侵入的全局主题监听:
方案1:兼容iOS13以下无SceneDelegate的项目
- 自定义
UIWindow子类,重写trait变化回调,发送全局通知:
class ThemeMonitorWindow: UIWindow { override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { super.traitCollectionDidChange(previousTraitCollection) // 只有深浅模式变化时才发通知,避免其他trait变化(比如大小类)触发无效回调 guard previousTraitCollection?.userInterfaceStyle != traitCollection.userInterfaceStyle else { return } NotificationCenter.default.post( name: NSNotification.Name("GlobalUserInterfaceStyleChanged"), object: traitCollection.userInterfaceStyle ) } }
- 在AppDelegate中替换默认Window为自定义Window,监听全局通知:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // 替换系统默认Window为自定义的监听Window self.window = ThemeMonitorWindow(frame: UIScreen.main.bounds) // 原有Window初始化逻辑不变,设置根控制器、显示Window self.window?.rootViewController = YourRootViewController() self.window?.makeKeyAndVisible() // 注册全局主题变化监听 NotificationCenter.default.addObserver( self, selector: #selector(handleStyleChange), name: NSNotification.Name("GlobalUserInterfaceStyleChanged"), object: nil ) return true } @objc private func handleStyleChange(notification: Notification) { guard let style = notification.object as? UIUserInterfaceStyle else { return } switch style { case .dark: print("切换到暗黑模式,执行全局主题配置") // 你的全局主题切换逻辑 case .light: print("切换到浅色模式,执行全局主题配置") case .unspecified: print("模式未指定") @unknown default: break } }
方案2:iOS13+使用SceneDelegate的项目
无需自定义Window,直接在SceneDelegate的系统回调中处理即可:
class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? func windowScene(_ windowScene: UIWindowScene, didUpdate previousTraitCollection: UITraitCollection?) { super.windowScene(windowScene, didUpdate: previousTraitCollection) guard previousTraitCollection?.userInterfaceStyle != windowScene.traitCollection.userInterfaceStyle else { return } let currentStyle = windowScene.traitCollection.userInterfaceStyle // 直接执行全局主题切换逻辑,或者发送全局通知给其他需要监听的模块 switch currentStyle { case .dark: print("dark") case .light: print("light") default: break } } }
该方案是苹果官方支持的标准实现,无审核风险,性能开销极低,不需要修改现有所有视图控制器的代码,即可实现全局主题切换逻辑。
内容的提问来源于stack exchange,提问作者Ethan Allen
相关产品推荐
相关产品推荐

