diff --git a/V2er/Assets.xcassets/Colors/AppBackground.colorset/Contents.json b/V2er/Assets.xcassets/Colors/AppBackground.colorset/Contents.json
new file mode 100644
index 0000000..3d8ee2e
--- /dev/null
+++ b/V2er/Assets.xcassets/Colors/AppBackground.colorset/Contents.json
@@ -0,0 +1,38 @@
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0.980",
+ "green" : "0.980",
+ "red" : "0.980"
+ }
+ },
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0.110",
+ "green" : "0.110",
+ "red" : "0.110"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
\ No newline at end of file
diff --git a/V2er/Assets.xcassets/Colors/BackgroundColor.colorset/Contents.json b/V2er/Assets.xcassets/Colors/BackgroundColor.colorset/Contents.json
new file mode 100644
index 0000000..3d84274
--- /dev/null
+++ b/V2er/Assets.xcassets/Colors/BackgroundColor.colorset/Contents.json
@@ -0,0 +1,38 @@
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "0.800",
+ "blue" : "0.886",
+ "green" : "0.886",
+ "red" : "0.886"
+ }
+ },
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "0.800",
+ "blue" : "0.118",
+ "green" : "0.110",
+ "red" : "0.110"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
\ No newline at end of file
diff --git a/V2er/Assets.xcassets/Colors/BorderColor.colorset/Contents.json b/V2er/Assets.xcassets/Colors/BorderColor.colorset/Contents.json
new file mode 100644
index 0000000..462bf3e
--- /dev/null
+++ b/V2er/Assets.xcassets/Colors/BorderColor.colorset/Contents.json
@@ -0,0 +1,38 @@
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "0.800",
+ "blue" : "0.910",
+ "green" : "0.910",
+ "red" : "0.910"
+ }
+ },
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "0.800",
+ "blue" : "0.247",
+ "green" : "0.235",
+ "red" : "0.235"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
\ No newline at end of file
diff --git a/V2er/Assets.xcassets/Colors/DimColor.colorset/Contents.json b/V2er/Assets.xcassets/Colors/DimColor.colorset/Contents.json
new file mode 100644
index 0000000..f6c9cf5
--- /dev/null
+++ b/V2er/Assets.xcassets/Colors/DimColor.colorset/Contents.json
@@ -0,0 +1,38 @@
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "0.600",
+ "blue" : "0.000",
+ "green" : "0.000",
+ "red" : "0.000"
+ }
+ },
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "0.600",
+ "blue" : "1.000",
+ "green" : "1.000",
+ "red" : "1.000"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
\ No newline at end of file
diff --git a/V2er/Assets.xcassets/Colors/ItemBackground.colorset/Contents.json b/V2er/Assets.xcassets/Colors/ItemBackground.colorset/Contents.json
new file mode 100644
index 0000000..a431dd9
--- /dev/null
+++ b/V2er/Assets.xcassets/Colors/ItemBackground.colorset/Contents.json
@@ -0,0 +1,38 @@
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0xFF",
+ "green" : "0xFF",
+ "red" : "0xFF"
+ }
+ },
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0x1E",
+ "green" : "0x1C",
+ "red" : "0x1C"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
\ No newline at end of file
diff --git a/V2er/Assets.xcassets/Colors/LightGray.colorset/Contents.json b/V2er/Assets.xcassets/Colors/LightGray.colorset/Contents.json
new file mode 100644
index 0000000..1beb2f7
--- /dev/null
+++ b/V2er/Assets.xcassets/Colors/LightGray.colorset/Contents.json
@@ -0,0 +1,38 @@
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0xF5",
+ "green" : "0xF5",
+ "red" : "0xF5"
+ }
+ },
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0x2E",
+ "green" : "0x2C",
+ "red" : "0x2C"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
\ No newline at end of file
diff --git a/V2er/Assets.xcassets/Colors/PrimaryText.colorset/Contents.json b/V2er/Assets.xcassets/Colors/PrimaryText.colorset/Contents.json
new file mode 100644
index 0000000..c4fd6eb
--- /dev/null
+++ b/V2er/Assets.xcassets/Colors/PrimaryText.colorset/Contents.json
@@ -0,0 +1,38 @@
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0x00",
+ "green" : "0x00",
+ "red" : "0x00"
+ }
+ },
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0xFF",
+ "green" : "0xFF",
+ "red" : "0xFF"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
\ No newline at end of file
diff --git a/V2er/Assets.xcassets/Colors/SecondaryBackground.colorset/Contents.json b/V2er/Assets.xcassets/Colors/SecondaryBackground.colorset/Contents.json
new file mode 100644
index 0000000..cca56c7
--- /dev/null
+++ b/V2er/Assets.xcassets/Colors/SecondaryBackground.colorset/Contents.json
@@ -0,0 +1,38 @@
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0.961",
+ "green" : "0.961",
+ "red" : "0.961"
+ }
+ },
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0.153",
+ "green" : "0.153",
+ "red" : "0.153"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
\ No newline at end of file
diff --git a/V2er/Assets.xcassets/Colors/SecondaryText.colorset/Contents.json b/V2er/Assets.xcassets/Colors/SecondaryText.colorset/Contents.json
new file mode 100644
index 0000000..41b70f4
--- /dev/null
+++ b/V2er/Assets.xcassets/Colors/SecondaryText.colorset/Contents.json
@@ -0,0 +1,38 @@
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0x55",
+ "green" : "0x55",
+ "red" : "0x55"
+ }
+ },
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0xAA",
+ "green" : "0xAA",
+ "red" : "0xAA"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
\ No newline at end of file
diff --git a/V2er/Assets.xcassets/Colors/SelectionColor.colorset/Contents.json b/V2er/Assets.xcassets/Colors/SelectionColor.colorset/Contents.json
new file mode 100644
index 0000000..da7da1f
--- /dev/null
+++ b/V2er/Assets.xcassets/Colors/SelectionColor.colorset/Contents.json
@@ -0,0 +1,38 @@
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "0.900",
+ "blue" : "0.000",
+ "green" : "0.000",
+ "red" : "0.000"
+ }
+ },
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "0.900",
+ "blue" : "1.000",
+ "green" : "1.000",
+ "red" : "1.000"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
\ No newline at end of file
diff --git a/V2er/Assets.xcassets/Colors/Separator.colorset/Contents.json b/V2er/Assets.xcassets/Colors/Separator.colorset/Contents.json
new file mode 100644
index 0000000..dbb0d23
--- /dev/null
+++ b/V2er/Assets.xcassets/Colors/Separator.colorset/Contents.json
@@ -0,0 +1,38 @@
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0xE8",
+ "green" : "0xE8",
+ "red" : "0xE8"
+ }
+ },
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0x2E",
+ "green" : "0x2C",
+ "red" : "0x2C"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
\ No newline at end of file
diff --git a/V2er/Assets.xcassets/Colors/TertiaryBackground.colorset/Contents.json b/V2er/Assets.xcassets/Colors/TertiaryBackground.colorset/Contents.json
new file mode 100644
index 0000000..65deedd
--- /dev/null
+++ b/V2er/Assets.xcassets/Colors/TertiaryBackground.colorset/Contents.json
@@ -0,0 +1,38 @@
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0xF5",
+ "green" : "0xF5",
+ "red" : "0xF5"
+ }
+ },
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0x1A",
+ "green" : "0x1A",
+ "red" : "0x1A"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
\ No newline at end of file
diff --git a/V2er/Assets.xcassets/Colors/TertiaryText.colorset/Contents.json b/V2er/Assets.xcassets/Colors/TertiaryText.colorset/Contents.json
new file mode 100644
index 0000000..3b03d34
--- /dev/null
+++ b/V2er/Assets.xcassets/Colors/TertiaryText.colorset/Contents.json
@@ -0,0 +1,38 @@
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0x99",
+ "green" : "0x99",
+ "red" : "0x99"
+ }
+ },
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0x66",
+ "green" : "0x66",
+ "red" : "0x66"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
\ No newline at end of file
diff --git a/V2er/Assets.xcassets/Colors/TintColor.colorset/Contents.json b/V2er/Assets.xcassets/Colors/TintColor.colorset/Contents.json
new file mode 100644
index 0000000..db7950d
--- /dev/null
+++ b/V2er/Assets.xcassets/Colors/TintColor.colorset/Contents.json
@@ -0,0 +1,38 @@
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0x38",
+ "green" : "0x38",
+ "red" : "0x38"
+ }
+ },
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0xFF",
+ "green" : "0xFF",
+ "red" : "0xFF"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
\ No newline at end of file
diff --git a/V2er/Assets.xcassets/Colors/URLColor.colorset/Contents.json b/V2er/Assets.xcassets/Colors/URLColor.colorset/Contents.json
new file mode 100644
index 0000000..84b6cc8
--- /dev/null
+++ b/V2er/Assets.xcassets/Colors/URLColor.colorset/Contents.json
@@ -0,0 +1,38 @@
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0x87",
+ "green" : "0x80",
+ "red" : "0x77"
+ }
+ },
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0xB8",
+ "green" : "0xA3",
+ "red" : "0x94"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
\ No newline at end of file
diff --git a/V2er/Assets.xcassets/Colors/bodyText.colorset/Contents.json b/V2er/Assets.xcassets/Colors/bodyText.colorset/Contents.json
index 6e8d0a5..94e7045 100644
--- a/V2er/Assets.xcassets/Colors/bodyText.colorset/Contents.json
+++ b/V2er/Assets.xcassets/Colors/bodyText.colorset/Contents.json
@@ -4,10 +4,28 @@
"color" : {
"color-space" : "srgb",
"components" : {
- "alpha" : "1.000",
- "blue" : "0x55",
- "green" : "0x55",
- "red" : "0x55"
+ "alpha" : "0.750",
+ "blue" : "0.000",
+ "green" : "0.000",
+ "red" : "0.000"
+ }
+ },
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "0.850",
+ "blue" : "1.000",
+ "green" : "1.000",
+ "red" : "1.000"
}
},
"idiom" : "universal"
@@ -17,4 +35,4 @@
"author" : "xcode",
"version" : 1
}
-}
+}
\ No newline at end of file
diff --git a/V2er/General/Color.swift b/V2er/General/Color.swift
index e98129a..9016edf 100644
--- a/V2er/General/Color.swift
+++ b/V2er/General/Color.swift
@@ -26,24 +26,62 @@ extension Color {
self.frame(width: .infinity)
}
- public static let border = hex(0xE8E8E8, alpha: 0.8)
- static let lightGray = hex(0xF5F5F5)
- static let almostClear = hex(0xFFFFFF, alpha: 0.000001)
- static let debugColor = hex(0xFF0000, alpha: 0.1)
-// static let bodyText = hex(0x555555)
- static let bodyText = hex(0x000000, alpha: 0.75)
- static let tintColor = hex(0x383838)
- static let bgColor = hex(0xE2E2E2, alpha: 0.8)
- static let itemBg: Color = .white
- static let dim = hex(0x000000, alpha: 0.6)
-// static let url = hex(0x60c2d4)
- static let url = hex(0x778087)
+ // MARK: - Adaptive Colors for Dark Mode
+
+ // Background Colors
+ public static let background = Color("AppBackground")
+ public static let secondaryBackground = Color("SecondaryBackground")
+ public static let tertiaryBackground = Color("TertiaryBackground")
+ public static let itemBackground = Color("ItemBackground")
+
+ // Text Colors
+ public static let primaryText = Color("PrimaryText")
+ public static let secondaryText = Color("SecondaryText")
+ public static let tertiaryText = Color("TertiaryText")
+
+ // UI Element Colors
+ public static let separator = Color("Separator")
+ public static let tint = Color("TintColor")
+ public static let selection = Color("SelectionColor")
+
+ // Legacy colors with dark mode support
+ public static let border = Color("BorderColor")
+ public static let lightGray = Color("LightGray")
+ public static let almostClear = hex(0xFFFFFF, alpha: 0.000001)
+ public static let debugColor = hex(0xFF0000, alpha: 0.1)
+ public static let bodyText = Color("BodyText")
+ public static let tintColor = Color("TintColor")
+ public static let bgColor = Color("BackgroundColor")
+ public static let itemBg = Color("ItemBackground")
+ public static let dim = Color("DimColor")
+ public static let url = Color("URLColor")
public var uiColor: UIColor {
return UIColor(self)
}
}
+// MARK: - Dynamic Color Creation
+extension Color {
+ static func dynamic(light: Color, dark: Color) -> Color {
+ return Color(UIColor { traitCollection in
+ switch traitCollection.userInterfaceStyle {
+ case .dark:
+ return UIColor(dark)
+ default:
+ return UIColor(light)
+ }
+ })
+ }
+
+ static func dynamicHex(light: Int, dark: Int, alpha: CGFloat = 1.0) -> Color {
+ return dynamic(
+ light: Color.hex(light, alpha: alpha),
+ dark: Color.hex(dark, alpha: alpha)
+ )
+ }
+}
+
extension UIColor {
convenience init(hex: Int, alpha: CGFloat = 1.0) {
let components = (
@@ -55,18 +93,47 @@ extension UIColor {
}
}
-
-
+// MARK: - Preview
struct Color_Previews: PreviewProvider {
static var previews: some View {
- VStack {
- Color.hex(0xFBFBFB).frame(width: 100, height: 100)
- Color.hex(0x00FF00, alpha: 0.2).frame(width: 100, height: 100)
- Color.hex(0xFF00FF).frame(width: 100, height: 100)
- Color.tintColor.frame(width: 100, height: 100)
- Color.lightGray.frame(width: 100, height: 100)
- Color.border.frame(width: 100, height: 100).opacity(0.5)
+ Group {
+ // Light Mode Preview
+ VStack(spacing: 20) {
+ Text("Light Mode")
+ .font(.title)
+ HStack {
+ Color.background.frame(width: 80, height: 80)
+ Color.secondaryBackground.frame(width: 80, height: 80)
+ Color.itemBackground.frame(width: 80, height: 80)
+ }
+ HStack {
+ Color.primaryText.frame(width: 80, height: 80)
+ Color.secondaryText.frame(width: 80, height: 80)
+ Color.tintColor.frame(width: 80, height: 80)
+ }
+ }
+ .padding()
+ .background(Color.background)
+ .environment(\.colorScheme, .light)
+
+ // Dark Mode Preview
+ VStack(spacing: 20) {
+ Text("Dark Mode")
+ .font(.title)
+ HStack {
+ Color.background.frame(width: 80, height: 80)
+ Color.secondaryBackground.frame(width: 80, height: 80)
+ Color.itemBackground.frame(width: 80, height: 80)
+ }
+ HStack {
+ Color.primaryText.frame(width: 80, height: 80)
+ Color.secondaryText.frame(width: 80, height: 80)
+ Color.tintColor.frame(width: 80, height: 80)
+ }
+ }
+ .padding()
+ .background(Color.background)
+ .environment(\.colorScheme, .dark)
}
-
}
-}
+}
\ No newline at end of file
diff --git a/V2er/General/V2erApp.swift b/V2er/General/V2erApp.swift
index 737d201..563714c 100644
--- a/V2er/General/V2erApp.swift
+++ b/V2er/General/V2erApp.swift
@@ -14,35 +14,60 @@ struct V2erApp: App {
public static var rootViewController: UIViewController?
public static var statusBarState: UIStatusBarStyle = .darkContent
public static var window: UIWindow?
+ @StateObject private var store = Store.shared
init() {
setupApperance()
}
private func setupApperance() {
+ // Navigation bar appearance will be set dynamically based on color scheme
+ let navAppearance = UINavigationBar.appearance()
+ navAppearance.isTranslucent = true
+ }
+
+ var body: some Scene {
+ WindowGroup {
+ RootView {
+ RootHostView()
+ .environmentObject(store)
+ .preferredColorScheme(store.appState.settingState.appearance.colorScheme)
+ .onAppear {
+ updateNavigationBarAppearance(for: store.appState.settingState.appearance)
+ }
+ .onChange(of: store.appState.settingState.appearance) { newValue in
+ updateNavigationBarAppearance(for: newValue)
+ }
+ }
+ }
+ }
+
+ private func updateNavigationBarAppearance(for appearance: AppearanceMode) {
let navbarAppearance = UINavigationBarAppearance()
- let tintColor = UIColor.black
+
+ // Determine if we should use dark mode
+ let isDarkMode: Bool
+ switch appearance {
+ case .light:
+ isDarkMode = false
+ case .dark:
+ isDarkMode = true
+ case .system:
+ isDarkMode = UITraitCollection.current.userInterfaceStyle == .dark
+ }
+
+ let tintColor = isDarkMode ? UIColor.white : UIColor.black
navbarAppearance.titleTextAttributes = [.foregroundColor: tintColor]
navbarAppearance.largeTitleTextAttributes = [.foregroundColor: tintColor]
navbarAppearance.backgroundColor = .clear
let navAppearance = UINavigationBar.appearance()
- navAppearance.isTranslucent = true
navAppearance.standardAppearance = navbarAppearance
navAppearance.compactAppearance = navbarAppearance
navAppearance.scrollEdgeAppearance = navbarAppearance
navAppearance.backgroundColor = .clear
navAppearance.tintColor = tintColor
}
-
- var body: some Scene {
- WindowGroup {
- RootView {
- RootHostView()
- .environmentObject(Store.shared)
- }
- }
- }
static func changeStatusBarStyle(_ style: UIStatusBarStyle) {
guard style != statusBarState else { return }
diff --git a/V2er/Info.plist b/V2er/Info.plist
index bf7762a..df25606 100644
--- a/V2er/Info.plist
+++ b/V2er/Info.plist
@@ -54,8 +54,6 @@
UIInterfaceOrientationLandscapeLeft
UIInterfaceOrientationLandscapeRight
- UIUserInterfaceStyle
- Light
UIViewControllerBasedStatusBarAppearance
diff --git a/V2er/State/DataFlow/Reducers/SettingReducer.swift b/V2er/State/DataFlow/Reducers/SettingReducer.swift
index 631fec2..ec3613e 100644
--- a/V2er/State/DataFlow/Reducers/SettingReducer.swift
+++ b/V2er/State/DataFlow/Reducers/SettingReducer.swift
@@ -9,7 +9,20 @@
import Foundation
func settingStateReducer(_ state: SettingState, _ action: Action) -> (SettingState, Action?) {
- return (SettingState(), nil)
+ var state = state
+ var followingAction: Action? = action
+
+ switch action {
+ case let action as SettingActions.ChangeAppearanceAction:
+ state.appearance = action.appearance
+ // Save to UserDefaults
+ UserDefaults.standard.set(action.appearance.rawValue, forKey: "appearanceMode")
+ followingAction = nil
+ default:
+ break
+ }
+
+ return (state, followingAction)
}
diff --git a/V2er/State/DataFlow/State/SettingState.swift b/V2er/State/DataFlow/State/SettingState.swift
index 8070bc2..29b6e58 100644
--- a/V2er/State/DataFlow/State/SettingState.swift
+++ b/V2er/State/DataFlow/State/SettingState.swift
@@ -7,7 +7,54 @@
//
import Foundation
+import SwiftUI
struct SettingState: FluxState {
+ var appearance: AppearanceMode = .system
+ init() {
+ // Load saved preference
+ if let savedMode = UserDefaults.standard.string(forKey: "appearanceMode"),
+ let mode = AppearanceMode(rawValue: savedMode) {
+ self.appearance = mode
+ }
+ }
}
+
+enum AppearanceMode: String, CaseIterable {
+ case light = "light"
+ case dark = "dark"
+ case system = "system"
+
+ var displayName: String {
+ switch self {
+ case .light:
+ return "浅色"
+ case .dark:
+ return "深色"
+ case .system:
+ return "跟随系统"
+ }
+ }
+
+ var colorScheme: ColorScheme? {
+ switch self {
+ case .light:
+ return .light
+ case .dark:
+ return .dark
+ case .system:
+ return nil
+ }
+ }
+}
+
+// Temporary: Define actions here until SettingActions.swift is properly added to project
+struct SettingActions {
+ private static let R: Reducer = .setting
+
+ struct ChangeAppearanceAction: Action {
+ var target: Reducer = R
+ let appearance: AppearanceMode
+ }
+}
\ No newline at end of file
diff --git a/V2er/View/Feed/FeedItemView.swift b/V2er/View/Feed/FeedItemView.swift
index 8fc602b..0318d53 100644
--- a/V2er/View/Feed/FeedItemView.swift
+++ b/V2er/View/Feed/FeedItemView.swift
@@ -37,6 +37,7 @@ struct FeedItemView: View {
.padding(.vertical, 4)
Text("评论\(data.replyNum.safe)")
.font(.footnote)
+ .foregroundColor(.secondaryText)
.greedyWidth(.trailing)
}
.padding(12)
diff --git a/V2er/View/Login/LoginPage.swift b/V2er/View/Login/LoginPage.swift
index fe08e1a..5fc0108 100644
--- a/V2er/View/Login/LoginPage.swift
+++ b/V2er/View/Login/LoginPage.swift
@@ -91,8 +91,8 @@ struct LoginPage: StateView {
.submitLabel(.continue)
.padding(.horizontal, padding)
.frame(maxWidth: .infinity, maxHeight: height)
- Color.gray
- .opacity(0.2)
+ Color.separator
+ .opacity(0.5)
.padding(.vertical, 14)
.frame(width: 1.5, height: height)
.padding(.horizontal, 2)
@@ -116,8 +116,8 @@ struct LoginPage: StateView {
.submitLabel(.go)
.keyboardType(.asciiCapable)
.disableAutocorrection(true)
- Color.gray
- .opacity(0.2)
+ Color.separator
+ .opacity(0.5)
.padding(.vertical, 14)
.frame(width: 1.5, height: height)
.padding(.horizontal, 2)
@@ -152,7 +152,7 @@ struct LoginPage: StateView {
} label: {
Text("Login")
.font(.headline)
- .foregroundColor(.white)
+ .foregroundColor(Color.itemBackground)
.padding()
.greedyWidth()
.background(Color.tintColor)
diff --git a/V2er/View/Me/MePage.swift b/V2er/View/Me/MePage.swift
index 0732387..336f01a 100644
--- a/V2er/View/Me/MePage.swift
+++ b/V2er/View/Me/MePage.swift
@@ -31,17 +31,17 @@ struct MePage: BaseHomePageView {
if !AccountState.hasSignIn() {
VStack {
Text("登录查看更多")
- .foregroundColor(.white)
+ .foregroundColor(.primaryText)
.font(.title2)
Button {
dispatch(LoginActions.ShowLoginPageAction())
} label: {
Text("登录")
.font(.headline)
- .foregroundColor(.white)
+ .foregroundColor(Color.itemBackground)
.padding()
.padding(.horizontal, 50)
- .background(Color.black)
+ .background(Color.tintColor)
.cornerRadius(15)
}
}
@@ -71,12 +71,12 @@ struct MePage: BaseHomePageView {
.foregroundColor(Color.bodyText)
Image(systemName: "chevron.right")
.font(.body.weight(.regular))
- .foregroundColor(Color.gray)
+ .foregroundColor(Color.secondaryText)
}
}
.padding(.horizontal, 12)
.padding(.vertical, 16)
- .background(.white)
+ .background(Color.itemBackground)
.padding(.bottom, 8)
.to {
UserDetailPage(userId: AccountState.userName)
diff --git a/V2er/View/Me/UserDetailPage.swift b/V2er/View/Me/UserDetailPage.swift
index 1a9b225..536df15 100644
--- a/V2er/View/Me/UserDetailPage.swift
+++ b/V2er/View/Me/UserDetailPage.swift
@@ -42,7 +42,7 @@ struct UserDetailPage: StateView {
}
var foreGroundColor: SwiftUI.Color {
- return shouldHideNavbar ? .white.opacity(0.9) : .tintColor
+ return shouldHideNavbar ? Color.primaryText.opacity(0.9) : .tintColor
}
var body: some View {
@@ -77,7 +77,7 @@ struct UserDetailPage: StateView {
.fade(duration: 0.25)
.resizable()
.blur(radius: 80, opaque: true)
- .overlay(Color.black.opacity(withAnimation {shouldHideNavbar ? 0.3 : 0.1}))
+ .overlay(Color.dynamic(light: .black, dark: .white).opacity(withAnimation {shouldHideNavbar ? 0.3 : 0.1}))
.frame(maxWidth: .infinity, maxHeight: height)
Spacer().background(.clear)
}
@@ -172,7 +172,7 @@ struct UserDetailPage: StateView {
AvatarView(url: model.avatar, size: heightOfNodeImage)
HStack(alignment: .center,spacing: 4) {
Circle()
- .fill(state.model.isOnline ? .green : .gray)
+ .fill(state.model.isOnline ? .green : Color.secondaryText)
.frame(width: 8, height: 8)
Text(model.userName)
.font(.headline.weight(.semibold))
@@ -203,7 +203,7 @@ struct UserDetailPage: StateView {
.background(Color.lightGray, in: RoundedRectangle(cornerRadius: 10))
.padding(.horizontal, 12)
.padding(.vertical, 10)
- .background(.white)
+ .background(Color.itemBackground)
.clipCorner(12, corners: [.topLeft, .topRight])
}
@@ -298,7 +298,7 @@ struct UserDetailPage: StateView {
NavigationLink(destination: TagDetailPage()) {
Text(data.tag)
.font(.footnote)
- .foregroundColor(.black)
+ .foregroundColor(Color.primaryText)
.lineLimit(1)
.padding(.horizontal, 14)
.padding(.vertical, 8)
@@ -339,14 +339,14 @@ struct UserDetailPage: StateView {
} label: {
Text(title)
.fontWeight(.bold)
- .foregroundColor(isSelected ? .white.opacity(0.9) : .tintColor)
+ .foregroundColor(isSelected ? Color.itemBackground.opacity(0.9) : .tintColor)
.frame(maxWidth: .infinity)
.padding(.vertical, 8)
.background {
VStack {
if isSelected {
RoundedRectangle(cornerRadius: 10)
- .fill(.black)
+ .fill(Color.primaryText)
.matchedGeometryEffect(id: "TAB", in: animation)
}
}
diff --git a/V2er/View/Settings/AppearanceSettingView.swift b/V2er/View/Settings/AppearanceSettingView.swift
index 095b0aa..a531739 100644
--- a/V2er/View/Settings/AppearanceSettingView.swift
+++ b/V2er/View/Settings/AppearanceSettingView.swift
@@ -9,22 +9,91 @@
import SwiftUI
struct AppearanceSettingView: View {
+ @EnvironmentObject private var store: Store
+ @State private var selectedAppearance: AppearanceMode = .system
+
var body: some View {
formView
.navBar("外观设置")
+ .onAppear {
+ selectedAppearance = store.appState.settingState.appearance
+ }
}
@ViewBuilder
private var formView: some View {
ScrollView {
- SectionItemView("字体大小")
-// .to {}
+ VStack(spacing: 0) {
+ // Dark Mode Section
+ VStack(alignment: .leading, spacing: 12) {
+ Text("主题")
+ .font(.caption)
+ .foregroundColor(.secondaryText)
+ .padding(.horizontal)
+ .padding(.top, 8)
+
+ VStack(spacing: 0) {
+ ForEach(AppearanceMode.allCases, id: \.self) { mode in
+ Button(action: {
+ selectedAppearance = mode
+ dispatch(SettingActions.ChangeAppearanceAction(appearance: mode))
+ }) {
+ HStack {
+ Text(mode.displayName)
+ .foregroundColor(.primaryText)
+ Spacer()
+ if selectedAppearance == mode {
+ Image(systemName: "checkmark")
+ .foregroundColor(.tintColor)
+ .font(.system(size: 14, weight: .semibold))
+ }
+ }
+ .padding(.horizontal)
+ .padding(.vertical, 12)
+ .background(Color.itemBackground)
+ }
+
+ if mode != AppearanceMode.allCases.last {
+ Divider()
+ .padding(.leading)
+ }
+ }
+ }
+ .background(Color.itemBackground)
+ .cornerRadius(10)
+ .padding(.horizontal)
+ }
+ .padding(.top)
+
+ // Font Size Section
+ VStack(alignment: .leading, spacing: 12) {
+ Text("字体")
+ .font(.caption)
+ .foregroundColor(.secondaryText)
+ .padding(.horizontal)
+ .padding(.top, 24)
+
+ SectionItemView("字体大小")
+ .background(Color.itemBackground)
+ .cornerRadius(10)
+ .padding(.horizontal)
+ }
+ }
}
+ .background(Color.background.ignoresSafeArea())
}
}
struct AppearanceSettingView_Previews: PreviewProvider {
static var previews: some View {
- AppearanceSettingView()
+ Group {
+ AppearanceSettingView()
+ .environmentObject(Store.shared)
+ .environment(\.colorScheme, .light)
+
+ AppearanceSettingView()
+ .environmentObject(Store.shared)
+ .environment(\.colorScheme, .dark)
+ }
}
-}
+}
\ No newline at end of file
diff --git a/V2er/View/Settings/SettingsPage.swift b/V2er/View/Settings/SettingsPage.swift
index 5ab607a..6bd932c 100644
--- a/V2er/View/Settings/SettingsPage.swift
+++ b/V2er/View/Settings/SettingsPage.swift
@@ -22,8 +22,11 @@ struct SettingsPage: View {
private var formView: some View {
ScrollView {
VStack(spacing: 0) {
- SectionItemView("通用设置", showDivider: false)
+ SectionItemView("外观设置", showDivider: false)
.padding(.top, 8)
+ .to { AppearanceSettingView() }
+
+ SectionItemView("通用设置")
.to { OtherSettingsView() }
SectionItemView("帮助与反馈")
diff --git a/V2er/View/Tag/TagDetailPage.swift b/V2er/View/Tag/TagDetailPage.swift
index dcd5aa7..4f00162 100644
--- a/V2er/View/Tag/TagDetailPage.swift
+++ b/V2er/View/Tag/TagDetailPage.swift
@@ -40,7 +40,7 @@ struct TagDetailPage: StateView, InstanceIdentifiable {
}
private var foreGroundColor: Color {
- shouldHideNavbar ? .white.opacity(0.9) : .tintColor
+ shouldHideNavbar ? Color.primaryText.opacity(0.9) : .tintColor
}
private var statusBarStyle: UIStatusBarStyle {
@@ -77,7 +77,7 @@ struct TagDetailPage: StateView, InstanceIdentifiable {
.fade(duration: 0.25)
.resizable()
.blur(radius: 80, opaque: true)
- .overlay(Color.black.opacity(withAnimation {shouldHideNavbar ? 0.3 : 0.1}))
+ .overlay(Color.dynamic(light: .black, dark: .white).opacity(withAnimation {shouldHideNavbar ? 0.3 : 0.1}))
.frame(height: bannerViewHeight * 1.2 + max(scrollY, 0))
Spacer()
}
@@ -206,7 +206,7 @@ struct TagDetailPage: StateView, InstanceIdentifiable {
}
}
}
- .background(.white)
+ .background(Color.itemBackground)
.clipCorner(12, corners: [.topLeft, .topRight])
}
diff --git a/V2er/View/Widget/NodeView.swift b/V2er/View/Widget/NodeView.swift
index 6c68252..19f8641 100644
--- a/V2er/View/Widget/NodeView.swift
+++ b/V2er/View/Widget/NodeView.swift
@@ -25,11 +25,11 @@ struct NodeView: View {
} label: {
Text(name)
.font(.footnote)
- .foregroundColor(.black)
+ .foregroundColor(Color.dynamic(light: .hex(0x666666), dark: .hex(0xCCCCCC)))
.lineLimit(1)
.padding(.horizontal, 14)
.padding(.vertical, 8)
- .background(Color.lightGray)
+ .background(Color.dynamic(light: Color.hex(0xF5F5F5), dark: Color.hex(0x2C2C2E)))
}
}
diff --git a/V2er/View/Widget/RichTextView/Webview.swift b/V2er/View/Widget/RichTextView/Webview.swift
index b592ea0..131f466 100644
--- a/V2er/View/Widget/RichTextView/Webview.swift
+++ b/V2er/View/Widget/RichTextView/Webview.swift
@@ -111,7 +111,7 @@ struct Webview : UIViewRepresentable {
return """
@@ -121,7 +121,7 @@ struct Webview : UIViewRepresentable {
return """
@@ -132,12 +132,12 @@ struct Webview : UIViewRepresentable {
diff --git a/V2er/View/Widget/SectionItemView.swift b/V2er/View/Widget/SectionItemView.swift
index 5fdec03..9688df9 100644
--- a/V2er/View/Widget/SectionItemView.swift
+++ b/V2er/View/Widget/SectionItemView.swift
@@ -27,7 +27,7 @@ struct SectionItemView: View {
SectionView(title, icon: icon, showDivider: showDivider) {
Image(systemName: "chevron.right")
.font(.body.weight(.regular))
- .foregroundColor(.gray)
+ .foregroundColor(.secondaryText)
.padding(.trailing, paddingH)
}
}
@@ -65,6 +65,6 @@ struct SectionView: View {
.padding(.vertical, 17)
.divider(showDivider ? 0.8 : 0.0)
}
- .background(.white)
+ .background(Color.itemBackground)
}
}
diff --git a/V2er/View/Widget/TabBar.swift b/V2er/View/Widget/TabBar.swift
index 889aab5..35a76e9 100644
--- a/V2er/View/Widget/TabBar.swift
+++ b/V2er/View/Widget/TabBar.swift
@@ -76,7 +76,7 @@ struct TabBar: View {
VStack {
Text(num.string)
.font(.system(size: 10))
- .foregroundColor(.white)
+ .foregroundColor(Color.itemBackground)
.padding(4)
.background {
Circle()
diff --git a/V2er/View/Widget/Toast.swift b/V2er/View/Widget/Toast.swift
index 463e48f..de9221f 100644
--- a/V2er/View/Widget/Toast.swift
+++ b/V2er/View/Widget/Toast.swift
@@ -58,9 +58,9 @@ extension View {
self
if isPresented.wrappedValue {
content()
- .visualBlur(bg: .white.opacity(0.95))
+ .visualBlur(bg: Color.itemBackground.opacity(0.95))
.cornerRadius(99)
- .shadow(color: .black.opacity(0.2), radius: 1.5)
+ .shadow(color: Color.primaryText.opacity(0.2), radius: 1.5)
.padding(.top, paddingTop)
.transition(AnyTransition.move(edge: .top))
.zIndex(1)
diff --git a/website b/website
index c3bbc04..b5b4f70 160000
--- a/website
+++ b/website
@@ -1 +1 @@
-Subproject commit c3bbc04bb0d0c1f262b1b6e28a464c8b1144e199
+Subproject commit b5b4f70430003ea84edf40eaea25dc484076505a