商品倒計時 for Swift

因公司項目添加商品倒計時需求币喧,故針對商品倒計時模塊做了個Demo琼富,也是對自己的總結(jié);

經(jīng)過370041534群里的朋友提醒啊送,既然是cell上有多個倒計時,可使用一個定時器控制cell上所有的倒計時欣孤,在這里感謝這位朋友馋没,并針對此問題進行了優(yōu)化。

效果圖

廢話不多說先放一個效果圖


效果圖

實現(xiàn)步驟

基礎(chǔ)實現(xiàn)

  1. 獲取當(dāng)前系統(tǒng)時間
// 獲取當(dāng)前時區(qū)的Date
static func getCurrentDate() -> Date {
    let nowDate = Date()
    let zone = NSTimeZone.system
    let interval = zone.secondsFromGMT(for: nowDate)
    let localeDate = nowDate.addingTimeInterval(TimeInterval(interval))
    return localeDate
 }
  1. 與服務(wù)器協(xié)商到期時間通過時間戳的方法返回降传,我們需要把時間戳轉(zhuǎn)換成Date
// 時間戳轉(zhuǎn)換成Date
func timestampWithDate() -> Date {
    let date = Date.init(timeIntervalSince1970: self)
    let zone = NSTimeZone.system
    let interval = zone.secondsFromGMT(for: date)
    let localeDate = date.addingTimeInterval(TimeInterval(interval))
    return localeDate
}
  1. 計算差值
    查看文檔可通過日歷類NSCalendardateComponents方法獲取兩個日期的差值篷朵,正好符合我們的需求。

The result of calculating the difference from start to end.

/// Returns the difference between two dates.
///
/// - parameter components: Which components to compare.
/// - parameter start: The starting date.
/// - parameter end: The ending date.
/// - returns: The result of calculating the difference from start to end.
public func dateComponents(_ components: Set<Calendar.Component>, from start: Date, to end: Date) -> DateComponents

這里需要注意components參數(shù),如果傳[.year, .month, .day, .hour, .minute, .second]是算差多少年月日時分秒声旺。
需求只要獲取差值的時分秒笔链,故只傳[ .hour, .minute, .second]即可,代碼如下:

// 獲取當(dāng)前時間
let nowDate = Date.getCurrentDate()
// 獲取結(jié)束時間
let endDate = endTime.timestampWithDate()
    
let calendar = NSCalendar.current
// The result of calculating the difference from start to end.
let components = calendar.dateComponents([.hour, .minute, .second], from: nowDate, to: endDate)
    
guard let hour = components.hour,
    let minute = components.minute,
    let second = components.second else { return }
    
print("差值為:\(hour)時\(minute)分\(second)秒")
  1. 通過定時器動態(tài)進行倒計時
    此處通過isStartTimer判斷是否需要執(zhí)行定時器腮猖,目的是為實現(xiàn)多cell下一個定時器操作多個倒計時鉴扫,具體請繼續(xù)往下看:
// 便利構(gòu)造器
convenience init(frame: CGRect, isStartTimer:Bool = true) {
    self.init(frame: frame)
    self.isStartTimer = isStartTimer
}

// 結(jié)束時間(傳遞接口返回的時間戳)
open var endTime:Double? {
    didSet {
        if isStartTimer == true {
            if codeTimer != nil { return }
            
            let queue = DispatchQueue.global()
            codeTimer = DispatchSource.makeTimerSource(queue: queue)
            codeTimer?.schedule(wallDeadline: .now(), repeating: .seconds(1))
            codeTimer?.setEventHandler { [weak self] in
                DispatchQueue.main.async(execute: {
                    self?.refreshTime()
                })
            }
            codeTimer?.resume()
        } else {
            refreshTime()
        }
    }
}
    
// 主要實現(xiàn)計算時間差值方法
func refreshTime() {
    guard let endTime = endTime else { return }
    
    // 獲取當(dāng)前時間
    let nowDate = Date.getCurrentDate()
    // 獲取結(jié)束時間
    let endDate = endTime.timestampWithDate()
    
    let calendar = NSCalendar.current
    // The result of calculating the difference from start to end.
    let components = calendar.dateComponents([.hour, .minute, .second], from: nowDate, to: endDate)
    
    guard let hour = components.hour,
        let minute = components.minute,
        let second = components.second else { return }
    
    if hour < 0 || minute < 0 || second < 0  {
        removeTimer()
        hourLabel.text = "00"
        minuteLabel.text = "00"
        secondLabel.text = "00"
    } else {
        hourLabel.text = "\(hour.keepInt())"
        minuteLabel.text = "\(minute.keepInt())"
        secondLabel.text = "\(second.keepInt())"
    }
}
    
// MARK: - 移除Timer
fileprivate func removeTimer() {
    if codeTimer != nil {
        codeTimer?.cancel()
        codeTimer = nil
    }
}

初始化時傳入true or false,默認true澈缺,用于頁面只有一個定時器的情況坪创,如只需要一個定時器單獨創(chuàng)建。

列表下一個定時器控制多個倒計時

想要實現(xiàn)一個定時器控制多個倒計時姐赡,我們需要獲取UITableViewvisibleCells屬性莱预,通過查看文檔此屬性是獲取UITableView的當(dāng)前顯示cell。
這樣我們就可以創(chuàng)建一個定時器雏吭,并在定時器方法中便利UITableView中當(dāng)前顯示的cell锁施,并給與賦值。
實現(xiàn)代碼如下:

convenience init(tableView: UITableView, dateArray: [Double]) {
    self.init()
    
    self.tableView = tableView
    self.dateArray = dateArray
    
    setCountDown()
}
    
// MARK: - 定時器
func setCountDown() {
    if codeTimer != nil { return }
    
    let queue = DispatchQueue.global()
    codeTimer = DispatchSource.makeTimerSource(queue: queue)
    codeTimer?.schedule(wallDeadline: .now(), repeating: .seconds(1))
    codeTimer?.setEventHandler { [weak self] in
        DispatchQueue.main.async(execute: {
            guard let cells = self?.tableView?.visibleCells else { return }
            guard let dateArray = self?.dateArray else { return }
            for cell in cells {
                guard let cell = cell as? TableViewCell else { return }
                cell.dateView.endTime = dateArray[cell.tag]
            }
        })
    }
    codeTimer?.resume()
}

這樣我們只要在初始化TableView后杖们,執(zhí)行convenience init(tableView: UITableView, dateArray: [Double])即可悉抵。

到目前為止自測過程中還遇到一個問題,快速滑動時時間會錯亂摘完。
分析了下發(fā)現(xiàn)是因為定時器每秒執(zhí)行姥饰,快速滑動UITableView時頁面數(shù)據(jù)并未刷新導(dǎo)致重用了。孝治。列粪。
思考了下,在TableView重用池中重新賦值谈飒,之后定時器方法會自動觸發(fā)岂座,可能有更好的方法實現(xiàn),歡迎指點杭措。
實現(xiàn)代碼如下:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: TableViewCell.tableViewCellReuse) as! TableViewCell
    cell.tag = indexPath.row
    // 重用池方法賦值费什,防止滑動時數(shù)據(jù)錯亂
    cell.dateView.endTime = dateArray[indexPath.row]
    return cell
}

針對防用戶修改時間,導(dǎo)致倒計時時間錯誤問題手素,后續(xù)會進行更新

完整Demo

傳送門

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末鸳址,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子泉懦,更是在濱河造成了極大的恐慌稿黍,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件崩哩,死亡現(xiàn)場離奇詭異巡球,居然都是意外死亡言沐,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進店門辕漂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來呢灶,“玉大人,你說我怎么就攤上這事钉嘹⊙炷耍” “怎么了?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵跋涣,是天一觀的道長缨睡。 經(jīng)常有香客問我,道長陈辱,這世上最難降的妖魔是什么奖年? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮沛贪,結(jié)果婚禮上陋守,老公的妹妹穿的比我還像新娘。我一直安慰自己利赋,他們只是感情好水评,可當(dāng)我...
    茶點故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著媚送,像睡著了一般中燥。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上塘偎,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天疗涉,我揣著相機與錄音,去河邊找鬼吟秩。 笑死咱扣,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的涵防。 我是一名探鬼主播闹伪,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼武学!你這毒婦竟也來了祭往?” 一聲冷哼從身側(cè)響起伦意,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤火窒,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后驮肉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體熏矿,經(jīng)...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了票编。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片褪储。...
    茶點故事閱讀 38,566評論 1 339
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖慧域,靈堂內(nèi)的尸體忽然破棺而出鲤竹,到底是詐尸還是另有隱情,我是刑警寧澤昔榴,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布辛藻,位于F島的核電站,受9級特大地震影響互订,放射性物質(zhì)發(fā)生泄漏吱肌。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一仰禽、第九天 我趴在偏房一處隱蔽的房頂上張望氮墨。 院中可真熱鬧,春花似錦吐葵、人聲如沸规揪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽粒褒。三九已至,卻和暖如春诚镰,著一層夾襖步出監(jiān)牢的瞬間奕坟,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工清笨, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留月杉,地道東北人。 一個月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓抠艾,卻偏偏與公主長得像苛萎,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子检号,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,440評論 2 348

推薦閱讀更多精彩內(nèi)容