This is actually a more complicated task than it seems.
First off, never sleep on the main queue. This locks up the main thread and makes your app appear unresponsive.
You need to have a data model to keep track of the title for each row and in this case, a timer so known when to update a row.
You also need to deal with the user tapping a row and then scrolling that row off screen and back.
It would also help to allow a user to tap several rows and have each update as needed. And you need to deal with the user leaving the table view during the delay between row updates.
Here is one solution that supports all of these issues.
class TableViewController: UITableViewController {
struct RowData {
var title: String
var timer: Timer?
init(_ title: String) {
self.title = title
}
}
var rowData = [RowData]()
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return rowData.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") ?? UITableViewCell(style: .default, reuseIdentifier: "cell")
let data = rowData[indexPath.row]
cell.textLabel?.text = data.title
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
var data = rowData[indexPath.row]
if data.timer == nil {
// No timer for this row - set to "hello" and start a timer
let timer = Timer(timeInterval: 2, target: self, selector: #selector(timerTick), userInfo: indexPath, repeats: false)
RunLoop.main.add(timer, forMode: .commonModes) // Allow timer to work while scrolling
data.title = "hello"
data.timer = timer
rowData[indexPath.row] = data
tableView.reloadRows(at: [ indexPath ], with: .fade)
}
}
@objc func timerTick(_ timer: Timer) {
// Update the row to "bye" and reset the timer
let indexPath = timer.userInfo as! IndexPath
var data = rowData[indexPath.row]
data.timer = nil
data.title = "bye"
rowData[indexPath.row] = data
tableView.reloadRows(at: [ indexPath ], with: .fade)
}
override func viewDidLoad() {
super.viewDidLoad()
// Load test data
for i in 1...50 {
rowData.append(RowData("Row \(i)"))
}
}
override func viewWillDisappear(_ animated: Bool) {
if isMovingFromParentViewController || isBeingDismissed {
// Cancel all timers
for data in rowData {
if let timer = data.timer {
timer.invalidate()
}
}
}
}
}
With a few changes you can also support the user tapping a row before it reaches the value of "bye" and have the row reset back to the original value. That is left as an exercise.