Swift 如何检测应用Interface Style是否为unspecified状态
根本原因
traitCollection.userInterfaceStyle返回的是当前界面最终生效的实际外观值,按照苹果的设计逻辑,这个值永远只会是.light或.dark,不会返回.unspecified——当你设置overrideUserInterfaceStyle = .unspecified时,代表不做强制覆盖,跟随系统当前的外观设置,因此traitCollection会直接返回系统当前的浅/深色状态,自然不会出现未指定值。
你要判断的「是否为跟随系统的unspecified状态」本质是用户配置的外观规则,和traitCollection返回的实际生效值是两个完全独立的概念,不要从traitCollection里取这个配置。
可行实现方案
核心逻辑
- 配置状态直接从
overrideUserInterfaceStyle属性读取:你给window赋值的覆盖规则会保存在这个属性本身,设置为.unspecified时直接读取该属性就能拿到对应值,不需要经过traitCollection解析。 - 新增本地持久化:APP杀进程重启后,
overrideUserInterfaceStyle会重置为默认值,需要把用户选择的配置存在本地,启动时自动恢复,保证全生命周期状态一致。 - 封装全局访问方法:给其他业务模块提供统一的调用入口,避免各处重复取window、读traitCollection的逻辑出错。
可直接复用的代码
首先定义统一的外观枚举,和你的选择cell逻辑对应:
enum Appearance: Int { case device = 0 // 跟随系统,对应unspecified case light = 1 case dark = 2 }
修改你原有的UIApplication扩展,新增持久化和状态读取逻辑,同时兼容iOS13+多Scene场景:
extension UIApplication { private static let storedAppearanceKey = "com.app.customAppearance" func changeInterfaceStyle(_ mode: UIUserInterfaceStyle) { // 取当前激活的keyWindow,兼容单window和多Scene场景 let targetWindow: UIWindow? if #available(iOS 13.0, *) { targetWindow = connectedScenes .filter { $0.activationState == .foregroundActive } .compactMap { $0 as? UIWindowScene } .flatMap { $0.windows } .first(where: \.isKeyWindow) } else { targetWindow = delegate?.window ?? nil } guard let keyWindow = targetWindow else { return } keyWindow.overrideUserInterfaceStyle = mode // 持久化用户选择,APP重启后不丢失 let storeValue: Int switch mode { case .light: storeValue = Appearance.light.rawValue case .dark: storeValue = Appearance.dark.rawValue default: storeValue = Appearance.device.rawValue } UserDefaults.standard.set(storeValue, forKey: Self.storedAppearanceKey) } /// 全局获取当前用户配置的外观规则 func currentConfiguredAppearance() -> Appearance { guard let storedValue = UserDefaults.standard.object(forKey: Self.storedAppearanceKey) as? Int, let appearance = Appearance(rawValue: storedValue) else { // 默认为跟随系统 return .device } return appearance } }
在APP启动入口恢复之前保存的配置,避免重启后配置失效:
// AppDelegate的didFinishLaunchingWithOptions方法,或SceneDelegate的willConnectTo方法中调用 let savedAppearance = UIApplication.shared.currentConfiguredAppearance() let overrideMode: UIUserInterfaceStyle switch savedAppearance { case .light: overrideMode = .light case .dark: overrideMode = .dark case .device: overrideMode = .unspecified } UIApplication.shared.changeInterfaceStyle(overrideMode)
业务模块调用方式
其他逻辑需要判断是否为unspecified(跟随系统)状态时,直接调用统一方法即可:
let isFollowSystem = UIApplication.shared.currentConfiguredAppearance() == .device
如果需要获取当前界面实际显示的是浅色还是深色(比如动态适配资源),再去读traitCollection.userInterfaceStyle即可,两个接口分别对应不同的使用场景,不要混用。
注意:不要尝试通过traitCollection反推用户配置,当系统外观刚好和用户强制设置的外观一致时(比如用户强制选浅色,系统当前也刚好是浅色),反推结果会完全错误。
内容的提问来源于stack exchange,提问作者taroPancake
相关产品推荐
相关产品推荐

