How to update UITableViewCells using NSTimer and NSNotificationCentre in Swift

NOTE. Ask answers in Swift .

What am I trying to do:

  • Update table cells to every 1 second and display a real-time countdown.

How am I doing it now:

  • I have a table view with cells that contain a label.

  • When the cells are generated, they call a function that calculates the time between today and the saved target date, and displays the countdown time remaining on the label.

  • To keep the cells updated every time, I use NSTimer , which reloads the table view every second.

  • PROBLEM: The countdown works, but when I try to perform the β€œScroll to NSTimer ” action, since NSTimer reloads the table view, it resets pulled out the cell so that it is no longer punched, and, of course, this is not acceptable for end users.

What I'm trying / Potential solutions

  • I heard that the best way to do this is to use the NSNotificationCenter , which is launched by NSTimer and add listeners to each cell. Every 1 second, a "broadcast" is sent from the NSNotificationCenter , the cell will receive a "broadcast" and will update the label.

  • I tried adding a listener to each cell in the piece of code where it generates a new cell:

     // Generate the cells override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { var cell = tableView.dequeueReusableCellWithIdentifier("Tablecell", forIndexPath: indexPath) as UITableViewCell let countdownEntry = fetchedResultController.objectAtIndexPath(indexPath) as CountdownEntry // MARK: addObserver to "listen" for broadcasts NSNotificationCenter.defaultCenter().addObserver(self, selector: "updateCountdownLabel", name: mySpecialNotificationKey, object: nil) func updateCountdownLabel() { println("broadcast received in cell") if let actualLabelCountdown = labelCountdown { // calculate time between stored date to today date var countdownValue = CountdownEngine.timeBetweenDatesRealTime(countdownEntry.date) actualLabelCountdown.text = countdownValue } } 
  • but, apparently, the selector-observer can configure functions at the controller level, and not in the cell. (I cannot get the "updateCountdownLabel" to run inside the cell. When I create a function with the same name at the level of the level controller, I can call the function)

Questions

  • So my question is: is this correct?
  • If so, how can I get the listener to run the function at the cell level?
  • If not, what is the best way to achieve this real-time countdown without interrupting Swipe on Cell actions?

Thanks in advance for your help!

+6
source share
2 answers

This may be a solution that does not reload the cells at all. Just make the cells listen for the update notification and change their label accordingly. I assume that you are a subclass of UITableViewCell and assign the property storedDate to the property. This property will be set when preparing the cell.

The timer just starts the notification.

Do not forget to unregister a cell from the notification center in dealloc

Here is a brief example.

A view controller containing your TableView:

 class ViewController: UIViewController, UITableViewDataSource { @IBOutlet weak var tableView: UITableView! var timer: NSTimer! //MARK: UI Updates func fireCellsUpdate() { let notification = NSNotification(name: "CustomCellUpdate", object: nil) NSNotificationCenter.defaultCenter().postNotification(notification) } //MARK: UITableView Data Source func numberOfSectionsInTableView(tableView: UITableView) -> Int { return 1 } func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 10 } func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let cellIdentifier = "CustomCell" let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as! CustomTableViewCell cell.timeInterval = 20 return cell } //MARK: View Lifecycle override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. self.timer = NSTimer(timeInterval: 1.0, target: self, selector: Selector("fireCellsUpdate"), userInfo: nil, repeats: true) NSRunLoop.currentRunLoop().addTimer(self.timer, forMode: NSRunLoopCommonModes) } deinit { self.timer?.invalidate() self.timer = nil } } 

Custom Cell Subclass:

 class CustomTableViewCell: UITableViewCell { @IBOutlet weak var label: UILabel! var timeInterval: NSTimeInterval = 0 { didSet { self.label.text = "\(timeInterval)" } } //MARK: UI Updates func updateUI() { if self.timeInterval > 0 { --self.timeInterval } } //MARK: Lifecycle override func awakeFromNib() { super.awakeFromNib() // Initialization code let notificationCenter = NSNotificationCenter.defaultCenter() notificationCenter.addObserver(self, selector: Selector("updateUI"), name: "CustomCellUpdate", object: nil) } deinit { NSNotificationCenter.defaultCenter().removeObserver(self) } } 

I am sure this example does not match your application logic. Just showing how everything glows together.

+13
source

As Ian MacDonald said, you should avoid reloading the cell when the timer is ticking, if you scroll. Also uncheck NSNotification, as It and timer essentially do arrogant

+1
source

Source: https://habr.com/ru/post/982541/


All Articles