Deepak Ror
Deepak Ror

Reputation: 2244

Time Countdown on each cell of UITableView in iOS

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

Answers (3)

Davender Verma
Davender Verma

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

Bonnke
Bonnke

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

Umair Aamir
Umair Aamir

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

Related Questions