Есть ли способ наблюдать изменения в fractionComplete в UIViewPropertyAnimator
Я изучал очень классный новый класс UIViewPropertyAnimator
в iOS 10. Он позволяет легко делать такие вещи, как пауза, возобновление и обратная анимация UIView во время полета. Раньше было, что вам нужно было манипулировать базовыми CAAnimations, созданной системой, чтобы сделать это с анимацией UIView.
Новый класс UIViewPropertyAnimator
имеет свойство fractionComplete
. Он варьируется от 0.0 (начало анимации) до 1.0 (конец анимации). Если вы измените значение, вы можете "счистить" анимацию от начала до конца.
У меня есть демонстрационный проект на Github, называемый KeyframeViewAnimations, который позволяет использовать слайдер для очистки UIView и CAAnimations назад и вперед, используя довольно тайные трюки CAAnimation. По мере запуска анимации слайдер перемещается от начала и до конца, чтобы показать ход анимации. Этот проект был написан в Objective-C до того, как Swift был выпущен, но основные методы одинаковы в Objective-C или Swift.
Я решил создать эквивалентный проект, используя UIViewPropertyAnimator
. Есть некоторые причуды, но это довольно прямолинейно. Единственное, что я не мог понять, как это сделать, - это наблюдать за развитием анимации fractionComplete
, чтобы я мог обновлять слайдер по мере продвижения анимации. Я попытался настроить KVO (наблюдение за ключом) в свойстве fractionComplete
, но он не вызывает уведомление KVO, пока анимация не будет завершена.
То, что я закончил, это настроить таймер, который выполняется на очень маленьком интервале, и повторно запрашивает свойство UIViewPropertyAnimator
fractionComplete
и обновляет слайдер по мере его продвижения. Это работает, но это не очень чистый способ делать что-то. Было бы намного лучше, если бы был способ получить уведомление о ходе анимации.
Я надеюсь, что там что-то мне не хватает.
Вы можете увидеть проект UIViewPropertyAnimator
на Github по этой ссылке: UIViewPropertyAnimator-test.
Код, который получает значение прогресса от аниматора, - это код:
func startTimer(_ start: Bool) {
if start {
timer = Timer.scheduledTimer(withTimeInterval: 0.02,
repeats: true) {
timer in
var value = Float(self.viewAnimator.fractionComplete)
if self.animatingBackwards {
value = 1 - value
}
self.animationSlider.value = value
}
}
else {
self.timer?.invalidate()
}
}
Я должен, вероятно, преобразовать код выше, чтобы использовать таймер CADisplayLink
вместо ванильного таймера (NSTimer), но я надеюсь, что там будет лучший способ.
Ответы
Ответ 1
Я думаю, что это возможно с расширением UIViewPropertyAnimator
и переопределением функции fractionComplete
.
Я сделал это с шаблоном делегирования, как показано ниже:
protocol MyPropertyAnimatorDelegate: class {
func myPropertyAnimator(_ animator:MyPropertyAnimator, didChange fractionComplete:CGFloat)
}
class MyPropertyAnimator: UIViewPropertyAnimator {
weak var delegate: MyPropertyAnimatorDelegate?
override var fractionComplete: CGFloat {
didSet {
delegate?.myPropertyAnimator(self, didChange: fractionComplete)
}
}
}
// USE
let animator = MyPropertyAnimator(duration: 5, curve: .linear) {
// your animation
}
animator.delegate = // any object which conferm to MyPropertyAnimatorDelegate
Ответ 2
Вы пытались создать подкласс UIViewPropertyAnimator, а затем переопределить свойство FractionComplete?
class MyPropertyAnimator : UIViewPropertyAnimator {
override var fractionComplete: CGFloat {
didSet {
print("didSetFraction")
}
}
}