【问题标题】:IOS: Updating Labels in a UITableViewCell using an NSTimerIOS:使用 NSTimer 更新 UITableViewCell 中的标签
【发布时间】:2015-04-16 17:42:34
【问题描述】:

我正在尝试创建一个倒计时应用程序,该应用程序将显示年数......距离即将到来的日期还有几秒钟。我希望能够在表格视图中查看几个倒计时。我有一些独立于表格视图单元格来创建倒计时的基本逻辑:

.
.
.
var timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: "countdown", userInfo: nil, repeats: true)

func  countdown() {
    let hourMinuteComponents: NSCalendarUnit =
        NSCalendarUnit.CalendarUnitYear |
        NSCalendarUnit.CalendarUnitMonth |
        NSCalendarUnit.CalendarUnitDay |
        NSCalendarUnit.CalendarUnitHour |
        NSCalendarUnit.CalendarUnitMinute |
        NSCalendarUnit.CalendarUnitSecond
    let timeDifference = userCalendar.components(
        hourMinuteComponents,
        fromDate: NSDate(),
        toDate: date1.enteredDate as NSDate,
        options: nil)
// Sample Output
    println("Year: \(timeDifference.year)")
    println("Month: \(timeDifference.month)")
    println("Day: \(timeDifference.day)")
    println("Hour: \(timeDifference.hour)")
    println("Minute: \(timeDifference.minute)")
    println("Second: \(timeDifference.second)")
}

这很好用,控制台显示倒计时。但是,当我尝试从单元格调用它时,我无法克服错误。

由于未捕获的异常而终止应用程序 'NSInvalidArgumentException',原因:'-[__NSCFTimer 行]: 无法识别的选择器发送到实例

这是无效的代码。

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    println("Index Path: \(indexPath)")
    let cell = tableView.dequeueReusableCellWithIdentifier("countDownCell") as! UITableViewCell
    let cellIndex = indexPath
    var timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: "update:", userInfo: cellIndex, repeats: true)
    //let secondlabel = cell.viewWithTag(1005) as! UILabel
    //secondlabel.text = "Test"


    return cell

}


func  update(cellIndex: NSIndexPath) {
    let hourMinuteComponents: NSCalendarUnit =
    NSCalendarUnit.CalendarUnitYear |
    NSCalendarUnit.CalendarUnitMonth |
    NSCalendarUnit.CalendarUnitDay |
        NSCalendarUnit.CalendarUnitHour |
        NSCalendarUnit.CalendarUnitMinute |
        NSCalendarUnit.CalendarUnitSecond
    let timeDifference = userCalendar.components(
        hourMinuteComponents,
        fromDate: NSDate(),
        toDate: date1.enteredDate as NSDate,
        options: nil)
    println("Year: \(timeDifference.year)")
    println("Month: \(timeDifference.month)")
    println("Day: \(timeDifference.day)")
    println("Hour: \(timeDifference.hour)")
    println("Minute: \(timeDifference.minute)")
    println("Second: \(timeDifference.second)")


    if let cell = tableView.cellForRowAtIndexPath(cellIndex) {
        let yearlabel = cell.viewWithTag(1000) as! UILabel
        let secondlabel = cell.viewWithTag(1005) as! UILabel!
    }

    secondlabel.text = "\(timeDifference.second)"


}

感谢您提供的任何帮助。我最初将我的修复尝试集中在更改我从计时器调用 update() 的方式上。我尝试显式传递单元格,然后认为尝试调用单元格的 indexPath 会更好。我还按照许多 NSTimer 帖子中的建议检查了我的语法。我认为我做对了(并且相信我已经尝试了几乎所有其他选项)。

代码在这一行中断:

if let cell = tableView.cellForRowAtIndexPath(cellIndex) { ...

【问题讨论】:

    标签: ios uitableview swift nstimer


    【解决方案1】:

    我会推荐一种不同的方法来构建您的应用程序。

    UITableView 将使用dequeueReusableCellWithIdentifier 方法“回收”其单元格。因此,如果用户要使用您的应用创建 100 次倒计时,但屏幕上最多只能看到 4 个单元格,当用户滚动时,这四个单元格将被排入表格视图的单元格缓存,然后由您的数据出列源代码。这样做是为了提高性能,以便表格视图平滑滚动。您可以在 Apple 的 Table View Programming Guide 中阅读有关此行为的更多信息。

    如果是我……

    1. 我首先要有一个可以跟踪倒计时状态的对象:倒计时开始日期、倒计时结束日期、倒计时名称等。

    2. 我会使用这些对象的数组作为表格视图表示的数据。

    3. 由于所有倒计时每秒更新一次,您可以通过询问表格视图当前显示的索引路径来更新当前显示的内容,将这些索引路径与您的倒计时对象匹配数组,然后更改所有不同标签的文本。这意味着您的视图控制器中只有一个 NSTimer 实例负责驱动更新。

    还有很多其他可能的方法来构建它,但我认为尝试管理许多计时器实例会导致一些白发。

    【讨论】:

    • 感谢您的精彩提示。那就是我打算去的地方。我对此很陌生,并且正在努力一次完成一件工作。我已经有了一个包含描述和倒计时结束日期的对象的框架。所有的计数器从现在开始计数,直到它们结束,然后它们就会过期。我正计划用这些对象填充一个数组并利用它来实例化每个单元格。
    • 从概念上讲,我提倡您拥有一个负责更新“整个屏幕”的计时器实例,而不是拥有多个负责更新“屏幕的这一小部分”的计时器实例。 "我认为当你跟随后者进入兔子洞时,很难保持所有计时器的有序性,这可能会导致奇怪和意想不到的行为。
    • 我想我明白你来自哪里。通过为每一行创建一个计时器,我可以在该行更改时遇到问题。例如,我只是试图删除一个正在运行的行并且计时器崩溃了,因为它不再有单元格可以引用。非常感谢您的指导。
    • 是的!这正是我的观点。我不认为我的建议更容易考虑——一个更新所有这些东西的计时器与一个需要更新的东西的计时器:前者并不明显。但是,考虑到UITableView 的工作原理,我认为您将花费更少的精力来对抗构建块。我链接的那个文档非常密集,但希望对让您了解表格视图非常有用。
    • @edlaney05 我太新了,无法对您的指导进行投票,但我整理出在 viewdidload() 上实例化了一个计时器,然后使用:let cells = tableView.indexPathsForVisibleRows() as Array!for i in cells 我可以更新一个计时器的数据
    【解决方案2】:

    传递给NSTimerscheduledTimerWithTimeInterval(_:target:selector:userInfo:repeats:) 的选择器接收到NSTimer 对象,而不是NSIndexPath

    也就是说,你的update(_:)方法应该定义为

    func update(timer: NSTimer) {
        cellIndex = timer.userInfo as! NSIndexPath
        ...
    }
    

    查看method's documentation了解更多信息

    【讨论】:

    • 马科斯——非常感谢!这解决了我的问题。我看过文档。我担心我总是很难解析它。但是,重新查看它,知道我在寻找什么,我确实找到了这个花絮,它应该把我推向正确的方向:“计时器将自己作为参数传递,因此该方法将采用以下模式”
    【解决方案3】:

    更简单的方法是继续运行您的 nstimer az it isvand 并调用 countdown 方法并在最后重新加载您的 UITableView。每次重新加载之前将值保留在 NSArray 中,并将其作为对象传递给 UITableView。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-11-07
      • 1970-01-01
      • 1970-01-01
      • 2021-09-18
      • 1970-01-01
      • 2011-08-17
      • 2021-02-05
      • 2016-07-07
      相关资源
      最近更新 更多