iOS16定时切换明暗模式时traitCollectionDidChange未触发的咨询
解决方案
首先明确:你遇到的是iOS的默认设计行为——为避免打断前台用户操作,系统定时切换明暗模式时,不会主动触发前台APP的traitCollectionDidChange,只有当APP进入后台再回到前台、或锁屏解锁后,才会同步模式变化。
针对导航APP的实时需求,以下是两种基于公开API的可行方案,无需自行实现昼夜判断逻辑:
方案一:监听系统设置变化通知
通过监听NSSettingsChanged通知(系统设置变更时触发),主动检查当前界面风格是否发生变化,进而触发traitCollectionDidChange逻辑。
修改你的WindowWithDarkModeMonitor类:
class WindowWithDarkModeMonitor: UIWindow { private var previousInterfaceStyle: UIUserInterfaceStyle? override init(frame: CGRect) { super.init(frame: frame) setupObserver() previousInterfaceStyle = traitCollection.userInterfaceStyle } required init?(coder: NSCoder) { super.init(coder: coder) setupObserver() previousInterfaceStyle = traitCollection.userInterfaceStyle } private func setupObserver() { NotificationCenter.default.addObserver( self, selector: #selector(checkInterfaceStyleChange), name: .NSSettingsChanged, object: nil ) } @objc private func checkInterfaceStyleChange() { let currentStyle = traitCollection.userInterfaceStyle guard let previousStyle = previousInterfaceStyle, previousStyle != currentStyle else { return } // 手动触发风格变化回调 traitCollectionDidChange(traitCollection) } override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { super.traitCollectionDidChange(previousTraitCollection) previousInterfaceStyle = traitCollection.userInterfaceStyle print("traitCollectionDidChange") // 这里添加你的界面更新逻辑 } deinit { NotificationCenter.default.removeObserver(self) } }
方案二:定时检查界面风格
如果担心NSSettingsChanged通知触发过于频繁,可以用定时器定期检查当前界面风格,仅在变化时触发更新:
class WindowWithDarkModeMonitor: UIWindow { private var previousInterfaceStyle: UIUserInterfaceStyle? private var styleCheckTimer: Timer? override init(frame: CGRect) { super.init(frame: frame) setupTimer() previousInterfaceStyle = traitCollection.userInterfaceStyle } required init?(coder: NSCoder) { super.init(coder: coder) setupTimer() previousInterfaceStyle = traitCollection.userInterfaceStyle } private func setupTimer() { // 每30秒检查一次,可根据需求调整间隔 styleCheckTimer = Timer.scheduledTimer( timeInterval: 30, target: self, selector: #selector(checkInterfaceStyleChange), userInfo: nil, repeats: true ) } @objc private func checkInterfaceStyleChange() { let currentStyle = traitCollection.userInterfaceStyle guard let previousStyle = previousInterfaceStyle, previousStyle != currentStyle else { return } traitCollectionDidChange(traitCollection) } override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { super.traitCollectionDidChange(previousTraitCollection) previousInterfaceStyle = traitCollection.userInterfaceStyle print("traitCollectionDidChange") // 界面更新逻辑 } deinit { styleCheckTimer?.invalidate() } }
注意事项
- 两种方案均基于公开API,不会涉及审核风险;
- 方案一的响应更及时,方案二更轻量化,可根据APP需求选择;
- 无论哪种方案,都要确保在
deinit中移除通知监听或销毁定时器,避免内存泄漏。
内容的提问来源于stack exchange,提问作者guido
相关产品推荐
相关产品推荐

