feat: in-app dark mode toggle (System / Light / Dark)
- AppearanceManager with UserDefaults persistence - Three modes: System (follows iOS), Light, Dark - Toggle in Home screen profile menu under "Appearance" - Applied via .preferredColorScheme at app root - Persists across app launches Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
A10003 /* Config.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10003 /* Config.swift */; };
|
||||
A10004 /* APIClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10004 /* APIClient.swift */; };
|
||||
A10005 /* AuthManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10005 /* AuthManager.swift */; };
|
||||
A10050 /* AppearanceManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10050 /* AppearanceManager.swift */; };
|
||||
A10006 /* LoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10006 /* LoginView.swift */; };
|
||||
A10007 /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10007 /* HomeView.swift */; };
|
||||
A10008 /* HomeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10008 /* HomeViewModel.swift */; };
|
||||
@@ -57,6 +58,7 @@
|
||||
B10003 /* Config.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Config.swift; sourceTree = "<group>"; };
|
||||
B10004 /* APIClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIClient.swift; sourceTree = "<group>"; };
|
||||
B10005 /* AuthManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthManager.swift; sourceTree = "<group>"; };
|
||||
B10050 /* AppearanceManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppearanceManager.swift; sourceTree = "<group>"; };
|
||||
B10006 /* LoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginView.swift; sourceTree = "<group>"; };
|
||||
B10007 /* HomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = "<group>"; };
|
||||
B10008 /* HomeViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeViewModel.swift; sourceTree = "<group>"; };
|
||||
@@ -137,6 +139,7 @@
|
||||
children = (
|
||||
B10004 /* APIClient.swift */,
|
||||
B10005 /* AuthManager.swift */,
|
||||
B10050 /* AppearanceManager.swift */,
|
||||
);
|
||||
path = Core;
|
||||
sourceTree = "<group>";
|
||||
@@ -411,6 +414,7 @@
|
||||
A10003 /* Config.swift in Sources */,
|
||||
A10004 /* APIClient.swift in Sources */,
|
||||
A10005 /* AuthManager.swift in Sources */,
|
||||
A10050 /* AppearanceManager.swift in Sources */,
|
||||
A10006 /* LoginView.swift in Sources */,
|
||||
A10007 /* HomeView.swift in Sources */,
|
||||
A10008 /* HomeViewModel.swift in Sources */,
|
||||
|
||||
43
ios/Platform/Platform/Core/AppearanceManager.swift
Normal file
43
ios/Platform/Platform/Core/AppearanceManager.swift
Normal file
@@ -0,0 +1,43 @@
|
||||
import SwiftUI
|
||||
|
||||
@Observable
|
||||
final class AppearanceManager {
|
||||
enum Mode: String, CaseIterable {
|
||||
case system, light, dark
|
||||
|
||||
var label: String {
|
||||
switch self {
|
||||
case .system: return "System"
|
||||
case .light: return "Light"
|
||||
case .dark: return "Dark"
|
||||
}
|
||||
}
|
||||
|
||||
var icon: String {
|
||||
switch self {
|
||||
case .system: return "circle.lefthalf.filled"
|
||||
case .light: return "sun.max.fill"
|
||||
case .dark: return "moon.fill"
|
||||
}
|
||||
}
|
||||
|
||||
var colorScheme: ColorScheme? {
|
||||
switch self {
|
||||
case .system: return nil
|
||||
case .light: return .light
|
||||
case .dark: return .dark
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var mode: Mode {
|
||||
didSet {
|
||||
UserDefaults.standard.set(mode.rawValue, forKey: "appearance_mode")
|
||||
}
|
||||
}
|
||||
|
||||
init() {
|
||||
let saved = UserDefaults.standard.string(forKey: "appearance_mode") ?? "system"
|
||||
mode = Mode(rawValue: saved) ?? .system
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import PhotosUI
|
||||
|
||||
struct HomeView: View {
|
||||
@Environment(AuthManager.self) private var auth
|
||||
@Environment(AppearanceManager.self) private var appearance
|
||||
@State private var vm = HomeViewModel()
|
||||
@State private var ringAnimated = false
|
||||
@Binding var selectedTab: Int
|
||||
@@ -50,6 +51,18 @@ struct HomeView: View {
|
||||
}
|
||||
}
|
||||
Divider()
|
||||
Menu {
|
||||
ForEach(AppearanceManager.Mode.allCases, id: \.self) { mode in
|
||||
Button {
|
||||
appearance.mode = mode
|
||||
} label: {
|
||||
Label(mode.label, systemImage: mode.icon)
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
Label("Appearance", systemImage: appearance.mode.icon)
|
||||
}
|
||||
Divider()
|
||||
Button(role: .destructive) {
|
||||
Task { await auth.logout() }
|
||||
} label: {
|
||||
|
||||
@@ -3,11 +3,14 @@ import SwiftUI
|
||||
@main
|
||||
struct PlatformApp: App {
|
||||
@State private var authManager = AuthManager()
|
||||
@State private var appearance = AppearanceManager()
|
||||
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
ContentView()
|
||||
.environment(authManager)
|
||||
.environment(appearance)
|
||||
.preferredColorScheme(appearance.mode.colorScheme)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user