lottie-ios 4.1.2 → 4.2.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 +27 -9
- package/Lottie.xcodeproj/project.pbxproj +158 -70
- package/Lottie.xcodeproj/xcuserdata/calstephens.xcuserdatad/xcschemes/xcschememanagement.plist +2 -2
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/IDEFindNavigatorScopes.plist +5 -0
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +258 -0
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/xcdebugger/Expressions.xcexplist +13 -2
- package/Package.swift +2 -1
- package/README.md +3 -3
- package/Rakefile +8 -4
- package/Sources/Private/CoreAnimation/Animations/CALayer+addAnimation.swift +16 -2
- package/Sources/Private/CoreAnimation/Animations/CombinedShapeAnimation.swift +1 -1
- package/Sources/Private/CoreAnimation/Animations/CustomPathAnimation.swift +1 -1
- package/Sources/Private/CoreAnimation/Animations/EllipseAnimation.swift +1 -1
- package/Sources/Private/CoreAnimation/Animations/GradientAnimations.swift +6 -6
- package/Sources/Private/CoreAnimation/Animations/LayerProperty.swift +76 -7
- package/Sources/Private/CoreAnimation/Animations/OpacityAnimation.swift +1 -1
- package/Sources/Private/CoreAnimation/Animations/RectangleAnimation.swift +1 -1
- package/Sources/Private/CoreAnimation/Animations/ShapeAnimation.swift +66 -102
- package/Sources/Private/CoreAnimation/Animations/StarAnimation.swift +2 -2
- package/Sources/Private/CoreAnimation/Animations/StrokeAnimation.swift +3 -3
- package/Sources/Private/CoreAnimation/Animations/TransformAnimations.swift +66 -17
- package/Sources/Private/CoreAnimation/CoreAnimationLayer.swift +55 -32
- package/Sources/Private/CoreAnimation/Extensions/Keyframes+combined.swift +16 -12
- package/Sources/Private/CoreAnimation/Layers/AnimationLayer.swift +3 -3
- package/Sources/Private/CoreAnimation/Layers/BaseCompositionLayer.swift +24 -11
- package/Sources/Private/CoreAnimation/Layers/ImageLayer.swift +2 -2
- package/Sources/Private/CoreAnimation/Layers/PreCompLayer.swift +1 -1
- package/Sources/Private/CoreAnimation/Layers/RepeaterLayer.swift +13 -2
- package/Sources/Private/CoreAnimation/Layers/ShapeLayer.swift +9 -1
- package/Sources/Private/CoreAnimation/ValueProviderStore.swift +22 -11
- package/Sources/Private/MainThread/LayerContainers/CompLayers/MaskContainerLayer.swift +1 -1
- package/Sources/Private/MainThread/LayerContainers/MainThreadAnimationLayer.swift +13 -2
- package/Sources/Private/MainThread/LayerContainers/Utility/LayerTransformNode.swift +16 -7
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/PathNodes/EllipseNode.swift +1 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/PathNodes/PolygonNode.swift +2 -2
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/PathNodes/RectNode.swift +1 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/PathNodes/StarNode.swift +2 -2
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderContainers/GroupNode.swift +20 -8
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/FillNode.swift +1 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/GradientFillNode.swift +1 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/GradientStrokeNode.swift +1 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/StrokeNode.swift +1 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/Text/TextAnimatorNode.swift +28 -9
- package/Sources/Private/Model/Assets/ImageAsset.swift +4 -3
- package/Sources/Private/Model/DotLottie/DotLottieAnimation.swift +2 -8
- package/Sources/Private/Model/DotLottie/DotLottieManifest.swift +3 -14
- package/Sources/Private/Model/DotLottie/DotLottieUtils.swift +11 -1
- package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+BackingConfiguration.swift +147 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+Helpers.swift +351 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+MemoryFile.swift +183 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+Progress.swift +66 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+Reading.swift +144 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+ReadingDeprecated.swift +49 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+Writing.swift +385 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+WritingDeprecated.swift +91 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+ZIP64.swift +170 -0
- package/Sources/Private/Model/DotLottie/{Zip/ZipArchive.swift → ZipFoundation/Archive.swift} +150 -227
- package/Sources/Private/Model/DotLottie/ZipFoundation/Data+Compression.swift +403 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/Data+CompressionDeprecated.swift +44 -0
- package/Sources/Private/Model/DotLottie/{Zip → ZipFoundation}/Data+Serialization.swift +62 -0
- package/Sources/Private/Model/DotLottie/{Zip/ZipEntry+Serialization.swift → ZipFoundation/Entry+Serialization.swift} +7 -7
- package/Sources/Private/Model/DotLottie/{Zip/ZipEntry+ZIP64.swift → ZipFoundation/Entry+ZIP64.swift} +13 -19
- package/Sources/Private/Model/DotLottie/{Zip/ZipEntry.swift → ZipFoundation/Entry.swift} +141 -10
- package/Sources/Private/Model/DotLottie/ZipFoundation/FileManager+ZIP.swift +368 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/README.md +24 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/URL+ZIP.swift +32 -0
- package/Sources/Private/Model/Extensions/Bundle.swift +5 -14
- package/Sources/Private/Model/Keyframes/KeyframeGroup.swift +31 -8
- package/Sources/Private/Model/Objects/Transform.swift +58 -17
- package/Sources/Private/Model/ShapeItems/Repeater.swift +41 -7
- package/Sources/Private/Model/ShapeItems/ShapeTransform.swift +61 -7
- package/Sources/Private/Model/Text/TextAnimator.swift +37 -5
- package/Sources/Private/RootAnimationLayer.swift +3 -1
- package/Sources/Private/Utility/Extensions/AnimationKeypathExtension.swift +12 -4
- package/Sources/Private/Utility/Extensions/DataExtension.swift +14 -4
- package/Sources/Private/Utility/Primitives/BezierPathRoundExtension.swift +11 -0
- package/Sources/Private/Utility/Primitives/ColorExtension.swift +10 -13
- package/Sources/Private/Utility/Primitives/VectorsExtensions.swift +28 -6
- package/Sources/Public/Animation/LottieAnimationHelpers.swift +12 -10
- package/Sources/Public/Animation/LottieAnimationView.swift +213 -186
- package/Sources/Public/DotLottie/DotLottieFile.swift +11 -34
- package/Sources/Public/DotLottie/DotLottieFileHelpers.swift +101 -74
- package/Sources/Public/iOS/Compatibility/CompatibleAnimationView.swift +90 -0
- package/Sources/Public/iOS/LottieAnimationViewBase.swift +1 -1
- package/Sources/Public/macOS/LottieAnimationViewBase.macOS.swift +1 -1
- package/lottie-ios.podspec +1 -1
- package/package.json +1 -1
- package/LottieAnimation/LottieAnimation.xcodeproj/project.xcworkspace/xcuserdata/calstephens.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/LottieAnimation/LottieAnimation.xcodeproj/project.xcworkspace/xcuserdata/valentinperignon.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/LottieAnimation/LottieAnimation.xcodeproj/xcuserdata/calstephens.xcuserdatad/xcschemes/xcschememanagement.plist +0 -14
- package/LottieAnimation/LottieAnimation.xcodeproj/xcuserdata/valentinperignon.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +0 -6
- package/LottieAnimation/LottieAnimation.xcodeproj/xcuserdata/valentinperignon.xcuserdatad/xcschemes/xcschememanagement.plist +0 -14
- package/Sources/Private/Model/DotLottie/Zip/Data+Compression.swift +0 -134
- package/Sources/Private/Model/DotLottie/Zip/FileManager+ZIP.swift +0 -130
- package/Sources/Private/Utility/Interpolatable/KeyframeGroup+Extensions.swift +0 -59
|
@@ -246,6 +246,10 @@ extension CGSize {
|
|
|
246
246
|
|
|
247
247
|
extension CATransform3D {
|
|
248
248
|
|
|
249
|
+
enum Axis {
|
|
250
|
+
case x, y, z
|
|
251
|
+
}
|
|
252
|
+
|
|
249
253
|
static func makeSkew(skew: CGFloat, skewAxis: CGFloat) -> CATransform3D {
|
|
250
254
|
let mCos = cos(skewAxis.toRadians())
|
|
251
255
|
let mSin = sin(skewAxis.toRadians())
|
|
@@ -311,20 +315,37 @@ extension CATransform3D {
|
|
|
311
315
|
anchor: CGPoint,
|
|
312
316
|
position: CGPoint,
|
|
313
317
|
scale: CGSize,
|
|
314
|
-
|
|
318
|
+
rotationX: CGFloat,
|
|
319
|
+
rotationY: CGFloat,
|
|
320
|
+
rotationZ: CGFloat,
|
|
315
321
|
skew: CGFloat?,
|
|
316
322
|
skewAxis: CGFloat?)
|
|
317
323
|
-> CATransform3D
|
|
318
324
|
{
|
|
319
325
|
if let skew = skew, let skewAxis = skewAxis {
|
|
320
|
-
return CATransform3DMakeTranslation(position.x, position.y, 0)
|
|
321
|
-
.
|
|
326
|
+
return CATransform3DMakeTranslation(position.x, position.y, 0)
|
|
327
|
+
.rotated(rotationX, axis: .x)
|
|
328
|
+
.rotated(rotationY, axis: .y)
|
|
329
|
+
.rotated(rotationZ, axis: .z)
|
|
330
|
+
.skewed(skew: -skew, skewAxis: skewAxis)
|
|
331
|
+
.scaled(scale * 0.01)
|
|
332
|
+
.translated(anchor * -1)
|
|
322
333
|
}
|
|
323
|
-
return CATransform3DMakeTranslation(position.x, position.y, 0)
|
|
334
|
+
return CATransform3DMakeTranslation(position.x, position.y, 0)
|
|
335
|
+
.rotated(rotationX, axis: .x)
|
|
336
|
+
.rotated(rotationY, axis: .y)
|
|
337
|
+
.rotated(rotationZ, axis: .z)
|
|
338
|
+
.scaled(scale * 0.01)
|
|
339
|
+
.translated(anchor * -1)
|
|
324
340
|
}
|
|
325
341
|
|
|
326
|
-
func rotated(_ degrees: CGFloat) -> CATransform3D {
|
|
327
|
-
CATransform3DRotate(
|
|
342
|
+
func rotated(_ degrees: CGFloat, axis: Axis) -> CATransform3D {
|
|
343
|
+
CATransform3DRotate(
|
|
344
|
+
self,
|
|
345
|
+
degrees.toRadians(),
|
|
346
|
+
axis == .x ? 1 : 0,
|
|
347
|
+
axis == .y ? 1 : 0,
|
|
348
|
+
axis == .z ? 1 : 0)
|
|
328
349
|
}
|
|
329
350
|
|
|
330
351
|
func translated(_ translation: CGPoint) -> CATransform3D {
|
|
@@ -338,4 +359,5 @@ extension CATransform3D {
|
|
|
338
359
|
func skewed(skew: CGFloat, skewAxis: CGFloat) -> CATransform3D {
|
|
339
360
|
CATransform3DConcat(CATransform3D.makeSkew(skew: skew, skewAxis: skewAxis), self)
|
|
340
361
|
}
|
|
362
|
+
|
|
341
363
|
}
|
|
@@ -59,9 +59,7 @@ extension LottieAnimation {
|
|
|
59
59
|
|
|
60
60
|
do {
|
|
61
61
|
/// Decode animation.
|
|
62
|
-
|
|
63
|
-
return nil
|
|
64
|
-
}
|
|
62
|
+
let json = try bundle.getAnimationData(name, subdirectory: subdirectory)
|
|
65
63
|
let animation = try LottieAnimation.from(data: json)
|
|
66
64
|
animationCache?.setAnimation(animation, forKey: cacheKey)
|
|
67
65
|
return animation
|
|
@@ -97,7 +95,10 @@ extension LottieAnimation {
|
|
|
97
95
|
animationCache?.setAnimation(animation, forKey: filepath)
|
|
98
96
|
return animation
|
|
99
97
|
} catch {
|
|
100
|
-
|
|
98
|
+
LottieLogger.shared.warn("""
|
|
99
|
+
Failed to load animation from filepath \(filepath)
|
|
100
|
+
with underlying error: \(error.localizedDescription)
|
|
101
|
+
""")
|
|
101
102
|
return nil
|
|
102
103
|
}
|
|
103
104
|
}
|
|
@@ -125,18 +126,19 @@ extension LottieAnimation {
|
|
|
125
126
|
return animation
|
|
126
127
|
}
|
|
127
128
|
|
|
128
|
-
/// Load jsonData from Asset
|
|
129
|
-
guard let json = Data.jsonData(from: name, in: bundle) else {
|
|
130
|
-
return nil
|
|
131
|
-
}
|
|
132
|
-
|
|
133
129
|
do {
|
|
130
|
+
/// Load jsonData from Asset
|
|
131
|
+
let json = try Data(assetName: name, in: bundle)
|
|
134
132
|
/// Decode animation.
|
|
135
133
|
let animation = try LottieAnimation.from(data: json)
|
|
136
134
|
animationCache?.setAnimation(animation, forKey: cacheKey)
|
|
137
135
|
return animation
|
|
138
136
|
} catch {
|
|
139
|
-
|
|
137
|
+
LottieLogger.shared.warn("""
|
|
138
|
+
Failed to load animation with asset name \(name)
|
|
139
|
+
in \(bundle.bundlePath)
|
|
140
|
+
with underlying error: \(error.localizedDescription)
|
|
141
|
+
""")
|
|
140
142
|
return nil
|
|
141
143
|
}
|
|
142
144
|
}
|
|
@@ -88,7 +88,7 @@ extension LottieLoopMode: Equatable {
|
|
|
88
88
|
// MARK: - LottieAnimationView
|
|
89
89
|
|
|
90
90
|
@IBDesignable
|
|
91
|
-
|
|
91
|
+
open class LottieAnimationView: LottieAnimationViewBase {
|
|
92
92
|
|
|
93
93
|
// MARK: Lifecycle
|
|
94
94
|
|
|
@@ -178,6 +178,166 @@ final public class LottieAnimationView: LottieAnimationViewBase {
|
|
|
178
178
|
commonInit()
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
+
// MARK: Open
|
|
182
|
+
|
|
183
|
+
/// Plays the animation from its current state to the end.
|
|
184
|
+
///
|
|
185
|
+
/// - Parameter completion: An optional completion closure to be called when the animation completes playing.
|
|
186
|
+
open func play(completion: LottieCompletionBlock? = nil) {
|
|
187
|
+
guard let animation = animation else {
|
|
188
|
+
return
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/// Build a context for the animation.
|
|
192
|
+
let context = AnimationContext(
|
|
193
|
+
playFrom: CGFloat(animation.startFrame),
|
|
194
|
+
playTo: CGFloat(animation.endFrame),
|
|
195
|
+
closure: completion)
|
|
196
|
+
removeCurrentAnimationIfNecessary()
|
|
197
|
+
addNewAnimationForContext(context)
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/// Plays the animation from a progress (0-1) to a progress (0-1).
|
|
201
|
+
///
|
|
202
|
+
/// - Parameter fromProgress: The start progress of the animation. If `nil` the animation will start at the current progress.
|
|
203
|
+
/// - Parameter toProgress: The end progress of the animation.
|
|
204
|
+
/// - Parameter loopMode: The loop behavior of the animation. If `nil` the view's `loopMode` property will be used.
|
|
205
|
+
/// - Parameter completion: An optional completion closure to be called when the animation stops.
|
|
206
|
+
open func play(
|
|
207
|
+
fromProgress: AnimationProgressTime? = nil,
|
|
208
|
+
toProgress: AnimationProgressTime,
|
|
209
|
+
loopMode: LottieLoopMode? = nil,
|
|
210
|
+
completion: LottieCompletionBlock? = nil)
|
|
211
|
+
{
|
|
212
|
+
guard let animation = animation else {
|
|
213
|
+
return
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
removeCurrentAnimationIfNecessary()
|
|
217
|
+
if let loopMode = loopMode {
|
|
218
|
+
/// Set the loop mode, if one was supplied
|
|
219
|
+
self.loopMode = loopMode
|
|
220
|
+
}
|
|
221
|
+
let context = AnimationContext(
|
|
222
|
+
playFrom: animation.frameTime(forProgress: fromProgress ?? currentProgress),
|
|
223
|
+
playTo: animation.frameTime(forProgress: toProgress),
|
|
224
|
+
closure: completion)
|
|
225
|
+
addNewAnimationForContext(context)
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/// Plays the animation from a start frame to an end frame in the animation's framerate.
|
|
229
|
+
///
|
|
230
|
+
/// - Parameter fromFrame: The start frame of the animation. If `nil` the animation will start at the current frame.
|
|
231
|
+
/// - Parameter toFrame: The end frame of the animation.
|
|
232
|
+
/// - Parameter loopMode: The loop behavior of the animation. If `nil` the view's `loopMode` property will be used.
|
|
233
|
+
/// - Parameter completion: An optional completion closure to be called when the animation stops.
|
|
234
|
+
open func play(
|
|
235
|
+
fromFrame: AnimationFrameTime? = nil,
|
|
236
|
+
toFrame: AnimationFrameTime,
|
|
237
|
+
loopMode: LottieLoopMode? = nil,
|
|
238
|
+
completion: LottieCompletionBlock? = nil)
|
|
239
|
+
{
|
|
240
|
+
removeCurrentAnimationIfNecessary()
|
|
241
|
+
if let loopMode = loopMode {
|
|
242
|
+
/// Set the loop mode, if one was supplied
|
|
243
|
+
self.loopMode = loopMode
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
let context = AnimationContext(
|
|
247
|
+
playFrom: fromFrame ?? currentFrame,
|
|
248
|
+
playTo: toFrame,
|
|
249
|
+
closure: completion)
|
|
250
|
+
addNewAnimationForContext(context)
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/// Plays the animation from a named marker to another marker.
|
|
254
|
+
///
|
|
255
|
+
/// Markers are point in time that are encoded into the Animation data and assigned
|
|
256
|
+
/// a name.
|
|
257
|
+
///
|
|
258
|
+
/// NOTE: If markers are not found the play command will exit.
|
|
259
|
+
///
|
|
260
|
+
/// - Parameter fromMarker: The start marker for the animation playback. If `nil` the
|
|
261
|
+
/// animation will start at the current progress.
|
|
262
|
+
/// - Parameter toMarker: The end marker for the animation playback.
|
|
263
|
+
/// - Parameter playEndMarkerFrame: A flag to determine whether or not to play the frame of the end marker. If the
|
|
264
|
+
/// end marker represents the end of the section to play, it should be to true. If the provided end marker
|
|
265
|
+
/// represents the beginning of the next section, it should be false.
|
|
266
|
+
/// - Parameter loopMode: The loop behavior of the animation. If `nil` the view's `loopMode` property will be used.
|
|
267
|
+
/// - Parameter completion: An optional completion closure to be called when the animation stops.
|
|
268
|
+
open func play(
|
|
269
|
+
fromMarker: String? = nil,
|
|
270
|
+
toMarker: String,
|
|
271
|
+
playEndMarkerFrame: Bool = true,
|
|
272
|
+
loopMode: LottieLoopMode? = nil,
|
|
273
|
+
completion: LottieCompletionBlock? = nil)
|
|
274
|
+
{
|
|
275
|
+
guard let animation = animation, let markers = animation.markerMap, let to = markers[toMarker] else {
|
|
276
|
+
return
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
removeCurrentAnimationIfNecessary()
|
|
280
|
+
if let loopMode = loopMode {
|
|
281
|
+
/// Set the loop mode, if one was supplied
|
|
282
|
+
self.loopMode = loopMode
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
let fromTime: CGFloat
|
|
286
|
+
if let fromName = fromMarker, let from = markers[fromName] {
|
|
287
|
+
fromTime = CGFloat(from.frameTime)
|
|
288
|
+
} else {
|
|
289
|
+
fromTime = currentFrame
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
let playTo = playEndMarkerFrame ? CGFloat(to.frameTime) : CGFloat(to.frameTime) - 1
|
|
293
|
+
let context = AnimationContext(
|
|
294
|
+
playFrom: fromTime,
|
|
295
|
+
playTo: playTo,
|
|
296
|
+
closure: completion)
|
|
297
|
+
addNewAnimationForContext(context)
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/// Plays the animation from a named marker to the end of the marker's duration.
|
|
301
|
+
///
|
|
302
|
+
/// A marker is a point in time with an associated duration that is encoded into the
|
|
303
|
+
/// animation data and assigned a name.
|
|
304
|
+
///
|
|
305
|
+
/// NOTE: If marker is not found the play command will exit.
|
|
306
|
+
///
|
|
307
|
+
/// - Parameter marker: The start marker for the animation playback.
|
|
308
|
+
/// - Parameter loopMode: The loop behavior of the animation. If `nil` the view's `loopMode` property will be used.
|
|
309
|
+
/// - Parameter completion: An optional completion closure to be called when the animation stops.
|
|
310
|
+
open func play(
|
|
311
|
+
marker: String,
|
|
312
|
+
loopMode: LottieLoopMode? = nil,
|
|
313
|
+
completion: LottieCompletionBlock? = nil)
|
|
314
|
+
{
|
|
315
|
+
guard let from = animation?.markerMap?[marker] else {
|
|
316
|
+
return
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
play(
|
|
320
|
+
fromFrame: from.frameTime,
|
|
321
|
+
toFrame: from.frameTime + from.durationFrameTime,
|
|
322
|
+
loopMode: loopMode,
|
|
323
|
+
completion: completion)
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/// Stops the animation and resets the view to its start frame.
|
|
327
|
+
///
|
|
328
|
+
/// The completion closure will be called with `false`
|
|
329
|
+
open func stop() {
|
|
330
|
+
removeCurrentAnimation()
|
|
331
|
+
currentFrame = 0
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/// Pauses the animation in its current state.
|
|
335
|
+
///
|
|
336
|
+
/// The completion closure will be called with `false`
|
|
337
|
+
open func pause() {
|
|
338
|
+
removeCurrentAnimation()
|
|
339
|
+
}
|
|
340
|
+
|
|
181
341
|
// MARK: Public
|
|
182
342
|
|
|
183
343
|
/// The configuration that this `LottieAnimationView` uses when playing its animation
|
|
@@ -294,6 +454,13 @@ final public class LottieAnimationView: LottieAnimationViewBase {
|
|
|
294
454
|
}
|
|
295
455
|
}
|
|
296
456
|
|
|
457
|
+
/// Whether or not the animation is masked to the bounds. Defaults to true.
|
|
458
|
+
public var maskAnimationToBounds = true {
|
|
459
|
+
didSet {
|
|
460
|
+
animationLayer?.masksToBounds = maskAnimationToBounds
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
297
464
|
/// Returns `true` if the animation is currently playing.
|
|
298
465
|
public var isAnimationPlaying: Bool {
|
|
299
466
|
guard let animationLayer = animationLayer else {
|
|
@@ -492,164 +659,6 @@ final public class LottieAnimationView: LottieAnimationViewBase {
|
|
|
492
659
|
animation = dotLottieAnimation.animation
|
|
493
660
|
}
|
|
494
661
|
|
|
495
|
-
/// Plays the animation from its current state to the end.
|
|
496
|
-
///
|
|
497
|
-
/// - Parameter completion: An optional completion closure to be called when the animation completes playing.
|
|
498
|
-
public func play(completion: LottieCompletionBlock? = nil) {
|
|
499
|
-
guard let animation = animation else {
|
|
500
|
-
return
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
/// Build a context for the animation.
|
|
504
|
-
let context = AnimationContext(
|
|
505
|
-
playFrom: CGFloat(animation.startFrame),
|
|
506
|
-
playTo: CGFloat(animation.endFrame),
|
|
507
|
-
closure: completion)
|
|
508
|
-
removeCurrentAnimationIfNecessary()
|
|
509
|
-
addNewAnimationForContext(context)
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
/// Plays the animation from a progress (0-1) to a progress (0-1).
|
|
513
|
-
///
|
|
514
|
-
/// - Parameter fromProgress: The start progress of the animation. If `nil` the animation will start at the current progress.
|
|
515
|
-
/// - Parameter toProgress: The end progress of the animation.
|
|
516
|
-
/// - Parameter loopMode: The loop behavior of the animation. If `nil` the view's `loopMode` property will be used.
|
|
517
|
-
/// - Parameter completion: An optional completion closure to be called when the animation stops.
|
|
518
|
-
public func play(
|
|
519
|
-
fromProgress: AnimationProgressTime? = nil,
|
|
520
|
-
toProgress: AnimationProgressTime,
|
|
521
|
-
loopMode: LottieLoopMode? = nil,
|
|
522
|
-
completion: LottieCompletionBlock? = nil)
|
|
523
|
-
{
|
|
524
|
-
guard let animation = animation else {
|
|
525
|
-
return
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
removeCurrentAnimationIfNecessary()
|
|
529
|
-
if let loopMode = loopMode {
|
|
530
|
-
/// Set the loop mode, if one was supplied
|
|
531
|
-
self.loopMode = loopMode
|
|
532
|
-
}
|
|
533
|
-
let context = AnimationContext(
|
|
534
|
-
playFrom: animation.frameTime(forProgress: fromProgress ?? currentProgress),
|
|
535
|
-
playTo: animation.frameTime(forProgress: toProgress),
|
|
536
|
-
closure: completion)
|
|
537
|
-
addNewAnimationForContext(context)
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
/// Plays the animation from a start frame to an end frame in the animation's framerate.
|
|
541
|
-
///
|
|
542
|
-
/// - Parameter fromFrame: The start frame of the animation. If `nil` the animation will start at the current frame.
|
|
543
|
-
/// - Parameter toFrame: The end frame of the animation.
|
|
544
|
-
/// - Parameter loopMode: The loop behavior of the animation. If `nil` the view's `loopMode` property will be used.
|
|
545
|
-
/// - Parameter completion: An optional completion closure to be called when the animation stops.
|
|
546
|
-
public func play(
|
|
547
|
-
fromFrame: AnimationFrameTime? = nil,
|
|
548
|
-
toFrame: AnimationFrameTime,
|
|
549
|
-
loopMode: LottieLoopMode? = nil,
|
|
550
|
-
completion: LottieCompletionBlock? = nil)
|
|
551
|
-
{
|
|
552
|
-
removeCurrentAnimationIfNecessary()
|
|
553
|
-
if let loopMode = loopMode {
|
|
554
|
-
/// Set the loop mode, if one was supplied
|
|
555
|
-
self.loopMode = loopMode
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
let context = AnimationContext(
|
|
559
|
-
playFrom: fromFrame ?? currentFrame,
|
|
560
|
-
playTo: toFrame,
|
|
561
|
-
closure: completion)
|
|
562
|
-
addNewAnimationForContext(context)
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
/// Plays the animation from a named marker to another marker.
|
|
566
|
-
///
|
|
567
|
-
/// Markers are point in time that are encoded into the Animation data and assigned
|
|
568
|
-
/// a name.
|
|
569
|
-
///
|
|
570
|
-
/// NOTE: If markers are not found the play command will exit.
|
|
571
|
-
///
|
|
572
|
-
/// - Parameter fromMarker: The start marker for the animation playback. If `nil` the
|
|
573
|
-
/// animation will start at the current progress.
|
|
574
|
-
/// - Parameter toMarker: The end marker for the animation playback.
|
|
575
|
-
/// - Parameter playEndMarkerFrame: A flag to determine whether or not to play the frame of the end marker. If the
|
|
576
|
-
/// end marker represents the end of the section to play, it should be to true. If the provided end marker
|
|
577
|
-
/// represents the beginning of the next section, it should be false.
|
|
578
|
-
/// - Parameter loopMode: The loop behavior of the animation. If `nil` the view's `loopMode` property will be used.
|
|
579
|
-
/// - Parameter completion: An optional completion closure to be called when the animation stops.
|
|
580
|
-
public func play(
|
|
581
|
-
fromMarker: String? = nil,
|
|
582
|
-
toMarker: String,
|
|
583
|
-
playEndMarkerFrame: Bool = true,
|
|
584
|
-
loopMode: LottieLoopMode? = nil,
|
|
585
|
-
completion: LottieCompletionBlock? = nil)
|
|
586
|
-
{
|
|
587
|
-
guard let animation = animation, let markers = animation.markerMap, let to = markers[toMarker] else {
|
|
588
|
-
return
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
removeCurrentAnimationIfNecessary()
|
|
592
|
-
if let loopMode = loopMode {
|
|
593
|
-
/// Set the loop mode, if one was supplied
|
|
594
|
-
self.loopMode = loopMode
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
let fromTime: CGFloat
|
|
598
|
-
if let fromName = fromMarker, let from = markers[fromName] {
|
|
599
|
-
fromTime = CGFloat(from.frameTime)
|
|
600
|
-
} else {
|
|
601
|
-
fromTime = currentFrame
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
let playTo = playEndMarkerFrame ? CGFloat(to.frameTime) : CGFloat(to.frameTime) - 1
|
|
605
|
-
let context = AnimationContext(
|
|
606
|
-
playFrom: fromTime,
|
|
607
|
-
playTo: playTo,
|
|
608
|
-
closure: completion)
|
|
609
|
-
addNewAnimationForContext(context)
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
/// Plays the animation from a named marker to the end of the marker's duration.
|
|
613
|
-
///
|
|
614
|
-
/// A marker is a point in time with an associated duration that is encoded into the
|
|
615
|
-
/// animation data and assigned a name.
|
|
616
|
-
///
|
|
617
|
-
/// NOTE: If marker is not found the play command will exit.
|
|
618
|
-
///
|
|
619
|
-
/// - Parameter marker: The start marker for the animation playback.
|
|
620
|
-
/// - Parameter loopMode: The loop behavior of the animation. If `nil` the view's `loopMode` property will be used.
|
|
621
|
-
/// - Parameter completion: An optional completion closure to be called when the animation stops.
|
|
622
|
-
public func play(
|
|
623
|
-
marker: String,
|
|
624
|
-
loopMode: LottieLoopMode? = nil,
|
|
625
|
-
completion: LottieCompletionBlock? = nil)
|
|
626
|
-
{
|
|
627
|
-
guard let from = animation?.markerMap?[marker] else {
|
|
628
|
-
return
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
play(
|
|
632
|
-
fromFrame: from.frameTime,
|
|
633
|
-
toFrame: from.frameTime + from.durationFrameTime,
|
|
634
|
-
loopMode: loopMode,
|
|
635
|
-
completion: completion)
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
/// Stops the animation and resets the view to its start frame.
|
|
639
|
-
///
|
|
640
|
-
/// The completion closure will be called with `false`
|
|
641
|
-
public func stop() {
|
|
642
|
-
removeCurrentAnimation()
|
|
643
|
-
currentFrame = 0
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
/// Pauses the animation in its current state.
|
|
647
|
-
///
|
|
648
|
-
/// The completion closure will be called with `false`
|
|
649
|
-
public func pause() {
|
|
650
|
-
removeCurrentAnimation()
|
|
651
|
-
}
|
|
652
|
-
|
|
653
662
|
/// Reloads the images supplied to the animation from the `imageProvider`
|
|
654
663
|
public func reloadImages() {
|
|
655
664
|
animationLayer?.reloadImages()
|
|
@@ -709,10 +718,17 @@ final public class LottieAnimationView: LottieAnimationViewBase {
|
|
|
709
718
|
}
|
|
710
719
|
|
|
711
720
|
/// Logs all child keypaths.
|
|
721
|
+
/// Logs the result of `allHierarchyKeypaths()` to the `LottieLogger`.
|
|
712
722
|
public func logHierarchyKeypaths() {
|
|
713
723
|
animationLayer?.logHierarchyKeypaths()
|
|
714
724
|
}
|
|
715
725
|
|
|
726
|
+
/// Computes and returns a list of all child keypaths in the current animation.
|
|
727
|
+
/// The returned list is the same as the log output of `logHierarchyKeypaths()`
|
|
728
|
+
public func allHierarchyKeypaths() -> [String] {
|
|
729
|
+
animationLayer?.allHierarchyKeypaths() ?? []
|
|
730
|
+
}
|
|
731
|
+
|
|
716
732
|
/// Searches for the nearest child layer to the first Keypath and adds the subview
|
|
717
733
|
/// to that layer. The subview will move and animate with the child layer.
|
|
718
734
|
/// Furthermore the subview will be in the child layers coordinate space.
|
|
@@ -1060,6 +1076,33 @@ final public class LottieAnimationView: LottieAnimationViewBase {
|
|
|
1060
1076
|
}
|
|
1061
1077
|
}
|
|
1062
1078
|
|
|
1079
|
+
/// Updates an in flight animation.
|
|
1080
|
+
func updateInFlightAnimation() {
|
|
1081
|
+
guard let animationContext = animationContext else { return }
|
|
1082
|
+
|
|
1083
|
+
guard animationContext.closure.animationState != .complete else {
|
|
1084
|
+
// Tried to re-add an already completed animation. Cancel.
|
|
1085
|
+
self.animationContext = nil
|
|
1086
|
+
return
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
/// Tell existing context to ignore its closure
|
|
1090
|
+
animationContext.closure.ignoreDelegate = true
|
|
1091
|
+
|
|
1092
|
+
/// Make a new context, stealing the completion block from the previous.
|
|
1093
|
+
let newContext = AnimationContext(
|
|
1094
|
+
playFrom: animationContext.playFrom,
|
|
1095
|
+
playTo: animationContext.playTo,
|
|
1096
|
+
closure: animationContext.closure.completionBlock)
|
|
1097
|
+
|
|
1098
|
+
/// Remove current animation, and freeze the current frame.
|
|
1099
|
+
let pauseFrame = realtimeAnimationFrame
|
|
1100
|
+
animationLayer?.removeAnimation(forKey: activeAnimationName)
|
|
1101
|
+
animationLayer?.currentFrame = pauseFrame
|
|
1102
|
+
|
|
1103
|
+
addNewAnimationForContext(newContext)
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1063
1106
|
// MARK: Fileprivate
|
|
1064
1107
|
|
|
1065
1108
|
/// Context describing the animation that is currently playing in this `LottieAnimationView`
|
|
@@ -1087,6 +1130,7 @@ final public class LottieAnimationView: LottieAnimationViewBase {
|
|
|
1087
1130
|
|
|
1088
1131
|
if let oldAnimation = animationLayer {
|
|
1089
1132
|
oldAnimation.removeFromSuperlayer()
|
|
1133
|
+
animationLayer = nil
|
|
1090
1134
|
}
|
|
1091
1135
|
|
|
1092
1136
|
invalidateIntrinsicContentSize()
|
|
@@ -1109,6 +1153,7 @@ final public class LottieAnimationView: LottieAnimationViewBase {
|
|
|
1109
1153
|
return
|
|
1110
1154
|
}
|
|
1111
1155
|
|
|
1156
|
+
animationLayer.animationView = self
|
|
1112
1157
|
animationLayer.renderScale = screenScale
|
|
1113
1158
|
|
|
1114
1159
|
viewLayer?.addSublayer(animationLayer)
|
|
@@ -1125,6 +1170,7 @@ final public class LottieAnimationView: LottieAnimationViewBase {
|
|
|
1125
1170
|
imageProvider: imageProvider.cachedImageProvider,
|
|
1126
1171
|
textProvider: textProvider,
|
|
1127
1172
|
fontProvider: fontProvider,
|
|
1173
|
+
maskAnimationToBounds: maskAnimationToBounds,
|
|
1128
1174
|
logger: logger)
|
|
1129
1175
|
}
|
|
1130
1176
|
|
|
@@ -1135,6 +1181,7 @@ final public class LottieAnimationView: LottieAnimationViewBase {
|
|
|
1135
1181
|
imageProvider: imageProvider.cachedImageProvider,
|
|
1136
1182
|
textProvider: textProvider,
|
|
1137
1183
|
fontProvider: fontProvider,
|
|
1184
|
+
maskAnimationToBounds: maskAnimationToBounds,
|
|
1138
1185
|
compatibilityTrackerMode: .track,
|
|
1139
1186
|
logger: logger)
|
|
1140
1187
|
|
|
@@ -1170,6 +1217,7 @@ final public class LottieAnimationView: LottieAnimationViewBase {
|
|
|
1170
1217
|
imageProvider: imageProvider.cachedImageProvider,
|
|
1171
1218
|
textProvider: textProvider,
|
|
1172
1219
|
fontProvider: fontProvider,
|
|
1220
|
+
maskAnimationToBounds: maskAnimationToBounds,
|
|
1173
1221
|
compatibilityTrackerMode: .abort,
|
|
1174
1222
|
logger: logger)
|
|
1175
1223
|
|
|
@@ -1214,6 +1262,12 @@ final public class LottieAnimationView: LottieAnimationViewBase {
|
|
|
1214
1262
|
let animationContext = animationContext
|
|
1215
1263
|
let currentFrame = currentFrame
|
|
1216
1264
|
|
|
1265
|
+
// Disable the completion handler delegate before tearing down the `CoreAnimationLayer`
|
|
1266
|
+
// and building the `MainThreadAnimationLayer`. Otherwise deinitializing the
|
|
1267
|
+
// `CoreAnimationLayer` would trigger the animation completion handler even though
|
|
1268
|
+
// the animation hasn't even started playing yet.
|
|
1269
|
+
animationContext?.closure.ignoreDelegate = true
|
|
1270
|
+
|
|
1217
1271
|
makeAnimationLayer(usingEngine: .mainThread)
|
|
1218
1272
|
|
|
1219
1273
|
// Set up the Main Thread animation layer using the same configuration that
|
|
@@ -1295,33 +1349,6 @@ final public class LottieAnimationView: LottieAnimationViewBase {
|
|
|
1295
1349
|
animationContext = nil
|
|
1296
1350
|
}
|
|
1297
1351
|
|
|
1298
|
-
/// Updates an in flight animation.
|
|
1299
|
-
fileprivate func updateInFlightAnimation() {
|
|
1300
|
-
guard let animationContext = animationContext else { return }
|
|
1301
|
-
|
|
1302
|
-
guard animationContext.closure.animationState != .complete else {
|
|
1303
|
-
// Tried to re-add an already completed animation. Cancel.
|
|
1304
|
-
self.animationContext = nil
|
|
1305
|
-
return
|
|
1306
|
-
}
|
|
1307
|
-
|
|
1308
|
-
/// Tell existing context to ignore its closure
|
|
1309
|
-
animationContext.closure.ignoreDelegate = true
|
|
1310
|
-
|
|
1311
|
-
/// Make a new context, stealing the completion block from the previous.
|
|
1312
|
-
let newContext = AnimationContext(
|
|
1313
|
-
playFrom: animationContext.playFrom,
|
|
1314
|
-
playTo: animationContext.playTo,
|
|
1315
|
-
closure: animationContext.closure.completionBlock)
|
|
1316
|
-
|
|
1317
|
-
/// Remove current animation, and freeze the current frame.
|
|
1318
|
-
let pauseFrame = realtimeAnimationFrame
|
|
1319
|
-
animationLayer?.removeAnimation(forKey: activeAnimationName)
|
|
1320
|
-
animationLayer?.currentFrame = pauseFrame
|
|
1321
|
-
|
|
1322
|
-
addNewAnimationForContext(newContext)
|
|
1323
|
-
}
|
|
1324
|
-
|
|
1325
1352
|
/// Adds animation to animation layer and sets the delegate. If animation layer or animation are nil, exits.
|
|
1326
1353
|
fileprivate func addNewAnimationForContext(_ animationContext: AnimationContext) {
|
|
1327
1354
|
guard let animationlayer = animationLayer, let animation = animation else {
|
|
@@ -39,34 +39,17 @@ public final class DotLottieFile {
|
|
|
39
39
|
/// Image provider for animations
|
|
40
40
|
private(set) var imageProvider: AnimationImageProvider?
|
|
41
41
|
|
|
42
|
-
/// Manifest.json file loading
|
|
43
|
-
lazy var manifest: DotLottieManifest? = {
|
|
44
|
-
let path = fileUrl.appendingPathComponent(DotLottieFile.manifestFileName)
|
|
45
|
-
return try? DotLottieManifest.load(from: path)
|
|
46
|
-
}()
|
|
47
|
-
|
|
48
|
-
/// Animation url for main animation
|
|
49
|
-
lazy var animationUrl: URL? = {
|
|
50
|
-
guard let animationId = manifest?.animations.first?.id else { return nil }
|
|
51
|
-
let dotLottieJson = "\(DotLottieFile.animationsFolderName)/\(animationId).json"
|
|
52
|
-
return fileUrl.appendingPathComponent(dotLottieJson)
|
|
53
|
-
}()
|
|
54
|
-
|
|
55
42
|
/// Animations folder url
|
|
56
43
|
lazy var animationsUrl: URL = fileUrl.appendingPathComponent("\(DotLottieFile.animationsFolderName)")
|
|
57
44
|
|
|
58
45
|
/// All files in animations folder
|
|
59
|
-
lazy var animationUrls: [URL] =
|
|
60
|
-
FileManager.default.urls(for: animationsUrl) ?? []
|
|
61
|
-
}()
|
|
46
|
+
lazy var animationUrls: [URL] = FileManager.default.urls(for: animationsUrl) ?? []
|
|
62
47
|
|
|
63
48
|
/// Images folder url
|
|
64
49
|
lazy var imagesUrl: URL = fileUrl.appendingPathComponent("\(DotLottieFile.imagesFolderName)")
|
|
65
50
|
|
|
66
51
|
/// All images in images folder
|
|
67
|
-
lazy var imageUrls: [URL] =
|
|
68
|
-
FileManager.default.urls(for: imagesUrl) ?? []
|
|
69
|
-
}()
|
|
52
|
+
lazy var imageUrls: [URL] = FileManager.default.urls(for: imagesUrl) ?? []
|
|
70
53
|
|
|
71
54
|
/// The `LottieAnimation` and `DotLottieConfiguration` for the given animation ID in this file
|
|
72
55
|
func animation(for id: String? = nil) -> DotLottieFile.Animation? {
|
|
@@ -85,14 +68,6 @@ public final class DotLottieFile {
|
|
|
85
68
|
|
|
86
69
|
private let fileUrl: URL
|
|
87
70
|
|
|
88
|
-
private var dotLottieAnimations: [DotLottieAnimation] {
|
|
89
|
-
manifest?.animations.map {
|
|
90
|
-
var animation = $0
|
|
91
|
-
animation.animationUrl = animationsUrl.appendingPathComponent("\($0.id).json")
|
|
92
|
-
return animation
|
|
93
|
-
} ?? []
|
|
94
|
-
}
|
|
95
|
-
|
|
96
71
|
/// Decompresses .lottie file from `URL` and saves to local temp folder
|
|
97
72
|
///
|
|
98
73
|
/// - Parameters:
|
|
@@ -102,7 +77,7 @@ public final class DotLottieFile {
|
|
|
102
77
|
try? FileManager.default.removeItem(at: destinationURL)
|
|
103
78
|
try FileManager.default.createDirectory(at: destinationURL, withIntermediateDirectories: true, attributes: nil)
|
|
104
79
|
try FileManager.default.unzipItem(at: url, to: destinationURL)
|
|
105
|
-
loadContent()
|
|
80
|
+
try loadContent()
|
|
106
81
|
try? FileManager.default.removeItem(at: destinationURL)
|
|
107
82
|
try? FileManager.default.removeItem(at: url)
|
|
108
83
|
}
|
|
@@ -120,14 +95,11 @@ public final class DotLottieFile {
|
|
|
120
95
|
}
|
|
121
96
|
|
|
122
97
|
/// Loads file content to memory
|
|
123
|
-
private func loadContent() {
|
|
98
|
+
private func loadContent() throws {
|
|
124
99
|
imageProvider = DotLottieImageProvider(filepath: imagesUrl)
|
|
125
100
|
|
|
126
|
-
animations =
|
|
127
|
-
|
|
128
|
-
return nil
|
|
129
|
-
}
|
|
130
|
-
|
|
101
|
+
animations = try loadManifest().animations.map { dotLottieAnimation in
|
|
102
|
+
let animation = try dotLottieAnimation.animation(url: animationsUrl)
|
|
131
103
|
let configuration = DotLottieConfiguration(
|
|
132
104
|
id: dotLottieAnimation.id,
|
|
133
105
|
imageProvider: imageProvider,
|
|
@@ -139,6 +111,11 @@ public final class DotLottieFile {
|
|
|
139
111
|
configuration: configuration)
|
|
140
112
|
}
|
|
141
113
|
}
|
|
114
|
+
|
|
115
|
+
private func loadManifest() throws -> DotLottieManifest {
|
|
116
|
+
let path = fileUrl.appendingPathComponent(DotLottieFile.manifestFileName)
|
|
117
|
+
return try DotLottieManifest.load(from: path)
|
|
118
|
+
}
|
|
142
119
|
}
|
|
143
120
|
|
|
144
121
|
extension String {
|