Repository: airbnb/lottie-ios
File: Sources/Private/CoreAnimation/Animations/GradientAnimations.swift (Swift)

Code before:

  /// Adds gradient-related animations to this layer, from the given `GradientFill`
  func addGradientAnimations(for gradient: GradientShapeItem, context: LayerAnimationContext) throws {
    // We have to set `colors` to a non-nil value with some valid number of colors
    // for the color animation below to have any effect
    colors = .init(
      repeating: CGColor.rgb(0, 0, 0),
      count: gradient.numberOfColors)

    try addAnimation(
      for: .colors,
      keyframes: gradient.colors.keyframes,
      value: { colorComponents in
        gradient.colorConfiguration(from: colorComponents).map { $0.color }
      },
      context: context)

    try addAnimation(
      for: .locations,
      keyframes: gradient.colors.keyframes,
      value: { colorComponents in
        gradient.colorConfiguration(from: colorComponents).map { $0.location }
      },
      context: context)

    switch gradient.gradientType {
    case .linear:
      try addLinearGradientAnimations(for: gradient, context: context)
    case .radial:
      try addRadialGradientAnimations(for: gradient, context: context)
    case .none:
      break
    }
  }

  // MARK: Private

  private func addLinearGradientAnimations(
    for gradient: GradientShapeItem,
    context: LayerAnimationContext)
    throws
  {
    type = .axial

    try addAnimation(
      for: .startPoint,
      keyframes: gradient.startPoint.keyframes,
      value: { absoluteStartPoint in
        percentBasedPointInBounds(from: absoluteStartPoint.pointValue)
      },
      context: context)

Review comment (suggestion):
While I was working on this I also noticed we were ignoring the opacity configuration on `GradientFill`s, so I added that here like we did for `Fill`s in #1589

Code after:

  /// Adds gradient-related animations to this layer, from the given `GradientFill`
  func addGradientAnimations(for gradient: GradientShapeItem, context: LayerAnimationContext) throws {
    // We have to set `colors` to a non-nil value with some valid number of colors
    // for the color animation below to have any effect
    colors = .init(
      repeating: CGColor.rgb(0, 0, 0),
      count: gradient.numberOfColors)

    try addAnimation(
      for: .colors,
      keyframes: gradient.colors.keyframes,
      value: { colorComponents in
        gradient.colorConfiguration(from: colorComponents).map { $0.color }
      },
      context: context)

    try addAnimation(
      for: .locations,
      keyframes: gradient.colors.keyframes,
      value: { colorComponents in
        gradient.colorConfiguration(from: colorComponents).map { $0.location }
      },
      context: context)

    try addOpacityAnimation(for: gradient.opacity, context: context)

    switch gradient.gradientType {
    case .linear:
      try addLinearGradientAnimations(for: gradient, context: context)
    case .radial:
      try addRadialGradientAnimations(for: gradient, context: context)
    case .none:
      break
    }
  }

  // MARK: Private

  private func addLinearGradientAnimations(
    for gradient: GradientShapeItem,
    context: LayerAnimationContext)
    throws
  {
    type = .axial

    try addAnimation(
      for: .startPoint,
      keyframes: gradient.startPoint.keyframes,
      value: { absoluteStartPoint in
        percentBasedPointInBounds(from: absoluteStartPoint.pointValue)