diff --git a/Yattee/Core/Settings/SettingsManager+CloudSync.swift b/Yattee/Core/Settings/SettingsManager+CloudSync.swift index d2eea94d..af7f912d 100644 --- a/Yattee/Core/Settings/SettingsManager+CloudSync.swift +++ b/Yattee/Core/Settings/SettingsManager+CloudSync.swift @@ -389,6 +389,10 @@ extension SettingsManager { clearCache() updateLastSyncTime() + + #if !os(tvOS) + Self.applyTheme(theme) + #endif } /// Refreshes settings from iCloud by copying iCloud values to local storage. @@ -445,5 +449,11 @@ extension SettingsManager { // Clear caches to force re-read from local storage clearCache() + + // Re-apply the theme in case it was among the synced changes — + // it is enforced at the window level, not via SwiftUI state. + #if !os(tvOS) + Self.applyTheme(theme) + #endif } } diff --git a/Yattee/Core/Settings/SettingsManager+General.swift b/Yattee/Core/Settings/SettingsManager+General.swift index 9d9eb730..89e76763 100644 --- a/Yattee/Core/Settings/SettingsManager+General.swift +++ b/Yattee/Core/Settings/SettingsManager+General.swift @@ -24,9 +24,28 @@ extension SettingsManager { set { _theme = newValue set(newValue.rawValue, for: .theme) + Self.applyTheme(newValue) } } + /// Forces the theme onto the platform windows directly. SwiftUI's + /// `.preferredColorScheme` fails to revert from dark back to light/system + /// while a sheet is presented, and on macOS it never covered secondary + /// window scenes (Settings), so the override is applied at the + /// UIKit/AppKit level instead. + static func applyTheme(_ theme: AppTheme) { + #if os(iOS) + for scene in UIApplication.shared.connectedScenes { + guard let windowScene = scene as? UIWindowScene else { continue } + for window in windowScene.windows { + window.overrideUserInterfaceStyle = theme.userInterfaceStyle + } + } + #elseif os(macOS) + NSApp.appearance = theme.appearance + #endif + } + var accentColor: AccentColor { get { if let cached = _accentColor { return cached } diff --git a/Yattee/Core/Settings/SettingsTypes.swift b/Yattee/Core/Settings/SettingsTypes.swift index 917e011d..530e657e 100644 --- a/Yattee/Core/Settings/SettingsTypes.swift +++ b/Yattee/Core/Settings/SettingsTypes.swift @@ -7,6 +7,11 @@ import Foundation import SwiftUI +#if canImport(UIKit) +import UIKit +#elseif canImport(AppKit) +import AppKit +#endif // MARK: - Theme & Appearance @@ -22,6 +27,24 @@ enum AppTheme: String, CaseIterable, Codable { case .dark: return .dark } } + + #if canImport(UIKit) + var userInterfaceStyle: UIUserInterfaceStyle { + switch self { + case .system: return .unspecified + case .light: return .light + case .dark: return .dark + } + } + #elseif canImport(AppKit) + var appearance: NSAppearance? { + switch self { + case .system: return nil + case .light: return NSAppearance(named: .aqua) + case .dark: return NSAppearance(named: .darkAqua) + } + } + #endif } enum AccentColor: String, CaseIterable, Codable { diff --git a/Yattee/YatteeApp.swift b/Yattee/YatteeApp.swift index ee8e909a..9dd6dfd1 100644 --- a/Yattee/YatteeApp.swift +++ b/Yattee/YatteeApp.swift @@ -70,7 +70,6 @@ struct YatteeApp: App { ContentView() .appEnvironment(appEnvironment) #if !os(tvOS) - .preferredColorScheme(appEnvironment.settingsManager.theme.colorScheme) .tint(appEnvironment.settingsManager.resolvedAccentColor) #endif #if os(macOS) @@ -85,6 +84,9 @@ struct YatteeApp: App { handleContinuedActivity(activity) } .onAppear { + #if !os(tvOS) + SettingsManager.applyTheme(appEnvironment.settingsManager.theme) + #endif registerBackgroundTasksIfNeeded() #if os(tvOS) TopShelfSnapshotWriter.startObserving(dataManager: appEnvironment.dataManager)