|
@@ -0,0 +1,108 @@
|
|
|
+//
|
|
|
+// XCThemeManager.swift
|
|
|
+// notebook
|
|
|
+//
|
|
|
+// Created by 邢铖 on 2023/11/18.
|
|
|
+//
|
|
|
+
|
|
|
+import Foundation
|
|
|
+
|
|
|
+public class XCThemeManager {
|
|
|
+
|
|
|
+ private class PrepareThemeBlock {
|
|
|
+ var block: ((XCThemeSpec?) -> Void)?
|
|
|
+ weak var attachObject: NSObject?
|
|
|
+ }
|
|
|
+
|
|
|
+ public static let shared = XCThemeManager()
|
|
|
+
|
|
|
+ public private(set) var selectedTheme: XCThemeSpec? {
|
|
|
+ didSet {
|
|
|
+ self.forceThemeInterfaceStyleForWindows()
|
|
|
+ var needRemovals = [PrepareThemeBlock]()
|
|
|
+ for it in self.prepareThemeBlocks {
|
|
|
+ if it.attachObject == nil {
|
|
|
+ needRemovals.append(it)
|
|
|
+ } else {
|
|
|
+ it.block?(self.selectedTheme)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ self.prepareThemeBlocks.removeAll { block in
|
|
|
+ for it in needRemovals {
|
|
|
+ if it === block {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public func prepareThemeBlock(target: NSObject?, _ block: ((XCThemeSpec?) -> Void)?) {
|
|
|
+ let blockClass = PrepareThemeBlock()
|
|
|
+ blockClass.attachObject = target
|
|
|
+ blockClass.block = block
|
|
|
+ self.prepareThemeBlocks.append(blockClass)
|
|
|
+ block?(self.selectedTheme)
|
|
|
+ }
|
|
|
+
|
|
|
+ private var themes: [String : XCThemeSpec]
|
|
|
+ private var prepareThemeBlocks = [PrepareThemeBlock]()
|
|
|
+
|
|
|
+ private init() {
|
|
|
+ self.themes = [:]
|
|
|
+ guard let path = Bundle.main.path(forResource: "ThemeResources", ofType: nil) else {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ for dir in (try? FileManager.default.contentsOfDirectory(atPath: path)) ?? [] {
|
|
|
+ if let theme = XCThemeSpec(themeId: dir) {
|
|
|
+ self.themes[dir] = theme
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // TODO: Mocked
|
|
|
+ self.selectedTheme = self.theme(forId: "autumn_mountain")
|
|
|
+
|
|
|
+ let originalMethod = class_getInstanceMethod(UIViewController.classForCoder(), #selector(UIViewController.viewDidLoad))!
|
|
|
+ let newMethod = class_getInstanceMethod(UIViewController.classForCoder(), #selector(UIViewController.XCThemeManagerSwizzledViewDidLoad))!
|
|
|
+ method_exchangeImplementations(originalMethod, newMethod)
|
|
|
+ }
|
|
|
+
|
|
|
+ private func theme(forId id: String) -> XCThemeSpec? {
|
|
|
+ return self.themes[id]
|
|
|
+ }
|
|
|
+
|
|
|
+ public func forceLoad() { }
|
|
|
+
|
|
|
+ public var currentUserInterfaceStyle: UIUserInterfaceStyle {
|
|
|
+ if let theme = self.selectedTheme {
|
|
|
+ return theme.interfaceStyle
|
|
|
+ } else {
|
|
|
+ return UITraitCollection.current.userInterfaceStyle
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fileprivate func forceThemeInterfaceStyle(_ viewController: UIViewController) {
|
|
|
+ viewController.overrideUserInterfaceStyle = self.currentUserInterfaceStyle
|
|
|
+ }
|
|
|
+
|
|
|
+ private func forceThemeInterfaceStyleForWindows() {
|
|
|
+ UIApplication.shared.connectedScenes
|
|
|
+ .filter( {$0.activationState == .foregroundActive})
|
|
|
+ .map( {$0 as? UIWindowScene})
|
|
|
+ .forEach({ $0?.windows.forEach({
|
|
|
+ $0.overrideUserInterfaceStyle = self.currentUserInterfaceStyle
|
|
|
+ })
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+fileprivate extension UIViewController {
|
|
|
+
|
|
|
+ @objc func XCThemeManagerSwizzledViewDidLoad() {
|
|
|
+ self.XCThemeManagerSwizzledViewDidLoad()
|
|
|
+ XCThemeManager.shared.forceThemeInterfaceStyle(self)
|
|
|
+ }
|
|
|
+
|
|
|
+}
|