// // QSLCountdownManager.swift // QuickSearchLocation // // Created by Trae AI on 2024-06-30. // import Foundation /// 倒计时类型 enum QSLCountdownType { case homeProduct // 首页商品倒计时 case trial // 试用倒计时 } /// 倒计时管理器代理协议 protocol QSLCountdownManagerDelegate: AnyObject { /// 倒计时更新 /// - Parameters: /// - manager: 倒计时管理器 /// - type: 倒计时类型 /// - remainingSeconds: 剩余秒数 func countdownManager(_ manager: QSLCountdownManager, didUpdateCountdown type: QSLCountdownType, remainingSeconds: Int) /// 倒计时结束 /// - Parameters: /// - manager: 倒计时管理器 /// - type: 倒计时类型 func countdownManager(_ manager: QSLCountdownManager, didFinishCountdown type: QSLCountdownType) } // 使代理方法可选实现 extension QSLCountdownManagerDelegate { func countdownManager(_ manager: QSLCountdownManager, didUpdateCountdown type: QSLCountdownType, remainingSeconds: Int) {} func countdownManager(_ manager: QSLCountdownManager, didFinishCountdown type: QSLCountdownType) {} } /// 倒计时管理器 class QSLCountdownManager { // MARK: - 单例 static let shared = QSLCountdownManager() private init() {} // MARK: - 属性 private var timer: Timer? private var isHomeProductCountdownActive = false private var isTrialCountdownActive = false // 倒计时剩余时间(秒) private var homeProductRemainingSeconds: Int = 0 private var trialRemainingSeconds: Int = 0 // 代理集合 private var delegates = NSHashTable.weakObjects() // 倒计时回调(保留原有回调机制作为备选) typealias CountdownCallback = (Int) -> Void private var homeProductCallback: CountdownCallback? private var trialCallback: CountdownCallback? // 倒计时结束回调(保留原有回调机制作为备选) typealias CountdownFinishCallback = () -> Void private var homeProductFinishCallback: CountdownFinishCallback? private var trialFinishCallback: CountdownFinishCallback? var selectGood: QSLGoodModel?{ didSet{ if(oldValue != nil){ NotificationCenter.default.post( name: Notification.Name("QSLHomeRefreshCouponViewNoti"), object: nil, userInfo: nil ) } } } var trialGood: QSLGoodModel?{ didSet{ if(oldValue != nil){ NotificationCenter.default.post( name: Notification.Name("QSLHomeRefreshCouponViewNoti"), object: nil, userInfo: nil ) } } } // MARK: - 代理管理 /// 添加代理 /// - Parameter delegate: 代理对象 func addDelegate(_ delegate: QSLCountdownManagerDelegate) { delegates.add(delegate) } /// 移除代理 /// - Parameter delegate: 代理对象 func removeDelegate(_ delegate: QSLCountdownManagerDelegate) { delegates.remove(delegate) } /// 移除所有代理 func removeAllDelegates() { delegates.removeAllObjects() } // MARK: - 公共方法 /// 开始倒计时 /// - Parameters: /// - type: 倒计时类型 /// - seconds: 倒计时秒数 /// - callback: 倒计时回调,返回剩余秒数 /// - finishCallback: 倒计时结束回调 /// - Returns: 是否成功开启倒计时 @discardableResult func startCountdown(type: QSLCountdownType, seconds: Int, callback: @escaping CountdownCallback, finishCallback: CountdownFinishCallback? = nil) -> Bool { // 检查该类型倒计时是否已结束,如果已结束则不能再开启 switch type { case .homeProduct: if isHomeProductCountdownActive { return false } homeProductRemainingSeconds = seconds homeProductCallback = callback homeProductFinishCallback = finishCallback isHomeProductCountdownActive = true case .trial: if isTrialCountdownActive { return false } trialRemainingSeconds = seconds trialCallback = callback trialFinishCallback = finishCallback isTrialCountdownActive = true } // 如果timer不存在,创建timer if timer == nil { timer = Timer.scheduledTimer(timeInterval: 0.001, target: self, selector: #selector(updateCountdown), userInfo: nil, repeats: true) RunLoop.current.add(timer!, forMode: .common) } // 立即执行一次回调,更新UI executeCallback(for: type) return true } /// 暂停倒计时 /// - Parameter type: 倒计时类型 func pauseCountdown(type: QSLCountdownType) { switch type { case .homeProduct: isHomeProductCountdownActive = false case .trial: isTrialCountdownActive = false } // 如果两种倒计时都不活跃,则销毁timer if !isHomeProductCountdownActive && !isTrialCountdownActive { invalidateTimer() } } /// 恢复倒计时 /// - Parameter type: 倒计时类型 /// - Returns: 是否成功恢复倒计时 @discardableResult func resumeCountdown(type: QSLCountdownType) -> Bool { switch type { case .homeProduct: if homeProductRemainingSeconds <= 0 { return false } isHomeProductCountdownActive = true case .trial: if trialRemainingSeconds <= 0 { return false } isTrialCountdownActive = true } // 如果timer不存在,创建timer if timer == nil { timer = Timer.scheduledTimer(timeInterval: 0.001, target: self, selector: #selector(updateCountdown), userInfo: nil, repeats: true) RunLoop.current.add(timer!, forMode: .common) } return true } /// 停止倒计时 /// - Parameter type: 倒计时类型 func stopCountdown(type: QSLCountdownType) { switch type { case .homeProduct: isHomeProductCountdownActive = false homeProductRemainingSeconds = 0 homeProductCallback = nil homeProductFinishCallback = nil case .trial: isTrialCountdownActive = false trialRemainingSeconds = 0 trialCallback = nil trialFinishCallback = nil } // 如果两种倒计时都不活跃,则销毁timer if !isHomeProductCountdownActive && !isTrialCountdownActive { invalidateTimer() } } /// 获取剩余时间 /// - Parameter type: 倒计时类型 /// - Returns: 剩余秒数 func getRemainingSeconds(for type: QSLCountdownType) -> Int { switch type { case .homeProduct: return homeProductRemainingSeconds case .trial: return trialRemainingSeconds } } /// 是否正在倒计时 /// - Parameter type: 倒计时类型 /// - Returns: 是否正在倒计时 func isCountdownActive(for type: QSLCountdownType) -> Bool { switch type { case .homeProduct: return isHomeProductCountdownActive case .trial: return isTrialCountdownActive } } // MARK: - 私有方法 /// 更新倒计时 @objc private func updateCountdown() { // 更新首页商品倒计时 if isHomeProductCountdownActive { if homeProductRemainingSeconds > 0 { homeProductRemainingSeconds -= 1 executeCallback(for: .homeProduct) notifyDelegatesCountdownUpdated(type: .homeProduct, remainingSeconds: homeProductRemainingSeconds) } // 检查是否结束 if homeProductRemainingSeconds <= 0 { isHomeProductCountdownActive = false homeProductFinishCallback?() homeProductCallback = nil homeProductFinishCallback = nil notifyDelegatesCountdownFinished(type: .homeProduct) } } // 更新试用倒计时 if isTrialCountdownActive { if trialRemainingSeconds > 0 { trialRemainingSeconds -= 1 executeCallback(for: .trial) notifyDelegatesCountdownUpdated(type: .trial, remainingSeconds: trialRemainingSeconds) } // 检查是否结束 if trialRemainingSeconds <= 0 { isTrialCountdownActive = false trialFinishCallback?() trialCallback = nil trialFinishCallback = nil notifyDelegatesCountdownFinished(type: .trial) } } // 如果两种倒计时都不活跃,则销毁timer if !isHomeProductCountdownActive && !isTrialCountdownActive { invalidateTimer() } } /// 执行倒计时回调 /// - Parameter type: 倒计时类型 private func executeCallback(for type: QSLCountdownType) { switch type { case .homeProduct: homeProductCallback?(homeProductRemainingSeconds) case .trial: trialCallback?(trialRemainingSeconds) } } /// 通知代理倒计时更新 /// - Parameters: /// - type: 倒计时类型 /// - remainingSeconds: 剩余秒数 private func notifyDelegatesCountdownUpdated(type: QSLCountdownType, remainingSeconds: Int) { for case let delegate as QSLCountdownManagerDelegate in delegates.allObjects { delegate.countdownManager(self, didUpdateCountdown: type, remainingSeconds: remainingSeconds) } } /// 通知代理倒计时结束 /// - Parameter type: 倒计时类型 private func notifyDelegatesCountdownFinished(type: QSLCountdownType) { for case let delegate as QSLCountdownManagerDelegate in delegates.allObjects { delegate.countdownManager(self, didFinishCountdown: type) } } /// 销毁定时器 private func invalidateTimer() { timer?.invalidate() timer = nil } }