如何在 Swift 中使用 NSTimer 和 NSNotificationCentre 更新 UITableViewCells
How to update UITableViewCells using NSTimer and NSNotificationCentre in Swift
注意:请在Swift中寻求答案。
我想做什么:
- 让 tableview 单元格每 1 秒更新一次并实时显示
倒计时。
我目前的表现如何:
我有一个包含标签的单元格的表格视图。
生成单元格后,它们会调用一个函数来计算今天和存储的目标日期之间的时间,并在标签中显示剩余的倒计时时间。
为了让细胞每秒更新一次标签,我
使用每秒重新加载 tableview 的 NSTimer
。
问题: 倒计时有效,但是当我尝试执行滑动删除操作时,因为 NSTimer
重新加载了表格视图,它重置
swiped cell 让它不再被刷,当然这对于最终用户来说是不能接受的。
我正在尝试什么/可能的解决方案
我听说更好的方法是使用由 NSTimer
触发的 NSNotificationCenter
并向每个单元格添加侦听器。每 1 秒从 NSNotificationCenter
发送一个 "broadcast",单元将收到 "broadcast" 并更新标签。
我尝试在生成新单元格的代码部分为每个单元格添加一个侦听器:
// 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's date
var countdownValue = CountdownEngine.timeBetweenDatesRealTime(countdownEntry.date)
actualLabelCountdown.text = countdownValue
}
}
但是观察者的选择器似乎只能定位视图控制器级别的函数,而不是单元格中的函数。 (我无法让 "updateCountdownLabel" 从单元格内触发。当我在视图控制器级别创建同名函数时,可以调用该函数)
问题
- 所以我的问题是:这是正确的方法吗?
- 如果是,我怎样才能让侦听器在单元级别触发函数?
- 如果不是,那么在不中断 Swipe on Cell 操作的情况下实现这种实时倒计时的最佳方法是什么?
在此先感谢您的帮助!
正如 Ian MacDonald 所建议的那样,如果您正在滑动,则应避免在计时器滴答作响时重新加载单元格。同时删除 NSNotification,因为它和计时器本质上是在做同样的事情
这可能是一种根本不重新加载单元格的解决方案。只需让单元格收听更新通知并相应地更改其标签即可。
我假设您将 UITableViewCell
子类化并给单元格一个 storedDate
属性。您将在准备单元格时设置 属性。
计时器只会触发通知。
记得在dealloc
的通知中心注销手机
这是一个简单的例子。
包含您的 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
}
}
自定义单元格子类:
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)
}
}
我很确定这个例子不符合您应用的逻辑。只是展示事物是如何一起发光的。
注意:请在Swift中寻求答案。
我想做什么:
- 让 tableview 单元格每 1 秒更新一次并实时显示 倒计时。
我目前的表现如何:
我有一个包含标签的单元格的表格视图。
生成单元格后,它们会调用一个函数来计算今天和存储的目标日期之间的时间,并在标签中显示剩余的倒计时时间。
为了让细胞每秒更新一次标签,我 使用每秒重新加载 tableview 的
NSTimer
。问题: 倒计时有效,但是当我尝试执行滑动删除操作时,因为
NSTimer
重新加载了表格视图,它重置 swiped cell 让它不再被刷,当然这对于最终用户来说是不能接受的。
我正在尝试什么/可能的解决方案
我听说更好的方法是使用由
NSTimer
触发的NSNotificationCenter
并向每个单元格添加侦听器。每 1 秒从NSNotificationCenter
发送一个 "broadcast",单元将收到 "broadcast" 并更新标签。我尝试在生成新单元格的代码部分为每个单元格添加一个侦听器:
// 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's date var countdownValue = CountdownEngine.timeBetweenDatesRealTime(countdownEntry.date) actualLabelCountdown.text = countdownValue } }
但是观察者的选择器似乎只能定位视图控制器级别的函数,而不是单元格中的函数。 (我无法让 "updateCountdownLabel" 从单元格内触发。当我在视图控制器级别创建同名函数时,可以调用该函数)
问题
- 所以我的问题是:这是正确的方法吗?
- 如果是,我怎样才能让侦听器在单元级别触发函数?
- 如果不是,那么在不中断 Swipe on Cell 操作的情况下实现这种实时倒计时的最佳方法是什么?
在此先感谢您的帮助!
正如 Ian MacDonald 所建议的那样,如果您正在滑动,则应避免在计时器滴答作响时重新加载单元格。同时删除 NSNotification,因为它和计时器本质上是在做同样的事情
这可能是一种根本不重新加载单元格的解决方案。只需让单元格收听更新通知并相应地更改其标签即可。
我假设您将 UITableViewCell
子类化并给单元格一个 storedDate
属性。您将在准备单元格时设置 属性。
计时器只会触发通知。
记得在dealloc
这是一个简单的例子。
包含您的 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
}
}
自定义单元格子类:
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)
}
}
我很确定这个例子不符合您应用的逻辑。只是展示事物是如何一起发光的。