說說最近對於 iOS 系統黑暗主題適配(兼容iOS 13 如下版本)的方案研究.git
iOS 13
開始 Apple 在系統層面支持了黑暗模式, 如今不少 App 也都支持了黑暗模式. 也有關於黑暗模式的不少成熟的開源實現方案, 按道理我沒有必要再去本身實現一套了. 可是在調查了相關方案實現後我發現仍是有一種更輕量, 代碼侵入更小, 更符合 Apple 風格並且學習及遷移成本都很小的方案.github
這套方案的核心思想是很是簡單的:swift
iOS 13
及以上返回系統支持的動態顏色 init(dynamicProvider: @escaping (UITraitCollection) -> UIColor)
,iOS 13
及以上版本直接設置 window
的 overrideUserInterfaceStyle
window
的 rootViewController
具體代碼實現以下api
在這一步咱們設置須要支持的主題數量markdown
enum Theme: Int, CaseIterable {
case none = 0
case light = 1
case dark = 2
var title: String {
switch self {
case .none: return "Follow"
case .light: return "Light"
case .dark: return "Dark"
}
}
@available(iOS 13.0, *)
var mode: UIUserInterfaceStyle {
switch self {
case .none: return .unspecified
case .light: return .light
case .dark: return .dark
}
}
}
複製代碼
class Tools {
@UserDefaultStorage(keyName: "appTheme")
static var _style: Int? // 此處用於全局存儲 UserDefaults 屬性
static var style: Theme {
get { return Theme(rawValue: (_style ?? 0)) ?? .dark }
set { _style = newValue.rawValue }
}
/// 創造顏色, 核心方法
static func makeColor(light: UIColor, dark: UIColor) -> UIColor {
if #available(iOS 13.0, *) {
return UIColor { $0.userInterfaceStyle == .light ? light : dark }
} else {
return Tools.style == .light ? light : dark
}
}
/// 創造 img, 核心方法
static func makeImage(light: UIImage, dark: UIImage) -> UIImage {
if #available(iOS 13.0, *) {
let image = UIImage()
image.imageAsset?.register(light, with: .init(userInterfaceStyle: .light))
image.imageAsset?.register(dark, with: .init(userInterfaceStyle: .dark))
return image
} else {
return Tools.style == .light ? light : dark
}
}
複製代碼
func changeTheme(theme: Theme) {
Tools.style = theme
guard let window = UIWindow.hl.getKeyWindow() else { return }
if #available(iOS 13.0, *) {
window.overrideUserInterfaceStyle = theme.mode
} else {
guard let rootVC = window.rootViewController else { return }
let tabbar = Tools.setTabVC(withIndex: self.index)
window.rootViewController = tabbar
}
}
複製代碼
style
屬性不少產品的要求是能夠自由切換黑暗與白天模式, 在使用自定義模式的時候就不跟隨系統了, 所以若是有這樣的需求的話就須要在啓動時進行判斷並設置app
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?)
-> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
// 若是是 iOS
if #available(iOS 13.0, *) {
window?.overrideUserInterfaceStyle = Tools.style.mode
}
window?.rootViewController = Tools.getTabVC(withIndex: 0)
window?.makeKeyAndVisible()
return true
}
複製代碼
在主題調研的這幾天內也看了一些開源實現的第三方庫, 說說對於這幾個庫的見解吧, 也可讓之後人少走點彎路ide
RxSwift
.Rx
的綁定創建關係, 每一個 view 的顏色的綁定關係有該 view 進行持有, 在收到信號後進行改變主題顏色SwiftTheme
是一個很經典的 Swift 語言寫的主題方案了.oop
NSObject
擴展出一個 Dictionary
存儲屬性, 將全部的設置過的控件存入其中, 而後在用戶觸發開關後以 Notification
的方式進行 notify, 收到 Notification
後對Dictionary
進行遍歷, 對其中的每一個控件的每一個屬性進行判斷而後從新賦值view
數量級較多狀況下性能可能會存在問題基於對上面幾種開源主流方案的對比, 本方案有着如下的優缺點:組件化
優勢性能
缺點
rootViewController
的方案了)traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?)
方法iOS 12
及如下系統切換時須要重置 rootViewController
, 所以全部 vc 都會被釋放掉再重建, 即若是其餘頁面有正在操做的邏輯, 那麼就會丟失現場總的來講, 本方案至關於提供了一種實現主題的精簡型方案.
Talk is cheap, show me the code!
基於本文思路的實現 Demo: github.com/HanleyLee/D…
本文做者 Hanley Lee, 首發於 閃耀旅途, 若是對本文比較承認, 歡迎 Follow