lottie-ios 4.2.0 → 4.3.0
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 +1542 -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 +346 -102
- 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/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 +7 -7
- package/Sources/Private/CoreAnimation/Extensions/Keyframes+combined.swift +67 -6
- package/Sources/Private/CoreAnimation/Layers/AnimationLayer.swift +2 -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 +258 -735
- 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 +2 -2
- 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
|
@@ -14,7 +14,6 @@ import Foundation
|
|
|
14
14
|
/// Keyframe data is wrapped in a dictionary { "k" : KeyframeData }.
|
|
15
15
|
/// The keyframe data can either be an array of keyframes or, if no animation is present, the raw value.
|
|
16
16
|
/// This helper object is needed to properly decode the json.
|
|
17
|
-
|
|
18
17
|
final class KeyframeGroup<T> {
|
|
19
18
|
|
|
20
19
|
// MARK: Lifecycle
|
|
@@ -86,8 +85,8 @@ extension KeyframeGroup: Decodable where T: Decodable {
|
|
|
86
85
|
|
|
87
86
|
guard
|
|
88
87
|
let value: T = keyframeData.startValue ?? previousKeyframeData?.endValue,
|
|
89
|
-
let time = keyframeData.time
|
|
90
|
-
{
|
|
88
|
+
let time = keyframeData.time
|
|
89
|
+
else {
|
|
91
90
|
/// Missing keyframe data. JSON must be corrupt.
|
|
92
91
|
throw DecodingError.dataCorruptedError(
|
|
93
92
|
forKey: KeyframeWrapperKey.keyframeData,
|
|
@@ -165,8 +164,8 @@ extension KeyframeGroup: DictionaryInitializable where T: AnyInitializable {
|
|
|
165
164
|
let data = try KeyframeData<T>(dictionary: frameDictionary)
|
|
166
165
|
guard
|
|
167
166
|
let value: T = data.startValue ?? previousKeyframeData?.endValue,
|
|
168
|
-
let time = data.time
|
|
169
|
-
{
|
|
167
|
+
let time = data.time
|
|
168
|
+
else {
|
|
170
169
|
throw InitializableError.invalidInput
|
|
171
170
|
}
|
|
172
171
|
keyframes.append(Keyframe<T>(
|
|
@@ -203,6 +202,10 @@ extension KeyframeGroup: Hashable where T: Hashable {
|
|
|
203
202
|
}
|
|
204
203
|
}
|
|
205
204
|
|
|
205
|
+
// MARK: Sendable
|
|
206
|
+
|
|
207
|
+
extension KeyframeGroup: Sendable where T: Sendable { }
|
|
208
|
+
|
|
206
209
|
extension Keyframe {
|
|
207
210
|
/// Creates a copy of this `Keyframe` with the same timing data, but a different value
|
|
208
211
|
func withValue<Value>(_ newValue: Value) -> Keyframe<Value> {
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// Created by Cal Stephens on 8/14/23.
|
|
2
|
+
// Copyright © 2023 Airbnb Inc. All rights reserved.
|
|
3
|
+
|
|
4
|
+
import Foundation
|
|
5
|
+
|
|
6
|
+
final class DropShadowEffect: LayerEffect {
|
|
7
|
+
|
|
8
|
+
// MARK: Lifecycle
|
|
9
|
+
|
|
10
|
+
required init(from decoder: Decoder) throws {
|
|
11
|
+
try super.init(from: decoder)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
required init(dictionary: [String: Any]) throws {
|
|
15
|
+
try super.init(dictionary: dictionary)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// MARK: Internal
|
|
19
|
+
|
|
20
|
+
/// The color of the drop shadow
|
|
21
|
+
var color: ColorEffectValue? {
|
|
22
|
+
value(named: "Shadow Color")
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/// Opacity between 0 and 255
|
|
26
|
+
var opacity: Vector1DEffectValue? {
|
|
27
|
+
value(named: "Opacity")
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/// The direction / angle of the drop shadow, in degrees
|
|
31
|
+
var direction: Vector1DEffectValue? {
|
|
32
|
+
value(named: "Direction")
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/// The distance of the drop shadow
|
|
36
|
+
var distance: Vector1DEffectValue? {
|
|
37
|
+
value(named: "Distance")
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/// The softness of the drop shadow
|
|
41
|
+
var softness: Vector1DEffectValue? {
|
|
42
|
+
value(named: "Softness")
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Created by Cal Stephens on 8/14/23.
|
|
2
|
+
// Copyright © 2023 Airbnb Inc. All rights reserved.
|
|
3
|
+
|
|
4
|
+
import Foundation
|
|
5
|
+
|
|
6
|
+
final class ColorEffectValue: EffectValue {
|
|
7
|
+
|
|
8
|
+
// MARK: Lifecycle
|
|
9
|
+
|
|
10
|
+
required init(from decoder: Decoder) throws {
|
|
11
|
+
let container = try decoder.container(keyedBy: CodingKeys.self)
|
|
12
|
+
value = try? container.decode(KeyframeGroup<LottieColor>.self, forKey: .value)
|
|
13
|
+
try super.init(from: decoder)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
required init(dictionary: [String: Any]) throws {
|
|
17
|
+
let valueDictionary: [String: Any] = try dictionary.value(for: CodingKeys.value)
|
|
18
|
+
value = try KeyframeGroup<LottieColor>(dictionary: valueDictionary)
|
|
19
|
+
try super.init(dictionary: dictionary)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// MARK: Internal
|
|
23
|
+
|
|
24
|
+
/// The value of the color
|
|
25
|
+
let value: KeyframeGroup<LottieColor>?
|
|
26
|
+
|
|
27
|
+
override func encode(to encoder: Encoder) throws {
|
|
28
|
+
try super.encode(to: encoder)
|
|
29
|
+
var container = encoder.container(keyedBy: CodingKeys.self)
|
|
30
|
+
try container.encode(value, forKey: .value)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// MARK: Private
|
|
34
|
+
|
|
35
|
+
private enum CodingKeys: String, CodingKey {
|
|
36
|
+
case value = "v"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// Created by Cal Stephens on 8/15/23.
|
|
2
|
+
// Copyright © 2023 Airbnb Inc. All rights reserved.
|
|
3
|
+
|
|
4
|
+
import Foundation
|
|
5
|
+
|
|
6
|
+
// MARK: - EffectValueType
|
|
7
|
+
|
|
8
|
+
/// https://lottiefiles.github.io/lottie-docs/schema/#/$defs/effect-values
|
|
9
|
+
enum EffectValueType: Int, Codable, Sendable {
|
|
10
|
+
case slider = 0
|
|
11
|
+
case angle = 1
|
|
12
|
+
case color = 2
|
|
13
|
+
case unknown = 9999
|
|
14
|
+
|
|
15
|
+
init(from decoder: Decoder) throws {
|
|
16
|
+
self = try EffectValueType(rawValue: decoder.singleValueContainer().decode(RawValue.self)) ?? .unknown
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// MARK: ClassFamily
|
|
21
|
+
|
|
22
|
+
extension EffectValueType: ClassFamily {
|
|
23
|
+
static var discriminator: Discriminator = .type
|
|
24
|
+
|
|
25
|
+
func getType() -> AnyObject.Type {
|
|
26
|
+
switch self {
|
|
27
|
+
case .slider:
|
|
28
|
+
return Vector1DEffectValue.self
|
|
29
|
+
case .angle:
|
|
30
|
+
return Vector1DEffectValue.self
|
|
31
|
+
case .color:
|
|
32
|
+
return ColorEffectValue.self
|
|
33
|
+
case .unknown:
|
|
34
|
+
// Unsupported
|
|
35
|
+
return LayerEffect.self
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// MARK: - EffectValue
|
|
41
|
+
|
|
42
|
+
class EffectValue: Codable, DictionaryInitializable {
|
|
43
|
+
|
|
44
|
+
// MARK: Lifecycle
|
|
45
|
+
|
|
46
|
+
required init(from decoder: Decoder) throws {
|
|
47
|
+
let container = try decoder.container(keyedBy: EffectValue.CodingKeys.self)
|
|
48
|
+
type = try container.decode(EffectValueType.self, forKey: .type)
|
|
49
|
+
name = try container.decode(String.self, forKey: .name)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
required init(dictionary: [String: Any]) throws {
|
|
53
|
+
type = EffectValueType(rawValue: try dictionary.value(for: CodingKeys.type)) ?? .unknown
|
|
54
|
+
name = try dictionary.value(for: CodingKeys.name) ?? "Effect"
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// MARK: Internal
|
|
58
|
+
|
|
59
|
+
/// The type of effect value
|
|
60
|
+
let type: EffectValueType
|
|
61
|
+
|
|
62
|
+
/// The name of the effect value
|
|
63
|
+
let name: String
|
|
64
|
+
|
|
65
|
+
// MARK: Fileprivate
|
|
66
|
+
|
|
67
|
+
fileprivate enum CodingKeys: String, CodingKey {
|
|
68
|
+
case type = "ty"
|
|
69
|
+
case name = "nm"
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
extension Array where Element == EffectValue {
|
|
74
|
+
static func fromDictionaries(_ dictionaries: [[String: Any]]) throws -> [EffectValue] {
|
|
75
|
+
try dictionaries.compactMap { dictionary in
|
|
76
|
+
let shapeType = dictionary[EffectValue.CodingKeys.type.rawValue] as? Int
|
|
77
|
+
switch EffectValueType(rawValue: shapeType ?? EffectValueType.unknown.rawValue) {
|
|
78
|
+
case .slider:
|
|
79
|
+
return try Vector1DEffectValue(dictionary: dictionary)
|
|
80
|
+
case .angle:
|
|
81
|
+
return try Vector1DEffectValue(dictionary: dictionary)
|
|
82
|
+
case .color:
|
|
83
|
+
return try ColorEffectValue(dictionary: dictionary)
|
|
84
|
+
case .unknown:
|
|
85
|
+
// Unsupported
|
|
86
|
+
return try EffectValue(dictionary: dictionary)
|
|
87
|
+
case nil:
|
|
88
|
+
return nil
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// MARK: - EffectValue + Sendable
|
|
95
|
+
|
|
96
|
+
/// Since `EffectValue` isn't `final`, we have to use `@unchecked Sendable` instead of `Sendable.`
|
|
97
|
+
/// All `EffectValue` subclasses are immutable `Sendable` values.
|
|
98
|
+
extension EffectValue: @unchecked Sendable { }
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Created by Cal Stephens on 8/14/23.
|
|
2
|
+
// Copyright © 2023 Airbnb Inc. All rights reserved.
|
|
3
|
+
|
|
4
|
+
import Foundation
|
|
5
|
+
|
|
6
|
+
final class Vector1DEffectValue: EffectValue {
|
|
7
|
+
|
|
8
|
+
// MARK: Lifecycle
|
|
9
|
+
|
|
10
|
+
required init(from decoder: Decoder) throws {
|
|
11
|
+
let container = try decoder.container(keyedBy: CodingKeys.self)
|
|
12
|
+
value = try? container.decode(KeyframeGroup<LottieVector1D>.self, forKey: .value)
|
|
13
|
+
try super.init(from: decoder)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
required init(dictionary: [String: Any]) throws {
|
|
17
|
+
let valueDictionary: [String: Any] = try dictionary.value(for: CodingKeys.value)
|
|
18
|
+
value = try KeyframeGroup<LottieVector1D>(dictionary: valueDictionary)
|
|
19
|
+
try super.init(dictionary: dictionary)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// MARK: Internal
|
|
23
|
+
|
|
24
|
+
/// The value of the slider
|
|
25
|
+
let value: KeyframeGroup<LottieVector1D>?
|
|
26
|
+
|
|
27
|
+
override func encode(to encoder: Encoder) throws {
|
|
28
|
+
try super.encode(to: encoder)
|
|
29
|
+
var container = encoder.container(keyedBy: CodingKeys.self)
|
|
30
|
+
try container.encode(value, forKey: .value)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// MARK: Private
|
|
34
|
+
|
|
35
|
+
private enum CodingKeys: String, CodingKey {
|
|
36
|
+
case value = "v"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// Created by Cal Stephens on 8/14/23.
|
|
2
|
+
// Copyright © 2023 Airbnb Inc. All rights reserved.
|
|
3
|
+
|
|
4
|
+
import Foundation
|
|
5
|
+
|
|
6
|
+
// MARK: - LayerEffectType
|
|
7
|
+
|
|
8
|
+
/// https://lottiefiles.github.io/lottie-docs/schema/#/$defs/effects
|
|
9
|
+
enum LayerEffectType: Int, Codable, Sendable {
|
|
10
|
+
case dropShadow = 25
|
|
11
|
+
case unknown = 9999
|
|
12
|
+
|
|
13
|
+
init(from decoder: Decoder) throws {
|
|
14
|
+
self = try LayerEffectType(rawValue: decoder.singleValueContainer().decode(RawValue.self)) ?? .unknown
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// MARK: ClassFamily
|
|
19
|
+
|
|
20
|
+
extension LayerEffectType: ClassFamily {
|
|
21
|
+
static var discriminator: Discriminator = .type
|
|
22
|
+
|
|
23
|
+
func getType() -> AnyObject.Type {
|
|
24
|
+
switch self {
|
|
25
|
+
case .dropShadow:
|
|
26
|
+
return DropShadowEffect.self
|
|
27
|
+
case .unknown:
|
|
28
|
+
// Unsupported
|
|
29
|
+
return LayerEffect.self
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// MARK: - LayerEffect
|
|
35
|
+
|
|
36
|
+
class LayerEffect: Codable, DictionaryInitializable {
|
|
37
|
+
|
|
38
|
+
// MARK: Lifecycle
|
|
39
|
+
|
|
40
|
+
required init(from decoder: Decoder) throws {
|
|
41
|
+
let container = try decoder.container(keyedBy: LayerEffect.CodingKeys.self)
|
|
42
|
+
name = try container.decodeIfPresent(String.self, forKey: .name) ?? "Effect"
|
|
43
|
+
type = try container.decode(LayerEffectType.self, forKey: .type)
|
|
44
|
+
effects = try container.decodeIfPresent([EffectValue].self, ofFamily: EffectValueType.self, forKey: .effects) ?? []
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
required init(dictionary: [String: Any]) throws {
|
|
48
|
+
name = (try? dictionary.value(for: CodingKeys.name)) ?? "Layer"
|
|
49
|
+
type = LayerEffectType(rawValue: try dictionary.value(for: CodingKeys.type)) ?? .unknown
|
|
50
|
+
if let valueDictionaries = dictionary[CodingKeys.effects.rawValue] as? [[String: Any]] {
|
|
51
|
+
effects = try [EffectValue].fromDictionaries(valueDictionaries)
|
|
52
|
+
} else {
|
|
53
|
+
effects = []
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// MARK: Internal
|
|
58
|
+
|
|
59
|
+
/// The name of the effect
|
|
60
|
+
let name: String
|
|
61
|
+
|
|
62
|
+
/// The type of effect
|
|
63
|
+
let type: LayerEffectType
|
|
64
|
+
|
|
65
|
+
/// Values that configure the behavior of the effect
|
|
66
|
+
let effects: [EffectValue]
|
|
67
|
+
|
|
68
|
+
/// Retrieves the `EffectValue` for the given name
|
|
69
|
+
func value<ValueType: EffectValue>(named name: String) -> ValueType? {
|
|
70
|
+
effects.first(where: {
|
|
71
|
+
$0.name == name && $0 is ValueType
|
|
72
|
+
}) as? ValueType
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// MARK: Fileprivate
|
|
76
|
+
|
|
77
|
+
fileprivate enum CodingKeys: String, CodingKey {
|
|
78
|
+
case name = "nm"
|
|
79
|
+
case type = "ty"
|
|
80
|
+
case effects = "ef"
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
extension Array where Element == LayerEffect {
|
|
85
|
+
static func fromDictionaries(_ dictionaries: [[String: Any]]) throws -> [LayerEffect] {
|
|
86
|
+
try dictionaries.compactMap { dictionary in
|
|
87
|
+
let shapeType = dictionary[LayerEffect.CodingKeys.type.rawValue] as? Int
|
|
88
|
+
switch LayerEffectType(rawValue: shapeType ?? LayerEffectType.unknown.rawValue) {
|
|
89
|
+
case .dropShadow:
|
|
90
|
+
return try DropShadowEffect(dictionary: dictionary)
|
|
91
|
+
case .unknown, nil:
|
|
92
|
+
// Unsupported
|
|
93
|
+
return try LayerEffect(dictionary: dictionary)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// MARK: - LayerEffect + Sendable
|
|
100
|
+
|
|
101
|
+
/// Since `LayerEffect` isn't `final`, we have to use `@unchecked Sendable` instead of `Sendable.`
|
|
102
|
+
/// All `LayerEffect` subclasses are immutable `Sendable` values.
|
|
103
|
+
extension LayerEffect: @unchecked Sendable { }
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// Created by Cal Stephens on 8/14/23.
|
|
2
|
+
// Copyright © 2023 Airbnb Inc. All rights reserved.
|
|
3
|
+
|
|
4
|
+
import Foundation
|
|
5
|
+
|
|
6
|
+
final class DropShadowStyle: LayerStyle {
|
|
7
|
+
|
|
8
|
+
// MARK: Lifecycle
|
|
9
|
+
|
|
10
|
+
required init(from decoder: Decoder) throws {
|
|
11
|
+
let container = try decoder.container(keyedBy: DropShadowStyle.CodingKeys.self)
|
|
12
|
+
opacity = try container.decode(KeyframeGroup<LottieVector1D>.self, forKey: .opacity)
|
|
13
|
+
color = try container.decode(KeyframeGroup<LottieColor>.self, forKey: .color)
|
|
14
|
+
angle = try container.decode(KeyframeGroup<LottieVector1D>.self, forKey: .angle)
|
|
15
|
+
size = try container.decode(KeyframeGroup<LottieVector1D>.self, forKey: .size)
|
|
16
|
+
distance = try container.decode(KeyframeGroup<LottieVector1D>.self, forKey: .distance)
|
|
17
|
+
try super.init(from: decoder)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
required init(dictionary: [String: Any]) throws {
|
|
21
|
+
let opacityDictionary: [String: Any] = try dictionary.value(for: CodingKeys.opacity)
|
|
22
|
+
opacity = try KeyframeGroup<LottieVector1D>(dictionary: opacityDictionary)
|
|
23
|
+
let colorDictionary: [String: Any] = try dictionary.value(for: CodingKeys.color)
|
|
24
|
+
color = try KeyframeGroup<LottieColor>(dictionary: colorDictionary)
|
|
25
|
+
let angleDictionary: [String: Any] = try dictionary.value(for: CodingKeys.angle)
|
|
26
|
+
angle = try KeyframeGroup<LottieVector1D>(dictionary: angleDictionary)
|
|
27
|
+
let sizeDictionary: [String: Any] = try dictionary.value(for: CodingKeys.size)
|
|
28
|
+
size = try KeyframeGroup<LottieVector1D>(dictionary: sizeDictionary)
|
|
29
|
+
let distanceDictionary: [String: Any] = try dictionary.value(for: CodingKeys.distance)
|
|
30
|
+
distance = try KeyframeGroup<LottieVector1D>(dictionary: distanceDictionary)
|
|
31
|
+
try super.init(dictionary: dictionary)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// MARK: Internal
|
|
35
|
+
|
|
36
|
+
/// The opacity of the drop shadow
|
|
37
|
+
let opacity: KeyframeGroup<LottieVector1D>
|
|
38
|
+
|
|
39
|
+
/// The color of the drop shadow
|
|
40
|
+
let color: KeyframeGroup<LottieColor>
|
|
41
|
+
|
|
42
|
+
/// The angle of the drop shadow, in degrees,
|
|
43
|
+
/// with `0` representing a shadow straight-down from the layer
|
|
44
|
+
/// (`offsetY=distance, offsetX=0`).
|
|
45
|
+
let angle: KeyframeGroup<LottieVector1D>
|
|
46
|
+
|
|
47
|
+
/// The size of the drop shadow
|
|
48
|
+
let size: KeyframeGroup<LottieVector1D>
|
|
49
|
+
|
|
50
|
+
/// The distance of the drop shadow
|
|
51
|
+
let distance: KeyframeGroup<LottieVector1D>
|
|
52
|
+
|
|
53
|
+
override func encode(to encoder: Encoder) throws {
|
|
54
|
+
try super.encode(to: encoder)
|
|
55
|
+
var container = encoder.container(keyedBy: CodingKeys.self)
|
|
56
|
+
try container.encode(opacity, forKey: .opacity)
|
|
57
|
+
try container.encode(color, forKey: .color)
|
|
58
|
+
try container.encode(angle, forKey: .angle)
|
|
59
|
+
try container.encode(size, forKey: .size)
|
|
60
|
+
try container.encode(distance, forKey: .distance)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// MARK: Private
|
|
64
|
+
|
|
65
|
+
private enum CodingKeys: String, CodingKey {
|
|
66
|
+
case color = "c"
|
|
67
|
+
case opacity = "o"
|
|
68
|
+
case angle = "a"
|
|
69
|
+
case size = "s"
|
|
70
|
+
case distance = "d"
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// Created by Cal Stephens on 8/14/23.
|
|
2
|
+
// Copyright © 2023 Airbnb Inc. All rights reserved.
|
|
3
|
+
|
|
4
|
+
import Foundation
|
|
5
|
+
|
|
6
|
+
// MARK: - LayerStyleType
|
|
7
|
+
|
|
8
|
+
enum LayerStyleType: Int, Codable, Sendable {
|
|
9
|
+
case dropShadow = 1
|
|
10
|
+
case unknown = 9999
|
|
11
|
+
|
|
12
|
+
init(from decoder: Decoder) throws {
|
|
13
|
+
self = try LayerStyleType(rawValue: decoder.singleValueContainer().decode(RawValue.self)) ?? .unknown
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// MARK: ClassFamily
|
|
18
|
+
|
|
19
|
+
extension LayerStyleType: ClassFamily {
|
|
20
|
+
static var discriminator: Discriminator = .type
|
|
21
|
+
|
|
22
|
+
func getType() -> AnyObject.Type {
|
|
23
|
+
switch self {
|
|
24
|
+
case .dropShadow:
|
|
25
|
+
return DropShadowStyle.self
|
|
26
|
+
case .unknown:
|
|
27
|
+
// Unsupported
|
|
28
|
+
return LayerStyle.self
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// MARK: - LayerStyle
|
|
34
|
+
|
|
35
|
+
class LayerStyle: Codable, DictionaryInitializable {
|
|
36
|
+
|
|
37
|
+
// MARK: Lifecycle
|
|
38
|
+
|
|
39
|
+
required init(from decoder: Decoder) throws {
|
|
40
|
+
let container = try decoder.container(keyedBy: LayerStyle.CodingKeys.self)
|
|
41
|
+
name = try container.decodeIfPresent(String.self, forKey: .name) ?? "Style"
|
|
42
|
+
type = try container.decode(LayerStyleType.self, forKey: .type)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
required init(dictionary: [String: Any]) throws {
|
|
46
|
+
name = (try? dictionary.value(for: CodingKeys.name)) ?? "Layer"
|
|
47
|
+
type = LayerStyleType(rawValue: try dictionary.value(for: CodingKeys.type)) ?? .unknown
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// MARK: Internal
|
|
51
|
+
|
|
52
|
+
/// The name of the style
|
|
53
|
+
let name: String
|
|
54
|
+
|
|
55
|
+
/// The type of style
|
|
56
|
+
let type: LayerStyleType
|
|
57
|
+
|
|
58
|
+
// MARK: Fileprivate
|
|
59
|
+
|
|
60
|
+
fileprivate enum CodingKeys: String, CodingKey {
|
|
61
|
+
case name = "nm"
|
|
62
|
+
case type = "ty"
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
extension Array where Element == LayerStyle {
|
|
67
|
+
static func fromDictionaries(_ dictionaries: [[String: Any]]) throws -> [LayerStyle] {
|
|
68
|
+
try dictionaries.compactMap { dictionary in
|
|
69
|
+
let shapeType = dictionary[LayerStyle.CodingKeys.type.rawValue] as? Int
|
|
70
|
+
switch LayerStyleType(rawValue: shapeType ?? LayerStyleType.unknown.rawValue) {
|
|
71
|
+
case .dropShadow:
|
|
72
|
+
return try DropShadowStyle(dictionary: dictionary)
|
|
73
|
+
case .unknown, nil:
|
|
74
|
+
// Unsupported
|
|
75
|
+
return try LayerStyle(dictionary: dictionary)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// MARK: - LayerStyle + Sendable
|
|
82
|
+
|
|
83
|
+
/// Since `LayerStyle` isn't `final`, we have to use `@unchecked Sendable` instead of `Sendable.`
|
|
84
|
+
/// All `LayerStyle` subclasses are immutable `Sendable` values.
|
|
85
|
+
extension LayerStyle: @unchecked Sendable { }
|
|
@@ -99,6 +99,8 @@ class LayerModel: Codable, DictionaryInitializable {
|
|
|
99
99
|
timeStretch = try container.decodeIfPresent(Double.self, forKey: .timeStretch) ?? 1
|
|
100
100
|
matte = try container.decodeIfPresent(MatteType.self, forKey: .matte)
|
|
101
101
|
hidden = try container.decodeIfPresent(Bool.self, forKey: .hidden) ?? false
|
|
102
|
+
styles = try container.decodeIfPresent([LayerStyle].self, ofFamily: LayerStyleType.self, forKey: .styles) ?? []
|
|
103
|
+
effects = try container.decodeIfPresent([LayerEffect].self, ofFamily: LayerEffectType.self, forKey: .effects) ?? []
|
|
102
104
|
}
|
|
103
105
|
|
|
104
106
|
required init(dictionary: [String: Any]) throws {
|
|
@@ -138,6 +140,16 @@ class LayerModel: Codable, DictionaryInitializable {
|
|
|
138
140
|
matte = nil
|
|
139
141
|
}
|
|
140
142
|
hidden = (try? dictionary.value(for: CodingKeys.hidden)) ?? false
|
|
143
|
+
if let styleDictionaries = dictionary[CodingKeys.styles.rawValue] as? [[String: Any]] {
|
|
144
|
+
styles = try [LayerStyle].fromDictionaries(styleDictionaries)
|
|
145
|
+
} else {
|
|
146
|
+
styles = []
|
|
147
|
+
}
|
|
148
|
+
if let effectDictionaries = dictionary[CodingKeys.effects.rawValue] as? [[String: Any]] {
|
|
149
|
+
effects = try [LayerEffect].fromDictionaries(effectDictionaries)
|
|
150
|
+
} else {
|
|
151
|
+
effects = []
|
|
152
|
+
}
|
|
141
153
|
}
|
|
142
154
|
|
|
143
155
|
// MARK: Internal
|
|
@@ -180,8 +192,15 @@ class LayerModel: Codable, DictionaryInitializable {
|
|
|
180
192
|
/// The type of matte if any.
|
|
181
193
|
let matte: MatteType?
|
|
182
194
|
|
|
195
|
+
/// Whether or not this layer is hidden, in which case it will not be rendered.
|
|
183
196
|
let hidden: Bool
|
|
184
197
|
|
|
198
|
+
/// A list of styles to apply to this layer
|
|
199
|
+
let styles: [LayerStyle]
|
|
200
|
+
|
|
201
|
+
/// A list of effects to apply to this layer
|
|
202
|
+
let effects: [LayerEffect]
|
|
203
|
+
|
|
185
204
|
// MARK: Fileprivate
|
|
186
205
|
|
|
187
206
|
fileprivate enum CodingKeys: String, CodingKey {
|
|
@@ -199,6 +218,8 @@ class LayerModel: Codable, DictionaryInitializable {
|
|
|
199
218
|
case timeStretch = "sr"
|
|
200
219
|
case matte = "tt"
|
|
201
220
|
case hidden = "hd"
|
|
221
|
+
case styles = "sy"
|
|
222
|
+
case effects = "ef"
|
|
202
223
|
}
|
|
203
224
|
}
|
|
204
225
|
|
|
@@ -226,3 +247,9 @@ extension Array where Element == LayerModel {
|
|
|
226
247
|
}
|
|
227
248
|
}
|
|
228
249
|
}
|
|
250
|
+
|
|
251
|
+
// MARK: - LayerModel + Sendable
|
|
252
|
+
|
|
253
|
+
/// Since `LayerModel` isn't `final`, we have to use `@unchecked Sendable` instead of `Sendable.`
|
|
254
|
+
/// All `LayerModel` subclasses are immutable `Sendable` values.
|
|
255
|
+
extension LayerModel: @unchecked Sendable { }
|
|
@@ -154,8 +154,7 @@ final class Transform: Codable, DictionaryInitializable {
|
|
|
154
154
|
let rotation = try? KeyframeGroup<LottieVector1D>(dictionary: rotationDictionary)
|
|
155
155
|
{
|
|
156
156
|
rotationZ = rotation
|
|
157
|
-
}
|
|
158
|
-
else {
|
|
157
|
+
} else {
|
|
159
158
|
rotationZ = KeyframeGroup(LottieVector1D(0))
|
|
160
159
|
}
|
|
161
160
|
rotation = nil
|
|
@@ -9,7 +9,7 @@ import Foundation
|
|
|
9
9
|
|
|
10
10
|
// MARK: - LineCap
|
|
11
11
|
|
|
12
|
-
enum LineCap: Int, Codable {
|
|
12
|
+
enum LineCap: Int, Codable, Sendable {
|
|
13
13
|
case none
|
|
14
14
|
case butt
|
|
15
15
|
case round
|
|
@@ -18,7 +18,7 @@ enum LineCap: Int, Codable {
|
|
|
18
18
|
|
|
19
19
|
// MARK: - LineJoin
|
|
20
20
|
|
|
21
|
-
enum LineJoin: Int, Codable {
|
|
21
|
+
enum LineJoin: Int, Codable, Sendable {
|
|
22
22
|
case none
|
|
23
23
|
case miter
|
|
24
24
|
case round
|