xcbosa mbp16 1 жил өмнө
parent
commit
40d8a52bb0

+ 22 - 0
Example/Pods/Local Podspecs/XCTheme.podspec.json

@@ -0,0 +1,22 @@
+{
+  "name": "XCTheme",
+  "version": "0.1.0",
+  "summary": "A short description of XCTheme.",
+  "description": "TODO: Add long description of the pod here.",
+  "homepage": "https://github.com/xcbosa mbp16/XCTheme",
+  "license": {
+    "type": "MIT",
+    "file": "LICENSE"
+  },
+  "authors": {
+    "xcbosa mbp16": "xcbosa@forgetive.org"
+  },
+  "source": {
+    "git": "https://github.com/xcbosa mbp16/XCTheme.git",
+    "tag": "0.1.0"
+  },
+  "platforms": {
+    "ios": "10.0"
+  },
+  "source_files": "XCTheme/Classes/**/*"
+}

+ 5 - 5
XCTheme.podspec

@@ -9,7 +9,7 @@
 Pod::Spec.new do |s|
   s.name             = 'XCTheme'
   s.version          = '0.1.0'
-  s.summary          = 'A short description of XCTheme.'
+  s.summary          = 'Theme Manager'
 
 # This description is used to generate tags and improve search results.
 #   * Think: What does it do? Why did you write it? What is the focus?
@@ -18,17 +18,17 @@ Pod::Spec.new do |s|
 #   * Finally, don't worry about the indent, CocoaPods strips it!
 
   s.description      = <<-DESC
-TODO: Add long description of the pod here.
+A Theme Manager for C Code Develop & C Notebook.
                        DESC
 
-  s.homepage         = 'https://github.com/xcbosa mbp16/XCTheme'
+  s.homepage         = 'https://git.forgetive.org/xcbosa/XCTheme.git'
   # s.screenshots     = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2'
   s.license          = { :type => 'MIT', :file => 'LICENSE' }
   s.author           = { 'xcbosa mbp16' => 'xcbosa@forgetive.org' }
-  s.source           = { :git => 'https://github.com/xcbosa mbp16/XCTheme.git', :tag => s.version.to_s }
+  s.source           = { :git => 'https://git.forgetive.org/xcbosa/XCTheme.git', :tag => s.version.to_s }
   # s.social_media_url = 'https://twitter.com/<TWITTER_USERNAME>'
 
-  s.ios.deployment_target = '10.0'
+  s.ios.deployment_target = '13.0'
 
   s.source_files = 'XCTheme/Classes/**/*'
   

+ 108 - 0
XCTheme/Classes/API/XCThemeManager.swift

@@ -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)
+    }
+    
+}

+ 89 - 0
XCTheme/Classes/API/XCThemeSpec.swift

@@ -0,0 +1,89 @@
+//
+//  XCThemeSpec.swift
+//  notebook
+//
+//  Created by 邢铖 on 2023/11/18.
+//
+
+import Foundation
+
+public class XCThemeSpec {
+    
+    public private(set) var themeId: String
+    public private(set) var originalDict: [String : Any]
+    public private(set) var interfaceStyle: UIUserInterfaceStyle
+    
+    public init?(themeId: String) {
+        self.interfaceStyle = .unspecified
+        self.themeId = themeId
+        self.originalDict = [:]
+        if (themeId.contains("/") || themeId.contains("\\")) {
+            return nil
+        }
+        guard let url = self.url(forFile: "theme.json") else {
+            return nil
+        }
+        guard let themeData = try? Data(Data(contentsOf: url)) else {
+            return nil
+        }
+        guard let themeJson = try? JSONSerialization.jsonObject(with: themeData) as? [String : Any] else {
+            return nil
+        }
+        guard let baseAppearance = themeJson["baseAppearance"] as? String else {
+            return nil
+        }
+        switch baseAppearance {
+        case "dark":
+            self.interfaceStyle = .dark
+            break
+        case "light":
+            self.interfaceStyle = .light
+            break
+        default:
+            return nil
+        }
+        self.originalDict = themeJson
+    }
+    
+    public var themeRootPath: String {
+        return Bundle.main.path(forResource: "ThemeResources/\(themeId)", ofType: nil) ?? ""
+    }
+    
+    public func path(forFile file : String) -> String {
+        return self.themeRootPath + "/\(file)"
+    }
+    
+    public func url(forFile file: String) -> URL? {
+        return URL(fileURLWithPath: self.path(forFile: file))
+    }
+    
+    public func image(forFile file: String) -> UIImage? {
+        return UIImage(contentsOfFile: self.path(forFile: file))
+    }
+    
+}
+
+extension XCThemeSpec {
+    
+    public var backgroundImage: UIImage? {
+        guard let background = self.originalDict["background"] as? String else {
+            return nil
+        }
+        return self.image(forFile: background)
+    }
+    
+    public var maskAlpha: CGFloat {
+        guard let component = self.originalDict["maskAlpha"] as? CGFloat else {
+            return 0.78
+        }
+        return component
+    }
+    
+    public var maskColor: UIColor {
+        guard let colorHex = self.originalDict["maskColor"] as? String else {
+            return UIColor(hexString: "#000000").withAlphaComponent(self.maskAlpha)
+        }
+        return UIColor(hexString: colorHex).withAlphaComponent(self.maskAlpha)
+    }
+    
+}

+ 9 - 0
XCTheme/Classes/Private/XCTheme.swift

@@ -0,0 +1,9 @@
+//
+//  XCThemeSpec.swift
+//  notebook
+//
+//  Created by 邢铖 on 2023/11/18.
+//
+
+import Foundation
+

+ 0 - 0
XCTheme/Classes/ReplaceMe.swift