Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added additional support for multiple animations in DotLottie #2074

Merged
merged 22 commits into from
Jun 13, 2023
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
3265c1d
Updated DotLottieAnimation optional attributes
eharrison Dec 9, 2022
592c8ff
Added snapshot test files
eharrison Dec 9, 2022
b3b8d0c
Merge branch 'airbnb:master' into master
eharrison May 17, 2023
d68d573
[FEAT] Added support for playing animations in sequence by calling ne…
eharrison Jun 9, 2023
0c2fb11
[CHORE] Updated formatting
eharrison Jun 9, 2023
171a815
[CHORE] Updated snapshots
eharrison Jun 9, 2023
f9a94de
Update Sources/Public/Animation/LottieAnimationView.swift
eharrison Jun 13, 2023
71a493d
Merge branch 'airbnb:master' into master
eharrison Jun 13, 2023
e0a50e5
[FEAT] Added support for playing animations in sequence by calling ne…
eharrison Jun 9, 2023
381e0c5
[CHORE] Updated formatting
eharrison Jun 9, 2023
2a50d79
[CHORE] Updated snapshots
eharrison Jun 9, 2023
cf1b463
Removed unnecessary functions and made methods public to allow extern…
eharrison Jun 13, 2023
ef762a9
Updated loadAnimation function location to LottieAnimationLayer
eharrison Jun 13, 2023
5a47544
Merge remote-tracking branch 'origin/dotlottie-multiple-animations' i…
eharrison Jun 13, 2023
64dad0b
Merge remote-tracking branch 'origin/dotlottie-multiple-animations' i…
eharrison Jun 13, 2023
3e3ad13
Fixed merge issues
eharrison Jun 13, 2023
d0b7a85
Update Sources/Private/Model/DotLottie/DotLottieAnimation.swift
eharrison Jun 13, 2023
24bffff
Update Sources/Public/Animation/LottieAnimationLayer.swift
eharrison Jun 13, 2023
238fad2
Added configuration documentation
eharrison Jun 13, 2023
a150ac6
Made animations array public inside DotLottieFile and removed unneces…
eharrison Jun 13, 2023
be48380
Merge remote-tracking branch 'origin/dotlottie-multiple-animations' i…
eharrison Jun 13, 2023
143f8ad
Fixed formatting
eharrison Jun 13, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions Example/iOS/ViewControllers/AnimationPreviewViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,21 @@ class AnimationPreviewViewController: UIViewController {
updateAnimation()
}),
]),

UIMenu(
title: "Navigate...",
children: [
UIAction(
title: "Next Animation",
handler: { [unowned self] _ in
nextAnimation()
}),
UIAction(
title: "Previous Animation",
handler: { [unowned self] _ in
previousAnimation()
}),
]),
]))
}

Expand All @@ -249,4 +264,15 @@ class AnimationPreviewViewController: UIViewController {
configureSettingsMenu()
}

private func nextAnimation() {
animationView.nextAnimation()
animationView.play()
configureSettingsMenu()
}

private func previousAnimation() {
animationView.previousAnimation()
animationView.play()
configureSettingsMenu()
}
}
8 changes: 8 additions & 0 deletions Lottie.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,9 @@
6C4878602901D8C70005AF07 /* DotLottieImageProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C48785F2901D8C70005AF07 /* DotLottieImageProvider.swift */; };
6C4878612901D8C70005AF07 /* DotLottieImageProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C48785F2901D8C70005AF07 /* DotLottieImageProvider.swift */; };
6C4878622901D8C70005AF07 /* DotLottieImageProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C48785F2901D8C70005AF07 /* DotLottieImageProvider.swift */; };
6C49605C2A33452900BDF3CB /* DotLottieAnimationMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C49605B2A33452900BDF3CB /* DotLottieAnimationMode.swift */; };
6C49605D2A33452900BDF3CB /* DotLottieAnimationMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C49605B2A33452900BDF3CB /* DotLottieAnimationMode.swift */; };
6C49605E2A33452900BDF3CB /* DotLottieAnimationMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C49605B2A33452900BDF3CB /* DotLottieAnimationMode.swift */; };
6CC544932902FF7D00212722 /* DotLottieCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C48785529017D1D0005AF07 /* DotLottieCache.swift */; };
6D0E635F28246BD0007C5DB6 /* Difference in Frameworks */ = {isa = PBXBuildFile; productRef = 6D0E635E28246BD0007C5DB6 /* Difference */; };
6D99D6432823790700E5205B /* LegacyGradientFillRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D99D6422823790700E5205B /* LegacyGradientFillRenderer.swift */; };
Expand Down Expand Up @@ -918,6 +921,7 @@
6C48785529017D1D0005AF07 /* DotLottieCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DotLottieCache.swift; sourceTree = "<group>"; };
6C4878592901811D0005AF07 /* DotLottieFileHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DotLottieFileHelpers.swift; sourceTree = "<group>"; };
6C48785F2901D8C70005AF07 /* DotLottieImageProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DotLottieImageProvider.swift; sourceTree = "<group>"; };
6C49605B2A33452900BDF3CB /* DotLottieAnimationMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DotLottieAnimationMode.swift; sourceTree = "<group>"; };
6D99D6422823790700E5205B /* LegacyGradientFillRenderer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyGradientFillRenderer.swift; sourceTree = "<group>"; };
6DB3BDB528243FA5002A276D /* ValueProvidersTests.swift */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = ValueProvidersTests.swift; sourceTree = "<group>"; tabWidth = 2; };
6DB3BDB7282454A6002A276D /* DictionaryInitializable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DictionaryInitializable.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1660,6 +1664,7 @@
6C4877FB28FF20140005AF07 /* DotLottieManifest.swift */,
6C4877E428FF20140005AF07 /* DotLottieUtils.swift */,
6C4877E228FF20140005AF07 /* DotLottieAnimation.swift */,
6C49605B2A33452900BDF3CB /* DotLottieAnimationMode.swift */,
6C48784A29008ACF0005AF07 /* DotLottieConfiguration.swift */,
6C48785F2901D8C70005AF07 /* DotLottieImageProvider.swift */,
6CFA1077290B12B900873A98 /* ZipFoundation */,
Expand Down Expand Up @@ -2015,6 +2020,7 @@
2EAF5AD727A0798700E00531 /* Vectors.swift in Sources */,
2E9C95E22822F43100677516 /* Group.swift in Sources */,
2E9C97112822F43100677516 /* Keyframes+combined.swift in Sources */,
6C49605C2A33452900BDF3CB /* DotLottieAnimationMode.swift in Sources */,
0887347B28F0CCDD00458627 /* LottieAnimationView.swift in Sources */,
2EAF5AD127A0798700E00531 /* AnimatedControl.swift in Sources */,
2E9C966F2822F43100677516 /* LayerTextProvider.swift in Sources */,
Expand Down Expand Up @@ -2262,6 +2268,7 @@
2EAF5AD827A0798700E00531 /* Vectors.swift in Sources */,
2E9C95E32822F43100677516 /* Group.swift in Sources */,
2E9C97122822F43100677516 /* Keyframes+combined.swift in Sources */,
6C49605D2A33452900BDF3CB /* DotLottieAnimationMode.swift in Sources */,
0887347C28F0CCDD00458627 /* LottieAnimationView.swift in Sources */,
2EAF5AD227A0798700E00531 /* AnimatedControl.swift in Sources */,
2E9C96702822F43100677516 /* LayerTextProvider.swift in Sources */,
Expand Down Expand Up @@ -2485,6 +2492,7 @@
2EAF5AD927A0798700E00531 /* Vectors.swift in Sources */,
2E9C95E42822F43100677516 /* Group.swift in Sources */,
2E9C97132822F43100677516 /* Keyframes+combined.swift in Sources */,
6C49605E2A33452900BDF3CB /* DotLottieAnimationMode.swift in Sources */,
0887347D28F0CCDD00458627 /* LottieAnimationView.swift in Sources */,
2EAF5AD327A0798700E00531 /* AnimatedControl.swift in Sources */,
2E9C96712822F43100677516 /* LayerTextProvider.swift in Sources */,
Expand Down
7 changes: 5 additions & 2 deletions Sources/Private/Model/DotLottie/DotLottieAnimation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,15 @@ struct DotLottieAnimation: Codable {
/// 1 or -1
var direction: Int? = 1

/// Auto plays animation
var autoplay: Bool? = false
eharrison marked this conversation as resolved.
Show resolved Hide resolved

/// mode - "bounce" | "normal"
var mode: String? = "normal"
var mode: DotLottieAnimationMode? = .normal

/// Loop mode for animation
var loopMode: LottieLoopMode {
mode == "bounce" ? .autoReverse : ((loop ?? false) ? .loop : .playOnce)
mode == .bounce ? .autoReverse : ((loop ?? false) ? .loop : .playOnce)
eharrison marked this conversation as resolved.
Show resolved Hide resolved
}

/// Animation speed
Expand Down
13 changes: 13 additions & 0 deletions Sources/Private/Model/DotLottie/DotLottieAnimationMode.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//
// DotLottieAnimationMode.swift
// Lottie
//
// Created by Evandro Hoffmann on 09/06/23.
//

import Foundation

enum DotLottieAnimationMode: String, Codable {
eharrison marked this conversation as resolved.
Show resolved Hide resolved
case normal
case bounce
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ struct DotLottieConfiguration {
var imageProvider: AnimationImageProvider?
var loopMode: LottieLoopMode
var speed: Double
var autoplay: Bool
}
82 changes: 75 additions & 7 deletions Sources/Public/Animation/LottieAnimationView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -648,15 +648,54 @@ open class LottieAnimationView: LottieAnimationViewBase {
from dotLottieFile: DotLottieFile)
{
guard let dotLottieAnimation = dotLottieFile.animation(for: animationId) else { return }
loadAnimation(dotLottieAnimation, from: dotLottieFile)
}

loopMode = dotLottieAnimation.configuration.loopMode
animationSpeed = CGFloat(dotLottieAnimation.configuration.speed)

if let imageProvider = dotLottieAnimation.configuration.imageProvider {
self.imageProvider = imageProvider
}
/// Sets the lottie file backing the animation view. Setting this will clear the
/// view's contents, completion blocks and current state. The new animation will
/// be loaded up and set to the beginning of its timeline.
/// The loopMode, animationSpeed and imageProvider will be set according
/// to lottie file settings
/// - Parameters:
/// - animationIndex: Internal animation index to play. Optional
/// Defaults to play first animation in file.
/// - dotLottieFile: Lottie file to play
public func loadAnimation(
eharrison marked this conversation as resolved.
Show resolved Hide resolved
animationIndex: Int,
eharrison marked this conversation as resolved.
Show resolved Hide resolved
from dotLottieFile: DotLottieFile)
{
guard let dotLottieAnimation = dotLottieFile.animation(for: animationIndex) else { return }
loadAnimation(dotLottieAnimation, from: dotLottieFile)
}

animation = dotLottieAnimation.animation
/// Exclusive for DotLottie animations. This will be ignored if playing json animation.
/// Plays the next animation in the array.
/// If there are no next animations, will loop back to the first animation in the array.
/// If there is only one animation, will reset to the beginning of the current animation.
/// Setting this will clear the view's contents, completion blocks and current state.
/// The new animation will be loaded up and set to the beginning of its timeline.
/// The loopMode, animationSpeed and imageProvider will be set according
/// to lottie file settings
public func nextAnimation() {
eharrison marked this conversation as resolved.
Show resolved Hide resolved
guard
let activeDotLottieFile, let activeDotLottieAnimationID,
let dotLottieAnimation = activeDotLottieFile.nextAnimation(after: activeDotLottieAnimationID) else { return }
loadAnimation(dotLottieAnimation, from: activeDotLottieFile)
}

/// Exclusive for DotLottie animations. This will be ignored if playing json animation.
/// Plays the previous animation in the array.
/// If there are no previous animations, will loop back to the last animation in the array.
/// If there is only one animation, will reset to the beginning of the current animation.
/// Setting this will clear the view's contents, completion blocks and current state.
/// The new animation will be loaded up and set to the beginning of its timeline.
/// The loopMode, animationSpeed and imageProvider will be set according
/// to lottie file settings
public func previousAnimation() {
guard
let activeDotLottieFile, let activeDotLottieAnimationID,
let dotLottieAnimation = activeDotLottieFile.previousAnimation(before: activeDotLottieAnimationID) else { return }
loadAnimation(dotLottieAnimation, from: activeDotLottieFile)
}

/// Reloads the images supplied to the animation from the `imageProvider`
Expand Down Expand Up @@ -1103,6 +1142,30 @@ open class LottieAnimationView: LottieAnimationViewBase {
addNewAnimationForContext(newContext)
}

/// Loads view with given DotLottie animation. Setting this will clear the
/// view's contents, completion blocks and current state. The new animation will
/// be loaded up and set to the beginning of its timeline.
/// The loopMode, animationSpeed and imageProvider will be set according
/// to lottie file settings
/// - Parameter dotLottieAnimation: DotLottieFile.Animation to load the view
/// - Parameter dotLottieFile: Lottie file to play
func loadAnimation(_ dotLottieAnimation: DotLottieFile.Animation, from dotLottieFile: DotLottieFile) {
loopMode = dotLottieAnimation.configuration.loopMode
animationSpeed = CGFloat(dotLottieAnimation.configuration.speed)

if let imageProvider = dotLottieAnimation.configuration.imageProvider {
self.imageProvider = imageProvider
}

animation = dotLottieAnimation.animation
activeDotLottieFile = dotLottieFile
activeDotLottieAnimationID = dotLottieAnimation.configuration.id

if dotLottieAnimation.configuration.autoplay {
play()
}
}

// MARK: Fileprivate

/// Context describing the animation that is currently playing in this `LottieAnimationView`
Expand All @@ -1115,6 +1178,9 @@ open class LottieAnimationView: LottieAnimationViewBase {

fileprivate var waitingToPlayAnimation = false

fileprivate var activeDotLottieFile: DotLottieFile?
eharrison marked this conversation as resolved.
Show resolved Hide resolved
fileprivate var activeDotLottieAnimationID: String?

fileprivate var activeAnimationName: String {
switch animationLayer?.primaryAnimationKey {
case .specific(let animationKey):
Expand Down Expand Up @@ -1347,6 +1413,8 @@ open class LottieAnimationView: LottieAnimationViewBase {
animationLayer?.removeAnimation(forKey: activeAnimationName)
updateAnimationFrame(pauseFrame)
animationContext = nil
activeDotLottieFile = nil
activeDotLottieAnimationID = nil
}

/// Adds animation to animation layer and sets the delegate. If animation layer or animation are nil, exits.
Expand Down
39 changes: 38 additions & 1 deletion Sources/Public/DotLottie/DotLottieFile.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,42 @@ public final class DotLottieFile {
}
}

/// The `LottieAnimation` and `DotLottieConfiguration` for the given animation index in this file
func animation(for index: Int) -> DotLottieFile.Animation? {
guard index < animations.count else { return nil }
return animations[index]
}

/// Returns the next animation in the array. Returns the first animation in case it's the last index
/// The next `LottieAnimation` and `DotLottieConfiguration`
func nextAnimation(after animationId: String) -> DotLottieFile.Animation? {
eharrison marked this conversation as resolved.
Show resolved Hide resolved
guard let index = animations.firstIndex(where: { animationId == $0.configuration.id }) else {
return nil
}

let nextIndex = index + 1
if nextIndex < animations.count {
return animations[nextIndex]
}

return animations.first
}

/// Returns the previous animation in the array. Returns the first animation in case it's the last index
/// The next `LottieAnimation` and `DotLottieConfiguration`
func previousAnimation(before animationId: String) -> DotLottieFile.Animation? {
guard let index = animations.firstIndex(where: { animationId == $0.configuration.id }) else {
return nil
}

let previousIndex = index - 1
if previousIndex > 0 {
return animations[previousIndex]
}

return animations.last
}

// MARK: Private

private static let manifestFileName = "manifest.json"
Expand Down Expand Up @@ -104,7 +140,8 @@ public final class DotLottieFile {
id: dotLottieAnimation.id,
imageProvider: imageProvider,
loopMode: dotLottieAnimation.loopMode,
speed: dotLottieAnimation.animationSpeed)
speed: dotLottieAnimation.animationSpeed,
autoplay: dotLottieAnimation.autoplay ?? false)

return DotLottieFile.Animation(
animation: animation,
Expand Down
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Supports Core Animation engine
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.