从调度更新时,UIProgressView 不会更新进度

UIProgressView won't update progress when updated from a dispatch

我正在尝试让进度条充当计时器并从 15 秒开始倒计时,这是我的代码:

private var timer: dispatch_source_t!
private var timeRemaining: Double = 15

override public func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)
    profilePicture.layer.cornerRadius = profilePicture.bounds.width / 2

    let queue = dispatch_queue_create("buzz.qualify.client.timer", nil)
    timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue)
    dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 10 * NSEC_PER_MSEC, 5 * NSEC_PER_MSEC)
    dispatch_source_set_event_handler(timer) {
        self.timeRemaining -= 0.01;
        self.timerBar.setProgress(Float(self.timeRemaining) / 15.0, animated: true)
        print(String(self.timerBar.progress))
    }
    dispatch_resume(timer)
}

print() 打印出正确的结果,但进度条从不更新,有时它会在大约 12-15% 满时进行一次更新,然后只跳到那里,然后什么都不做。

怎样才能让这个条稳定的往下流,然后在定时器结束的时候执行一个任务而不阻塞UI线程

您必须始终在主线程上更新 UI。我不是 Swift 专家,但您目前似乎正在尝试在后台更新 UIProgressView

尝试像这样更新主队列上的 UIProgressView

dispatch_async(dispatch_get_main_queue(),^{
    self.timerBar.setProgress(Float(self.timeRemaining) / 15.0, animated: true)
    print(String(self.timerBar.progress))
})

在 siburb 的回答中,他正确地指出应该确保 UI 更新发生在主线程上。

但我有一个次要观察结果,即您每秒进行 100 次更新,这样做没有意义,因为最大屏幕刷新率为每秒 60 帧。

但是,显示器 link 就像一个计时器,只不过它 link 设置为屏幕刷新率。你可以这样做:

var displayLink: CADisplayLink?
var startTime: CFAbsoluteTime?
let duration = 15.0

func startDisplayLink() {
    startTime = CFAbsoluteTimeGetCurrent()
    displayLink = CADisplayLink(target: self, selector: "handleDisplayLink:")
    displayLink?.addToRunLoop(NSRunLoop.mainRunLoop(), forMode: NSRunLoopCommonModes)
}

func stopDisplayLink() {
    displayLink?.invalidate()
    displayLink = nil
}

func handleDisplayLink(displayLink: CADisplayLink) {
    let percentComplete = Float((CFAbsoluteTimeGetCurrent() - startTime!) / duration)
    if percentComplete < 1.0 {
        self.timerBar.setProgress(1.0 - percentComplete, animated: false)
    } else {
        stopDisplayLink()
        self.timerBar.setProgress(0.0, animated: false)
    }
}

override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)

    startDisplayLink()
}

或者,如果您在后台线程上执行某些操作,希望 post 更新到 UIProgressView 的速度快于主线程可以为它们提供服务的速度,那么我会 post使用 DISPATCH_SOURCE_TYPE_DATA_ADD.

类型的调度源到主线程

但是,如果您只是想在某个固定时间段内更新进度视图,显示 link 可能比计时器更好。