因公司項目添加商品倒計時需求币喧,故針對商品倒計時模塊做了個Demo琼富,也是對自己的總結(jié);
經(jīng)過370041534群里的朋友提醒啊送,既然是cell上有多個倒計時,可使用一個定時器控制cell上所有的倒計時欣孤,在這里感謝這位朋友馋没,并針對此問題進行了優(yōu)化。
效果圖
廢話不多說先放一個效果圖
實現(xiàn)步驟
基礎(chǔ)實現(xiàn)
- 獲取當(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
}
- 與服務(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
}
- 計算差值
查看文檔可通過日歷類NSCalendar
的dateComponents
方法獲取兩個日期的差值篷朵,正好符合我們的需求。
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)秒")
- 通過定時器動態(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)一個定時器控制多個倒計時姐赡,我們需要獲取UITableView
的visibleCells
屬性莱预,通過查看文檔此屬性是獲取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ù)會進行更新