I am trying to create an application that would run several timers in a table view and I got stuck on the initialisation of timers. I try to adapt this cool tutorial https://www.raywenderlich.com/113835-ios-timer-tutorial#toc-anchor-002 to my needs. Right now I only want to be able to fire an appropriate timer by clicking a table view cell that would, in its turn, call updateTime() in TimerTableViewCell. I seem not to be able to instantiate several timers at the same time. When one timer is running, clicking other cells won’t result in anything. Is anybody able to let me know where I am making a mistake?
TableViewController with list of timers looks like this right now:
import UIKit
class TimerListTableViewController: UITableViewController {
// MARK: - Properties
var timers: [MyTimer] = []
var running: [String] = []
var ticker: Timer?
override func viewDidLoad() {
super.viewDidLoad()
// load fake timer array on app start
loadTimerArray()
}
// fake timer data array
func loadTimerArray() {
let timer1 = MyTimer(title: "Cucumbers", minutes: 30, seconds: 15)
let timer2 = MyTimer(title: "Eggs", minutes: 8, seconds: 0)
let timer3 = MyTimer(title: "Spagetti", minutes: 3, seconds: 0)
timers += [timer1, timer2, timer3]
}
// MARK: - TableView data source
// there will always be one section in table
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
// number of row equals number of timers in timers array
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return timers.count
}
// cell/row set up
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TimerCell", for: indexPath)
if let cell = cell as? TimerTableViewCell {
cell.timer = timers[indexPath.row]
}
return cell
}
// MARK: - TableViewDelegate
// when rows tapped
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// deselect row after tap
tableView.deselectRow(at: indexPath, animated: true)
let context = ["timerID":indexPath.row]
createTimer(with: context)
}
}
// MARK: - Timer
extension TimerListTableViewController {
@objc func updateTimer(_ timerWithContext: Timer) {
let context = timerWithContext.userInfo as! [String:Int]
let cellIndexPath = IndexPath(row: context["timerID"]!, section: 0)
if let cell = tableView.cellForRow(at: cellIndexPath) as? TimerTableViewCell {
cell.updateTime()
print(cellIndexPath)
}
}
func createTimer(with context: [String:Int]) {
let context = context
// in case theres no timer instance
if ticker == nil {
print("*** timer with \(context)")
// set timer to repeatedly call updateTimer
let ticker = Timer(timeInterval: 1.0, target: self, selector: #selector(updateTimer), userInfo: context, repeats: true)
// using commmon runloop to preven timers from freezing when scrolling
RunLoop.current.add(ticker, forMode: .common)
// add timer tolerance reduce energy impact
ticker.tolerance = 0.1
self.ticker = ticker
}
}
// called to cancel timer
func cancelTimer() {
ticker?.invalidate()
ticker = nil
}
}