Reputation: 31
I want to make tableView and after tap on cell to change text in cell and to do it inside one function. In one of first versions of Swift it worked and now it just changed after finishing
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
this is a sample of code:
import UIKit
class TableViewController: UITableViewController {
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")
cell?.textLabel?.text = "cell \(indexPath.row + 1)"
return cell!
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath)
cell?.textLabel?.text = "hello"
sleep(2)
cell?.textLabel?.text = "bye"
sleep(2)
}
}
I want it to work like this: tap on cell -> cell text changed to "hello", waits for 2 seconds, then changed to "bye", waits another 2 seconds and continues
But now it works like this: tap on cell -> waits for 4 seconds, changed to "bye" and continues
Upvotes: 3
Views: 1365
Reputation: 318794
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.
Upvotes: 1
Reputation: 3494
You can use DispatchQueue
to get the better result:
cell?.textLabel?.text = "hello"
tableView.isUserInteractionEnabled = false
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
// your code here
cell?.textLabel?.text = "bye"
tableView.isUserInteractionEnabled = true
}
Upvotes: 1