iOS 相当于 Android ValueAnimator

Fon*_*nix 2 animation ios

我想要一些能够模仿ValueAnimatorAndroid 中 iOS 行为的东西。在正常情况下,我会使用UIView动画,但不幸的是,我尝试随时间插值的对象值不适用于普通动画。

在我的特定情况下,我使用Lottie动画,并尝试通过动画更改动画的进度UIView,但动画不是随时间改变进度值,而是跳转到最终值。例如:

let lottieAnim = ...
lottieAnim.animationProgress = 0

UIView.animate(withDuration: 2) {
    lottieAnim.animationProgress = 1
}
Run Code Online (Sandbox Code Playgroud)

此示例不会随着时间的推移对 Lottie 动画进行动画处理,而只是跳至末尾。我知道 Lottie 有播放动画的方法,但我尝试使用自定义动画曲线来设置进度(它是没有有限持续时间的进度动画),这就是为什么我需要使用动画UIView来插入值。

我需要一些间歇性更新来模仿动画的东西,首先想到的是 AndroidValueAnimator类。

Fon*_*nix 6

我整理了一个类,它将ValueAnimator在某种程度上模仿android,并且允许您在需要时指定自己的动画曲线(默认只是线性的)

使用简单

let valueAnimator = ValueAnimator(duration: 2) { value in
  animationView.animationProgress = value
}

valueAnimator.start()
Run Code Online (Sandbox Code Playgroud)

高级用法

let valueAnimator = ValueAnimator(from: 0, to: 100, duration: 60, animationCurveFunction: { time, duration in
  return atan(time)*2/Double.pi
}, valueUpdater: { value in
  animationView.animationProgress = value
})

valueAnimator.start()
Run Code Online (Sandbox Code Playgroud)

您可以随时取消它,也可以使用:

valueAnimator.cancel()
Run Code Online (Sandbox Code Playgroud)

Swift 5 中的 Value Animator 类

// swiftlint:disable:next private_over_fileprivate
fileprivate var defaultFunction: (TimeInterval, TimeInterval) -> (Double) = { time, duration in
  return time / duration
}

class ValueAnimator {

  let from: Double
  let to: Double
  var duration: TimeInterval = 0
  var startTime: Date!
  var displayLink: CADisplayLink?
  var animationCurveFunction: (TimeInterval, TimeInterval) -> (Double)
  var valueUpdater: (Double) -> Void

  init (from: Double = 0, to: Double = 1, duration: TimeInterval, animationCurveFunction: @escaping (TimeInterval, TimeInterval) -> (Double) = defaultFunction, valueUpdater: @escaping (Double) -> Void) {
    self.from = from
    self.to = to
    self.duration = duration
    self.animationCurveFunction = animationCurveFunction
    self.valueUpdater = valueUpdater
  }

  func start() {
    displayLink = CADisplayLink(target: self, selector: #selector(update))
    displayLink?.add(to: .current, forMode: .default)
  }

  @objc
  private func update() {

    if startTime == nil {
      startTime = Date()
      valueUpdater(from + (to - from) * animationCurveFunction(0, duration))
      return
    }

    var timeElapsed = Date().timeIntervalSince(startTime)
    var stop = false

    if timeElapsed > duration {
      timeElapsed = duration
      stop = true
    }

    valueUpdater(from + (to - from) * animationCurveFunction(timeElapsed, duration))

    if stop {
      cancel()
    }
  }

  func cancel() {
    self.displayLink?.remove(from: .current, forMode: .default)
    self.displayLink = nil
  }
}
Run Code Online (Sandbox Code Playgroud)

您可能可以将其改进为更通用,但这符合我的目的。