向 NSRunLoop 添加计时器的正确方法

Correct way to add a timer to NSRunLoop

我正在尝试向 NSRunLoop 添加计时器。我的预期结果是,一旦将计时器添加到循环中,它们就会开始相互独立地倒计时。

我的代码现在看起来像这样:

    var timer = NSTimer()
    let mainRunLoop:NSRunLoop = NSRunLoop()

    func blurViewActive(gestureRecognizer:UIGestureRecognizer) {
        if (gestureRecognizer.state == UIGestureRecognizerState.Began){
            println("STATE BEGAN")
            var point = gestureRecognizer.locationInView(self.tv)
            if let indexPath = self.tv.indexPathForRowAtPoint(point){
                let data = messageList[indexPath.row] as Messages
                if let theCell = self.tv.cellForRowAtIndexPath(indexPath) as? TableViewCell{

                    self.timer = NSTimer(timeInterval: 1, target: self, selector: "updateCounter", userInfo: nil, repeats: true)

                    self.mainRunLoop.addTimer(timer, forMode: NSRunLoopCommonModes)
                    mainRunLoop.run()
                }
        }
}
}
    var counter = 10
    func updateCounter(){
        if counter == 0{
        timer.invalidate()
        }else{
        counter = --counter
        println(counter)
        }
    }

现在,按下我的按钮时似乎没有任何反应。我的理解是,一旦计时器被添加到 运行 循环中,它将独立启动 运行ning。

任何有关如何正确完成此操作的建议将不胜感激。

定时器方法总是有一个参数,即定时器对象本身。所以 "updateCounter" 作为选择器可能是错误的。

你的代码有两个问题。

首先,mainRunLoop.run()阻塞了主线程。 使用你自己的 runloop 有点棘手,但实际上在这里没有必要。 您可以使用

在主运行循环上创建一个计时器 运行
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: "updateCounter", userInfo: nil, repeats: true)

并完全删除您的 mainRunLoop

另一个问题是您使用单个计时器变量和计数器。 如果您希望每个 table 视图行都有独立的计时器,那么您需要多个独立的计时器和计数器变量。

一种可能的解决方案是使用两个词典

var timerDict : [ NSIndexPath : NSTimer ] = [:]
var counterDict : [ NSIndexPath : Int ] = [:]

其中存储每个活动倒计时的计时器和当前计数器, 使用索引路径作为键。

长按时,如果有计时器,您将检查第一本词典 此行已处于活动状态,如果没有,请创建并启动一个新行:

    if timerDict[indexPath] == nil {
        // No timer running for this row, start a new one:
        counterDict[indexPath] = 10
        timerDict[indexPath] = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: "updateCounter:",
             userInfo: indexPath, repeats: true)
    }

请注意,索引路径作为 userInfo: 参数传递给 定时器。然后回调方法可以检索索引路径 从传递的 timer 参数并相应地采取行动:

func updateCounter(timer : NSTimer) {
    if let indexPath = timer.userInfo as? NSIndexPath {
        if var counter = counterDict[indexPath] {
            if counter == 0 {
                // stop timer and remove from dictionaries:
                timer.invalidate()
                timerDict.removeValueForKey(indexPath)
                counterDict.removeValueForKey(indexPath)
                println("indexPath: \(indexPath) DONE")
           } else {
                // decrement counter and update dictionary:
                --counter
                println("indexPath: \(indexPath) counter: \(counter)")
                counterDict[indexPath] = counter
            }
        }
    }
}

另请注意(正如@gnasher729 在他的回答中所说),正确的类型 定时器回调是

func updateCounter(timer : NSTimer) { ... }

使用相应的选择器 "updateCounter:" 后跟一个冒号。