162 lines
4.1 KiB
Swift
162 lines
4.1 KiB
Swift
//
|
|
// LayerContainer.swift
|
|
// lottie-swift
|
|
//
|
|
// Created by Brandon Withrow on 1/22/19.
|
|
//
|
|
|
|
import Foundation
|
|
import QuartzCore
|
|
|
|
// MARK: - CompositionLayer
|
|
|
|
/// The base class for a child layer of CompositionContainer
|
|
class CompositionLayer: CALayer, KeypathSearchable {
|
|
|
|
// MARK: Lifecycle
|
|
|
|
init(layer: LayerModel, size: CGSize) {
|
|
transformNode = LayerTransformNode(transform: layer.transform)
|
|
if let masks = layer.masks?.filter({ $0.mode != .none }), !masks.isEmpty {
|
|
maskLayer = MaskContainerLayer(masks: masks)
|
|
} else {
|
|
maskLayer = nil
|
|
}
|
|
matteType = layer.matte
|
|
inFrame = layer.inFrame.cgFloat
|
|
outFrame = layer.outFrame.cgFloat
|
|
timeStretch = layer.timeStretch.cgFloat
|
|
startFrame = layer.startTime.cgFloat
|
|
keypathName = layer.name
|
|
childKeypaths = [transformNode.transformProperties]
|
|
super.init()
|
|
anchorPoint = .zero
|
|
actions = [
|
|
"opacity" : NSNull(),
|
|
"transform" : NSNull(),
|
|
"bounds" : NSNull(),
|
|
"anchorPoint" : NSNull(),
|
|
"sublayerTransform" : NSNull(),
|
|
]
|
|
|
|
contentsLayer.anchorPoint = .zero
|
|
contentsLayer.bounds = CGRect(origin: .zero, size: size)
|
|
contentsLayer.actions = [
|
|
"opacity" : NSNull(),
|
|
"transform" : NSNull(),
|
|
"bounds" : NSNull(),
|
|
"anchorPoint" : NSNull(),
|
|
"sublayerTransform" : NSNull(),
|
|
"hidden" : NSNull(),
|
|
]
|
|
compositingFilter = layer.blendMode.filterName
|
|
addSublayer(contentsLayer)
|
|
|
|
if let maskLayer = maskLayer {
|
|
contentsLayer.mask = maskLayer
|
|
}
|
|
|
|
name = layer.name
|
|
}
|
|
|
|
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? CompositionLayer else {
|
|
fatalError("Wrong Layer Class")
|
|
}
|
|
transformNode = layer.transformNode
|
|
matteType = layer.matteType
|
|
inFrame = layer.inFrame
|
|
outFrame = layer.outFrame
|
|
timeStretch = layer.timeStretch
|
|
startFrame = layer.startFrame
|
|
keypathName = layer.keypathName
|
|
childKeypaths = [transformNode.transformProperties]
|
|
maskLayer = nil
|
|
super.init(layer: layer)
|
|
}
|
|
|
|
required init?(coder _: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
// MARK: Internal
|
|
|
|
weak var layerDelegate: CompositionLayerDelegate?
|
|
|
|
let transformNode: LayerTransformNode
|
|
|
|
let contentsLayer = CALayer()
|
|
|
|
let maskLayer: MaskContainerLayer?
|
|
|
|
let matteType: MatteType?
|
|
|
|
let inFrame: CGFloat
|
|
let outFrame: CGFloat
|
|
let startFrame: CGFloat
|
|
let timeStretch: CGFloat
|
|
|
|
// MARK: Keypath Searchable
|
|
|
|
let keypathName: String
|
|
|
|
final var childKeypaths: [KeypathSearchable]
|
|
|
|
var renderScale: CGFloat = 1 {
|
|
didSet {
|
|
updateRenderScale()
|
|
}
|
|
}
|
|
|
|
var matteLayer: CompositionLayer? {
|
|
didSet {
|
|
if let matte = matteLayer {
|
|
if let type = matteType, type == .invert {
|
|
mask = InvertedMatteLayer(inputMatte: matte)
|
|
} else {
|
|
mask = matte
|
|
}
|
|
} else {
|
|
mask = nil
|
|
}
|
|
}
|
|
}
|
|
|
|
var keypathProperties: [String: AnyNodeProperty] {
|
|
[:]
|
|
}
|
|
|
|
var keypathLayer: CALayer? {
|
|
contentsLayer
|
|
}
|
|
|
|
final func displayWithFrame(frame: CGFloat, forceUpdates: Bool) {
|
|
transformNode.updateTree(frame, forceUpdates: forceUpdates)
|
|
let layerVisible = frame.isInRangeOrEqual(inFrame, outFrame)
|
|
/// Only update contents if current time is within the layers time bounds.
|
|
if layerVisible {
|
|
displayContentsWithFrame(frame: frame, forceUpdates: forceUpdates)
|
|
maskLayer?.updateWithFrame(frame: frame, forceUpdates: forceUpdates)
|
|
}
|
|
contentsLayer.transform = transformNode.globalTransform
|
|
contentsLayer.opacity = transformNode.opacity
|
|
contentsLayer.isHidden = !layerVisible
|
|
layerDelegate?.frameUpdated(frame: frame)
|
|
}
|
|
|
|
func displayContentsWithFrame(frame _: CGFloat, forceUpdates _: Bool) {
|
|
/// To be overridden by subclass
|
|
}
|
|
|
|
func updateRenderScale() {
|
|
contentsScale = renderScale
|
|
}
|
|
}
|
|
|
|
// MARK: - CompositionLayerDelegate
|
|
|
|
protocol CompositionLayerDelegate: AnyObject {
|
|
func frameUpdated(frame: CGFloat)
|
|
}
|