AnyValueProvider.swift 3.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
//
//  AnyValueProvider.swift
//  lottie-swift
//
//  Created by Brandon Withrow on 1/30/19.
//

import Foundation

// MARK: - AnyValueProvider

/// `AnyValueProvider` is a protocol that return animation data for a property at a
/// given time. Every frame a `LottieAnimationView` queries all of its properties and asks
/// if their ValueProvider has an update. If it does the LottieAnimationView will read the
/// property and update that portion of the animation.
///
/// Value Providers can be used to dynamically set animation properties at run time.
public protocol AnyValueProvider {

  /// The Type of the value provider
  var valueType: Any.Type { get }

  /// The type-erased storage for this Value Provider
  var typeErasedStorage: AnyValueProviderStorage { get }

  /// Asks the provider if it has an update for the given frame.
  func hasUpdate(frame: AnimationFrameTime) -> Bool

}

extension AnyValueProvider {
  /// Asks the provider to update the container with its value for the frame.
  public func value(frame: AnimationFrameTime) -> Any {
    typeErasedStorage.value(frame: frame)
  }
}

// MARK: - ValueProvider

/// A base protocol for strongly-typed Value Providers
protocol ValueProvider: AnyValueProvider {
  associatedtype Value: AnyInterpolatable

  /// The strongly-typed storage for this Value Provider
  var storage: ValueProviderStorage<Value> { get }
}

extension ValueProvider {
  public var typeErasedStorage: AnyValueProviderStorage {
    switch storage {
    case .closure(let typedClosure):
      .closure(typedClosure)

    case .singleValue(let typedValue):
      .singleValue(typedValue)

    case .keyframes(let keyframes):
      .keyframes(
        keyframes.map { keyframe in
          keyframe.withValue(keyframe.value as Any)
        },
        interpolate: storage.value(frame:))
    }
  }
}

// MARK: - ValueProviderStorage

/// The underlying storage of a `ValueProvider`
public enum ValueProviderStorage<T: AnyInterpolatable> {
  /// The value provider stores a single value that is used on all frames
  case singleValue(T)

  /// The value provider stores a group of keyframes
  ///  - The main-thread rendering engine interpolates values in these keyframes
  ///    using `T`'s `Interpolatable` implementation.
  ///  - The Core Animation rendering engine constructs a `CAKeyframeAnimation`
  ///    using these keyframes. The Core Animation render server performs
  ///    the interpolation, without calling `T`'s `Interpolatable` implementation.
  case keyframes([Keyframe<T>])

  /// The value provider stores a closure that is invoked on every frame
  ///  - This is only supported by the main-thread rendering engine
  case closure((AnimationFrameTime) -> T)

  // MARK: Internal

  func value(frame: AnimationFrameTime) -> T {
    switch self {
    case .singleValue(let value):
      value

    case .closure(let closure):
      closure(frame)

    case .keyframes(let keyframes):
      KeyframeInterpolator(keyframes: ContiguousArray(keyframes)).storage.value(frame: frame)
    }
  }
}

// MARK: - AnyValueProviderStorage

/// A type-erased representation of `ValueProviderStorage`
public enum AnyValueProviderStorage {
  /// The value provider stores a single value that is used on all frames
  case singleValue(Any)

  /// The value provider stores a group of keyframes
  ///  - Since we can't interpolate a type-erased `KeyframeGroup`,
  ///    the interpolation has to be performed in the `interpolate` closure.
  case keyframes([Keyframe<Any>], interpolate: (AnimationFrameTime) -> Any)

  /// The value provider stores a closure that is invoked on every frame
  case closure((AnimationFrameTime) -> Any)

  // MARK: Internal

  func value(frame: AnimationFrameTime) -> Any {
    switch self {
    case .singleValue(let value):
      value

    case .closure(let closure):
      closure(frame)

    case .keyframes(_, let valueForFrame):
      valueForFrame(frame)
    }
  }
}