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

iOS如何通过NotificationCenter监听深浅模式切换替代代理方法

问题原因

你原有代码无法触发回调的核心原因是:UITraitCollection 没有对外公开可被监听的系统通知,你传入name: nil的写法虽然会监听所有通知,但 UIKit 触发 trait 变化时并不会发送以UIScreen.main.traitCollection为发送对象的公开通知,因此回调方法永远不会被触发。

正确实现方案

我们可以通过系统允许的UIWindow/UIWindowScene的trait变化回调来触发全局通知,实现无侵入的全局主题监听:

方案1:兼容iOS13以下无SceneDelegate的项目

  1. 自定义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
        )
    }
}
  1. 在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.20 04:36:03