From f5e9f66f767c3e9d110bd3ce3c9f81432730d0fa Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Tue, 16 Apr 2024 12:03:52 -0400 Subject: [PATCH] Fix app background colors not updating when preference changed This only fully fixes it on iOS 17, but it seems to be the best we can do --- .../TuskerPreferences/PreferenceStore.swift | 5 ++ Tusker/Extensions/View+AppListStyle.swift | 87 ++++++++++++++----- Tusker/Preferences/Colors.swift | 30 +++++++ Tusker/Screens/Report/ReportView.swift | 2 +- 4 files changed, 103 insertions(+), 21 deletions(-) diff --git a/Packages/TuskerPreferences/Sources/TuskerPreferences/PreferenceStore.swift b/Packages/TuskerPreferences/Sources/TuskerPreferences/PreferenceStore.swift index a3a52f06..945e3162 100644 --- a/Packages/TuskerPreferences/Sources/TuskerPreferences/PreferenceStore.swift +++ b/Packages/TuskerPreferences/Sources/TuskerPreferences/PreferenceStore.swift @@ -74,4 +74,9 @@ extension PreferenceStore { public func hasFeatureFlag(_ flag: FeatureFlag) -> Bool { enabledFeatureFlags.contains(flag) } + + + public func getValue(preferenceKeyPath: KeyPath>) -> Key.Value { + self[keyPath: preferenceKeyPath].preference.wrappedValue + } } diff --git a/Tusker/Extensions/View+AppListStyle.swift b/Tusker/Extensions/View+AppListStyle.swift index 1ca3c12b..472bfc3c 100644 --- a/Tusker/Extensions/View+AppListStyle.swift +++ b/Tusker/Extensions/View+AppListStyle.swift @@ -8,26 +8,11 @@ import SwiftUI import Combine +import TuskerPreferences extension View { - @MainActor - @ViewBuilder - func appGroupedListBackground(container: UIAppearanceContainer.Type, applyBackground: Bool = true) -> some View { - if #available(iOS 16.0, *) { - if applyBackground { - self - .scrollContentBackground(.hidden) - .background(Color.appGroupedBackground.edgesIgnoringSafeArea(.all)) - } else { - self - .scrollContentBackground(.hidden) - } - } else { - self - .onAppear { - UITableView.appearance(whenContainedInInstancesOf: [container]).backgroundColor = .appGroupedBackground - } - } + func appGroupedListBackground(container: UIAppearanceContainer.Type) -> some View { + self.modifier(AppGroupedListBackground(container: container)) } func appGroupedListRowBackground() -> some View { @@ -35,11 +20,45 @@ extension View { } } -private struct AppGroupedListRowBackground: ViewModifier { +private struct AppGroupedListBackground: ViewModifier { + let container: any UIAppearanceContainer.Type @Environment(\.colorScheme) private var colorScheme + @Environment(\.pureBlackDarkMode) private var environmentPureBlackDarkMode + + private var pureBlackDarkMode: Bool { + // using @PreferenceObserving just does not work for this, so try the environment key when available + // if it's not available, the color won't update automatically, but it will be correct when the view is created + if #available(iOS 17.0, *) { + environmentPureBlackDarkMode + } else { + false + } + } func body(content: Content) -> some View { - if colorScheme == .dark, !Preferences.shared.pureBlackDarkMode { + if #available(iOS 16.0, *) { + if colorScheme == .dark, !pureBlackDarkMode { + content + .scrollContentBackground(.hidden) + .background(Color.appGroupedBackground.edgesIgnoringSafeArea(.all)) + } else { + content + } + } else { + content + .onAppear { + UITableView.appearance(whenContainedInInstancesOf: [container]).backgroundColor = .appGroupedBackground + } + } + } +} + +private struct AppGroupedListRowBackground: ViewModifier { + @Environment(\.colorScheme) private var colorScheme + @PreferenceObserving(\.$pureBlackDarkMode) private var pureBlackDarkMode + + func body(content: Content) -> some View { + if colorScheme == .dark, !pureBlackDarkMode { content .listRowBackground(Color.appGroupedCellBackground) } else { @@ -47,3 +66,31 @@ private struct AppGroupedListRowBackground: ViewModifier { } } } + +@propertyWrapper +private struct PreferenceObserving: DynamicProperty { + typealias PrefKeyPath = KeyPath> + + let keyPath: PrefKeyPath + @StateObject private var observer: Observer + + init(_ keyPath: PrefKeyPath) { + self.keyPath = keyPath + self._observer = StateObject(wrappedValue: Observer(keyPath: keyPath)) + } + + var wrappedValue: Key.Value { + Preferences.shared.getValue(preferenceKeyPath: keyPath) + } + + @MainActor + private class Observer: ObservableObject { + private var cancellable: AnyCancellable? + + init(keyPath: PrefKeyPath) { + cancellable = Preferences.shared[keyPath: keyPath].sink { [unowned self] _ in + self.objectWillChange.send() + } + } + } +} diff --git a/Tusker/Preferences/Colors.swift b/Tusker/Preferences/Colors.swift index a3601cd2..99e660d3 100644 --- a/Tusker/Preferences/Colors.swift +++ b/Tusker/Preferences/Colors.swift @@ -143,3 +143,33 @@ extension UIMutableTraits { set { self[PureBlackDarkModeTrait.self] = newValue } } } + +@available(iOS 17.0, *) +private struct PureBlackDarkModeKey: UITraitBridgedEnvironmentKey { + static let defaultValue: Bool = false + + static func read(from traitCollection: UITraitCollection) -> Bool { + traitCollection[PureBlackDarkModeTrait.self] + } + + static func write(to mutableTraits: inout any UIMutableTraits, value: Bool) { + mutableTraits[PureBlackDarkModeTrait.self] = value + } +} + +extension EnvironmentValues { + var pureBlackDarkMode: Bool { + get { + if #available(iOS 17.0, *) { + self[PureBlackDarkModeKey.self] + } else { + true + } + } + set { + if #available(iOS 17.0, *) { + self[PureBlackDarkModeKey.self] = newValue + } + } + } +} diff --git a/Tusker/Screens/Report/ReportView.swift b/Tusker/Screens/Report/ReportView.swift index 5a255a78..d51fae17 100644 --- a/Tusker/Screens/Report/ReportView.swift +++ b/Tusker/Screens/Report/ReportView.swift @@ -157,7 +157,7 @@ struct ReportView: View { .appGroupedListRowBackground() } .listStyle(.insetGrouped) - .appGroupedListBackground(container: UIHostingController.self, applyBackground: true) + .appGroupedListBackground(container: UIHostingController.self) .alertWithData("Error Reporting", data: $error, actions: { error in Button("OK") {} }, message: { error in