Christian Ray Leovido
Christian Ray Leovido

Reputation: 758

Multiple timers in UITableViewCell (Swift)

I have UITableViewcells that are created at different moments in time and I would like each one of them to have an independent timer that triggers when the object is added with reloadData()

This is what I have done so far

import UIKit

var timer = Timer()
var blinkStatus:Bool! = false
var time = 300


class LiveViewCell: UITableViewCell {

    let str = String(format:"%02d:%02d", (time / 60), (time % 100))
    func processTimer() {

        if time > 0 {
            time -= 1
            timeRemainingLbl.text = String(time)
        } else if time == 0 {
            timer.invalidate()
            timeRemainingLbl.text = "Due!"
            timer = Timer.scheduledTimer(timeInterval: 0.5, target: self, selector: #selector(LiveViewCell.blinkTimer), userInfo: nil, repeats: true)

        }

    }

    func blinkTimer() {
        if blinkStatus == false {
            timeRemainingLbl.textColor = UIColor(red:1.00, green:0.00, blue:0.00, alpha:1.0)
            blinkStatus = true
        } else if blinkStatus == true {
            timeRemainingLbl.textColor = UIColor.clear
            blinkStatus = false
        }

    }


    @IBOutlet weak var tableNumberLeftLabel: UILabel!
    @IBOutlet weak var guestNumbersLabel: UILabel!

    @IBOutlet weak var timeInTableLabel: UILabel!
    @IBOutlet weak var tableNumberLbl: UILabel!
    @IBOutlet weak var timeRemainingLbl: UILabel!


    var table: Table!


    func configureLiveCell(_ NT: Table) {

        layer.cornerRadius = 20
        timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(LiveViewCell.processTimer), userInfo: nil, repeats: true)
        tableNumberLbl.text = "T" + String(NT.number)
        timeRemainingLbl.text = String(time)

    }
}

The problem comes when configureLiveCell gets called whenever I create a new cell. The timer seems to speed up and I would like each timer to be independent.

Upvotes: 6

Views: 6591

Answers (2)

Hardik Thakkar
Hardik Thakkar

Reputation: 15951

Here is a code to manage timer in UITableview Cell in swift 2.0

IN ViewController

class ViewController: UIViewController, UITableViewDelegate , UITableViewDataSource{

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return 1
    }

    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 5
    }

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("TblCell", forIndexPath: indexPath) as! TblCell

        cell.selectionStyle = .None
        return cell
    }

    func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        let cell : TblCell = (tableView.cellForRowAtIndexPath(indexPath) as? TblCell)!
        cell.setupTimer(with: indexPath.row)
    }
}

IN UITableviewCell

class TblCell: UITableViewCell {
    @IBOutlet weak var lblTimer: UILabel!

    var couponTimer : NSTimer?
    var startTime : NSDate!
    var currentIndex : Int = 1

    func setupTimer(`with` indexPath: Int){
        currentIndex = indexPath + 1

        self.startTime = NSDate()

        if self.couponTimer != nil {
            self.couponTimer?.invalidate()
            self.couponTimer = nil
        }

        couponTimer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: #selector(self.calculateTime), userInfo: nil, repeats: true);

        NSRunLoop.currentRunLoop().addTimer(couponTimer!, forMode: NSRunLoopCommonModes)
        couponTimer?.fire()
    }

    func stopTimer(){
        if self.couponTimer != nil {
            self.couponTimer?.invalidate()
            self.couponTimer = nil
        }
    }

    func calculateTime() {
        let dateFormatter = NSDateFormatter()
        dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"

        let dateFormatter1 = NSDateFormatter()
        dateFormatter1.dateFormat = "yyyy-MM-dd"

        let tomorrow = NSCalendar.currentCalendar()
            .dateByAddingUnit(
                .Day,
                value: currentIndex,
                toDate: NSDate(),
                options: []
        )

        let tomorrowDate = dateFormatter1.stringFromDate(tomorrow!)
        let tomorrowActiveDate = dateFormatter.dateFromString(tomorrowDate + " " + "10:15:00")

        let currentDate   = NSDate()

        let strTimer : String = tomorrowActiveDate!.offsetFrom(currentDate)
        self.lblTimer.text = strTimer
        self.lblTimer.textColor = UIColor.whiteColor()
        self.lblTimer.backgroundColor = UIColor(red: 255.0/255.0, green: 44.0/255.0, blue: 86.0/255.0, alpha: 1.0)
    }
}

Extension

extension NSDate {

    func offsetFrom(date:NSDate) -> String {

        let dayHourMinuteSecond: NSCalendarUnit = [.Day, .Hour, .Minute, .Second]
        let difference = NSCalendar.currentCalendar().components(dayHourMinuteSecond, fromDate: date, toDate: self, options: [])

        var seconds : String = ""
        var minutes : String = ""
        var hours : String = ""
        var days : String = ""

        let tmp1 : String = String(format: "%.2d", difference.second)
        let tmp2 : String = String(format: "%.2d", difference.minute)
        let tmp3 : String = String(format: "%.2d", difference.hour)
        let tmp4 : String = String(format: "%d", difference.day)

        seconds = "\(tmp1)"
        minutes = "\(tmp2)" + ":" + seconds
        hours = "\(tmp3)" + ":" + minutes
        days = "\(tmp4)d" + " " + hours

        if difference.second >= 0 && difference.minute >= 0 && difference.hour >= 0 && difference.day >= 0 {
            return days
        }
        else {
            return ""
        }
    }
}

Upvotes: 0

rmaddy
rmaddy

Reputation: 318774

Override the prepareForReuse method.

override func prepareForReuse() {
    super.prepareForReuse()

    timer.invalidate()
}

This will be called just before a cell is returned to be used by another row. By invalidating the timer here, your configureLiveCell doesn't create yet another timer.

BTW - you should also add a deinit method and invalidate the timer there too.

You also make the timer property optional and set it to nil after you invalidate it. And of course you need to add proper checks to deal with it being an optional.

And one last change you must make is to change timer, time, and blinkStatus so they are instance variables by moving them inside the class.

Upvotes: 11

Related Questions