Есть ли способ наблюдать изменения в 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")
        }
    }
}