设计目标
与objective-c的DKNightMode相同,对于当前控件实现背景色,字体颜色等在不同模式下的颜色设置和变更,同时提供模式变更的闭包调用
实现原理
基础设计
NSObject的扩展
动态属性notificaitonManager
- 该设计主要的目的是实现隐式的NSNotificationCenter的注册和去注册,通过observer weak持有self,避免循环引用;利用selectorToBoolMap确保selector的有效性;析构时隐式确保self从notificationCenter中移除
- 通过addNightObserver,实现NSNotificaitonCenter的注册和selector有效性性检查.
- NightNightThemeChangeNotification是每次theme发生变化时的notificaiton通知逻辑
private class NotificationManager {
var selectorToBoolMap: [Selector: Bool] = [:]
weak var observer: NSObject?
init(observer: NSObject) {
self.observer = observer
}
deinit {
if let observer = observer {
NotificationCenter.default.removeObserver(observer)
}
}
}
extension NSObject {
fileprivate var notificationManager: NotificationManager {
get {
if let manager = objc_getAssociatedObject(self, ¬ificationManagerKey) as? NotificationManager {
return manager
}
self.notificationManager = NotificationManager(observer: self)
return self.notificationManager
}
set {
objc_setAssociatedObject(self, ¬ificationManagerKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
func addNightObserver(_ selector: Selector) {
if let bool = notificationManager.selectorToBoolMap[selector] {
if bool {
return
}
} else {
notificationManager.selectorToBoolMap[selector] = true
}
NotificationCenter.default.addObserver(self, selector: selector, name: NSNotification.Name(rawValue: NightNightThemeChangeNotification), object: nil)
}
}
动态属性key对应的颜色值
func getMixedColor(_ key: UnsafeRawPointer) -> MixedColor? {
return objc_getAssociatedObject(self, key) as? MixedColor
}
func setMixedColor(_ key: UnsafeRawPointer, value: MixedColor?) {
objc_setAssociatedObject(self, key, value, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
addNightObserver(#selector(_updateTheme))
}
动态属性key对应的颜色值保存,在设置时注册notificationManager及对应的updateTheme函数的调用。
updateTheme和_updateCurrentStatus
实现updateTheme是所有NSObject对应的响应通知的入口,而_updateCurrentStatus则是各种不同控件在响应时的独立行为,所有需要响应的控件都有必要实现该方法的继承。例如:
public extension UITableView {
override func _updateCurrentStatus() {
super._updateCurrentStatus()
if let mixedSeparatorColor = mixedSeparatorColor {
separatorColor = mixedSeparatorColor.unfold()
}
if let mixedSectionIndexBackgroundColor = mixedSectionIndexBackgroundColor {
sectionIndexBackgroundColor = mixedSectionIndexBackgroundColor.unfold()
}
}
}
public extension UIView {
override func _updateCurrentStatus() {
super._updateCurrentStatus()
if let mixedBackgroundColor = mixedBackgroundColor {
backgroundColor = mixedBackgroundColor.unfold()
}
if let mixedTintColor = mixedTintColor {
tintColor = mixedTintColor.unfold()
}
}
}
动态闭包Customize
该设计主要是提供在模式转换下的闭包调用,在这个层面上不知道为何作者并没有开放成public方法,所以私下里我对这个逻辑进行重新整理
open class Customize: NSObject {
fileprivate var closures: [(NightNight.Theme) -> ()] = []
fileprivate weak var correspondingObject: NSObject?
fileprivate convenience init(obj: NSObject) {
self.init()
self.correspondingObject = obj
NotificationCenter.default.addObserver(self, selector: #selector(_callAllExistingClosures), name: NSNotification.Name(rawValue: NightNightThemeChangeNotification), object: nil)
}
@objc func _callAllExistingClosures() {
closures.forEach {
$0(NightNight.theme)
}
}
}
通过扩展属性customiz,向所有空间提供模式变更下的闭包调用。
各类型控件的颜色扩展
添加关联的属性字,实现多模式下颜色值的保存,实现_updateCurrentState方法
public extension UIView {
var mixedBackgroundColor: MixedColor? {
get { return getMixedColor(&Keys.backgroundColor) }
set {
backgroundColor = newValue?.unfold()
setMixedColor(&Keys.backgroundColor, value: newValue)
}
}
var mixedTintColor: MixedColor? {
get { return getMixedColor(&Keys.tintColor) }
set {
tintColor = newValue?.unfold()
setMixedColor(&Keys.tintColor, value: newValue)
}
}
override func _updateCurrentStatus() {
super._updateCurrentStatus()
if let mixedBackgroundColor = mixedBackgroundColor {
backgroundColor = mixedBackgroundColor.unfold()
}
if let mixedTintColor = mixedTintColor {
tintColor = mixedTintColor.unfold()
}
}
}
模式切换的各资源类型
- 利用模板实现有关模式切换的类型
public class MixedResource<T> {
public let normalResource: T
public let nightResource: T
public init(normal: T, night: T) {
normalResource = normal
nightResource = night
}
public func unfold() -> T {
switch NightNight.theme {
case .normal: return normalResource
case .night: return nightResource
}
}
}
- UIColor,UIImage, UIStatusBarStyle,UIBarStyle,UIKeyboardAppearance
public class MixedImage: MixedResource<UIImage> {
public override init(normal: UIImage, night: UIImage) {
super.init(normal: normal, night: night)
}
public convenience init(normal: String, night: String) {
self.init(normal: UIImage(named: normal)!, night: UIImage(named: night)!)
}
}
public class MixedColor: MixedResource<UIColor> {
public override init(normal: UIColor, night: UIColor) {
super.init(normal: normal, night: night)
}
public init(normal: Int, night: Int) {
let normalColor = UIColor(rgb: normal)
let nightColor = UIColor(rgb: night)
super.init(normal: normalColor, night: nightColor)
}
}
该设计提供更多弹性增加可在模式变更下切换的资源类文章来源:https://www.toymoban.com/news/detail-796974.html
GitHub地址
github地址文章来源地址https://www.toymoban.com/news/detail-796974.html
到了这里,关于swift NightNight夜间模式设计分析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!