| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323 |
- //
- // 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<AnyObject>.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
- }
- }
|