150 lines
4.4 KiB
Swift
150 lines
4.4 KiB
Swift
//
|
|
// TextCompositionLayer.swift
|
|
// lottie-swift
|
|
//
|
|
// Created by Brandon Withrow on 1/25/19.
|
|
//
|
|
|
|
import CoreGraphics
|
|
import CoreText
|
|
import Foundation
|
|
import QuartzCore
|
|
|
|
/// Needed for NSMutableParagraphStyle...
|
|
#if os(OSX)
|
|
import AppKit
|
|
#else
|
|
import UIKit
|
|
#endif
|
|
|
|
extension TextJustification {
|
|
var textAlignment: NSTextAlignment {
|
|
switch self {
|
|
case .left:
|
|
return .left
|
|
case .right:
|
|
return .right
|
|
case .center:
|
|
return .center
|
|
}
|
|
}
|
|
|
|
var caTextAlignement: CATextLayerAlignmentMode {
|
|
switch self {
|
|
case .left:
|
|
return .left
|
|
case .right:
|
|
return .right
|
|
case .center:
|
|
return .center
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - TextCompositionLayer
|
|
|
|
final class TextCompositionLayer: CompositionLayer {
|
|
|
|
// MARK: Lifecycle
|
|
|
|
init(textLayer: TextLayerModel, textProvider: AnimationTextProvider, fontProvider: AnimationFontProvider) {
|
|
var rootNode: TextAnimatorNode?
|
|
for animator in textLayer.animators {
|
|
rootNode = TextAnimatorNode(parentNode: rootNode, textAnimator: animator)
|
|
}
|
|
self.rootNode = rootNode
|
|
textDocument = KeyframeInterpolator(keyframes: textLayer.text.keyframes)
|
|
|
|
self.textProvider = textProvider
|
|
self.fontProvider = fontProvider
|
|
|
|
super.init(layer: textLayer, size: .zero)
|
|
contentsLayer.addSublayer(self.textLayer)
|
|
self.textLayer.masksToBounds = false
|
|
self.textLayer.isGeometryFlipped = true
|
|
|
|
if let rootNode = rootNode {
|
|
childKeypaths.append(rootNode)
|
|
}
|
|
}
|
|
|
|
required init?(coder _: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
override init(layer: Any) {
|
|
/// Used for creating shadow model layers. Read More here: https://developer.apple.com/documentation/quartzcore/calayer/1410842-init
|
|
guard let layer = layer as? TextCompositionLayer else {
|
|
fatalError("init(layer:) Wrong Layer Class")
|
|
}
|
|
rootNode = nil
|
|
textDocument = nil
|
|
|
|
textProvider = DefaultTextProvider()
|
|
fontProvider = DefaultFontProvider()
|
|
|
|
super.init(layer: layer)
|
|
}
|
|
|
|
// MARK: Internal
|
|
|
|
let rootNode: TextAnimatorNode?
|
|
let textDocument: KeyframeInterpolator<TextDocument>?
|
|
|
|
let textLayer = CoreTextRenderLayer()
|
|
var textProvider: AnimationTextProvider
|
|
var fontProvider: AnimationFontProvider
|
|
|
|
override func displayContentsWithFrame(frame: CGFloat, forceUpdates: Bool) {
|
|
guard let textDocument = textDocument else { return }
|
|
|
|
textLayer.contentsScale = renderScale
|
|
|
|
let documentUpdate = textDocument.hasUpdate(frame: frame)
|
|
let animatorUpdate = rootNode?.updateContents(frame, forceLocalUpdate: forceUpdates) ?? false
|
|
guard documentUpdate == true || animatorUpdate == true else { return }
|
|
|
|
rootNode?.rebuildOutputs(frame: frame)
|
|
|
|
// Get Text Attributes
|
|
let text = textDocument.value(frame: frame) as! TextDocument
|
|
let strokeColor = rootNode?.textOutputNode.strokeColor ?? text.strokeColorData?.cgColorValue
|
|
let strokeWidth = rootNode?.textOutputNode.strokeWidth ?? CGFloat(text.strokeWidth ?? 0)
|
|
let tracking = (CGFloat(text.fontSize) * (rootNode?.textOutputNode.tracking ?? CGFloat(text.tracking))) / 1000.0
|
|
let matrix = rootNode?.textOutputNode.xform ?? CATransform3DIdentity
|
|
let textString = textProvider.textFor(keypathName: keypathName, sourceText: text.text)
|
|
let ctFont = fontProvider.fontFor(family: text.fontFamily, size: CGFloat(text.fontSize))
|
|
|
|
// Set all of the text layer options
|
|
textLayer.text = textString
|
|
textLayer.font = ctFont
|
|
textLayer.alignment = text.justification.textAlignment
|
|
textLayer.lineHeight = CGFloat(text.lineHeight)
|
|
textLayer.tracking = tracking
|
|
|
|
if let fillColor = rootNode?.textOutputNode.fillColor {
|
|
textLayer.fillColor = fillColor
|
|
} else if let fillColor = text.fillColorData?.cgColorValue {
|
|
textLayer.fillColor = fillColor
|
|
} else {
|
|
textLayer.fillColor = nil
|
|
}
|
|
|
|
textLayer.preferredSize = text.textFrameSize?.sizeValue
|
|
textLayer.strokeOnTop = text.strokeOverFill ?? false
|
|
textLayer.strokeWidth = strokeWidth
|
|
textLayer.strokeColor = strokeColor
|
|
textLayer.sizeToFit()
|
|
|
|
textLayer.opacity = Float(rootNode?.textOutputNode.opacity ?? 1)
|
|
textLayer.transform = CATransform3DIdentity
|
|
textLayer.position = text.textFramePosition?.pointValue ?? CGPoint.zero
|
|
textLayer.transform = matrix
|
|
}
|
|
|
|
override func updateRenderScale() {
|
|
super.updateRenderScale()
|
|
textLayer.contentsScale = renderScale
|
|
}
|
|
}
|