lottie-ios 4.2.0 → 4.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/main.yml +117 -11
- package/Gemfile +1 -0
- package/Gemfile.lock +5 -0
- package/Lottie.xcodeproj/project.pbxproj +1546 -194
- package/Lottie.xcodeproj/project.xcworkspace/xcuserdata/calstephens.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/Lottie.xcodeproj/xcshareddata/xcschemes/Lottie (iOS).xcscheme +1 -1
- package/Lottie.xcodeproj/xcshareddata/xcschemes/Lottie (macOS).xcscheme +1 -1
- package/Lottie.xcodeproj/xcshareddata/xcschemes/Lottie (tvOS).xcscheme +4 -5
- package/Lottie.xcodeproj/xcshareddata/xcschemes/Lottie (visionOS).xcscheme +66 -0
- package/Lottie.xcodeproj/xcuserdata/calstephens.xcuserdatad/xcschemes/xcschememanagement.plist +18 -0
- package/Lottie.xcworkspace/xcshareddata/swiftpm/Package.resolved +2 -11
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +374 -116
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/xcdebugger/Expressions.xcexplist +16 -5
- package/Package.resolved +2 -2
- package/Package.swift +16 -9
- package/README.md +2 -2
- package/Rakefile +122 -25
- package/Sources/Private/CoreAnimation/Animations/CALayer+addAnimation.swift +3 -4
- package/Sources/Private/CoreAnimation/Animations/DropShadowAnimation.swift +160 -0
- package/Sources/Private/CoreAnimation/Animations/LayerProperty.swift +83 -10
- package/Sources/Private/CoreAnimation/Animations/TransformAnimations.swift +85 -79
- package/Sources/Private/CoreAnimation/CoreAnimationLayer.swift +11 -7
- package/Sources/Private/CoreAnimation/Extensions/Keyframes+combined.swift +67 -6
- package/Sources/Private/CoreAnimation/Layers/AnimationLayer.swift +21 -2
- package/Sources/Private/CoreAnimation/Layers/BaseCompositionLayer.swift +9 -0
- package/Sources/Private/CoreAnimation/Layers/ImageLayer.swift +1 -0
- package/Sources/Private/CoreAnimation/Layers/LayerModel+makeAnimationLayer.swift +1 -1
- package/Sources/Private/CoreAnimation/Layers/RepeaterLayer.swift +2 -0
- package/Sources/Private/CoreAnimation/Layers/ShapeLayer.swift +11 -13
- package/Sources/Private/CoreAnimation/Layers/SolidLayer.swift +20 -5
- package/Sources/Private/CoreAnimation/Layers/TextLayer.swift +13 -3
- package/Sources/Private/CoreAnimation/ValueProviderStore.swift +6 -4
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Diffing/Collection+Diff.swift +263 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Diffing/Diffable.swift +18 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Diffing/DiffableSection.swift +16 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Diffing/IndexChangeset.swift +187 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Diffing/SectionedChangeset.swift +32 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Logging/EpoxyLogger.swift +99 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/CallbackContextEpoxyModeled.swift +8 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/EpoxyModelArrayBuilder.swift +48 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/EpoxyModelProperty.swift +158 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/EpoxyModelStorage.swift +88 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/EpoxyModeled.swift +54 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Internal/AnyEpoxyModelProperty.swift +29 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Internal/ClassReference.swift +39 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/AnimatedProviding.swift +10 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/DataIDProviding.swift +57 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/DidDisplayProviding.swift +41 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/DidEndDisplayingProviding.swift +41 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/DidSelectProviding.swift +36 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/ErasedContentProviding.swift +49 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/MakeViewProviding.swift +60 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/SetBehaviorsProviding.swift +38 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/SetContentProviding.swift +38 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/StyleIDProviding.swift +37 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/TraitCollectionProviding.swift +14 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/ViewDifferentiatorProviding.swift +34 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/ViewProviding.swift +13 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/Providers/WillDisplayProviding.swift +41 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Model/ViewEpoxyModeled.swift +10 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/README.md +31 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxySwiftUIHostingController.swift +46 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxySwiftUIHostingView.swift +391 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxySwiftUIIntrinsicContentSizeInvalidator.swift +44 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxySwiftUILayoutMargins.swift +51 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/EpoxyableView+SwiftUIView.swift +172 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/LayoutUtilities/MeasuringViewRepresentable.swift +128 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/LayoutUtilities/SwiftUIMeasurementContainer.swift +452 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/SwiftUIView.swift +148 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/UIView+SwiftUIView.swift +40 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/SwiftUI/UIViewConfiguringSwiftUIView.swift +43 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Views/BehaviorsConfigurableView.swift +45 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Views/ContentConfigurableView.swift +36 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Views/EpoxyableView.swift +5 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Views/StyledView.swift +42 -0
- package/Sources/Private/EmbeddedLibraries/EpoxyCore/Views/ViewType.swift +51 -0
- package/Sources/Private/EmbeddedLibraries/README.md +27 -0
- package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+BackingConfiguration.swift +4 -4
- package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+MemoryFile.swift +2 -2
- package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+Writing.swift +4 -4
- package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive.swift +8 -7
- package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Data+Compression.swift +5 -5
- package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/FileManager+ZIP.swift +4 -4
- package/Sources/Private/MainThread/LayerContainers/CompLayers/ImageCompositionLayer.swift +6 -0
- package/Sources/Private/MainThread/LayerContainers/CompLayers/MaskContainerLayer.swift +3 -1
- package/Sources/Private/MainThread/LayerContainers/CompLayers/PreCompositionLayer.swift +18 -5
- package/Sources/Private/MainThread/LayerContainers/CompLayers/TextCompositionLayer.swift +31 -3
- package/Sources/Private/MainThread/LayerContainers/MainThreadAnimationLayer.swift +33 -8
- package/Sources/Private/MainThread/LayerContainers/Utility/CachedImageProvider.swift +8 -1
- package/Sources/Private/MainThread/LayerContainers/Utility/CompositionLayersInitializer.swift +13 -4
- package/Sources/Private/MainThread/LayerContainers/Utility/LayerFontProvider.swift +2 -2
- package/Sources/Private/MainThread/LayerContainers/Utility/LayerImageProvider.swift +1 -0
- package/Sources/Private/MainThread/LayerContainers/Utility/LayerTextProvider.swift +4 -4
- package/Sources/Private/MainThread/NodeRenderSystem/NodeProperties/Protocols/NodePropertyMap.swift +1 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/ModifierNodes/TrimPathNode.swift +3 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/Renderables/GradientFillRenderer.swift +2 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/Renderables/LegacyGradientFillRenderer.swift +2 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/GradientFillNode.swift +1 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/GradientStrokeNode.swift +2 -2
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/StrokeNode.swift +1 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/Text/TextAnimatorNode.swift +1 -1
- package/Sources/Private/MainThread/NodeRenderSystem/RenderLayers/ShapeContainerLayer.swift +6 -2
- package/Sources/Private/Model/Assets/Asset.swift +8 -0
- package/Sources/Private/Model/Assets/AssetLibrary.swift +2 -2
- package/Sources/Private/Model/Assets/ImageAsset.swift +3 -3
- package/Sources/Private/Model/DotLottie/DotLottieAnimation.swift +16 -2
- package/Sources/Private/Model/DotLottie/DotLottieImageProvider.swift +20 -7
- package/Sources/Private/Model/Extensions/KeyedDecodingContainerExtensions.swift +26 -0
- package/Sources/Private/Model/Keyframes/KeyframeGroup.swift +8 -5
- package/Sources/Private/Model/LayerEffects/DropShadowEffect.swift +45 -0
- package/Sources/Private/Model/LayerEffects/EffectValues/ColorEffectValue.swift +38 -0
- package/Sources/Private/Model/LayerEffects/EffectValues/EffectValue.swift +98 -0
- package/Sources/Private/Model/LayerEffects/EffectValues/Vector1DEffectValue.swift +38 -0
- package/Sources/Private/Model/LayerEffects/LayerEffect.swift +103 -0
- package/Sources/Private/Model/LayerStyles/DropShadowStyle.swift +72 -0
- package/Sources/Private/Model/LayerStyles/LayerStyle.swift +85 -0
- package/Sources/Private/Model/Layers/LayerModel.swift +27 -0
- package/Sources/Private/Model/Objects/Marker.swift +1 -1
- package/Sources/Private/Model/Objects/Transform.swift +1 -2
- package/Sources/Private/Model/ShapeItems/GradientFill.swift +1 -1
- package/Sources/Private/Model/ShapeItems/GradientStroke.swift +2 -2
- package/Sources/Private/Model/ShapeItems/Merge.swift +1 -1
- package/Sources/Private/Model/ShapeItems/ShapeItem.swift +31 -26
- package/Sources/Private/Model/ShapeItems/ShapeTransform.swift +0 -9
- package/Sources/Private/Model/ShapeItems/Star.swift +1 -1
- package/Sources/Private/Model/Text/Font.swift +2 -2
- package/Sources/Private/Model/Text/Glyph.swift +1 -1
- package/Sources/Private/RootAnimationLayer.swift +2 -2
- package/Sources/Private/Utility/Debugging/LayerDebugging.swift +3 -1
- package/Sources/Private/Utility/Extensions/AnimationKeypathExtension.swift +32 -2
- package/Sources/Private/Utility/Helpers/AnimationContext.swift +4 -2
- package/Sources/Private/Utility/Helpers/AnyEquatable.swift +24 -0
- package/Sources/Private/Utility/Helpers/Binding+Map.swift +18 -0
- package/Sources/Private/Utility/Helpers/View+ValueChanged.swift +20 -0
- package/Sources/Private/Utility/LottieAnimationSource.swift +41 -0
- package/Sources/Private/Utility/Primitives/BezierPath.swift +2 -2
- package/Sources/Private/Utility/Primitives/BezierPathRoundExtension.swift +2 -2
- package/Sources/Private/Utility/Primitives/VectorsExtensions.swift +1 -1
- package/Sources/Public/Animation/LottieAnimation.swift +21 -2
- package/Sources/Public/Animation/LottieAnimationLayer.swift +1464 -0
- package/Sources/Public/Animation/LottieAnimationView.swift +259 -736
- package/Sources/Public/Animation/LottiePlaybackMode.swift +117 -0
- package/Sources/Public/Animation/LottieView.swift +462 -0
- package/Sources/Public/AnimationCache/AnimationCacheProvider.swift +2 -1
- package/Sources/Public/AnimationCache/DefaultAnimationCache.swift +5 -6
- package/Sources/Public/Configuration/DecodingStrategy.swift +15 -0
- package/Sources/Public/Configuration/LottieConfiguration.swift +47 -0
- package/Sources/Public/Configuration/ReducedMotionOption.swift +114 -0
- package/Sources/Public/{LottieConfiguration.swift → Configuration/RenderingEngineOption.swift} +2 -57
- package/Sources/Public/{iOS → Controls}/AnimatedButton.swift +56 -13
- package/Sources/Public/{iOS → Controls}/AnimatedControl.swift +80 -8
- package/Sources/Public/{iOS → Controls}/AnimatedSwitch.swift +71 -31
- package/Sources/Public/Controls/LottieButton.swift +122 -0
- package/Sources/Public/Controls/LottieSwitch.swift +144 -0
- package/Sources/Public/Controls/LottieViewType.swift +79 -0
- package/Sources/Public/DotLottie/DotLottieConfiguration.swift +24 -0
- package/Sources/Public/DotLottie/DotLottieFile.swift +21 -6
- package/Sources/Public/DotLottie/DotLottieFileHelpers.swift +79 -0
- package/Sources/Public/DynamicProperties/AnimationKeypath.swift +11 -1
- package/Sources/Public/DynamicProperties/ValueProviders/ColorValueProvider.swift +14 -0
- package/Sources/Public/DynamicProperties/ValueProviders/FloatValueProvider.swift +13 -0
- package/Sources/Public/DynamicProperties/ValueProviders/GradientValueProvider.swift +22 -5
- package/Sources/Public/DynamicProperties/ValueProviders/PointValueProvider.swift +14 -0
- package/Sources/Public/DynamicProperties/ValueProviders/SizeValueProvider.swift +13 -0
- package/Sources/Public/FontProvider/AnimationFontProvider.swift +8 -0
- package/Sources/Public/ImageProvider/AnimationImageProvider.swift +24 -0
- package/Sources/Public/Keyframes/Keyframe.swift +6 -0
- package/Sources/Public/Primitives/Vectors.swift +2 -2
- package/Sources/Public/TextProvider/AnimationTextProvider.swift +79 -6
- package/Sources/Public/iOS/AnimationSubview.swift +1 -1
- package/Sources/Public/iOS/BundleImageProvider.swift +16 -2
- package/Sources/Public/iOS/Compatibility/CompatibleAnimationView.swift +6 -4
- package/Sources/Public/iOS/FilepathImageProvider.swift +22 -3
- package/Sources/Public/iOS/LottieAnimationViewBase.swift +7 -1
- package/Sources/Public/macOS/BundleImageProvider.macOS.swift +15 -1
- package/Sources/Public/macOS/FilepathImageProvider.macOS.swift +21 -2
- package/lottie-ios.podspec +2 -1
- package/package.json +1 -1
- package/Sources/Private/Model/DotLottie/DotLottieConfiguration.swift +0 -15
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+Helpers.swift +0 -0
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+Progress.swift +0 -0
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+Reading.swift +0 -0
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+ReadingDeprecated.swift +0 -0
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+WritingDeprecated.swift +0 -0
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Archive+ZIP64.swift +0 -0
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Data+CompressionDeprecated.swift +0 -0
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Data+Serialization.swift +0 -0
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Entry+Serialization.swift +0 -0
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Entry+ZIP64.swift +0 -0
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/Entry.swift +0 -0
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/README.md +0 -0
- /package/Sources/Private/{Model/DotLottie → EmbeddedLibraries}/ZipFoundation/URL+ZIP.swift +0 -0
|
@@ -26,6 +26,16 @@ struct LayerProperty<ValueRepresentation> {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
extension LayerProperty where ValueRepresentation: Equatable {
|
|
29
|
+
/// Initializes a `LayerProperty` that corresponds to a property on `CALayer`
|
|
30
|
+
/// or some other `CALayer` subclass like `CAShapeLayer`.
|
|
31
|
+
/// - Parameters:
|
|
32
|
+
/// - caLayerKeypath: The Objective-C `#keyPath` to the `CALayer` property,
|
|
33
|
+
/// e.g. `#keyPath(CALayer.opacity)` or `#keyPath(CAShapeLayer.path)`.
|
|
34
|
+
/// - defaultValue: The default value of the property (e.g. the value of the
|
|
35
|
+
/// property immediately after calling `CALayer.init()`). Knowing this value
|
|
36
|
+
/// lets us perform some optimizations in `CALayer+addAnimation`.
|
|
37
|
+
/// - customizableProperty: A description of how this property can be customized
|
|
38
|
+
/// dynamically at runtime using `AnimationView.setValueProvider(_:keypath:)`.
|
|
29
39
|
init(
|
|
30
40
|
caLayerKeypath: String,
|
|
31
41
|
defaultValue: ValueRepresentation?,
|
|
@@ -52,7 +62,10 @@ struct CustomizableProperty<ValueRepresentation> {
|
|
|
52
62
|
|
|
53
63
|
/// A closure that coverts the type-erased value of an `AnyValueProvider`
|
|
54
64
|
/// to the strongly-typed representation used by this property, if possible.
|
|
55
|
-
|
|
65
|
+
/// - `value` is the value for the current frame that should be converted,
|
|
66
|
+
/// as returned by `AnyValueProvider.typeErasedStorage`.
|
|
67
|
+
/// - `valueProvider` is the `AnyValueProvider` that returned the type-erased value.
|
|
68
|
+
let conversion: (_ value: Any, _ valueProvider: AnyValueProvider) -> ValueRepresentation?
|
|
56
69
|
}
|
|
57
70
|
|
|
58
71
|
// MARK: - PropertyName
|
|
@@ -66,6 +79,8 @@ enum PropertyName: String, CaseIterable {
|
|
|
66
79
|
case scale = "Scale"
|
|
67
80
|
case position = "Position"
|
|
68
81
|
case rotation = "Rotation"
|
|
82
|
+
case strokeWidth = "Stroke Width"
|
|
83
|
+
case gradientColors = "Colors"
|
|
69
84
|
}
|
|
70
85
|
|
|
71
86
|
// MARK: CALayer properties
|
|
@@ -159,6 +174,34 @@ extension LayerProperty {
|
|
|
159
174
|
},
|
|
160
175
|
customizableProperty: nil /* currently unsupported */ )
|
|
161
176
|
}
|
|
177
|
+
|
|
178
|
+
static var shadowOpacity: LayerProperty<CGFloat> {
|
|
179
|
+
.init(
|
|
180
|
+
caLayerKeypath: #keyPath(CALayer.shadowOpacity),
|
|
181
|
+
defaultValue: 0,
|
|
182
|
+
customizableProperty: nil /* currently unsupported */ )
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
static var shadowColor: LayerProperty<CGColor> {
|
|
186
|
+
.init(
|
|
187
|
+
caLayerKeypath: #keyPath(CALayer.shadowColor),
|
|
188
|
+
defaultValue: .rgb(0, 0, 0),
|
|
189
|
+
customizableProperty: nil /* currently unsupported */ )
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
static var shadowRadius: LayerProperty<CGFloat> {
|
|
193
|
+
.init(
|
|
194
|
+
caLayerKeypath: #keyPath(CALayer.shadowRadius),
|
|
195
|
+
defaultValue: 3.0,
|
|
196
|
+
customizableProperty: nil /* currently unsupported */ )
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
static var shadowOffset: LayerProperty<CGSize> {
|
|
200
|
+
.init(
|
|
201
|
+
caLayerKeypath: #keyPath(CALayer.shadowOffset),
|
|
202
|
+
defaultValue: CGSize(width: 0, height: -3.0),
|
|
203
|
+
customizableProperty: nil /* currently unsupported */ )
|
|
204
|
+
}
|
|
162
205
|
}
|
|
163
206
|
|
|
164
207
|
// MARK: CAShapeLayer properties
|
|
@@ -182,7 +225,7 @@ extension LayerProperty {
|
|
|
182
225
|
.init(
|
|
183
226
|
caLayerKeypath: #keyPath(CAShapeLayer.lineWidth),
|
|
184
227
|
defaultValue: 1,
|
|
185
|
-
customizableProperty:
|
|
228
|
+
customizableProperty: .floatValue(.strokeWidth))
|
|
186
229
|
}
|
|
187
230
|
|
|
188
231
|
static var lineDashPhase: LayerProperty<CGFloat> {
|
|
@@ -221,14 +264,14 @@ extension LayerProperty {
|
|
|
221
264
|
.init(
|
|
222
265
|
caLayerKeypath: #keyPath(CAGradientLayer.colors),
|
|
223
266
|
defaultValue: nil,
|
|
224
|
-
customizableProperty:
|
|
267
|
+
customizableProperty: .gradientColors)
|
|
225
268
|
}
|
|
226
269
|
|
|
227
270
|
static var locations: LayerProperty<[CGFloat]> {
|
|
228
271
|
.init(
|
|
229
272
|
caLayerKeypath: #keyPath(CAGradientLayer.locations),
|
|
230
273
|
defaultValue: nil,
|
|
231
|
-
customizableProperty:
|
|
274
|
+
customizableProperty: .gradientLocations)
|
|
232
275
|
}
|
|
233
276
|
|
|
234
277
|
static var startPoint: LayerProperty<CGPoint> {
|
|
@@ -252,7 +295,7 @@ extension CustomizableProperty {
|
|
|
252
295
|
static var color: CustomizableProperty<CGColor> {
|
|
253
296
|
.init(
|
|
254
297
|
name: [.color],
|
|
255
|
-
conversion: { typeErasedValue in
|
|
298
|
+
conversion: { typeErasedValue, _ in
|
|
256
299
|
guard let color = typeErasedValue as? LottieColor else {
|
|
257
300
|
return nil
|
|
258
301
|
}
|
|
@@ -264,7 +307,7 @@ extension CustomizableProperty {
|
|
|
264
307
|
static var opacity: CustomizableProperty<CGFloat> {
|
|
265
308
|
.init(
|
|
266
309
|
name: [.opacity],
|
|
267
|
-
conversion: { typeErasedValue in
|
|
310
|
+
conversion: { typeErasedValue, _ in
|
|
268
311
|
guard let vector = typeErasedValue as? LottieVector1D else { return nil }
|
|
269
312
|
|
|
270
313
|
// Lottie animation files express opacity as a numerical percentage value
|
|
@@ -277,7 +320,7 @@ extension CustomizableProperty {
|
|
|
277
320
|
static var scaleX: CustomizableProperty<CGFloat> {
|
|
278
321
|
.init(
|
|
279
322
|
name: [.scale],
|
|
280
|
-
conversion: { typeErasedValue in
|
|
323
|
+
conversion: { typeErasedValue, _ in
|
|
281
324
|
guard let vector = typeErasedValue as? LottieVector3D else { return nil }
|
|
282
325
|
|
|
283
326
|
// Lottie animation files express scale as a numerical percentage value
|
|
@@ -290,7 +333,7 @@ extension CustomizableProperty {
|
|
|
290
333
|
static var scaleY: CustomizableProperty<CGFloat> {
|
|
291
334
|
.init(
|
|
292
335
|
name: [.scale],
|
|
293
|
-
conversion: { typeErasedValue in
|
|
336
|
+
conversion: { typeErasedValue, _ in
|
|
294
337
|
guard let vector = typeErasedValue as? LottieVector3D else { return nil }
|
|
295
338
|
|
|
296
339
|
// Lottie animation files express scale as a numerical percentage value
|
|
@@ -303,7 +346,7 @@ extension CustomizableProperty {
|
|
|
303
346
|
static var rotation: CustomizableProperty<CGFloat> {
|
|
304
347
|
.init(
|
|
305
348
|
name: [.rotation],
|
|
306
|
-
conversion: { typeErasedValue in
|
|
349
|
+
conversion: { typeErasedValue, _ in
|
|
307
350
|
guard let vector = typeErasedValue as? LottieVector1D else { return nil }
|
|
308
351
|
|
|
309
352
|
// Lottie animation files express rotation in degrees
|
|
@@ -316,6 +359,36 @@ extension CustomizableProperty {
|
|
|
316
359
|
static var position: CustomizableProperty<CGPoint> {
|
|
317
360
|
.init(
|
|
318
361
|
name: [.position],
|
|
319
|
-
conversion: {
|
|
362
|
+
conversion: { typeErasedValue, _ in
|
|
363
|
+
guard let vector = typeErasedValue as? LottieVector3D else { return nil }
|
|
364
|
+
return vector.pointValue
|
|
365
|
+
})
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
static var gradientColors: CustomizableProperty<[CGColor]> {
|
|
369
|
+
.init(
|
|
370
|
+
name: [.gradientColors],
|
|
371
|
+
conversion: { _, typeErasedValueProvider in
|
|
372
|
+
guard let gradientValueProvider = typeErasedValueProvider as? GradientValueProvider else { return nil }
|
|
373
|
+
return gradientValueProvider.colors.map { $0.cgColorValue }
|
|
374
|
+
})
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
static var gradientLocations: CustomizableProperty<[CGFloat]> {
|
|
378
|
+
.init(
|
|
379
|
+
name: [.gradientColors],
|
|
380
|
+
conversion: { _, typeErasedValueProvider in
|
|
381
|
+
guard let gradientValueProvider = typeErasedValueProvider as? GradientValueProvider else { return nil }
|
|
382
|
+
return gradientValueProvider.locations.map { CGFloat($0) }
|
|
383
|
+
})
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
static func floatValue(_ name: PropertyName...) -> CustomizableProperty<CGFloat> {
|
|
387
|
+
.init(
|
|
388
|
+
name: name,
|
|
389
|
+
conversion: { typeErasedValue, _ in
|
|
390
|
+
guard let vector = typeErasedValue as? LottieVector1D else { return nil }
|
|
391
|
+
return vector.cgFloatValue
|
|
392
|
+
})
|
|
320
393
|
}
|
|
321
394
|
}
|
|
@@ -6,7 +6,7 @@ import QuartzCore
|
|
|
6
6
|
// MARK: - TransformModel
|
|
7
7
|
|
|
8
8
|
/// This protocol mirrors the interface of `Transform`,
|
|
9
|
-
/// but
|
|
9
|
+
/// but is also implemented by `ShapeTransform` to allow
|
|
10
10
|
/// both transform types to share the same animation implementation.
|
|
11
11
|
protocol TransformModel {
|
|
12
12
|
/// The anchor point of the transform.
|
|
@@ -32,6 +32,12 @@ protocol TransformModel {
|
|
|
32
32
|
|
|
33
33
|
/// The rotation of the transform on Z axis.
|
|
34
34
|
var rotationZ: KeyframeGroup<LottieVector1D> { get }
|
|
35
|
+
|
|
36
|
+
/// The skew of the transform (only present on `ShapeTransform`s)
|
|
37
|
+
var _skew: KeyframeGroup<LottieVector1D>? { get }
|
|
38
|
+
|
|
39
|
+
/// The skew axis of the transform (only present on `ShapeTransform`s)
|
|
40
|
+
var _skewAxis: KeyframeGroup<LottieVector1D>? { get }
|
|
35
41
|
}
|
|
36
42
|
|
|
37
43
|
// MARK: - Transform + TransformModel
|
|
@@ -40,6 +46,8 @@ extension Transform: TransformModel {
|
|
|
40
46
|
var _position: KeyframeGroup<LottieVector3D>? { position }
|
|
41
47
|
var _positionX: KeyframeGroup<LottieVector1D>? { positionX }
|
|
42
48
|
var _positionY: KeyframeGroup<LottieVector1D>? { positionY }
|
|
49
|
+
var _skew: KeyframeGroup<LottieVector1D>? { nil }
|
|
50
|
+
var _skewAxis: KeyframeGroup<LottieVector1D>? { nil }
|
|
43
51
|
}
|
|
44
52
|
|
|
45
53
|
// MARK: - ShapeTransform + TransformModel
|
|
@@ -49,6 +57,8 @@ extension ShapeTransform: TransformModel {
|
|
|
49
57
|
var _position: KeyframeGroup<LottieVector3D>? { position }
|
|
50
58
|
var _positionX: KeyframeGroup<LottieVector1D>? { nil }
|
|
51
59
|
var _positionY: KeyframeGroup<LottieVector1D>? { nil }
|
|
60
|
+
var _skew: KeyframeGroup<LottieVector1D>? { skew }
|
|
61
|
+
var _skewAxis: KeyframeGroup<LottieVector1D>? { skewAxis }
|
|
52
62
|
}
|
|
53
63
|
|
|
54
64
|
// MARK: - CALayer + TransformModel
|
|
@@ -66,15 +76,18 @@ extension CALayer {
|
|
|
66
76
|
context: LayerAnimationContext)
|
|
67
77
|
throws
|
|
68
78
|
{
|
|
69
|
-
// CALayers don't support animating skew with its own set of keyframes.
|
|
70
|
-
// If the transform includes a skew, we have to combine all of the transform
|
|
71
|
-
// components into a single set of keyframes.
|
|
72
|
-
// Only `ShapeTransform` supports skews.
|
|
73
79
|
if
|
|
74
|
-
|
|
75
|
-
|
|
80
|
+
// CALayers don't support animating skew with its own set of keyframes.
|
|
81
|
+
// If the transform includes a skew, we have to combine all of the transform
|
|
82
|
+
// components into a single set of keyframes.
|
|
83
|
+
transformModel.hasSkew
|
|
84
|
+
// Negative `scale.x` values aren't applied correctly by Core Animation when animating
|
|
85
|
+
// `transform.scale.x` and `transform.scale.y` using separate `CAKeyframeAnimation`s
|
|
86
|
+
// (https://openradar.appspot.com/FB9862872). If the transform includes negative `scale.x`
|
|
87
|
+
// values, we have to combine all of the transform components into a single set of keyframes.
|
|
88
|
+
|| transformModel.hasNegativeXScaleValues
|
|
76
89
|
{
|
|
77
|
-
try addCombinedTransformAnimation(for:
|
|
90
|
+
try addCombinedTransformAnimation(for: transformModel, context: context)
|
|
78
91
|
}
|
|
79
92
|
|
|
80
93
|
else {
|
|
@@ -159,65 +172,10 @@ extension CALayer {
|
|
|
159
172
|
// Lottie animation files express scale as a numerical percentage value
|
|
160
173
|
// (e.g. 50%, 100%, 200%) so we divide by 100 to get the decimal values
|
|
161
174
|
// expected by Core Animation (e.g. 0.5, 1.0, 2.0).
|
|
162
|
-
|
|
163
|
-
// This appears to be because we animate `transform.scale.x` and `transform.scale.y`
|
|
164
|
-
// as separate `CAKeyframeAnimation`s instead of using a single animation of `transform` itself.
|
|
165
|
-
// https://openradar.appspot.com/FB9862872
|
|
166
|
-
// - To work around this, we set up a `rotationY` animation below
|
|
167
|
-
// to flip the view horizontally, which gives us the desired effect.
|
|
168
|
-
abs(CGFloat(scale.x) / 100)
|
|
175
|
+
CGFloat(scale.x) / 100
|
|
169
176
|
},
|
|
170
177
|
context: context)
|
|
171
178
|
|
|
172
|
-
/// iOS 14 and earlier doesn't properly support rendering transforms with
|
|
173
|
-
/// negative `scale.x` values: https://github.com/airbnb/lottie-ios/issues/1882
|
|
174
|
-
let osSupportsNegativeScaleValues: Bool = {
|
|
175
|
-
#if os(iOS) || os(tvOS)
|
|
176
|
-
if #available(iOS 15.0, tvOS 15.0, *) {
|
|
177
|
-
return true
|
|
178
|
-
} else {
|
|
179
|
-
return false
|
|
180
|
-
}
|
|
181
|
-
#else
|
|
182
|
-
// We'll assume this works correctly on macOS until told otherwise
|
|
183
|
-
return true
|
|
184
|
-
#endif
|
|
185
|
-
}()
|
|
186
|
-
|
|
187
|
-
lazy var hasNegativeXScaleValues = transformModel.scale.keyframes.contains(where: { $0.value.x < 0 })
|
|
188
|
-
|
|
189
|
-
// When `scale.x` is negative, we have to rotate the view
|
|
190
|
-
// half way around the y axis to flip it horizontally.
|
|
191
|
-
// - We don't do this in snapshot tests because it breaks the tests
|
|
192
|
-
// in surprising ways that don't happen at runtime. Definitely not ideal.
|
|
193
|
-
// - This isn't supported on iOS 14 and earlier either, so we have to
|
|
194
|
-
// log a compatibility error on devices running older OSs.
|
|
195
|
-
if TestHelpers.snapshotTestsAreRunning {
|
|
196
|
-
if hasNegativeXScaleValues {
|
|
197
|
-
context.logger.warn("""
|
|
198
|
-
Negative `scale.x` values are not displayed correctly in snapshot tests
|
|
199
|
-
""")
|
|
200
|
-
}
|
|
201
|
-
} else {
|
|
202
|
-
if !osSupportsNegativeScaleValues, hasNegativeXScaleValues {
|
|
203
|
-
try context.logCompatibilityIssue("""
|
|
204
|
-
iOS 14 and earlier does not support rendering negative `scale.x` values
|
|
205
|
-
""")
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
try addAnimation(
|
|
209
|
-
for: .rotationY,
|
|
210
|
-
keyframes: transformModel.scale,
|
|
211
|
-
value: { scale in
|
|
212
|
-
if scale.x < 0 {
|
|
213
|
-
return .pi
|
|
214
|
-
} else {
|
|
215
|
-
return 0
|
|
216
|
-
}
|
|
217
|
-
},
|
|
218
|
-
context: context)
|
|
219
|
-
}
|
|
220
|
-
|
|
221
179
|
try addAnimation(
|
|
222
180
|
for: .scaleY,
|
|
223
181
|
keyframes: transformModel.scale,
|
|
@@ -225,9 +183,6 @@ extension CALayer {
|
|
|
225
183
|
// Lottie animation files express scale as a numerical percentage value
|
|
226
184
|
// (e.g. 50%, 100%, 200%) so we divide by 100 to get the decimal values
|
|
227
185
|
// expected by Core Animation (e.g. 0.5, 1.0, 2.0).
|
|
228
|
-
// - Negative `scaleY` values are correctly applied (they flip the view
|
|
229
|
-
// vertically), so we don't have to apply an additional rotation animation
|
|
230
|
-
// like we do for `scaleX`.
|
|
231
186
|
CGFloat(scale.y) / 100
|
|
232
187
|
},
|
|
233
188
|
context: context)
|
|
@@ -258,7 +213,7 @@ extension CALayer {
|
|
|
258
213
|
}
|
|
259
214
|
|
|
260
215
|
// Lottie animation files express rotation in degrees
|
|
261
|
-
// (e.g. 90º, 180º, 360º) so we
|
|
216
|
+
// (e.g. 90º, 180º, 360º) so we convert to radians to get the
|
|
262
217
|
// values expected by Core Animation (e.g. π/2, π, 2π)
|
|
263
218
|
|
|
264
219
|
try addAnimation(
|
|
@@ -282,7 +237,7 @@ extension CALayer {
|
|
|
282
237
|
keyframes: transformModel.rotationZ,
|
|
283
238
|
value: { rotationDegrees in
|
|
284
239
|
// Lottie animation files express rotation in degrees
|
|
285
|
-
// (e.g. 90º, 180º, 360º) so we
|
|
240
|
+
// (e.g. 90º, 180º, 360º) so we convert to radians to get the
|
|
286
241
|
// values expected by Core Animation (e.g. π/2, π, 2π)
|
|
287
242
|
rotationDegrees.cgFloatValue * .pi / 180
|
|
288
243
|
},
|
|
@@ -291,26 +246,44 @@ extension CALayer {
|
|
|
291
246
|
|
|
292
247
|
/// Adds an animation for the entire `transform` key by combining all of the
|
|
293
248
|
/// position / size / rotation / skew animations into a single set of keyframes.
|
|
294
|
-
/// This is
|
|
295
|
-
///
|
|
249
|
+
/// This is more expensive that animating each component separately, since
|
|
250
|
+
/// it may require manually interpolating the keyframes at each frame.
|
|
296
251
|
private func addCombinedTransformAnimation(
|
|
297
|
-
for transformModel:
|
|
252
|
+
for transformModel: TransformModel,
|
|
298
253
|
context: LayerAnimationContext)
|
|
299
254
|
throws
|
|
300
255
|
{
|
|
256
|
+
// Core Animation doesn't animate skew changes properly. If the skew value
|
|
257
|
+
// changes over the course of the animation then we have to manually
|
|
258
|
+
// compute the `CATransform3D` for each frame individually.
|
|
259
|
+
let requiresManualInterpolation = transformModel.hasSkewAnimation
|
|
260
|
+
|
|
301
261
|
let combinedTransformKeyframes = Keyframes.combined(
|
|
302
|
-
transformModel.
|
|
303
|
-
transformModel.
|
|
262
|
+
transformModel.anchorPoint,
|
|
263
|
+
transformModel._position ?? KeyframeGroup(LottieVector3D(x: 0.0, y: 0.0, z: 0.0)),
|
|
264
|
+
transformModel._positionX ?? KeyframeGroup(LottieVector1D(0)),
|
|
265
|
+
transformModel._positionY ?? KeyframeGroup(LottieVector1D(0)),
|
|
304
266
|
transformModel.scale,
|
|
305
267
|
transformModel.rotationX,
|
|
306
268
|
transformModel.rotationY,
|
|
307
269
|
transformModel.rotationZ,
|
|
308
|
-
transformModel.
|
|
309
|
-
transformModel.
|
|
310
|
-
|
|
311
|
-
|
|
270
|
+
transformModel._skew ?? KeyframeGroup(LottieVector1D(0)),
|
|
271
|
+
transformModel._skewAxis ?? KeyframeGroup(LottieVector1D(0)),
|
|
272
|
+
requiresManualInterpolation: requiresManualInterpolation,
|
|
273
|
+
makeCombinedResult: {
|
|
274
|
+
anchor, position, positionX, positionY, scale, rotationX, rotationY, rotationZ, skew, skewAxis
|
|
275
|
+
-> CATransform3D in
|
|
276
|
+
|
|
277
|
+
let transformPosition: CGPoint
|
|
278
|
+
if transformModel._positionX != nil, transformModel._positionY != nil {
|
|
279
|
+
transformPosition = CGPoint(x: positionX.cgFloatValue, y: positionY.cgFloatValue)
|
|
280
|
+
} else {
|
|
281
|
+
transformPosition = position.pointValue
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return CATransform3D.makeTransform(
|
|
312
285
|
anchor: anchor.pointValue,
|
|
313
|
-
position:
|
|
286
|
+
position: transformPosition,
|
|
314
287
|
scale: scale.sizeValue,
|
|
315
288
|
rotationX: rotationX.cgFloatValue,
|
|
316
289
|
rotationY: rotationY.cgFloatValue,
|
|
@@ -327,3 +300,36 @@ extension CALayer {
|
|
|
327
300
|
}
|
|
328
301
|
|
|
329
302
|
}
|
|
303
|
+
|
|
304
|
+
extension TransformModel {
|
|
305
|
+
/// Whether or not this transform has a non-zero skew value
|
|
306
|
+
var hasSkew: Bool {
|
|
307
|
+
guard
|
|
308
|
+
let _skew = _skew,
|
|
309
|
+
let _skewAxis = _skewAxis,
|
|
310
|
+
!_skew.keyframes.isEmpty,
|
|
311
|
+
!_skewAxis.keyframes.isEmpty
|
|
312
|
+
else {
|
|
313
|
+
return false
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return _skew.keyframes.contains(where: { $0.value.cgFloatValue != 0 })
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/// Whether or not this transform has a non-zero skew value which animates
|
|
320
|
+
var hasSkewAnimation: Bool {
|
|
321
|
+
guard
|
|
322
|
+
hasSkew,
|
|
323
|
+
let _skew = _skew,
|
|
324
|
+
let _skewAxis = _skewAxis
|
|
325
|
+
else { return false }
|
|
326
|
+
|
|
327
|
+
return _skew.keyframes.count > 1
|
|
328
|
+
|| _skewAxis.keyframes.count > 1
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/// Whether or not this `TransformModel` has any negative X scale values
|
|
332
|
+
var hasNegativeXScaleValues: Bool {
|
|
333
|
+
scale.keyframes.contains(where: { $0.value.x < 0 })
|
|
334
|
+
}
|
|
335
|
+
}
|
|
@@ -17,7 +17,7 @@ final class CoreAnimationLayer: BaseAnimationLayer {
|
|
|
17
17
|
init(
|
|
18
18
|
animation: LottieAnimation,
|
|
19
19
|
imageProvider: AnimationImageProvider,
|
|
20
|
-
textProvider:
|
|
20
|
+
textProvider: AnimationKeypathTextProvider,
|
|
21
21
|
fontProvider: AnimationFontProvider,
|
|
22
22
|
maskAnimationToBounds: Bool,
|
|
23
23
|
compatibilityTrackerMode: CompatibilityTracker.Mode,
|
|
@@ -94,8 +94,8 @@ final class CoreAnimationLayer: BaseAnimationLayer {
|
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
/// The parent `
|
|
98
|
-
weak var
|
|
97
|
+
/// The parent `LottieAnimationLayer` that manages this layer
|
|
98
|
+
weak var lottieAnimationLayer: LottieAnimationLayer?
|
|
99
99
|
|
|
100
100
|
/// A closure that is called after this layer sets up its animation.
|
|
101
101
|
/// If the animation setup was unsuccessful and encountered compatibility issues,
|
|
@@ -108,9 +108,9 @@ final class CoreAnimationLayer: BaseAnimationLayer {
|
|
|
108
108
|
didSet { reloadImages() }
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
/// The `
|
|
111
|
+
/// The `AnimationKeypathTextProvider` that `TextLayer`'s use to retrieve texts,
|
|
112
112
|
/// that they should use to render their text context
|
|
113
|
-
var textProvider:
|
|
113
|
+
var textProvider: AnimationKeypathTextProvider {
|
|
114
114
|
didSet {
|
|
115
115
|
// We need to rebuild the current animation after updating the text provider,
|
|
116
116
|
// since this is used in `TextLayer.setupAnimations(context:)`
|
|
@@ -209,6 +209,7 @@ final class CoreAnimationLayer: BaseAnimationLayer {
|
|
|
209
209
|
private let valueProviderStore: ValueProviderStore
|
|
210
210
|
private let compatibilityTracker: CompatibilityTracker
|
|
211
211
|
private let logger: LottieLogger
|
|
212
|
+
private let loggingState = LoggingState()
|
|
212
213
|
|
|
213
214
|
/// The current playback state of the animation that is displayed in this layer
|
|
214
215
|
private var currentPlaybackState: PlaybackState? {
|
|
@@ -265,6 +266,7 @@ final class CoreAnimationLayer: BaseAnimationLayer {
|
|
|
265
266
|
valueProviderStore: valueProviderStore,
|
|
266
267
|
compatibilityTracker: compatibilityTracker,
|
|
267
268
|
logger: logger,
|
|
269
|
+
loggingState: loggingState,
|
|
268
270
|
currentKeypath: AnimationKeypath(keys: []),
|
|
269
271
|
textProvider: textProvider,
|
|
270
272
|
recordHierarchyKeypath: configuration.recordHierarchyKeypath)
|
|
@@ -316,7 +318,7 @@ final class CoreAnimationLayer: BaseAnimationLayer {
|
|
|
316
318
|
else { return }
|
|
317
319
|
|
|
318
320
|
if isAnimationPlaying == true {
|
|
319
|
-
|
|
321
|
+
lottieAnimationLayer?.updateInFlightAnimation()
|
|
320
322
|
} else {
|
|
321
323
|
let currentFrame = currentFrame
|
|
322
324
|
removeAnimations()
|
|
@@ -449,7 +451,9 @@ extension CoreAnimationLayer: RootAnimationLayer {
|
|
|
449
451
|
}
|
|
450
452
|
|
|
451
453
|
func forceDisplayUpdate() {
|
|
452
|
-
// Unimplemented
|
|
454
|
+
// Unimplemented
|
|
455
|
+
// - We can't call `display()` here, because it would cause unexpected frame animations:
|
|
456
|
+
// https://github.com/airbnb/lottie-ios/issues/2193
|
|
453
457
|
}
|
|
454
458
|
|
|
455
459
|
func logHierarchyKeypaths() {
|
|
@@ -10,12 +10,18 @@ enum Keyframes {
|
|
|
10
10
|
/// Combines the given keyframe groups of `Keyframe<T>`s into a single keyframe group of of `Keyframe<[T]>`s
|
|
11
11
|
/// - If all of the `KeyframeGroup`s have the exact same animation timing, the keyframes are merged
|
|
12
12
|
/// - Otherwise, the keyframes are manually interpolated at each frame in the animation
|
|
13
|
-
static func combined<T>(
|
|
13
|
+
static func combined<T>(
|
|
14
|
+
_ allGroups: [KeyframeGroup<T>],
|
|
15
|
+
requiresManualInterpolation: Bool = false)
|
|
16
|
+
-> KeyframeGroup<[T]>
|
|
14
17
|
where T: AnyInterpolatable
|
|
15
18
|
{
|
|
16
|
-
Keyframes.combined(
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
Keyframes.combined(
|
|
20
|
+
allGroups,
|
|
21
|
+
requiresManualInterpolation: requiresManualInterpolation,
|
|
22
|
+
makeCombinedResult: { untypedValues in
|
|
23
|
+
untypedValues.compactMap { $0 as? T }
|
|
24
|
+
})
|
|
19
25
|
}
|
|
20
26
|
|
|
21
27
|
/// Combines the given keyframe groups of `Keyframe<T>`s into a single keyframe group of of `Keyframe<[T]>`s
|
|
@@ -24,6 +30,7 @@ enum Keyframes {
|
|
|
24
30
|
static func combined<T1, T2, CombinedResult>(
|
|
25
31
|
_ k1: KeyframeGroup<T1>,
|
|
26
32
|
_ k2: KeyframeGroup<T2>,
|
|
33
|
+
requiresManualInterpolation: Bool = false,
|
|
27
34
|
makeCombinedResult: (T1, T2) throws -> CombinedResult)
|
|
28
35
|
rethrows
|
|
29
36
|
-> KeyframeGroup<CombinedResult>
|
|
@@ -31,6 +38,7 @@ enum Keyframes {
|
|
|
31
38
|
{
|
|
32
39
|
try Keyframes.combined(
|
|
33
40
|
[k1, k2],
|
|
41
|
+
requiresManualInterpolation: requiresManualInterpolation,
|
|
34
42
|
makeCombinedResult: { untypedValues in
|
|
35
43
|
guard
|
|
36
44
|
let t1 = untypedValues[0] as? T1,
|
|
@@ -48,12 +56,14 @@ enum Keyframes {
|
|
|
48
56
|
_ k1: KeyframeGroup<T1>,
|
|
49
57
|
_ k2: KeyframeGroup<T2>,
|
|
50
58
|
_ k3: KeyframeGroup<T3>,
|
|
59
|
+
requiresManualInterpolation: Bool = false,
|
|
51
60
|
makeCombinedResult: (T1, T2, T3) -> CombinedResult)
|
|
52
61
|
-> KeyframeGroup<CombinedResult>
|
|
53
62
|
where T1: AnyInterpolatable, T2: AnyInterpolatable, T3: AnyInterpolatable
|
|
54
63
|
{
|
|
55
64
|
Keyframes.combined(
|
|
56
65
|
[k1, k2, k3],
|
|
66
|
+
requiresManualInterpolation: requiresManualInterpolation,
|
|
57
67
|
makeCombinedResult: { untypedValues in
|
|
58
68
|
guard
|
|
59
69
|
let t1 = untypedValues[0] as? T1,
|
|
@@ -76,6 +86,7 @@ enum Keyframes {
|
|
|
76
86
|
_ k5: KeyframeGroup<T5>,
|
|
77
87
|
_ k6: KeyframeGroup<T6>,
|
|
78
88
|
_ k7: KeyframeGroup<T7>,
|
|
89
|
+
requiresManualInterpolation: Bool = false,
|
|
79
90
|
makeCombinedResult: (T1, T2, T3, T4, T5, T6, T7) -> CombinedResult)
|
|
80
91
|
-> KeyframeGroup<CombinedResult>
|
|
81
92
|
where T1: AnyInterpolatable, T2: AnyInterpolatable, T3: AnyInterpolatable, T4: AnyInterpolatable,
|
|
@@ -83,6 +94,7 @@ enum Keyframes {
|
|
|
83
94
|
{
|
|
84
95
|
Keyframes.combined(
|
|
85
96
|
[k1, k2, k3, k4, k5, k6, k7],
|
|
97
|
+
requiresManualInterpolation: requiresManualInterpolation,
|
|
86
98
|
makeCombinedResult: { untypedValues in
|
|
87
99
|
guard
|
|
88
100
|
let t1 = untypedValues[0] as? T1,
|
|
@@ -110,6 +122,7 @@ enum Keyframes {
|
|
|
110
122
|
_ k6: KeyframeGroup<T6>,
|
|
111
123
|
_ k7: KeyframeGroup<T7>,
|
|
112
124
|
_ k8: KeyframeGroup<T8>,
|
|
125
|
+
requiresManualInterpolation: Bool = false,
|
|
113
126
|
makeCombinedResult: (T1, T2, T3, T4, T5, T6, T7, T8) -> CombinedResult)
|
|
114
127
|
-> KeyframeGroup<CombinedResult>
|
|
115
128
|
where T1: AnyInterpolatable, T2: AnyInterpolatable, T3: AnyInterpolatable, T4: AnyInterpolatable,
|
|
@@ -117,6 +130,7 @@ enum Keyframes {
|
|
|
117
130
|
{
|
|
118
131
|
Keyframes.combined(
|
|
119
132
|
[k1, k2, k3, k4, k5, k6, k7, k8],
|
|
133
|
+
requiresManualInterpolation: requiresManualInterpolation,
|
|
120
134
|
makeCombinedResult: { untypedValues in
|
|
121
135
|
guard
|
|
122
136
|
let t1 = untypedValues[0] as? T1,
|
|
@@ -133,6 +147,48 @@ enum Keyframes {
|
|
|
133
147
|
})
|
|
134
148
|
}
|
|
135
149
|
|
|
150
|
+
/// Combines the given keyframe groups of `Keyframe<T>`s into a single keyframe group of of `Keyframe<[T]>`s
|
|
151
|
+
/// - If all of the `KeyframeGroup`s have the exact same animation timing, the keyframes are merged
|
|
152
|
+
/// - Otherwise, the keyframes are manually interpolated at each frame in the animation
|
|
153
|
+
static func combined<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, CombinedResult>(
|
|
154
|
+
_ k1: KeyframeGroup<T1>,
|
|
155
|
+
_ k2: KeyframeGroup<T2>,
|
|
156
|
+
_ k3: KeyframeGroup<T3>,
|
|
157
|
+
_ k4: KeyframeGroup<T4>,
|
|
158
|
+
_ k5: KeyframeGroup<T5>,
|
|
159
|
+
_ k6: KeyframeGroup<T6>,
|
|
160
|
+
_ k7: KeyframeGroup<T7>,
|
|
161
|
+
_ k8: KeyframeGroup<T8>,
|
|
162
|
+
_ k9: KeyframeGroup<T9>,
|
|
163
|
+
_ k10: KeyframeGroup<T10>,
|
|
164
|
+
requiresManualInterpolation: Bool = false,
|
|
165
|
+
makeCombinedResult: (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) -> CombinedResult)
|
|
166
|
+
-> KeyframeGroup<CombinedResult>
|
|
167
|
+
where T1: AnyInterpolatable, T2: AnyInterpolatable, T3: AnyInterpolatable, T4: AnyInterpolatable,
|
|
168
|
+
T5: AnyInterpolatable, T6: AnyInterpolatable, T7: AnyInterpolatable, T8: AnyInterpolatable,
|
|
169
|
+
T9: AnyInterpolatable, T10: AnyInterpolatable
|
|
170
|
+
{
|
|
171
|
+
Keyframes.combined(
|
|
172
|
+
[k1, k2, k3, k4, k5, k6, k7, k8, k9, k10],
|
|
173
|
+
requiresManualInterpolation: requiresManualInterpolation,
|
|
174
|
+
makeCombinedResult: { untypedValues in
|
|
175
|
+
guard
|
|
176
|
+
let t1 = untypedValues[0] as? T1,
|
|
177
|
+
let t2 = untypedValues[1] as? T2,
|
|
178
|
+
let t3 = untypedValues[2] as? T3,
|
|
179
|
+
let t4 = untypedValues[3] as? T4,
|
|
180
|
+
let t5 = untypedValues[4] as? T5,
|
|
181
|
+
let t6 = untypedValues[5] as? T6,
|
|
182
|
+
let t7 = untypedValues[6] as? T7,
|
|
183
|
+
let t8 = untypedValues[7] as? T8,
|
|
184
|
+
let t9 = untypedValues[8] as? T9,
|
|
185
|
+
let t10 = untypedValues[9] as? T10
|
|
186
|
+
else { return nil }
|
|
187
|
+
|
|
188
|
+
return makeCombinedResult(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10)
|
|
189
|
+
})
|
|
190
|
+
}
|
|
191
|
+
|
|
136
192
|
// MARK: Private
|
|
137
193
|
|
|
138
194
|
/// Combines the given `[KeyframeGroup]` of `Keyframe<T>`s into a single `KeyframeGroup` of `Keyframe<CombinedResult>`s
|
|
@@ -141,8 +197,12 @@ enum Keyframes {
|
|
|
141
197
|
///
|
|
142
198
|
/// `makeCombinedResult` is a closure that takes an array of keyframe values (with the exact same length as `AnyKeyframeGroup`),
|
|
143
199
|
/// casts them to the expected type, and combined them into the final resulting keyframe.
|
|
200
|
+
///
|
|
201
|
+
/// `requiresManualInterpolation` determines whether the keyframes must be computed using `Keyframes.manuallyInterpolated`,
|
|
202
|
+
/// which interpolates the value at each frame, or if the keyframes can simply be combined.
|
|
144
203
|
private static func combined<CombinedResult>(
|
|
145
204
|
_ allGroups: [AnyKeyframeGroup],
|
|
205
|
+
requiresManualInterpolation: Bool,
|
|
146
206
|
makeCombinedResult: ([Any]) throws -> CombinedResult?)
|
|
147
207
|
rethrows
|
|
148
208
|
-> KeyframeGroup<CombinedResult>
|
|
@@ -155,6 +215,7 @@ enum Keyframes {
|
|
|
155
215
|
let animatingKeyframes = untypedGroups.filter { $0.keyframes.count > 1 }
|
|
156
216
|
|
|
157
217
|
guard
|
|
218
|
+
!requiresManualInterpolation,
|
|
158
219
|
!allGroups.isEmpty,
|
|
159
220
|
animatingKeyframes.allSatisfy({ $0.hasSameTimingParameters(as: animatingKeyframes[0]) })
|
|
160
221
|
else {
|
|
@@ -222,7 +283,7 @@ enum Keyframes {
|
|
|
222
283
|
extension KeyframeGroup {
|
|
223
284
|
/// Whether or not all of the keyframes in this `KeyframeGroup` have the same
|
|
224
285
|
/// timing parameters as the corresponding keyframe in the other given `KeyframeGroup`
|
|
225
|
-
func hasSameTimingParameters<
|
|
286
|
+
func hasSameTimingParameters<U>(as other: KeyframeGroup<U>) -> Bool {
|
|
226
287
|
guard keyframes.count == other.keyframes.count else {
|
|
227
288
|
return false
|
|
228
289
|
}
|
|
@@ -236,7 +297,7 @@ extension KeyframeGroup {
|
|
|
236
297
|
extension Keyframe {
|
|
237
298
|
/// Whether or not this keyframe has the same timing parameters as the given keyframe,
|
|
238
299
|
/// excluding `spatialInTangent` and `spatialOutTangent`.
|
|
239
|
-
fileprivate func hasSameTimingParameters<
|
|
300
|
+
fileprivate func hasSameTimingParameters<U>(as other: Keyframe<U>) -> Bool {
|
|
240
301
|
time == other.time
|
|
241
302
|
&& isHold == other.isHold
|
|
242
303
|
&& inTangent == other.inTangent
|