NSObject+Theme.swift 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. //
  2. // NSObject+Theme.swift
  3. // SwiftTheme
  4. //
  5. // Created by Gesen on 16/1/22.
  6. // Copyright © 2016年 Gesen. All rights reserved.
  7. //
  8. import UIKit
  9. fileprivate typealias setCGColorValueIMP = @convention(c) (NSObject, Selector, CGColor) -> Void
  10. fileprivate typealias setCGFloatValueIMP = @convention(c) (NSObject, Selector, CGFloat) -> Void
  11. fileprivate typealias setValueForStateIMP = @convention(c) (NSObject, Selector, AnyObject, UIControl.State) -> Void
  12. fileprivate typealias setKeyboardValueIMP = @convention(c) (NSObject, Selector, UIKeyboardAppearance) -> Void
  13. fileprivate typealias setActivityStyleValueIMP = @convention(c) (NSObject, Selector, UIActivityIndicatorView.Style) -> Void
  14. fileprivate typealias setScrollStyleValueIMP = @convention(c) (NSObject, Selector, UIScrollView.IndicatorStyle) -> Void
  15. #if os(iOS)
  16. fileprivate typealias setBarStyleValueIMP = @convention(c) (NSObject, Selector, UIBarStyle) -> Void
  17. fileprivate typealias setStatusBarStyleValueIMP = @convention(c) (NSObject, Selector, UIStatusBarStyle, Bool) -> Void
  18. #endif
  19. extension NSObject {
  20. typealias ThemePickers = [String: ThemePicker]
  21. var themePickers: ThemePickers {
  22. get {
  23. if let themePickers = objc_getAssociatedObject(self, &themePickersKey) as? ThemePickers {
  24. return themePickers
  25. }
  26. let initValue = ThemePickers()
  27. objc_setAssociatedObject(self, &themePickersKey, initValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
  28. return initValue
  29. }
  30. set {
  31. objc_setAssociatedObject(self, &themePickersKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
  32. _removeThemeNotification()
  33. if newValue.isEmpty == false { _setupThemeNotification() }
  34. }
  35. }
  36. func performThemePicker(selector: String, picker: ThemePicker?) {
  37. let sel = Selector(selector)
  38. guard responds(to: sel) else { return }
  39. guard let value = picker?.value() else { return }
  40. if let statePicker = picker as? ThemeStatePicker {
  41. let setState = unsafeBitCast(method(for: sel), to: setValueForStateIMP.self)
  42. statePicker.values.forEach {
  43. guard let value = $1.value() else {
  44. print("SwiftTheme WARNING: Missing value for ThemeStatePicker! Selector: \(String(describing: sel))")
  45. return
  46. }
  47. setState(self, sel, value as AnyObject, UIControl.State(rawValue: $0))
  48. }
  49. }
  50. else if let statusBarStylePicker = picker as? ThemeStatusBarStylePicker {
  51. #if os(iOS)
  52. let setStatusBarStyle = unsafeBitCast(method(for: sel), to: setStatusBarStyleValueIMP.self)
  53. setStatusBarStyle(self, sel, value as! UIStatusBarStyle, statusBarStylePicker.animated)
  54. #endif
  55. }
  56. else if picker is ThemeBarStylePicker {
  57. #if os(iOS)
  58. let setBarStyle = unsafeBitCast(method(for: sel), to: setBarStyleValueIMP.self)
  59. setBarStyle(self, sel, value as! UIBarStyle)
  60. #endif
  61. }
  62. else if picker is ThemeKeyboardAppearancePicker {
  63. let setKeyboard = unsafeBitCast(method(for: sel), to: setKeyboardValueIMP.self)
  64. setKeyboard(self, sel, value as! UIKeyboardAppearance)
  65. }
  66. else if picker is ThemeActivityIndicatorViewStylePicker {
  67. let setActivityStyle = unsafeBitCast(method(for: sel), to: setActivityStyleValueIMP.self)
  68. setActivityStyle(self, sel, value as! UIActivityIndicatorView.Style)
  69. }
  70. else if picker is ThemeScrollViewIndicatorStylePicker {
  71. let setIndicatorStyle = unsafeBitCast(method(for: sel), to: setScrollStyleValueIMP.self)
  72. setIndicatorStyle(self, sel, value as! UIScrollView.IndicatorStyle)
  73. }
  74. else if picker is ThemeCGFloatPicker {
  75. let setCGFloat = unsafeBitCast(method(for: sel), to: setCGFloatValueIMP.self)
  76. setCGFloat(self, sel, value as! CGFloat)
  77. }
  78. else if picker is ThemeCGColorPicker {
  79. let setCGColor = unsafeBitCast(method(for: sel), to: setCGColorValueIMP.self)
  80. setCGColor(self, sel, value as! CGColor)
  81. }
  82. else { perform(sel, with: value) }
  83. }
  84. }
  85. extension NSObject {
  86. fileprivate func _setupThemeNotification() {
  87. if #available(iOS 9.0, tvOS 9.0, *) {
  88. NotificationCenter.default.addObserver(self, selector: #selector(_updateTheme), name: NSNotification.Name(rawValue: ThemeUpdateNotification), object: nil)
  89. } else {
  90. NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: ThemeUpdateNotification), object: nil, queue: nil, using: { [weak self] notification in self?._updateTheme() })
  91. }
  92. }
  93. fileprivate func _removeThemeNotification() {
  94. NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: ThemeUpdateNotification), object: nil)
  95. }
  96. @objc private func _updateTheme() {
  97. themePickers.forEach { selector, picker in
  98. UIView.animate(withDuration: O2ThemeManager.animationDuration) {
  99. self.performThemePicker(selector: selector, picker: picker)
  100. }
  101. }
  102. }
  103. }
  104. private var themePickersKey = ""