我有一个从闭包中运行的计时器来帮助执行一些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)但它不会重新创建计时器,如果用户离开视图控制器,我希望能够停止计时器。
有没有更好的方法来实现这一点?我知道你不应该让计时器退役。必须有一种方法可以每次创建一个唯一的实例。
--编辑
以下是索引路径打印的内容:
[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
}
}我意识到这不是最优雅的解决方案,但是我有可能还没有完全正确设置的嵌套集合视图,所以在这种情况下可以解决这个问题。
和downloadTimer:
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()
}
})
}
}发布于 2017-02-11 07:08:24
安排和使用时间的最佳方法如下所示:
//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
https://stackoverflow.com/questions/42169526
复制相似问题