2016-01-21 5 views
1

Я пытаюсь сделать прогресс бар выступать в качестве таймера и обратный отсчет от 15 секунд, вот мой код:UIProgressView не будет обновлять прогресс при обновлении от отправки

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) 
} 

Печать() печатает правильный результат, но индикатор прогресса никогда не обновляется, в какой-то момент он будет делать одно обновление на уровне 12-15% и просто JUMP, а затем больше ничего не делать.

Как я могу заставить эту панель неуклонно стекать вниз, а затем выполнить задачу в конце таймера без блокировки потока пользовательского интерфейса.

ответ

5

В ответ siburb, он правильно указывает на то, что должен убедиться, что обновления пользовательского интерфейса произойдет в основном потоке.

Но у меня есть вторичное наблюдение, а именно, что вы делаете 100 обновлений в секунду, и нет смысла делать это так быстро, потому что максимальная частота обновления экрана составляет 60 кадров в секунду.

Однако ссылка на дисплей напоминает таймер, за исключением того, что он связан с частотой обновления экрана. Вы могли бы сделать что-то вроде:

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() 
} 

С другой стороны, если вы делаете что-то на фоне потока, который хочет отправлять обновления UIProgressView быстрее, чем основной поток может обслуживать их, то я отправлю, что основной с использованием источника отправки типа DISPATCH_SOURCE_TYPE_DATA_ADD.

Но если вы просто пытаетесь обновить представление прогресса в течение некоторого фиксированного периода времени, ссылка на изображение может быть лучше, чем таймер.

+0

Спасибо за информацию о максимальной частоте обновления 60 кадров в секунду, я не знал, что устройства закрыты. Также удобно, что это уже связано с основной нитью. Принято. – Hobbyist

+0

Я вернулся и, наконец, реализовал это, однако прогресс для бара все еще не обновляется. - Таймер выполняет логику правильно, но пользовательский интерфейс не обновляется, как должен. – Hobbyist

+0

Я только что проверил это снова, и он отлично работает. Таким образом, если он не работает для вас, вы должны проверить несколько вещей: 1. Вы нажимаете код, начинающий цикл запуска? Вы нажимаете код внутри 'handleDisplayLink'? Добавьте выражения 'print' в обоих случаях и убедитесь, что они нажимают на код, который вы так думаете. 2. Убедитесь, что вы планируете отображение ссылки на главной рабочей точке. 3. Убедитесь, что вы не делаете ничего другого, чтобы заблокировать основной поток. Не видя полного кода, его трудно диагностировать, но это будет что-то простое. – Rob

2

Вы всегда должны обновлять пользовательский интерфейс в основном потоке. Я не эксперт Swift, но похоже, что вы в настоящее время пытаетесь обновить UIProgressView в фоновом режиме.

Попробуйте обновить UIProgressView на главной очереди, как это:

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