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

Code before:
  /// More details: https://developer.apple.com/documentation/quartzcore/calayer/1410842-init
  override init(layer: Any) {
    guard let typedLayer = layer as? Self else {
      fatalError("\(Self.self).init(layer:) incorrectly called with \(type(of: layer))")
    }

    textLayerModel = typedLayer.textLayerModel
    super.init(layer: typedLayer)
  }

  // MARK: Internal

  func configureRenderLayer(with context: LayerContext) throws {
    // We can't use `CATextLayer`, because it doesn't support enough features we use.
    // Instead, we use the same `CoreTextRenderLayer` (with a custom `draw` implementation)
    // used by the Main Thread rendering engine. This means the Core Animation engine can't
    // _animate_ text properties, but it can display static text without any issues.
    let text = try textLayerModel.text.exactlyOneKeyframe(context: context, description: "text layer text")

    // The Core Animation engine doesn't currently support `TextAnimator`s.
    //  - We could add support for animating the transform-related properties without much trouble.
    //  - We may be able to support animating `fillColor` by getting clever with layer blend modes
    //    or masks (e.g. use `CoreTextRenderLayer` to draw black glyphs, and then fill them in
    //    using a `CAShapeLayer`).

    // Question 3: Should we somehow initialize LayerContextAnimations at this stage
    // and pass it to `self.setupAnimations(context: layerAnimationsContext)`
    if !textLayerModel.animators.isEmpty {
      try context.logCompatibilityIssue("""
        The Core Animation rendering engine currently doesn't support text animators.
        """)
    }

    renderLayer.text = text.text
    renderLayer.font = context.fontProvider.fontFor(family: text.fontFamily, size: CGFloat(text.fontSize))

    renderLayer.alignment = text.justification.textAlignment
    renderLayer.lineHeight = CGFloat(text.lineHeight)
    renderLayer.tracking = (CGFloat(text.fontSize) * CGFloat(text.tracking)) / 1000

    renderLayer.fillColor = text.fillColorData?.cgColorValue
    renderLayer.strokeColor = text.strokeColorData?.cgColorValue
    renderLayer.strokeWidth = CGFloat(text.strokeWidth ?? 0)
    renderLayer.strokeOnTop = text.strokeOverFill ?? false

    renderLayer.preferredSize = text.textFrameSize?.sizeValue
    renderLayer.sizeToFit()

    renderLayer.transform = CATransform3DIdentity
    renderLayer.position = text.textFramePosition?.pointValue ?? .zero
  }

Review comment (suggestion):
I think we can remove the `renderLayer.text = text.text` call from this method -- it only needs to be done once, and `setupAnimations` is fine.

Code after:
    let sourceText = try textLayerModel.text.exactlyOneKeyframe(
      context: textAnimationContext,
      description: "text layer text")

    renderLayer.text = context.textProvider.textFor(
      keypathName: textAnimationContext.currentKeypath.fullPath,
      sourceText: sourceText.text)

    renderLayer.sizeToFit()
  }

  func configureRenderLayer(with context: LayerContext) throws {
    // We can't use `CATextLayer`, because it doesn't support enough features we use.
    // Instead, we use the same `CoreTextRenderLayer` (with a custom `draw` implementation)
    // used by the Main Thread rendering engine. This means the Core Animation engine can't
    // _animate_ text properties, but it can display static text without any issues.
    let text = try textLayerModel.text.exactlyOneKeyframe(context: context, description: "text layer text")

    // The Core Animation engine doesn't currently support `TextAnimator`s.
    //  - We could add support for animating the transform-related properties without much trouble.
    //  - We may be able to support animating `fillColor` by getting clever with layer blend modes
    //    or masks (e.g. use `CoreTextRenderLayer` to draw black glyphs, and then fill them in
    //    using a `CAShapeLayer`).
    if !textLayerModel.animators.isEmpty {
      try context.logCompatibilityIssue("""
        The Core Animation rendering engine currently doesn't support text animators.
        """)
    }

    renderLayer.font = context.fontProvider.fontFor(family: text.fontFamily, size: CGFloat(text.fontSize))

    renderLayer.alignment = text.justification.textAlignment
    renderLayer.lineHeight = CGFloat(text.lineHeight)
    renderLayer.tracking = (CGFloat(text.fontSize) * CGFloat(text.tracking)) / 1000

    renderLayer.fillColor = text.fillColorData?.cgColorValue
    renderLayer.strokeColor = text.strokeColorData?.cgColorValue
    renderLayer.strokeWidth = CGFloat(text.strokeWidth ?? 0)
    renderLayer.strokeOnTop = text.strokeOverFill ?? false

    renderLayer.preferredSize = text.textFrameSize?.sizeValue
    renderLayer.sizeToFit()

    renderLayer.transform = CATransform3DIdentity
    renderLayer.position = text.textFramePosition?.pointValue ?? .zero
  }

  // MARK: Private

  private let textLayerModel: TextLayerModel
  private let renderLayer = CoreTextRenderLayer()