Explorar el Código

1.新增 Int 随机数
2.新增GCD 定时器
3.新增 Down 的一些下载方法,和方法封装,虽然暂时没用到
4.新增圆形进度条

100Years hace 3 semanas
padre
commit
da8f285858

+ 8 - 0
TSSmalCoacopods/Classes/Ex/Int+Ex.swift

@@ -61,3 +61,11 @@ public extension Int {
          return currentTimestamp
      }
 }
+
+public extension Int {
+    private static var uniqueCounter: Int64 = 0
+    
+    static var uuid: Int {
+        return Int(OSAtomicIncrement64(&uniqueCounter))
+    }
+}

+ 1 - 0
TSSmalCoacopods/Classes/Ex/UIView+Ex.swift

@@ -286,6 +286,7 @@ public extension UIView {
             }
         }
     }
+    
 }
 
 

+ 92 - 29
TSSmalCoacopods/Classes/Tool/TSCommonTool/TSCommonTool+Down.swift

@@ -11,7 +11,7 @@ public extension TSCommonTool {
     /// - Parameters:
     ///   - url: 文件的 URL 地址
     ///   - completion: 完成回调,返回本地缓存路径或错误
-    public static func downloadAndCacheFile(from urlString: String, fileEx:String? = nil, cacheDirectory:String = "cacheAll",completion: @escaping (String?, Error?) -> Void) {
+    public static func downloadAndCacheFile(from urlString: String, fileEx:String? = nil,missingEx:String? = nil, cacheDirectory:String = "cacheAll",completion: @escaping (String?, Error?) -> Void) {
         
         guard let url = URL(string: urlString) else{
             completion(nil, NSError(domain: "url null", code: 0))
@@ -19,38 +19,17 @@ public extension TSCommonTool {
         }
         
         
-        if !urlString.contains("http") && urlString.contains("/"){
-            completion(urlString.fillCachePath, nil)
+        guard let cachedFileURL = checkURLString(
+            from: urlString,
+            fileEx: fileEx,
+            missingEx: missingEx,
+            cacheDirectory: cacheDirectory,
+            completion: completion
+        ) else {
             return
         }
         
         let fileManager = FileManager.default
-        
-        // 获取缓存目录下的 `cacheVideo` 文件夹路径
-        let cachesDirectory = fileManager.urls(for: .cachesDirectory, in: .userDomainMask).first!
-        let cacheVideoDirectory = cachesDirectory.appendingPathComponent(cacheDirectory)
-        
-        // 创建 `cacheVideo` 文件夹(如果不存在)
-        if !fileManager.fileExists(atPath: cacheVideoDirectory.path) {
-            do {
-                try fileManager.createDirectory(at: cacheVideoDirectory, withIntermediateDirectories: true, attributes: nil)
-            } catch {
-                completion(nil, error)
-                return
-            }
-        }
-        
-        var fileName = url.path.md5
-        
-        // 使用 URL 的 MD5 哈希值作为缓存文件名,附加 URL 的后缀名
-        var fileExtension = fileEx
-        fileExtension = fileExtension ?? (url.pathExtension.isEmpty ? "" : url.pathExtension)
-        if let fileExtension = fileExtension,fileExtension.count > 0 {
-            fileName = url.path.md5 + ".\(fileExtension)"
-        }
-
-        let cachedFileURL = cacheVideoDirectory.appendingPathComponent(fileName)
-        
         //检查文件是否已存在于缓存中
         if fileManager.fileExists(atPath: cachedFileURL.path) {
             print("文件已存在于缓存中: \(cachedFileURL)")
@@ -94,4 +73,88 @@ public extension TSCommonTool {
         
         task.resume()
     }
+    
+    
+    //检查 url 对不对
+    public static func checkURLString(
+        from urlString: String,
+        fileEx:String? = nil,
+    missingEx:String? = nil,
+        cacheDirectory:String = "cacheAll",
+        completion:((String?, Error?) -> Void)? = nil
+    )->URL?
+    {
+        guard let url = URL(string: urlString) else{
+            completion?(nil, NSError(domain: "url null", code: 0))
+            return nil
+        }
+        
+        
+        if !urlString.contains("http") && urlString.contains("/"){
+            completion?(urlString.fillCachePath, nil)
+            return nil
+        }
+        
+        let fileManager = FileManager.default
+        
+        // 获取缓存目录下的 `cacheVideo` 文件夹路径
+        let cachesDirectory = fileManager.urls(for: .cachesDirectory, in: .userDomainMask).first!
+        let cacheVideoDirectory = cachesDirectory.appendingPathComponent(cacheDirectory)
+        
+        // 创建 `cacheVideo` 文件夹(如果不存在)
+        if !fileManager.fileExists(atPath: cacheVideoDirectory.path) {
+            do {
+                try fileManager.createDirectory(at: cacheVideoDirectory, withIntermediateDirectories: true, attributes: nil)
+            } catch {
+                completion?(nil, error)
+                return nil
+            }
+        }
+        
+        var fileName = url.path.md5
+        
+        // 使用 URL 的 MD5 哈希值作为缓存文件名,附加 URL 的后缀名
+        var fileExtension = ""
+        
+        if let fileEx = fileEx {
+            fileExtension = "fileEx"
+        }else{
+            var missingExStr = ""
+            if let missingEx = missingEx {
+                missingExStr = missingEx
+            }
+            fileExtension = url.pathExtension.isEmpty ? missingExStr : url.pathExtension
+        }
+
+        if fileExtension.count > 0 {
+            fileName = url.path.md5 + ".\(fileExtension)"
+        }
+        
+        let cachedFileURL = cacheVideoDirectory.appendingPathComponent(fileName)
+        return cachedFileURL
+
+    }
+    
+    //获取 urlstring 本地的缓存 url path
+    public static func getCachedURLString(
+        from urlString: String,
+        fileEx:String? = nil,
+    missingEx:String? = nil,
+        cacheDirectory:String = "cacheAll")->URL?{
+        
+        if let cachedFileURL = checkURLString(
+            from: urlString,
+            fileEx: fileEx,
+            missingEx: missingEx,
+            cacheDirectory: cacheDirectory
+        ){
+            //检查文件是否已存在于缓存中
+            if FileManager.default.fileExists(atPath: cachedFileURL.path) {
+                print("文件已存在于缓存中: \(cachedFileURL)")
+                return cachedFileURL
+            }
+        }
+        
+        return nil
+    }
 }

+ 2 - 172
TSSmalCoacopods/Classes/Tool/TSCommonTool/TSCommonTool+MultDown.swift

@@ -7,108 +7,6 @@
 
 public extension TSCommonTool {
     
-    //    /// 多任务下载并缓存文件,依据 URL 的后缀名动态设置文件名
-    //    /// - Parameters:
-    //    ///   - url: 文件的 URL 地址
-    //    ///   - completion: 完成回调,返回本地缓存路径或错误
-    //    public static func multidownloadAndCacheFile(
-    //        from urlString: String,
-    //        fileEx:String? = nil,
-    //        cacheDirectory:String = "cacheAll",
-    //        progressHandler: @escaping (Double) -> Void,
-    //        completion: @escaping (String?, Error?) -> Void) -> TSMultiTaskDownloader?
-    //    {
-    //
-    //        guard let url = URL(string: urlString) else{
-    //            completion(nil, NSError(domain: "url null", code: 0))
-    //            return nil
-    //        }
-    //
-    //
-    //        if !urlString.contains("http") && urlString.contains("/"){
-    //            completion(urlString.fillCachePath, nil)
-    //            return nil
-    //        }
-    //
-    //        let fileManager = FileManager.default
-    //
-    //        // 获取缓存目录下的 `cacheVideo` 文件夹路径
-    //        let cachesDirectory = fileManager.urls(for: .cachesDirectory, in: .userDomainMask).first!
-    //        let cacheVideoDirectory = cachesDirectory.appendingPathComponent(cacheDirectory)
-    //
-    //        // 创建 `cacheVideo` 文件夹(如果不存在)
-    //        if !fileManager.fileExists(atPath: cacheVideoDirectory.path) {
-    //            do {
-    //                try fileManager.createDirectory(at: cacheVideoDirectory, withIntermediateDirectories: true, attributes: nil)
-    //            } catch {
-    //                completion(nil, error)
-    //                return nil
-    //            }
-    //        }
-    //
-    //        var fileName = url.path.md5
-    //
-    //        // 使用 URL 的 MD5 哈希值作为缓存文件名,附加 URL 的后缀名
-    //        var fileExtension = fileEx
-    //        fileExtension = fileExtension ?? (url.pathExtension.isEmpty ? "" : url.pathExtension)
-    //        if let fileExtension = fileExtension,fileExtension.count > 0 {
-    //            fileName = url.path.md5 + ".\(fileExtension)"
-    //        }
-    //
-    //        let cachedFileURL = cacheVideoDirectory.appendingPathComponent(fileName)
-    //
-    //        //检查文件是否已存在于缓存中
-    //        if fileManager.fileExists(atPath: cachedFileURL.path) {
-    //            print("文件已存在于缓存中: \(cachedFileURL)")
-    //            completion(cachedFileURL.path, nil)
-    //            return nil
-    //        }
-    //
-    //
-    //        let downloader = TSMultiTaskDownloader.shared
-    //
-    //        let url1 = URL(string: "https://example.com/file1.zip")!
-    //        let url2 = URL(string: "https://example.com/file2.zip")!
-    //
-    //        downloader.downloadFile(from: url, progressHandler: { progress in
-    //            print("Download progress for file1: \(progress * 100)%")
-    //            progressHandler(progress)
-    //        }, completionHandler: { tempFileURL, error in
-    //            if let error = error {
-    //                DispatchQueue.main.async {
-    //                    completion(nil, error)
-    //                }
-    //                return
-    //            }
-    //
-    //            guard let tempFileURL = tempFileURL else {
-    //                DispatchQueue.main.async {
-    //                    completion(nil, NSError(domain: "DownloadError", code: -1, userInfo: [NSLocalizedDescriptionKey: "临时文件路径不存在"]))
-    //                }
-    //                return
-    //            }
-    //
-    //            do {
-    //                if fileManager.fileExists(atPath: cachedFileURL.path) {
-    //                    try fileManager.removeItem(atPath:cachedFileURL.path)
-    //                    dePrint("下载成功,移除已有的旧文件: \(cachedFileURL)")
-    //                }
-    //                try fileManager.moveItem(at: tempFileURL, to: cachedFileURL)
-    //                dePrint("文件下载并缓存成功: \(cachedFileURL)")
-    //                DispatchQueue.main.async {
-    //                    completion(cachedFileURL.path, nil)
-    //                }
-    //            } catch {
-    //                dePrint("文件下载成功,但失败:\(error)")
-    //                DispatchQueue.main.async {
-    //                    completion(nil, error)
-    //                }
-    //            }
-    //        })
-    //
-    //        return downloader
-    //    }
-    
     /// 多任务下载并缓存文件,依据 URL 的后缀名动态设置文件名
     /// - Parameters:
     ///   - url: 文件的 URL 地址
@@ -116,6 +14,7 @@ public extension TSCommonTool {
     public static func multidownloadAndCacheFile(
         from urlString: String,
         fileEx:String? = nil,
+     missingEx:String? = nil,
         cacheDirectory:String = "cacheAll",
         progressHandler: @escaping (Double) -> Void,
         completion: @escaping (String?, Error?) -> Void) -> TSMultiTaskDownloader?
@@ -130,8 +29,8 @@ public extension TSCommonTool {
         guard let cachedFileURL = checkURLString(
             from: urlString,
             fileEx: fileEx,
+            missingEx: missingEx,
             cacheDirectory: cacheDirectory,
-            progressHandler: progressHandler,
             completion: completion
         ) else {
             return nil
@@ -186,74 +85,5 @@ public extension TSCommonTool {
         return downloader
     }
     
-    //检查 url 对不对
-    public static func checkURLString(
-        from urlString: String,
-        fileEx:String? = nil,
-        cacheDirectory:String = "cacheAll",
-        progressHandler:((Double) -> Void)? = nil,
-        completion:((String?, Error?) -> Void)? = nil
-    )->URL?
-    {
-        guard let url = URL(string: urlString) else{
-            completion?(nil, NSError(domain: "url null", code: 0))
-            return nil
-        }
-        
-        
-        if !urlString.contains("http") && urlString.contains("/"){
-            completion?(urlString.fillCachePath, nil)
-            return nil
-        }
-        
-        let fileManager = FileManager.default
-        
-        // 获取缓存目录下的 `cacheVideo` 文件夹路径
-        let cachesDirectory = fileManager.urls(for: .cachesDirectory, in: .userDomainMask).first!
-        let cacheVideoDirectory = cachesDirectory.appendingPathComponent(cacheDirectory)
-        
-        // 创建 `cacheVideo` 文件夹(如果不存在)
-        if !fileManager.fileExists(atPath: cacheVideoDirectory.path) {
-            do {
-                try fileManager.createDirectory(at: cacheVideoDirectory, withIntermediateDirectories: true, attributes: nil)
-            } catch {
-                completion?(nil, error)
-                return nil
-            }
-        }
-        
-        var fileName = url.path.md5
-        
-        // 使用 URL 的 MD5 哈希值作为缓存文件名,附加 URL 的后缀名
-        var fileExtension = fileEx
-        fileExtension = fileExtension ?? (url.pathExtension.isEmpty ? "" : url.pathExtension)
-        if let fileExtension = fileExtension,fileExtension.count > 0 {
-            fileName = url.path.md5 + ".\(fileExtension)"
-        }
-        
-        let cachedFileURL = cacheVideoDirectory.appendingPathComponent(fileName)
-        return cachedFileURL
 
-    }
-    
-    //获取 urlstring 本地的缓存 url path
-    public static func getCachedURLString(
-        from urlString: String,
-        fileEx:String? = nil,
-        cacheDirectory:String = "cacheAll")->URL?{
-        
-        if let cachedFileURL = checkURLString(
-            from: urlString,
-            fileEx: fileEx,
-            cacheDirectory: cacheDirectory
-        ){
-            //检查文件是否已存在于缓存中
-            if FileManager.default.fileExists(atPath: cachedFileURL.path) {
-                print("文件已存在于缓存中: \(cachedFileURL)")
-                return cachedFileURL
-            }
-        }
-        
-        return nil
-    }
 }

+ 5 - 1
TSSmalCoacopods/Classes/Tool/TSCommonTool/TSCommonTool.swift

@@ -17,7 +17,11 @@ open class TSCommonTool {
         from urlString: String,
         completion: @escaping (UIImage?, Error?) -> Void
     ) {
-        
+        if urlString.contains("http") == false,
+           let image = UIImage(named: urlString){
+            completion(image, nil)
+            return
+        }
         guard let url = URL(string: urlString) else{
             completion(nil, NSError(domain: "url null", code: 0))
             return

+ 50 - 0
TSSmalCoacopods/Classes/Tool/TSGCDTimer.swift

@@ -0,0 +1,50 @@
+//
+//  GCDTimer.swift
+//  AIRingtone
+//
+//  Created by 100Years on 2025/3/21.
+//
+
+import Foundation
+
+public class TSGCDTimer {
+    private var timer: DispatchSourceTimer?
+    private var isRunning: Bool = false
+
+    /// 启动定时器
+    /// - Parameters:
+    ///   - interval: 时间间隔(秒)
+    ///   - repeats: 是否重复
+    ///   - queue: 执行队列(默认主队列)
+    ///   - handler: 定时器触发时的回调
+    public func start(interval: TimeInterval, repeats: Bool = true, queue: DispatchQueue = .main, handler: @escaping () -> Void) {
+        // 如果定时器已经存在,先停止
+        stop()
+
+        // 创建定时器
+        timer = DispatchSource.makeTimerSource(queue: queue)
+        let deadline: DispatchTime = .now() + interval
+        let repeating: DispatchTimeInterval = repeats ? .seconds(Int(interval)) : .never
+
+        timer?.schedule(deadline: deadline, repeating: repeating)
+        timer?.setEventHandler(handler: handler)
+        timer?.resume()
+
+        isRunning = true
+    }
+
+    /// 停止定时器
+    public func stop() {
+        if isRunning {
+            timer?.cancel()
+            timer = nil
+            isRunning = false
+        }
+    }
+
+    /// 释放定时器
+    deinit {
+        stop()
+        dePrint("GCDTimer deinit")
+    }
+}

+ 120 - 0
TSSmalCoacopods/Classes/View/TSCircularProgressView.swift

@@ -0,0 +1,120 @@
+//
+//  TSCircularProgressView.swift
+//  Pods
+//
+//  Created by 100Years on 2025/3/21.
+//
+
+import UIKit
+
+open class TSCircularProgressView: UIView {
+    // MARK: - Properties
+    public var trackLayer: CAShapeLayer!
+    public var progressLayer: CAShapeLayer!
+    
+    /// 进度条颜色
+    public var progressColor: UIColor = .blue {
+        didSet {
+            progressLayer.strokeColor = progressColor.cgColor
+            //print("Progress Color Updated: \(progressColor)")
+        }
+    }
+    
+    /// 背景轨道颜色
+    public var trackColor: UIColor = .lightGray {
+        didSet {
+            trackLayer.strokeColor = trackColor.cgColor
+        }
+    }
+    
+    /// 线条粗细
+    public var lineWidth: CGFloat = 10.0 {
+        didSet {
+            trackLayer.lineWidth = lineWidth
+            progressLayer.lineWidth = lineWidth
+            setNeedsLayout()
+        }
+    }
+    
+    /// 进度(0.0 到 1.0)
+    public var progress: CGFloat = 0.0 {
+        didSet {
+            progress = min(max(progress, 0.0), 1.0) // 限制范围
+//            print("Progress Updated: \(progress)")
+            progressLayer.strokeEnd = progress
+        }
+    }
+    
+    /// 绘制方向(默认顺时针)
+    public var isClockwise: Bool = true {
+        didSet {
+            setNeedsLayout()
+        }
+    }
+    
+    // MARK: - Initialization
+    override public init(frame: CGRect) {
+        super.init(frame: frame)
+        setupLayers()
+    }
+    
+    required public init?(coder: NSCoder) {
+        super.init(coder: coder)
+        setupLayers()
+    }
+    
+    // MARK: - Setup Layers
+    public func setupLayers() {
+        // 背景轨道
+        trackLayer = CAShapeLayer()
+        trackLayer.fillColor = UIColor.clear.cgColor
+        trackLayer.strokeColor = trackColor.cgColor
+        trackLayer.lineWidth = lineWidth
+        trackLayer.lineCap = .round
+        layer.addSublayer(trackLayer)
+        
+        // 进度条
+        progressLayer = CAShapeLayer()
+        progressLayer.fillColor = UIColor.clear.cgColor
+        progressLayer.strokeColor = progressColor.cgColor
+        progressLayer.lineWidth = lineWidth
+        progressLayer.lineCap = .round
+        progressLayer.strokeEnd = progress
+        layer.addSublayer(progressLayer)
+        
+        //print("Progress Layer Added: \(progressLayer)")
+    }
+    
+    // MARK: - Layout
+    override public func layoutSubviews() {
+        super.layoutSubviews()
+        //print("layoutSubviews Called")
+        updatePaths()
+    }
+    
+    public func updatePaths() {
+        let center = CGPoint(x: bounds.midX, y: bounds.midY)
+        let radius = min(bounds.width, bounds.height) / 2 - lineWidth / 2
+        
+        // 背景轨道路径
+        let trackPath = UIBezierPath(
+            arcCenter: center,
+            radius: radius,
+            startAngle: -.pi / 2, // 从顶部开始
+            endAngle: .pi * 1.5,   // 到顶部结束
+            clockwise: true // 背景轨道始终为顺时针
+        )
+        trackLayer.path = trackPath.cgPath
+        
+        // 进度条路径
+        let progressPath = UIBezierPath(
+            arcCenter: center,
+            radius: radius,
+            startAngle: -.pi / 2, // 从顶部开始
+            endAngle: -.pi / 2 + .pi * 2 * (isClockwise ? 1 : -1), // 根据 isClockwise 控制方向
+            clockwise: isClockwise
+        )
+        progressLayer.path = progressPath.cgPath
+        //print("Progress Layer Path: \(progressLayer.path)")
+    }
+}

+ 6 - 2
TSSmalCoacopods/Classes/View/TSPlaceholderTextView/TSPlaceholderTextView.swift

@@ -51,7 +51,9 @@ open class TSPlaceholderTextView: UITextView {
             updatePlaceholderVisibility()
         }
     }
-
+    
+    public var textViewTextChanged:(()->Void)?
+    
     // Initializer
     public init(placeholder: String? = nil,
          text: String? = nil,
@@ -106,11 +108,12 @@ open class TSPlaceholderTextView: UITextView {
        
         if let textView = notification.object as? UITextView {
             handleTextViewTextCount(textView: textView)
+            textViewTextChanged?()
         }
         updatePlaceholderVisibility()
     }
     
-    func handleTextViewTextCount(textView:UITextView) {
+    open func handleTextViewTextCount(textView:UITextView) {
         if textView.text.count > maxLength {
             textView.text = String(textView.text.prefix(maxLength))
         }
@@ -121,6 +124,7 @@ open class TSPlaceholderTextView: UITextView {
         textContainerInset = textInsets
         setNeedsLayout()
     }
+
     
     // Layout placeholder
     open override func layoutSubviews() {