所有 NSTimers 运行ning 在前一个无效的计时器之后停止执行而不是继续 运行

All NSTimers running after a previous timer that has been invalidated cease execution instead of continuing to run

在我的实现中,我在 UITableView 上同时使用 运行 多个计时器,当一个计时器结束时,我将其无效并允许将其从所述视图中删除。目前我遇到的问题 运行 是当一个定时器被删除时,所有其他定时器在它之后停止执行。

public class TimerManager{
    static let instance = TimerManager()
    private var delegates = [TimerDelegate]()
    var currentTimers = [TimerObject]()

    public func timerAdded(timer: TimerObject){
        self.currentTimers.append(timer)
        timer.timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: #selector(persistTimer(_:)), userInfo: "\(TimerManager.instance.currentTimers.count-1)", repeats: false)
        timer.isRunning = true
        for delegate in delegates{
            delegate.startTimer(self.currentTimers[self.currentTimers.count-1])
        }
    }

    @objc public func persistTimer(timer: NSTimer){
        if let indexString = timer.userInfo as? String{
            if let index = Int(indexString){
                if let currentTimer = self.currentTimers[safe: index]{
                    if currentTimer.isRunning{
                        currentTimer.timeDuration -= 1
                        for delegate in delegates{
                            delegate.timerStarted(index)
                        }
                    }
                }
            }
        }

    }

    public func addTime(timerId: Int, amount: Int){
        for delegate in delegates{
            delegate.addTimeToTimer(timerId, amount: amount)
            UIApplication.sharedApplication().cancelLocalNotification(currentTimers[timerId].notification)
            currentTimers[timerId].notification.fireDate = NSDate(timeIntervalSinceNow: Double(currentTimers[timerId].timeDuration))
        }
    }

    public func subtractTime(timerId: Int, amount: Int){
        for delegate in delegates{
            delegate.subtractTimeFromTimer(timerId, amount: amount)
            UIApplication.sharedApplication().cancelLocalNotification(currentTimers[timerId].notification)
            currentTimers[timerId].notification.fireDate = NSDate(timeIntervalSinceNow: Double(currentTimers[timerId].timeDuration))
        }
    }

    public func removeDelegate(removedDelegate: TimerDelegate){
        for i in 0..<delegates.count{
            if delegates[i] === removedDelegate{
                delegates.removeAtIndex(i)
            }
        }
    }

    public func cancelTimer(timerId: Int){
        let currentTimer = currentTimers[timerId]
        currentTimer.timer.invalidate()
        UIApplication.sharedApplication().cancelLocalNotification(currentTimer.notification)
        currentTimers.removeAtIndex(timerId)
    }

    private init() {}
}

上面就是我的TimerManager单例。 以下是我的删除逻辑:

//Delete cell functionality
    public override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
        if( editingStyle == .Delete){
            let screenSize: CGRect = UIScreen.mainScreen().bounds
            let screenWidth = screenSize.width
            let screenHeight = screenSize.height
            let deletedTimer = TimerManager.instance.currentTimers[indexPath.row]
            if (deletedTimer.isRunning){
                deletedTimer.timer.invalidate()// <-- invalidates all timers for some reason
                UIApplication.sharedApplication().cancelLocalNotification(deletedTimer.notification)
            }
            TimerManager.instance.currentTimers.removeAtIndex(indexPath.row)
            self.tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
            self.tableView.frame = CGRect.init(x: 0, y: 100, width: screenWidth, height: screenHeight * 0.0625 * CGFloat(TimerManager.instance.currentTimers.count))
        }
    }

这是我启动计时器的方式:

public func startTimer(timer: TimerObject) {
        let newIndexPath = NSIndexPath(forRow: TimerManager.instance.currentTimers.count-1, inSection: 0)
        self.tableView.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: .Bottom)
        let screenSize: CGRect = UIScreen.mainScreen().bounds
        let screenWidth = screenSize.width
        let screenHeight = screenSize.height
        self.view.frame = CGRectMake(0, 100, screenWidth, screenHeight * 0.0625 * CGFloat(TimerManager.instance.currentTimers.count))
        self.view.alpha = 0.95

    }

这是我的 TimerObject 的组成部分:

public class TimerObject{
    var alreadySet = false
    var isRunning = false
    var timer = NSTimer()
    var timeDuration = Int()
    var timerString = String()
    var doneString = String()
    let notification = UILocalNotification()

    init(timeDuration: Int, timerString: String, doneString: String){
        self.timeDuration = timeDuration
        self.timerString = timerString
        self.doneString = doneString
        notification.alertBody = doneString
    }
}

TimerObject 的实例化:

private func setTextLinks(string: String){
        if let timersForInstruction = parsedOutTimers{
            stepTextView.linkTextAttributes = [NSForegroundColorAttributeName : UIColor.redColor()]
            for timer in timersForInstruction{
                stepTextView.addLink(string.substringFromRange(timer.range)) {
                    let newTimer = TimerObject.init(timeDuration: timer.lowerBound * 60, timerString: self.stepTitle
                    newTimer.notification.fireDate = NSDate(timeIntervalSinceNow: Double(newTimer.timeDuration))
                    TimerManager.instance.timerAdded(newTimer)
                }
            }
            stepTextView.processLinks()
        }
    }

我会说这是使用它的地方:

//where we update the cells
    public func timerIsRunning(timerIndex: Int){
        if let currentTimer = TimerManager.instance.currentTimers[safe: timerIndex]{
            guard let cell = self.tableView.cellForRowAtIndexPath(NSIndexPath.init(
                forRow: timerIndex,
                inSection: 0)) as? TimerCell else { return }
            if currentTimer.timeDuration > 0{
                currentTimer.timeDuration -= 1
                cell.timerDisplay.text = "\(currentTimer.timerString) \t\t\t - \(currentTimer.timeDuration / 60):\((currentTimer.timeDuration % 60).format("02")) + "
                cell.timerDisplay.processLinks()
            }else if(currentTimer.timeDuration <= 0){
                currentTimer.isRunning = false
                cell.timerDisplay.text = "Finished"
                currentTimer.timer.invalidate()
                currentTimer.notification.fireDate = NSDate(timeIntervalSinceNow: 0)
                currentTimer.notification.alertBody = currentTimer.doneString
                currentTimer.notification.alertAction = "Ok"
                if(self.isBeingPresented()){
                    NSNotificationCenter.defaultCenter().postNotificationName("timerDidFinish", object: currentTimer.notification.alertBody)
                }
                if(UIApplicationState.Background == UIApplication.sharedApplication().applicationState){
                    UIApplication.sharedApplication().scheduleLocalNotification(currentTimer.notification)
                }
            }else{
                //handle pause 
            }
        }

    }

我真诚地希望这些信息足以供您使用。 任何解决方案或替代方案都会有很大帮助。

我能够通过为每个对象实例化一个新计时器来解决我自己的问题,只有一个计时器正在更新所有对象的视图。

private var timer = NSTimer()
    var isFirstTimer = false


    public func timerAdded(newTimerObject: TimerObject){
        if(isFirstTimer){
            self.timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: #selector(persistTimers(_:)), userInfo: nil, repeats: true)
        }
        self.currentTimers.append(newTimerObject)
        newTimerObject.isRunning = true
        for delegate in delegates{
            delegate.startTimer(self.currentTimers[self.currentTimers.count-1])
        }
    }

    @objc public func persistTimers(timer: NSTimer){
        for i in 0..<self.currentTimers.count{
            if let currentTimer = currentTimers[safe: i]{
                if(currentTimer.isRunning){
                    currentTimer.timeDuration -= 1
                    for delegate in delegates{
                        delegate.timerStarted(i)
                    }
                }
            }
        }
    }

这就是我最终在我的单例声明中所做的更改,并且 我最终删除了

currentTimer.timeDuration -= 1

从 timerStarted 函数到 persistTime 函数的行...我意识到我的实现和风格有很多不足之处,但这是一个开始。毕竟 iOS 编程才 2 周。

编辑:

我添加的 "[safe: ]" 扩展用于安全索引访问:

public extension CollectionType{
    subscript (safe index: Index) -> Generator.Element? {
        return indices.contains(index) ? self[index] : nil
    }
}