Merge commit '7621e2f8dec938cf48181c8b10afc9b01f444e68' into beta

This commit is contained in:
Ilya Laktyushin
2025-12-06 02:17:48 +04:00
commit 8344b97e03
28070 changed files with 7995182 additions and 0 deletions
@@ -0,0 +1,35 @@
// Created by Cal Stephens on 12/15/21.
// Copyright © 2021 Airbnb Inc. All rights reserved.
import QuartzCore
// MARK: - CALayer + fillBoundsOfSuperlayer
extension CALayer {
/// Updates the `bounds` of this layer to fill the bounds of its `superlayer`
/// without setting `frame` (which is not permitted if the layer can rotate)
@nonobjc
func fillBoundsOfSuperlayer() {
guard let superlayer = superlayer else { return }
if let customLayerLayer = self as? CustomLayoutLayer {
customLayerLayer.layout(superlayerBounds: superlayer.bounds)
}
else {
// By default the `anchorPoint` of a layer is `CGPoint(x: 0.5, y: 0.5)`.
// Setting it to `.zero` makes the layer have the same coordinate space
// as its superlayer, which lets use use `superlayer.bounds` directly.
anchorPoint = .zero
bounds = superlayer.bounds
}
}
}
// MARK: - CustomLayoutLayer
/// A `CALayer` that sets a custom `bounds` and `anchorPoint` relative to its superlayer
protocol CustomLayoutLayer: CALayer {
func layout(superlayerBounds: CGRect)
}
@@ -0,0 +1,37 @@
// Created by Cal Stephens on 1/11/22.
// Copyright © 2022 Airbnb Inc. All rights reserved.
// MARK: - KeyframeGroup + exactlyOneKeyframe
extension KeyframeGroup {
/// Retrieves the first `Keyframe` from this group,
/// and asserts that there are not any extra keyframes that would be ignored
///
/// - There are several places in Lottie animation definitions where multiple
/// sets of keyframe timings can be provided for properties that have to
/// be applied to a single `CALayer` property (for example, the definition for a
/// `Rectangle` technically lets you animate `size`, `position`, and `cornerRadius`
/// separately, but these all have to be combined into a single `CAKeyframeAnimation`
/// on the `CAShapeLayer.path` property.
///
/// - In those sorts of cases, we currently choose one one `KeyframeGroup` to provide the
/// timing information, and disallow simultaneous animations on the other properties.
///
func exactlyOneKeyframe(
context: CompatibilityTrackerProviding,
description: String,
fileID _: StaticString = #fileID,
line _: UInt = #line)
throws
-> Keyframe<T>
{
try context.compatibilityAssert(
keyframes.count == 1,
"""
The Core Animation rendering engine does not support animating multiple keyframes
for \(description) values (due to limitations of Core Animation `CAKeyframeAnimation`s).
""")
return keyframes[0]
}
}
@@ -0,0 +1,61 @@
// Created by Cal Stephens on 1/28/22.
// Copyright © 2022 Airbnb Inc. All rights reserved.
// MARK: - Keyframes
enum Keyframes {
/// Combines the given `[KeyframeGroup]` of `Keyframe<T>`s
/// into a single `KeyframeGroup` of `Keyframe<[T]>`s
/// if all of the `KeyframeGroup`s have the exact same animation timing
static func combinedIfPossible<T>(_ groups: [KeyframeGroup<T>]) -> KeyframeGroup<[T]>? {
guard
!groups.isEmpty,
groups.allSatisfy({ $0.hasSameTimingParameters(as: groups[0]) })
else { return nil }
var combinedKeyframes = ContiguousArray<Keyframe<[T]>>()
for index in groups[0].keyframes.indices {
let baseKeyframe = groups[0].keyframes[index]
let combinedValues = groups.map { $0.keyframes[index].value }
combinedKeyframes.append(baseKeyframe.withValue(combinedValues))
}
return KeyframeGroup(keyframes: combinedKeyframes)
}
/// Combines the given `[KeyframeGroup?]` of `Keyframe<T>`s
/// into a single `KeyframeGroup` of `Keyframe<[T]>`s
/// if all of the `KeyframeGroup`s have the exact same animation timing
static func combinedIfPossible<T>(_ groups: [KeyframeGroup<T>?]) -> KeyframeGroup<[T]>? {
let nonOptionalGroups = groups.compactMap { $0 }
guard nonOptionalGroups.count == groups.count else { return nil }
return combinedIfPossible(nonOptionalGroups)
}
}
extension KeyframeGroup {
/// Whether or not all of the keyframes in this `KeyframeGroup` have the same
/// timing parameters as the corresponding keyframe in the other given `KeyframeGroup`
func hasSameTimingParameters<A>(as other: KeyframeGroup<A>) -> Bool {
guard keyframes.count == other.keyframes.count else {
return false
}
return zip(keyframes, other.keyframes).allSatisfy {
$0.hasSameTimingParameters(as: $1)
}
}
}
extension Keyframe {
/// Whether or not this keyframe has the same timing parameters as the given keyframe
func hasSameTimingParameters<A>(as other: Keyframe<A>) -> Bool {
time == other.time
&& isHold == other.isHold
&& inTangent == other.inTangent
&& outTangent == other.outTangent
&& spatialInTangent == other.spatialInTangent
&& spatialOutTangent == other.spatialOutTangent
}
}