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
|
@@ -16,10 +16,12 @@ final class PreCompositionLayer: CompositionLayer {
|
|
|
16
16
|
precomp: PreCompLayerModel,
|
|
17
17
|
asset: PrecompAsset,
|
|
18
18
|
layerImageProvider: LayerImageProvider,
|
|
19
|
-
|
|
19
|
+
layerTextProvider: LayerTextProvider,
|
|
20
|
+
textProvider: AnimationKeypathTextProvider,
|
|
20
21
|
fontProvider: AnimationFontProvider,
|
|
21
22
|
assetLibrary: AssetLibrary?,
|
|
22
|
-
frameRate: CGFloat
|
|
23
|
+
frameRate: CGFloat,
|
|
24
|
+
rootAnimationLayer: MainThreadAnimationLayer?)
|
|
23
25
|
{
|
|
24
26
|
animationLayers = []
|
|
25
27
|
if let keyframes = precomp.timeRemapping?.keyframes {
|
|
@@ -36,11 +38,14 @@ final class PreCompositionLayer: CompositionLayer {
|
|
|
36
38
|
let layers = asset.layers.initializeCompositionLayers(
|
|
37
39
|
assetLibrary: assetLibrary,
|
|
38
40
|
layerImageProvider: layerImageProvider,
|
|
41
|
+
layerTextProvider: layerTextProvider,
|
|
39
42
|
textProvider: textProvider,
|
|
40
43
|
fontProvider: fontProvider,
|
|
41
|
-
frameRate: frameRate
|
|
44
|
+
frameRate: frameRate,
|
|
45
|
+
rootAnimationLayer: rootAnimationLayer)
|
|
42
46
|
|
|
43
47
|
var imageLayers = [ImageCompositionLayer]()
|
|
48
|
+
var textLayers = [TextCompositionLayer]()
|
|
44
49
|
|
|
45
50
|
var mattedLayer: CompositionLayer? = nil
|
|
46
51
|
|
|
@@ -50,6 +55,9 @@ final class PreCompositionLayer: CompositionLayer {
|
|
|
50
55
|
if let imageLayer = layer as? ImageCompositionLayer {
|
|
51
56
|
imageLayers.append(imageLayer)
|
|
52
57
|
}
|
|
58
|
+
if let textLayer = layer as? TextCompositionLayer {
|
|
59
|
+
textLayers.append(textLayer)
|
|
60
|
+
}
|
|
53
61
|
if let matte = mattedLayer {
|
|
54
62
|
/// The previous layer requires this layer to be its matte
|
|
55
63
|
matte.matteLayer = layer
|
|
@@ -69,6 +77,7 @@ final class PreCompositionLayer: CompositionLayer {
|
|
|
69
77
|
childKeypaths.append(contentsOf: layers)
|
|
70
78
|
|
|
71
79
|
layerImageProvider.addImageLayers(imageLayers)
|
|
80
|
+
layerTextProvider.addTextLayers(textLayers)
|
|
72
81
|
}
|
|
73
82
|
|
|
74
83
|
override init(layer: Any) {
|
|
@@ -107,12 +116,16 @@ final class PreCompositionLayer: CompositionLayer {
|
|
|
107
116
|
} else {
|
|
108
117
|
localFrame = (frame - startFrame) / timeStretch
|
|
109
118
|
}
|
|
110
|
-
|
|
119
|
+
for animationLayer in animationLayers {
|
|
120
|
+
animationLayer.displayWithFrame(frame: localFrame, forceUpdates: forceUpdates)
|
|
121
|
+
}
|
|
111
122
|
}
|
|
112
123
|
|
|
113
124
|
override func updateRenderScale() {
|
|
114
125
|
super.updateRenderScale()
|
|
115
|
-
|
|
126
|
+
for animationLayer in animationLayers {
|
|
127
|
+
animationLayer.renderScale = renderScale
|
|
128
|
+
}
|
|
116
129
|
}
|
|
117
130
|
|
|
118
131
|
// MARK: Fileprivate
|
|
@@ -47,7 +47,12 @@ final class TextCompositionLayer: CompositionLayer {
|
|
|
47
47
|
|
|
48
48
|
// MARK: Lifecycle
|
|
49
49
|
|
|
50
|
-
init(
|
|
50
|
+
init(
|
|
51
|
+
textLayer: TextLayerModel,
|
|
52
|
+
textProvider: AnimationKeypathTextProvider,
|
|
53
|
+
fontProvider: AnimationFontProvider,
|
|
54
|
+
rootAnimationLayer: MainThreadAnimationLayer?)
|
|
55
|
+
{
|
|
51
56
|
var rootNode: TextAnimatorNode?
|
|
52
57
|
for animator in textLayer.animators {
|
|
53
58
|
rootNode = TextAnimatorNode(parentNode: rootNode, textAnimator: animator)
|
|
@@ -57,6 +62,7 @@ final class TextCompositionLayer: CompositionLayer {
|
|
|
57
62
|
|
|
58
63
|
self.textProvider = textProvider
|
|
59
64
|
self.fontProvider = fontProvider
|
|
65
|
+
self.rootAnimationLayer = rootAnimationLayer
|
|
60
66
|
|
|
61
67
|
super.init(layer: textLayer, size: .zero)
|
|
62
68
|
contentsLayer.addSublayer(self.textLayer)
|
|
@@ -92,8 +98,18 @@ final class TextCompositionLayer: CompositionLayer {
|
|
|
92
98
|
let textDocument: KeyframeInterpolator<TextDocument>?
|
|
93
99
|
|
|
94
100
|
let textLayer = CoreTextRenderLayer()
|
|
95
|
-
var textProvider:
|
|
101
|
+
var textProvider: AnimationKeypathTextProvider
|
|
96
102
|
var fontProvider: AnimationFontProvider
|
|
103
|
+
weak var rootAnimationLayer: MainThreadAnimationLayer?
|
|
104
|
+
|
|
105
|
+
lazy var fullAnimationKeypath: AnimationKeypath = {
|
|
106
|
+
// Individual layers don't know their full keypaths, so we have to delegate
|
|
107
|
+
// to the `MainThreadAnimationLayer` to search the layer hierarchy and find
|
|
108
|
+
// the full keypath (which includes this layer's parent layers)
|
|
109
|
+
rootAnimationLayer?.keypath(for: self)
|
|
110
|
+
// If that failed for some reason, just use the last path component (which we do have here)
|
|
111
|
+
?? AnimationKeypath(keypath: keypathName)
|
|
112
|
+
}()
|
|
97
113
|
|
|
98
114
|
override func displayContentsWithFrame(frame: CGFloat, forceUpdates: Bool) {
|
|
99
115
|
guard let textDocument = textDocument else { return }
|
|
@@ -108,11 +124,23 @@ final class TextCompositionLayer: CompositionLayer {
|
|
|
108
124
|
|
|
109
125
|
// Get Text Attributes
|
|
110
126
|
let text = textDocument.value(frame: frame) as! TextDocument
|
|
127
|
+
|
|
128
|
+
// Prior to Lottie 4.3.0 the Main Thread rendering engine always just used `LegacyAnimationTextProvider`
|
|
129
|
+
// and called it with the `keypathName` (only the last path component of the full keypath).
|
|
130
|
+
// Starting in Lottie 4.3.0 we use `AnimationKeypathTextProvider` instead if implemented.
|
|
131
|
+
let textString: String
|
|
132
|
+
if let keypathTextValue = textProvider.text(for: fullAnimationKeypath, sourceText: text.text) {
|
|
133
|
+
textString = keypathTextValue
|
|
134
|
+
} else if let legacyTextProvider = textProvider as? LegacyAnimationTextProvider {
|
|
135
|
+
textString = legacyTextProvider.textFor(keypathName: keypathName, sourceText: text.text)
|
|
136
|
+
} else {
|
|
137
|
+
textString = text.text
|
|
138
|
+
}
|
|
139
|
+
|
|
111
140
|
let strokeColor = rootNode?.textOutputNode.strokeColor ?? text.strokeColorData?.cgColorValue
|
|
112
141
|
let strokeWidth = rootNode?.textOutputNode.strokeWidth ?? CGFloat(text.strokeWidth ?? 0)
|
|
113
142
|
let tracking = (CGFloat(text.fontSize) * (rootNode?.textOutputNode.tracking ?? CGFloat(text.tracking))) / 1000.0
|
|
114
143
|
let matrix = rootNode?.textOutputNode.xform ?? CATransform3DIdentity
|
|
115
|
-
let textString = textProvider.textFor(keypathName: keypathName, sourceText: text.text)
|
|
116
144
|
let ctFont = fontProvider.fontFor(family: text.fontFamily, size: CGFloat(text.fontSize))
|
|
117
145
|
|
|
118
146
|
// Set all of the text layer options
|
|
@@ -21,7 +21,7 @@ final class MainThreadAnimationLayer: CALayer, RootAnimationLayer {
|
|
|
21
21
|
init(
|
|
22
22
|
animation: LottieAnimation,
|
|
23
23
|
imageProvider: AnimationImageProvider,
|
|
24
|
-
textProvider:
|
|
24
|
+
textProvider: AnimationKeypathTextProvider,
|
|
25
25
|
fontProvider: AnimationFontProvider,
|
|
26
26
|
maskAnimationToBounds: Bool,
|
|
27
27
|
logger: LottieLogger)
|
|
@@ -37,9 +37,11 @@ final class MainThreadAnimationLayer: CALayer, RootAnimationLayer {
|
|
|
37
37
|
let layers = animation.layers.initializeCompositionLayers(
|
|
38
38
|
assetLibrary: animation.assetLibrary,
|
|
39
39
|
layerImageProvider: layerImageProvider,
|
|
40
|
+
layerTextProvider: layerTextProvider,
|
|
40
41
|
textProvider: textProvider,
|
|
41
42
|
fontProvider: fontProvider,
|
|
42
|
-
frameRate: CGFloat(animation.framerate)
|
|
43
|
+
frameRate: CGFloat(animation.framerate),
|
|
44
|
+
rootAnimationLayer: self)
|
|
43
45
|
|
|
44
46
|
var imageLayers = [ImageCompositionLayer]()
|
|
45
47
|
var textLayers = [TextCompositionLayer]()
|
|
@@ -139,7 +141,9 @@ final class MainThreadAnimationLayer: CALayer, RootAnimationLayer {
|
|
|
139
141
|
if respectAnimationFrameRate {
|
|
140
142
|
newFrame = floor(newFrame)
|
|
141
143
|
}
|
|
142
|
-
|
|
144
|
+
for animationLayer in animationLayers {
|
|
145
|
+
animationLayer.displayWithFrame(frame: newFrame, forceUpdates: forceDisplayUpdateOnEachFrame)
|
|
146
|
+
}
|
|
143
147
|
}
|
|
144
148
|
|
|
145
149
|
// MARK: Internal
|
|
@@ -147,8 +151,16 @@ final class MainThreadAnimationLayer: CALayer, RootAnimationLayer {
|
|
|
147
151
|
/// The animatable Current Frame Property
|
|
148
152
|
@NSManaged var currentFrame: CGFloat
|
|
149
153
|
|
|
150
|
-
/// The parent `
|
|
151
|
-
weak var
|
|
154
|
+
/// The parent `LottieAnimationLayer` that manages this layer
|
|
155
|
+
weak var lottieAnimationLayer: LottieAnimationLayer?
|
|
156
|
+
|
|
157
|
+
/// Whether or not to use `forceDisplayUpdate()` when rendering each individual frame.
|
|
158
|
+
/// - The main thread rendering engine implements optimizations to decrease the amount
|
|
159
|
+
/// of properties that have to be re-rendered on each frame. There are some cases
|
|
160
|
+
/// where this can result in bugs / incorrect behavior, so we allow it to be disabled.
|
|
161
|
+
/// - Forcing a full render on every frame will decrease performance, and is not recommended
|
|
162
|
+
/// except as a workaround to a bug in the main thread rendering engine.
|
|
163
|
+
var forceDisplayUpdateOnEachFrame = false
|
|
152
164
|
|
|
153
165
|
var animationLayers: ContiguousArray<CompositionLayer>
|
|
154
166
|
|
|
@@ -175,11 +187,13 @@ final class MainThreadAnimationLayer: CALayer, RootAnimationLayer {
|
|
|
175
187
|
|
|
176
188
|
var renderScale: CGFloat = 1 {
|
|
177
189
|
didSet {
|
|
178
|
-
|
|
190
|
+
for animationLayer in animationLayers {
|
|
191
|
+
animationLayer.renderScale = renderScale
|
|
192
|
+
}
|
|
179
193
|
}
|
|
180
194
|
}
|
|
181
195
|
|
|
182
|
-
var textProvider:
|
|
196
|
+
var textProvider: AnimationKeypathTextProvider {
|
|
183
197
|
get { layerTextProvider.textProvider }
|
|
184
198
|
set { layerTextProvider.textProvider = newValue }
|
|
185
199
|
}
|
|
@@ -199,7 +213,9 @@ final class MainThreadAnimationLayer: CALayer, RootAnimationLayer {
|
|
|
199
213
|
|
|
200
214
|
/// Forces the view to update its drawing.
|
|
201
215
|
func forceDisplayUpdate() {
|
|
202
|
-
|
|
216
|
+
for animationLayer in animationLayers {
|
|
217
|
+
animationLayer.displayWithFrame(frame: currentFrame, forceUpdates: true)
|
|
218
|
+
}
|
|
203
219
|
}
|
|
204
220
|
|
|
205
221
|
func logHierarchyKeypaths() {
|
|
@@ -258,6 +274,15 @@ final class MainThreadAnimationLayer: CALayer, RootAnimationLayer {
|
|
|
258
274
|
return nil
|
|
259
275
|
}
|
|
260
276
|
|
|
277
|
+
func keypath(for layerToFind: CALayer) -> AnimationKeypath? {
|
|
278
|
+
for layer in animationLayers {
|
|
279
|
+
if let foundKeypath = layer.keypath(for: layerToFind) {
|
|
280
|
+
return foundKeypath
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
return nil
|
|
284
|
+
}
|
|
285
|
+
|
|
261
286
|
func animatorNodes(for keypath: AnimationKeypath) -> [AnimatorNode]? {
|
|
262
287
|
var results = [AnimatorNode]()
|
|
263
288
|
for layer in animationLayers {
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
import CoreGraphics
|
|
5
5
|
import Foundation
|
|
6
|
+
import QuartzCore
|
|
6
7
|
|
|
7
8
|
// MARK: - CachedImageProvider
|
|
8
9
|
|
|
@@ -35,6 +36,11 @@ private final class CachedImageProvider: AnimationImageProvider {
|
|
|
35
36
|
|
|
36
37
|
let imageCache: NSCache<NSString, CGImage> = .init()
|
|
37
38
|
let imageProvider: AnimationImageProvider
|
|
39
|
+
|
|
40
|
+
func contentsGravity(for asset: ImageAsset) -> CALayerContentsGravity {
|
|
41
|
+
imageProvider.contentsGravity(for: asset)
|
|
42
|
+
}
|
|
43
|
+
|
|
38
44
|
}
|
|
39
45
|
|
|
40
46
|
extension AnimationImageProvider {
|
|
@@ -42,6 +48,7 @@ extension AnimationImageProvider {
|
|
|
42
48
|
/// It wraps the current provider as image loader, and uses `NSCache` to cache the images for resue.
|
|
43
49
|
/// The cache will be reset when the `animation` is reset.
|
|
44
50
|
var cachedImageProvider: AnimationImageProvider {
|
|
45
|
-
|
|
51
|
+
guard cacheEligible else { return self }
|
|
52
|
+
return CachedImageProvider(imageProvider: self)
|
|
46
53
|
}
|
|
47
54
|
}
|
package/Sources/Private/MainThread/LayerContainers/Utility/CompositionLayersInitializer.swift
CHANGED
|
@@ -13,9 +13,12 @@ extension Array where Element == LayerModel {
|
|
|
13
13
|
func initializeCompositionLayers(
|
|
14
14
|
assetLibrary: AssetLibrary?,
|
|
15
15
|
layerImageProvider: LayerImageProvider,
|
|
16
|
-
|
|
16
|
+
layerTextProvider: LayerTextProvider,
|
|
17
|
+
textProvider: AnimationKeypathTextProvider,
|
|
17
18
|
fontProvider: AnimationFontProvider,
|
|
18
|
-
frameRate: CGFloat
|
|
19
|
+
frameRate: CGFloat,
|
|
20
|
+
rootAnimationLayer: MainThreadAnimationLayer?)
|
|
21
|
+
-> [CompositionLayer]
|
|
19
22
|
{
|
|
20
23
|
var compositionLayers = [CompositionLayer]()
|
|
21
24
|
var layerMap = [Int : CompositionLayer]()
|
|
@@ -45,10 +48,12 @@ extension Array where Element == LayerModel {
|
|
|
45
48
|
precomp: precompLayer,
|
|
46
49
|
asset: precompAsset,
|
|
47
50
|
layerImageProvider: layerImageProvider,
|
|
51
|
+
layerTextProvider: layerTextProvider,
|
|
48
52
|
textProvider: textProvider,
|
|
49
53
|
fontProvider: fontProvider,
|
|
50
54
|
assetLibrary: assetLibrary,
|
|
51
|
-
frameRate: frameRate
|
|
55
|
+
frameRate: frameRate,
|
|
56
|
+
rootAnimationLayer: rootAnimationLayer)
|
|
52
57
|
compositionLayers.append(precompContainer)
|
|
53
58
|
layerMap[layer.index] = precompContainer
|
|
54
59
|
} else if
|
|
@@ -62,7 +67,11 @@ extension Array where Element == LayerModel {
|
|
|
62
67
|
compositionLayers.append(imageContainer)
|
|
63
68
|
layerMap[layer.index] = imageContainer
|
|
64
69
|
} else if let textLayer = layer as? TextLayerModel {
|
|
65
|
-
let textContainer = TextCompositionLayer(
|
|
70
|
+
let textContainer = TextCompositionLayer(
|
|
71
|
+
textLayer: textLayer,
|
|
72
|
+
textProvider: textProvider,
|
|
73
|
+
fontProvider: fontProvider,
|
|
74
|
+
rootAnimationLayer: rootAnimationLayer)
|
|
66
75
|
compositionLayers.append(textContainer)
|
|
67
76
|
layerMap[layer.index] = textContainer
|
|
68
77
|
} else {
|
|
@@ -47,6 +47,7 @@ final class LayerImageProvider {
|
|
|
47
47
|
for imageLayer in imageLayers {
|
|
48
48
|
if let asset = imageAssets[imageLayer.imageReferenceID] {
|
|
49
49
|
imageLayer.image = imageProvider.imageForAsset(asset: asset)
|
|
50
|
+
imageLayer.imageContentsGravity = imageProvider.contentsGravity(for: asset)
|
|
50
51
|
}
|
|
51
52
|
}
|
|
52
53
|
}
|
|
@@ -12,7 +12,7 @@ final class LayerTextProvider {
|
|
|
12
12
|
|
|
13
13
|
// MARK: Lifecycle
|
|
14
14
|
|
|
15
|
-
init(textProvider:
|
|
15
|
+
init(textProvider: AnimationKeypathTextProvider) {
|
|
16
16
|
self.textProvider = textProvider
|
|
17
17
|
textLayers = []
|
|
18
18
|
reloadTexts()
|
|
@@ -22,7 +22,7 @@ final class LayerTextProvider {
|
|
|
22
22
|
|
|
23
23
|
private(set) var textLayers: [TextCompositionLayer]
|
|
24
24
|
|
|
25
|
-
var textProvider:
|
|
25
|
+
var textProvider: AnimationKeypathTextProvider {
|
|
26
26
|
didSet {
|
|
27
27
|
reloadTexts()
|
|
28
28
|
}
|
|
@@ -33,8 +33,8 @@ final class LayerTextProvider {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
func reloadTexts() {
|
|
36
|
-
textLayers
|
|
37
|
-
|
|
36
|
+
for textLayer in textLayers {
|
|
37
|
+
textLayer.textProvider = textProvider
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
}
|
|
@@ -133,7 +133,9 @@ final class TrimPathNode: AnimatorNode {
|
|
|
133
133
|
|
|
134
134
|
/// First get the total length of all paths.
|
|
135
135
|
var totalLength: CGFloat = 0
|
|
136
|
-
|
|
136
|
+
for upstreamPath in upstreamPaths {
|
|
137
|
+
totalLength = totalLength + upstreamPath.totalLength
|
|
138
|
+
}
|
|
137
139
|
|
|
138
140
|
/// Now determine the start and end cut lengths
|
|
139
141
|
let startLength = startPosition * totalLength
|
|
@@ -82,7 +82,8 @@ private final class GradientFillLayer: CALayer {
|
|
|
82
82
|
bitsPerComponent: 8,
|
|
83
83
|
bytesPerRow: ctx.width,
|
|
84
84
|
space: maskColorSpace,
|
|
85
|
-
bitmapInfo: 0)
|
|
85
|
+
bitmapInfo: 0)
|
|
86
|
+
else { return }
|
|
86
87
|
let flipVertical = CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: CGFloat(maskContext.height))
|
|
87
88
|
maskContext.concatenate(flipVertical)
|
|
88
89
|
maskContext.concatenate(ctx.ctm)
|
|
@@ -104,7 +104,8 @@ final class LegacyGradientFillRenderer: PassThroughOutputNode, Renderable {
|
|
|
104
104
|
bitsPerComponent: 8,
|
|
105
105
|
bytesPerRow: inContext.width,
|
|
106
106
|
space: maskColorSpace,
|
|
107
|
-
bitmapInfo: 0)
|
|
107
|
+
bitmapInfo: 0)
|
|
108
|
+
else { return }
|
|
108
109
|
let flipVertical = CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: CGFloat(maskContext.height))
|
|
109
110
|
maskContext.concatenate(flipVertical)
|
|
110
111
|
maskContext.concatenate(inContext.ctm)
|
package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/GradientFillNode.swift
CHANGED
|
@@ -27,7 +27,7 @@ final class GradientFillProperties: NodePropertyMap, KeypathSearchable {
|
|
|
27
27
|
PropertyName.opacity.rawValue : opacity,
|
|
28
28
|
"Start Point" : startPoint,
|
|
29
29
|
"End Point" : endPoint,
|
|
30
|
-
|
|
30
|
+
PropertyName.gradientColors.rawValue : colors,
|
|
31
31
|
]
|
|
32
32
|
properties = Array(keypathProperties.values)
|
|
33
33
|
}
|
package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/GradientStrokeNode.swift
CHANGED
|
@@ -47,8 +47,8 @@ final class GradientStrokeProperties: NodePropertyMap, KeypathSearchable {
|
|
|
47
47
|
PropertyName.opacity.rawValue : opacity,
|
|
48
48
|
"Start Point" : startPoint,
|
|
49
49
|
"End Point" : endPoint,
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
PropertyName.gradientColors.rawValue : colors,
|
|
51
|
+
PropertyName.strokeWidth.rawValue : width,
|
|
52
52
|
"Dashes" : dashPattern,
|
|
53
53
|
"Dash Phase" : dashPhase,
|
|
54
54
|
]
|
|
@@ -38,7 +38,7 @@ final class StrokeNodeProperties: NodePropertyMap, KeypathSearchable {
|
|
|
38
38
|
keypathProperties = [
|
|
39
39
|
PropertyName.opacity.rawValue : opacity,
|
|
40
40
|
PropertyName.color.rawValue : color,
|
|
41
|
-
|
|
41
|
+
PropertyName.strokeWidth.rawValue : width,
|
|
42
42
|
"Dashes" : dashPattern,
|
|
43
43
|
"Dash Phase" : dashPhase,
|
|
44
44
|
]
|
|
@@ -99,7 +99,7 @@ final class TextAnimatorNodeProperties: NodePropertyMap, KeypathSearchable {
|
|
|
99
99
|
|
|
100
100
|
if let keyframeGroup = textAnimator.strokeWidth {
|
|
101
101
|
strokeWidth = NodeProperty(provider: KeyframeInterpolator(keyframes: keyframeGroup.keyframes))
|
|
102
|
-
properties[
|
|
102
|
+
properties[PropertyName.strokeWidth.rawValue] = strokeWidth
|
|
103
103
|
} else {
|
|
104
104
|
strokeWidth = nil
|
|
105
105
|
}
|
|
@@ -56,7 +56,9 @@ class ShapeContainerLayer: CALayer {
|
|
|
56
56
|
rebuildContents(forFrame: forFrame)
|
|
57
57
|
}
|
|
58
58
|
guard isHidden == false else { return }
|
|
59
|
-
|
|
59
|
+
for renderLayer in renderLayers {
|
|
60
|
+
renderLayer.markRenderUpdates(forFrame: forFrame)
|
|
61
|
+
}
|
|
60
62
|
}
|
|
61
63
|
|
|
62
64
|
func hasRenderUpdate(forFrame _: CGFloat) -> Bool {
|
|
@@ -69,7 +71,9 @@ class ShapeContainerLayer: CALayer {
|
|
|
69
71
|
|
|
70
72
|
func updateRenderScale() {
|
|
71
73
|
contentsScale = renderScale
|
|
72
|
-
|
|
74
|
+
for renderLayer in renderLayers {
|
|
75
|
+
renderLayer.renderScale = renderScale
|
|
76
|
+
}
|
|
73
77
|
}
|
|
74
78
|
|
|
75
79
|
}
|
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
|
|
8
8
|
import Foundation
|
|
9
9
|
|
|
10
|
+
// MARK: - Asset
|
|
11
|
+
|
|
10
12
|
public class Asset: Codable, DictionaryInitializable {
|
|
11
13
|
|
|
12
14
|
// MARK: Lifecycle
|
|
@@ -41,3 +43,9 @@ public class Asset: Codable, DictionaryInitializable {
|
|
|
41
43
|
case id
|
|
42
44
|
}
|
|
43
45
|
}
|
|
46
|
+
|
|
47
|
+
// MARK: Sendable
|
|
48
|
+
|
|
49
|
+
/// Since `Asset` isn't `final`, we have to use `@unchecked Sendable` instead of `Sendable.`
|
|
50
|
+
/// All `Asset` subclasses are immutable `Sendable` values.
|
|
51
|
+
extension Asset: @unchecked Sendable { }
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import Foundation
|
|
9
9
|
|
|
10
|
-
final class AssetLibrary: Codable, AnyInitializable {
|
|
10
|
+
final class AssetLibrary: Codable, AnyInitializable, Sendable {
|
|
11
11
|
|
|
12
12
|
// MARK: Lifecycle
|
|
13
13
|
|
|
@@ -44,7 +44,7 @@ final class AssetLibrary: Codable, AnyInitializable {
|
|
|
44
44
|
var decodedAssets = [String : Asset]()
|
|
45
45
|
var imageAssets = [String : ImageAsset]()
|
|
46
46
|
var precompAssets = [String : PrecompAsset]()
|
|
47
|
-
|
|
47
|
+
for dictionary in dictionaries {
|
|
48
48
|
if dictionary[PrecompAsset.CodingKeys.layers.rawValue] != nil {
|
|
49
49
|
let asset = try PrecompAsset(dictionary: dictionary)
|
|
50
50
|
decodedAssets[asset.id] = asset
|
|
@@ -70,7 +70,7 @@ extension Data {
|
|
|
70
70
|
///
|
|
71
71
|
/// Returns nil when the input is not recognized as valid Data URL.
|
|
72
72
|
/// - parameter imageAsset: The image asset that contains Data URL.
|
|
73
|
-
|
|
73
|
+
init?(imageAsset: ImageAsset) {
|
|
74
74
|
self.init(dataString: imageAsset.name)
|
|
75
75
|
}
|
|
76
76
|
|
|
@@ -79,7 +79,7 @@ extension Data {
|
|
|
79
79
|
/// Returns nil when the input is not recognized as valid Data URL.
|
|
80
80
|
/// - parameter dataString: The data string to parse.
|
|
81
81
|
/// - parameter options: Options for the string parsing. Default value is `[]`.
|
|
82
|
-
|
|
82
|
+
init?(dataString: String, options: DataURLReadOptions = []) {
|
|
83
83
|
let trimmedDataString = dataString.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
84
84
|
guard
|
|
85
85
|
dataString.hasPrefix("data:"),
|
|
@@ -103,7 +103,7 @@ extension Data {
|
|
|
103
103
|
|
|
104
104
|
// MARK: Internal
|
|
105
105
|
|
|
106
|
-
|
|
106
|
+
struct DataURLReadOptions: OptionSet {
|
|
107
107
|
let rawValue: Int
|
|
108
108
|
|
|
109
109
|
/// Will read Data URL using Data(contentsOf:)
|
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
|
|
8
8
|
import Foundation
|
|
9
9
|
|
|
10
|
+
// MARK: - DotLottieAnimation
|
|
11
|
+
|
|
10
12
|
struct DotLottieAnimation: Codable {
|
|
11
13
|
/// Id of Animation
|
|
12
14
|
var id: String
|
|
@@ -21,11 +23,16 @@ struct DotLottieAnimation: Codable {
|
|
|
21
23
|
var direction: Int? = 1
|
|
22
24
|
|
|
23
25
|
/// mode - "bounce" | "normal"
|
|
24
|
-
var mode:
|
|
26
|
+
var mode: DotLottieAnimationMode? = .normal
|
|
25
27
|
|
|
26
28
|
/// Loop mode for animation
|
|
27
29
|
var loopMode: LottieLoopMode {
|
|
28
|
-
mode
|
|
30
|
+
switch mode {
|
|
31
|
+
case .bounce:
|
|
32
|
+
return .autoReverse
|
|
33
|
+
case .normal, nil:
|
|
34
|
+
return (loop ?? false) ? .loop : .playOnce
|
|
35
|
+
}
|
|
29
36
|
}
|
|
30
37
|
|
|
31
38
|
/// Animation speed
|
|
@@ -41,3 +48,10 @@ struct DotLottieAnimation: Codable {
|
|
|
41
48
|
return try LottieAnimation.from(data: data)
|
|
42
49
|
}
|
|
43
50
|
}
|
|
51
|
+
|
|
52
|
+
// MARK: - DotLottieAnimationMode
|
|
53
|
+
|
|
54
|
+
enum DotLottieAnimationMode: String, Codable {
|
|
55
|
+
case normal
|
|
56
|
+
case bounce
|
|
57
|
+
}
|
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
//
|
|
7
7
|
|
|
8
8
|
import Foundation
|
|
9
|
-
#if
|
|
9
|
+
#if canImport(UIKit)
|
|
10
10
|
import UIKit
|
|
11
|
-
#elseif
|
|
11
|
+
#elseif canImport(AppKit)
|
|
12
12
|
import AppKit
|
|
13
13
|
#endif
|
|
14
14
|
|
|
@@ -51,23 +51,36 @@ class DotLottieImageProvider: AnimationImageProvider {
|
|
|
51
51
|
private var images = [String: CGImage]()
|
|
52
52
|
|
|
53
53
|
private func loadImages() {
|
|
54
|
-
filepath.urls
|
|
54
|
+
for url in filepath.urls {
|
|
55
55
|
#if os(iOS) || os(tvOS) || os(watchOS) || targetEnvironment(macCatalyst)
|
|
56
56
|
if
|
|
57
|
-
let data = try? Data(contentsOf:
|
|
57
|
+
let data = try? Data(contentsOf: url),
|
|
58
58
|
let image = UIImage(data: data)?.cgImage
|
|
59
59
|
{
|
|
60
|
-
images[
|
|
60
|
+
images[url.lastPathComponent] = image
|
|
61
61
|
}
|
|
62
62
|
#elseif os(macOS)
|
|
63
63
|
if
|
|
64
|
-
let data = try? Data(contentsOf:
|
|
64
|
+
let data = try? Data(contentsOf: url),
|
|
65
65
|
let image = NSImage(data: data)?.lottie_CGImage
|
|
66
66
|
{
|
|
67
|
-
images[
|
|
67
|
+
images[url.lastPathComponent] = image
|
|
68
68
|
}
|
|
69
69
|
#endif
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
}
|
|
74
|
+
|
|
75
|
+
// MARK: Hashable
|
|
76
|
+
|
|
77
|
+
extension DotLottieImageProvider: Hashable {
|
|
78
|
+
static func ==(_ lhs: DotLottieImageProvider, _ rhs: DotLottieImageProvider) -> Bool {
|
|
79
|
+
lhs.filepath == rhs.filepath
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
func hash(into hasher: inout Hasher) {
|
|
83
|
+
hasher.combine(filepath)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
}
|
|
@@ -41,4 +41,30 @@ extension KeyedDecodingContainer {
|
|
|
41
41
|
}
|
|
42
42
|
return list
|
|
43
43
|
}
|
|
44
|
+
|
|
45
|
+
/// Decode a heterogeneous list of objects for a given family if the given key is present.
|
|
46
|
+
/// - Parameters:
|
|
47
|
+
/// - heterogeneousType: The decodable type of the list.
|
|
48
|
+
/// - family: The ClassFamily enum for the type family.
|
|
49
|
+
/// - key: The CodingKey to look up the list in the current container.
|
|
50
|
+
/// - Returns: The resulting list of heterogeneousType elements.
|
|
51
|
+
func decodeIfPresent<T: Decodable, U: ClassFamily>(_: [T].Type, ofFamily family: U.Type, forKey key: K) throws -> [T]? {
|
|
52
|
+
var container: UnkeyedDecodingContainer
|
|
53
|
+
do {
|
|
54
|
+
container = try nestedUnkeyedContainer(forKey: key)
|
|
55
|
+
} catch {
|
|
56
|
+
return nil
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
var list = [T]()
|
|
60
|
+
var tmpContainer = container
|
|
61
|
+
while !container.isAtEnd {
|
|
62
|
+
let typeContainer = try container.nestedContainer(keyedBy: Discriminator.self)
|
|
63
|
+
let family: U = try typeContainer.decode(U.self, forKey: U.discriminator)
|
|
64
|
+
if let type = family.getType() as? T.Type {
|
|
65
|
+
list.append(try tmpContainer.decode(type))
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return list
|
|
69
|
+
}
|
|
44
70
|
}
|