Is there a way to observe changes to fractionCompl

2019-03-14 18:13发布

I've been looking at the very cool new UIViewPropertyAnimator class in iOS 10. It lets you easily do things like pause, resume, and reverse in-flight UIView animations. It used to be that you had to manipulate the underlying CAAnimations the system created in order to do that with UIView animations.

The new UIViewPropertyAnimator class has a property fractionComplete. It varies from 0.0 (beginning of animation) to 1.0 (end of animation.) If you change the value, you can "scrub" an animation from beginning to end.

I have a demo project on Github called KeyframeViewAnimations that lets you use a slider to scrub UIView and CAAnimations back and forth using fairly arcane CAAnimation tricks. As the animation runs, the slider moves from beginning to end to show the progress of the animation. That project was written in Objective-C before Swift was released, but the underlying techniques are the same in Objective-C or Swift.

I decided to create an equivalent project using UIViewPropertyAnimator. There are some quirks, but it's fairly straightforward. One thing I could not figure out how to do cleanly was to observe the progress of the animation's fractionComplete property so that I could update the slider as the animation progresses. I tried setting up KVO (key-value-observing) on the fractionComplete property, but it doesn't trigger a KVO notification until the animation is complete.

What I ended up doing is setting up a timer that runs on a very small interval and queries the UIViewPropertyAnimator's fractionComplete property repeatedly and updating the slider as it progresses. It works, but it's not a very clean way of doing things. It would be much better if there was a way to get notified about the progress of the animation.

I'm hoping there's something I'm missing.

You can see the UIViewPropertyAnimator based project on Github at this link: UIViewPropertyAnimator-test.

The code that gets the progress value from the animator is this code:

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

I should probably convert the code above to use a CADisplayLink timer instead of a vanilla Timer (NSTimer) but I'm hoping there's a better way.

1条回答
不美不萌又怎样
2楼-- · 2019-03-14 18:51

Did you try subclassing UIViewPropertyAnimator and then overriding the fractionComplete property?

class MyPropertyAnimator : UIViewPropertyAnimator {
    override var fractionComplete: CGFloat {
        didSet {
            print("didSetFraction")
        }
    }
}
查看更多
登录 后发表回答