Reputation: 2244
I want to show countdown timer on every cell of tableview.
Steps
I have offer list from server with expire time. So after then I want show this expire countdown timer with each of cell on UITableView.
I am doing something like this. But no result.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! TimeCell
cell.expiryTimeInterval = 2
return cell
}
**My Cell Class where I am starting timer. Printing Step1 & Step2 only **
class TimeCell: UITableViewCell {
@IBOutlet weak var myLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
private var timer: Timer?
private var timeCounter: Double = 0
var expiryTimeInterval : TimeInterval? {
didSet {
startTimer()
print("Step 1 \(expiryTimeInterval!)")
}
}
private func startTimer() {
if let interval = expiryTimeInterval {
timeCounter = interval
print("Step 2 \(interval)")
if #available(iOS 10.0, *) {
timer = Timer(timeInterval: 1.0,
repeats: true,
block: { [weak self] _ in
guard let strongSelf = self else {
return
}
strongSelf.onComplete()
})
} else {
timer = Timer(timeInterval: 1,
target: self,
selector: #selector(onComplete),
userInfo: nil,
repeats: true)
}
}
}
@objc func onComplete() {
print("Step 3")
guard timeCounter >= 0 else {
timer?.invalidate()
timer = nil
return
}
myLabel.text = String(format: "%d", timeCounter)
timeCounter -= 1
}
}
Upvotes: 3
Views: 6024
Reputation: 573
import UIKit
class CompetitionTableViewCell: UITableViewCell {
//MARK:- IBOutlets
@IBOutlet weak var mainView: UIView!
@IBOutlet weak var imageCompetition: UIImageView!
@IBOutlet weak var btnStart: UIButton!
@IBOutlet weak var lblTime: UILabel!
@IBOutlet weak var lblCompititor: UILabel!
@IBOutlet weak var lblPoints: UILabel!
@IBOutlet weak var lblNameCompetition: UILabel!
@IBOutlet weak var btnGoForTest: UIButton!
//MARK:- Variable
private var timer: Timer?
private var timeCounter: Double = 0
//MARK:- IBActions
var expiryTimeInterval: TimeInterval? {
didSet {
if timer == nil
{
startTimer()
RunLoop.current.add(timer!, forMode: .commonModes)
}
}
}
private func startTimer() {
if let interval = expiryTimeInterval {
timeCounter = interval
if #available(iOS 10.0, *) {
timer = Timer(timeInterval: 1.0,
repeats: true,
block: { [weak self] _ in
guard let strongSelf = self else {
return
}
strongSelf.onComplete()
})
} else {
timer = Timer(timeInterval: 1.0,
target: self,
selector: #selector(onComplete),
userInfo: nil,
repeats: true)
}
}
}
@objc func onComplete() {
guard timeCounter >= 0 else {
btnGoForTest.isUserInteractionEnabled = false
lblTime.text = "Time ended."
timer?.invalidate()
timer = nil
return
}
btnGoForTest.isUserInteractionEnabled = true
let hours = Int(timeCounter) / 3600
let minutes = Int(timeCounter) / 60 % 60
let seconds = Int(timeCounter) % 60
lblTime.text = String(format:"%02i:%02i:%02i", hours, minutes, seconds)
timeCounter -= 1
print("\(timeCounter)")
}
override func awakeFromNib() {
super.awakeFromNib()
btnStart.setCornerRadiusForBtn()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
override func prepareForReuse() {
super.prepareForReuse()
timer?.invalidate()
timer = nil
}
}
/***** code for your listing tableview ***************/
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CompetitionTableViewCell") as! CompetitionTableViewCell
cell.btnGoForTest.tag = indexPath.row
cell.lblTime.tag = indexPath.row
//cell.lblTime.tag = indexPath.row
let dicttemp : Dictionary = arrAllCoursesList[indexPath.row] as! Dictionary<String,Any>
if let getTime = dicttemp["endtime"] as? Int
{
cell.lblTime.text = ""
let getTime1 : Double = Double(getTime)
cell.expiryTimeInterval = getTime1
}
return cell
}
Upvotes: 0
Reputation: 896
@Umair Aamir I implemented your solution, and it's working properly, but, next lines of code are critical, so timer will not be initialized each time when you scroll...
Its not enough to check if timer == nil
override func prepareForReuse() {
super.prepareForReuse()
timer?.invalidate()
timer = nil
}
You should add this in your cell subclass. Hope it helps!
Upvotes: 3
Reputation: 1644
I’d rather suggest you to create a custom cell and in that cell class, create a timer based on your value from server. This way every cell would have its own timer. That’s what you probably want. In your current implementation, your timer handler is unaware of cell or its row number.
class MyCustomCell: UITableViewCell {
@IBOutlet weak private var myLabel: UILabel!
private var timer: Timer?
private var timeCounter: Double = 0
var expiryTimeInterval: TimeInterval? {
didSet {
startTimer()
}
}
private func startTimer() {
if let interval = expiryTimeInterval {
timeCounter = interval
if #available(iOS 10.0, *) {
timer = Timer(timeInterval: 1.0,
repeats: true,
block: { [weak self] _ in
guard let strongSelf = self else {
return
}
strongSelf.onComplete()
})
} else {
timer = Timer(timeInterval: 1.0,
target: self,
selector: #selector(onComplete),
userInfo: nil,
repeats: true)
}
}
}
@objc func onComplete() {
guard timeCounter >= 0 else {
timer?.invalidate()
timer = nil
return
}
myLabel.text = String(format: "%d", timeCounter)
timeCounter -= 1
}
}
Usage
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! MyCustomCell
cell.expiryTimeInterval = 10
return cell
}
Go to you cell nib
or storyboard
and change the class name for TimeCell
to MyCustomCell
Upvotes: 8