SwiftUI 開發之旅:適配深色模式

語言: CN / TW / HK

theme: smartblue

持續創作,加速成長!這是我參與「掘金日新計劃 · 10 月更文挑戰」的第1天,點選檢視活動詳情

大家好,我是 new_cheng

我們在開發 swiftui 的時候,雖然 swiftui 預設支援深色模式,對於一些基本的檢視,比如文字,背景都有預設的深色模式樣式。

但是為了提高使用者體驗,一般還需要適配深色模式。而對於深色模式的適配,不應在開發完應用後,再去考慮往應用中加入深色模式,應該在開發的初期就做好規劃,在開發過程中就可以邊開發邊測試深色模式的顯示效果。

那既然要適配深色模式,在 swiftui 中,我們該採用什麼方式來做呢?

有 2 種常用的的編碼方案可以去適配深色模式。

1. UIColor 適配深色模式

可以通過擴充套件 UIColor 的方式來使用顏色,我們只需要簡單的橋接 UIColor。

```swift extension UIColor { convenience init(light: UIColor, dark: UIColor) { self.init { traitCollection in switch traitCollection.userInterfaceStyle { case .light, .unspecified: return light case .dark: return dark @unknown default: return light } } } }

extension Color { // 再定義一個顏色 static let defaultBackground = Color(light: .white, dark: .black)

init(light: Color, dark: Color) { self.init(UIColor(light: UIColor(light), dark: UIColor(dark))) } } ```

swift // 使用 Text("文字").foregroundColor(Color.defaultBackground)

2. 使用 Assets.xcassets 適配深色模式

Assets.xcassets 允許我們定義 Color Set,一個 Color Set 包含深色模式和淺色模式所顯示的顏色,我們可以定義多個 Color Set,比如文字,背景,圖表等等;通過在 UI 中使用不同的顏色就能實現深色模式的適配,下面來看看具體的操作。

定義 Color Set

在 swiftui 專案選擇 -> Assets.xcassets -> 右鍵 -> New Color Se -> 設定名稱

image.png

左邊的顏色(Any Appearance)預設就是淺色模式顯示的顏色,右邊如圖所示就是深色模式顯示的顏色。

選擇顏色,我們就可以設定顏色的具體色值,Xcode 提供了幾種顏色的設定方式,這裡選擇 RGB 即可。

image.png

在這裡,我們定義了一個名為 textColor 的 Color Set,下一步我們會將其應用到程式碼中。

擴充套件 Color

為了更方便的使用的我們定義的顏色集,我們可以擴充套件 Color。

建立一個名為 Color.swift 的檔案:

```swift import Foundation import SwiftUI

public extension Color {

static let textColor = Color("textColor")

} ```

對於使用 Color 作為引數的修飾符,比如 foregroundColor,background,我們就可以很方便的直接通過顏色名稱來使用:

```swift import SwiftUI

struct DetailHome: View { var body: some View { Text("姓名").foregroundColor(.textColor) } } ```

xcode 會提供程式碼編寫提示。

在 xcode 的預覽中,將顏色模式設定為 dark appearance,就能檢視效果。

image.png

3. 固定的顏色模式

當我們不想適配深色模式,我們可以鎖死顏色模式,不管系統當前的設定是深色模式還是淺色模式,我們都只固定使用某一種顏色模式。

修改專案的 info.plist 檔案,新增一條 Appearance 配置,value 為 Dark,App 將會預設使用深色模式,value 為 Light,將會預設使用淺色模式。

image.png

4. 編碼控制深色模式

當你希望在程式碼中細粒度的控制顏色時,可以通過 colorScheme 環境變數來實現。

```swift import SwiftUI

struct DetailHome: View { @Environment(.colorScheme) var colorScheme: ColorScheme

var body: some View {
    Text((colorScheme == .dark) ? "暗黑模式" : "淺色模式")
}

} ```

image.pngimage.png

5. 手動切換深色模式

想要讓使用者手動控制應用的顏色模式,我們可以提供一個設定頁面。

建立一個 AppSetting.swift 檔案,用於檢測和儲存當前應用顯示的顏色模式。

這裡我們用到了 UserDefaults,這是使用者預設資料庫的介面,一般用於儲存使用者資訊、App 設定等基礎資訊,但不宜用於儲存大量資料。

當用戶在系統、深色、淺色3個選項之間切換的時候,我們會獲取到對應的值並賦值給 darkModeSettings,從而去設定 window.overrideUserInterfaceStyle 來改變應用的顏色模式。

此方式只會影響 window 及其所有子檢視和子檢視控制器。

```swift import SwiftUI

class AppSetting: ObservableObject {     @Published var darkModeSettings: Int = UserDefaults.standard.integer(forKey: "darkMode") {         didSet {             UserDefaults.standard.set(self.darkModeSettings, forKey: "darkMode")             let scenes = UIApplication.shared.connectedScenes             let windowScene = scenes.first as? UIWindowScene             let window = windowScene?.windows.first             switch self.darkModeSettings {             case 0:                 window?.overrideUserInterfaceStyle = .unspecified             case 1:                 window?.overrideUserInterfaceStyle = .light             case 2:                 window?.overrideUserInterfaceStyle = .dark             default:                 window?.overrideUserInterfaceStyle = .unspecified             }         }     } } ```

使用者設定深色模式頁面:

```swift import SwiftUI

struct MyDarkModel: Identifiable {     let id: Int     let image: String     let name: String }

struct Test2: View {     @Environment(.colorScheme) var colorScheme: ColorScheme     @EnvironmentObject var appSettings: AppSetting          let darkModel: [MyDarkModel] = [         MyDarkModel(id: 0, image: "img_dark_auto", name: "系統"),         MyDarkModel(id: 1, image: "img_dark_off", name: "淺色"),         MyDarkModel(id: 2, image: "img_dark_on", name: "深色"),     ]

var body: some View {         VStack() {             List {                 Section {                     HStack(spacing: 0) {                         ForEach(darkModel) { item in                             Button (action: { // 點選按鈕時傳遞顏色模式的值                                 appSettings.darkModeSettings = item.id                             }) {                                 if appSettings.darkModeSettings == item.id {                                     Text(item.name)                                         .padding(.horizontal, 24)                                         .padding(.vertical, 12)                                         .foregroundColor( appSettings.darkModeSettings == item.id ? Color.white : Color.primary)                                     .background(appSettings.darkModeSettings == item.id ? Color.blue : Color.white)                                     .cornerRadius(20)                                 } else {                                     Text(item.name)                                         .padding(.horizontal, 24)                                         .padding(.vertical, 12)                                         .foregroundColor(Color.primary)                                 }

}                             .buttonStyle(BorderlessButtonStyle())                             if item.id < 2 {                                 Spacer()                             }                         }                     }                     .padding(.horizontal, 4)                     .padding(.vertical)                 }                 Text((colorScheme == .dark) ? "暗黑模式" : "淺色模式")             }         }     } }

struct Test2_Previews: PreviewProvider {     static var previews: some View {         Test2().environmentObject(AppSetting())     } } ```

image.pngimage.png

總結

通過本文,我們學會了如果去適配深色模式,掌握了 Color Set、overrideUserInterfaceStyle 等使用,還有如何支援使用者手動切換顏色模式。對於深色模式的適配,推薦採用 Assets.xcassets 的方式去定義一個完整的顏色集來適配。

這是 SwiftUI 開發之旅專欄的文章,是 swiftui 開發學習的經驗總結及實用技巧分享,歡迎關注該專欄,會堅持輸出。同時歡迎關注我的個人公眾號 @JSHub:提供最新的開發資訊速報,優質的技術乾貨推薦。或是檢視我的個人部落格:Devcursor

👍點贊:如果有收穫和幫助,請點個贊支援一下!

🌟收藏:歡迎收藏文章,隨時檢視!

💬評論:歡迎評論交流學習,共同進步!