无效并创建计时器的新实例

时间:2017-02-10 22:15:48

标签: ios swift timer

我有一个计时器,我在一个闭包内运行,以帮助执行一些ui更新(进度条)。它适用于第一个项目,但我希望它适用于多个项目。因此,每次运行闭包时,都会运行一个新的计时器实例和新的userInfo。

    // dispatch and add run loop to get it to fire
    DispatchQueue.main.async(execute: {
        timer = Timer.init(timeInterval: 0.25, target: self, selector: #selector(self.downloadTimer(_:)), userInfo: [innerCell, collectionView, indexPath], repeats: true)
        RunLoop.main.add(timer, forMode: RunLoopMode.defaultRunLoopMode)
    })

当下面的这个功能完成后,我希望重新创建计时器并将新的userInfo传递给选择器..

func downloadTimer(_ timer: Timer) {

    // need to pass arguments to timer as array inside userInfo
    // then create dictionary and extract

    let dict = timer.userInfo as! NSArray
    let cell = dict[0] as! InnerCollectionCell
    let collectionView = dict[1] as! UICollectionView
    let indexPath = dict[2] as! IndexPath


    // the index path isnt getting reset

    // without casting the progress bar in each cell was displaying when coming back to DownloadCollectionController
    if let downloadingCell = collectionView.cellForItem(at: indexPath) as? InnerCollectionCell {

        DispatchQueue.main.async(execute: {

            downloadingCell.progressBar.isHidden = false
            downloadingCell.contentView.bringSubview(toFront: cell.progressBar)
            downloadingCell.progressBar.setProgress(downloadProgress, animated: true)
            downloadingCell.setNeedsDisplay()


            if downloadProgress >= 1.0 {
                print("TIMER STOPPED")
                downloadingCell.progressBar.isHidden = true
                //downloadQueue.operations[0].cancel()
                timer.invalidate()
                collectionView.reloadItems(at: [indexPath])

            }
        })
    }

}

重新运行包含timer = Timer的闭包,但新的用户信息未传递给计时器,它只是继续运行原始userInfo。如果我使计时器无效,它就不能被解雇了?

我已尝试在本地创建它以进行初始触发:

var timer = Timer()
timer = Timer.init(timeInterval: 0.25, target: self, selector: #selector(self.downloadTimer(_:)), userInfo: [innerCell, collectionView, indexPath], repeats: true)

但它没有重新创建计时器,我希望能够在用户离开viewcontroller时停止它。

有没有更好的方法来实现这一目标?我知道你不应该退休计时器。每次都必须有一种创建它的唯一实例的方法。

----编辑----

这是索引路径打印的内容:

[0, 5]
[0, 5]
[0, 5]

[0, 5]
[0, 2]
[0, 5]
[0, 2]

[0, 1]
[0, 1]
[0, 1]
[0, 1]

所以它需要一段时间才能使计时器失效,并且在那段时间内计时器已被重新触发,并且该函数正在使用旧的索引路径,直到progress = 1

----编辑----

唯一发生失效的地方是进度> = 1

            if downloadProgress >= 1.0 {
                timer.invalidate()
                downloadingCell.progressBar.isHidden = true
                downloadingCell.progressBar.progress = 0
                collectionView.reloadData()
            }

然后因为如果单元满足某些条件,则从cellForItemAt触发定时器,当调用collectionView.reloadData()时,应重新验证定时器并发送更新的userInfo。

所以这是cellForItemAt中的块:

for operation in downloadQueue.operations {
        if operation.name == self.multiPartArray[collectionView.tag][indexPath.item].name  {
            // edits for all queued operations
            innerCell.spinner.isHidden = false
            innerCell.spinner.startAnimating()
            innerCell.contentView.bringSubview(toFront: innerCell.spinner)
            innerCell.spinner.activityIndicatorViewStyle = .gray
            innerCell.spinner.color = UIColor.darkGray

        // hide the progress on all in queue it will be pushed to front when needed
        innerCell.progressBar.isHidden = true

        if operation.name == downloadQueue.operations[0].name {
            // edits for the active downlaod in queue

            // hide the spinner for currently downloading cell
            // innerCell.spinner.isHidden = true
            innerCell.spinner.isHidden = false
            innerCell.spinner.startAnimating()
            innerCell.contentView.bringSubview(toFront: innerCell.spinner)
            innerCell.spinner.activityIndicatorViewStyle = .gray
            innerCell.spinner.color = UIColor.blue

                DispatchQueue.main.async(execute: {
                    let timer = Timer.init(timeInterval: 0.5, target: self, selector: #selector(self.downloadTimer(_:)), userInfo: [innerCell, collectionView, indexPath, collectionView.tag], repeats: true)
                    RunLoop.main.add(timer, forMode: RunLoopMode.defaultRunLoopMode)
                })

            break
        } else {
            // edits spinner that isnt currently downloading
            innerCell.progressBar.isHidden = true
            innerCell.spinner.color = UIColor.darkGray
        }
    } else {
        // edits the spinner that isnt in queue
        innerCell.progressBar.isHidden = true
        innerCell.spinner.color = UIColor.darkGray
    }
}

我意识到它不是最优雅的解决方案,但是我已经有了嵌套的集合视图,这些视图可能没有完全正确设置,因此在这种情况下解决这个问题。

下载游戏:

func downloadTimer(_ timer: Timer) {

    // need to pass arguments to timer as array inside userInfo
    // then create dictionary and extract
    let dict = timer.userInfo as! NSArray
    let cell = dict[0] as! InnerCollectionCell
    let collectionView = dict[1] as! UICollectionView
    let indexPath = dict[2] as! IndexPath
    let tag = dict[3] as! Int

    print(indexPath)

    // without casting the progress bar in each cell was displaying when coming back to DownloadCollectionController
    if let downloadingCell = collectionView.cellForItem(at: indexPath) as? InnerCollectionCell {

       // self.outerCollectionView.reloadItems(at: [IndexPath(item: tag, section: 0)])

        DispatchQueue.main.async(execute: {

            downloadingCell.spinner.isHidden = true
            downloadingCell.progressBar.isHidden = false
            downloadingCell.contentView.bringSubview(toFront: cell.progressBar)
            downloadingCell.progressBar.setProgress(downloadProgress, animated: true)
            downloadingCell.setNeedsDisplay()

            if downloadProgress >= 1.0 {
                timer.invalidate()
                downloadingCell.progressBar.isHidden = true
                downloadingCell.progressBar.progress = 0
                collectionView.reloadData()
            }
        })
    }
}

1 个答案:

答案 0 :(得分:1)

安排和使用时间的最佳方式是:

//schedules timer at an 4 seconds interval + pass desired userInfo 
var myTimer = Timer.scheduledTimer(timeInterval: 4.0
        , target: self, selector: #selector(timerSelector), userInfo: nil, repeats: true)

//timer selector peform your actions there and  
func timerSelector(dt:Timer){  
}

//invalidate timer when needed like this
myTimer?.invalidate()

要重新安排计时器,只需再次拨打Timer.scheduledTimer