说说最近对于 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