6

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.

rmaddy
  • 314,917
  • 42
  • 532
  • 579

2 Answers2

10

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.

rmaddy
  • 314,917
  • 42
  • 532
  • 579
  • Where should I place prepareForReuse()? – Christian Ray Leovido Nov 01 '16 at 00:54
  • In your cell class. – rmaddy Nov 01 '16 at 00:57
  • It works but whenever I go to another view controller, all cells add up and speed up. I haven't mentioned that this LiveViewCell appears in all viewControllers – Christian Ray Leovido Nov 01 '16 at 15:19
  • Did you do everything I stated in my answer? Did you make the variables into proper instance variables instead of globals? Did you add the `deinit` method? Did you add the `prepareForReuse`? – rmaddy Nov 01 '16 at 16:45
  • I did everything except for the `deinit` method. Here I have written `timer?.invalidate()`. Right now, the problem is that all timers don't share the same new time in every `viewController` and start again from 300. It seems like they are independent, but don't share the same new time. – Christian Ray Leovido Nov 01 '16 at 20:13
  • Your original question has been answered and solved, correct? If you now have a new issue I suggest you close this question (accept the answer if appropriate), and post a new question with specific details about your new issue. – rmaddy Nov 01 '16 at 20:15
  • in the `deinit`, I don't need to set it to `nil` right? Just invalidating is enough right? – mfaani Sep 22 '18 at 19:37
0

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 ""
        }
    }
}
Hardik Thakkar
  • 15,269
  • 2
  • 94
  • 81