实现方式有四个重点:
1.定义一个协议,协议里规定了实现主题颜色的各种方法。
2.定义一个主题类,包含两个关键属性style和colors,style表示对模式变化的监听,colors则可以根据style返回对应的颜色类。
3.扩展NSObject,定义一个闭包,可以在style发生变化的时候被调用,同来通知全局主题发生变化。
4.分别定义颜色类:夜间类和白天类。
定义协议ThemeColorProtocol:
protocol ThemeColorProtocol {
var themeBackgroundColor: UIColor { get }
var themeNavigationBarColor: UIColor { get }
}
说明:采用协议的类必须要实现协议的方法,也就是各种主题色。
定义两个主题类,都采用协议ThemeColorProtocol:
DarkColor(夜间模式)
/// 夜间颜色
class DarkColor: NSObject, ThemeColorProtocol {
static let sharedInstance = DarkColor()
fileprivate override init() {
super.init()
}
var themeBackgroundColor: UIColor {
get {
return UIColor.black
}
}
var themeNavigationBarColor: UIColor {
return UIColor.red
}
}
DefaultColor(日间模式)
/// 默认颜色
class DefaultColor: NSObject, ThemeColorProtocol {
static let sharedInstance = DefaultColor()
fileprivate override init() {
super.init()
}
var themeBackgroundColor: UIColor {
get {
return UIColor.white
}
}
var themeNavigationBarColor: UIColor {
get {
return UIColor.white
}
}
}
创建主题颜色类ThemeColor:
class ThemeColor: NSObject {
static let DarkThemeString = "Dark"
static let DefaultThemeString = "Default"
static let sharedInstance = ThemeColor()
fileprivate override init() {
super.init()
}
static var colors: ThemeColorProtocol {
get {
if ThemeColor().style == ThemeColor.DarkThemeString {
return DarkColor()
}
else {
return DefaultColor()
}
}
}
var style: String {
get {
//默认情况下放回default模式
if let style = UserDefaults.standard.object(forKey: "StyleKey") {
return style as! String
}
else {
return ThemeColor.DefaultThemeString
}
}
set {
UserDefaults.standard.set(newValue, forKey: "StyleKey")
NotificationCenter.default.post(name: NSNotification.Name(rawValue: THEME_CHANGE_NOTIFICAION), object: nil)//style的值发生变化就发出通知。这样我们在设置里改变主题模式时会马上让APP变化。
}
}
}
说明:这是个单例类(沿用V2的单例模式,其实不用也没事),有两个关键属性colors和style。
- colors是ThemeColorProtocol类型的,任何采用了ThemeColorProtocol协议的实例都可以赋值给colors属性,所以这里根据style的值就可以返回对应的颜色实例(黑夜类实例或者白天类实例)。所以我们设置背景的时候可以直接通过colors属性调用协议里对应的颜色属性,如:
//不用这么判断
if ThemeColor.sharedInstance.style == ThemeColor.DarkThemeString {
self.view.backgroundColor = ThemeColor.colors.themeBackgroundColor
}
//直接写就行了
self.view.backgroundColor = ThemeColor.colors.themeBackgroundColor
- style有两个值DarkThemeString和DefaultThemeString,分别对应黑夜模式和白天模式。get方法会从userDefaults里读取StyleKey关键字的值,如果为nil则返回默认值DefaultThemeString。set方法会把设置的新值存到userDefault,同时发出style值变化的通知(这里style值前后是否相同都会发出通知)
通知全局:
我们使用colors的属性有两种情况,一种是我们主动设置颜色时,如代码
self.view.backgroundColor = ThemeColor.colors.themeBackgroundColor
另外一种是style发生变化时,通知全局变化。一般我们可以使用kvo监听style变化,然后在回调里再次执行上面这句代码,但是为了方便,我扩展NSObject定义了一个闭包函数
let THEME_CHANGE_NOTIFICAION = "thmemChangedNotificaion"
//MARK: - 主题更改时,自动执行
extension NSObject {
fileprivate struct AssociatedKeys {
static var thmemChanged = "thmemChanged"
}
/// 当前主题更改时、第一次设置时 自动调用的闭包
public typealias ThemeChangedClosure = @convention(block) (_ style:String) -> Void
/// 自动调用的闭包
/// 设置时,会注册一个通知,当ThemeColor的style关键字变化时,会发送这里注册的通知,同时执行selector对应的函数。
var thmemChangedHandler:ThemeChangedClosure? {
get {
let closureObject: AnyObject? = objc_getAssociatedObject(self, &AssociatedKeys.thmemChanged) as AnyObject?
guard closureObject != nil else{
return nil
}
let closure = unsafeBitCast(closureObject, to: ThemeChangedClosure.self)
return closure
}
set{
guard let value = newValue else{
return
}
let dealObject: AnyObject = unsafeBitCast(value, to: AnyObject.self)
objc_setAssociatedObject(self, &AssociatedKeys.thmemChanged,dealObject,objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
NotificationCenter.default.addObserver(self, selector: #selector(themeChangeNotification), name:NSNotification.Name(rawValue: THEME_CHANGE_NOTIFICAION) , object: nil)
self.themeChangeNotification()//默认执行一次,为了设置初始颜色
}
}
func themeChangeNotification() {
if let closure = self.thmemChangedHandler {
closure(ThemeColor().style)
}
}
}
说明:使用runtime把每个调用了闭包的实例注册通知,并在调用闭包的同时就执行一次闭包,用来设置初始颜色。