Skip to content

Commit

Permalink
Merge pull request #8 from LottieFiles/multi-animation-support
Browse files Browse the repository at this point in the history
Multi animation support
  • Loading branch information
samuelOsborne authored Jan 9, 2024
2 parents ffc8ddd + 471d6c6 commit d3f48e9
Show file tree
Hide file tree
Showing 27 changed files with 2,135 additions and 509 deletions.
16 changes: 9 additions & 7 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,33 +6,35 @@ import PackageDescription
let package = Package(
name: "DotLottie",
// Todo - When Thorvg can build for arm, add more platforms here!
platforms: [.iOS(.v16), .macOS(.v11)],
platforms: [.iOS(.v14), .macOS(.v11)],
products: [
// Products define the executables and libraries a package produces, and make them visible to other packages.
.library(
name: "DotLottie",
targets: ["DotLottie", "Thorvg"]),
targets: ["DotLottie", "DotLottiePlayer"]),
],
dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
// .package(path: "./Sources/DotLottie/Thorvg")
.package(url: "https://github.com/weichsel/ZIPFoundation.git", .upToNextMajor(from: "0.9.0"))
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages this package depends on.
.target(
name: "DotLottie",
dependencies: ["Thorvg", "ZIPFoundation"],
dependencies: ["DotLottiePlayer", "ZIPFoundation"],
path: "Sources/DotLottie/"),
.testTarget(
name: "DotLottieTests",
dependencies: ["DotLottie"]),
.binaryTarget(
name: "Thorvg",
path: "./Sources/Thorvg/exports/Framework/Thorvg.xcframework"
// path: "./Sources/DotLottie/Backup/Thorvg/Thorvg.xcframework.zip"
name: "DotLottiePlayer",
path: "./Sources/Thorvg-rust/DotLottiePlayer.xcframework"
),
// .binaryTarget(
// name: "Thorvg",
// path: "./Sources/Thorvg/exports/framework/Thorvg.xcframework"
// ),
]
)
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@

- iPhone, iPhone Simulator (x86), MacOS (x86, ARM)


Note: This is due to the compilation of Thorvg. We're working on supporting more platforms!

## Usage
Expand Down Expand Up @@ -40,7 +39,7 @@ Set up DotLottieAnimation inside a View. Optionally pass playback settings.
```swift
struct AnimationView: View {
var body: some View {
DotLottieAnimation(fileName: "cool_animation", autoplay: true, loop: true).view()
DotLottieAnimation(fileName: "cool_animation", playbackConfig: PlaybackConfig(autoplay: true, loop: true)).view()
}
}
```
Expand All @@ -66,7 +65,7 @@ Coming soon!

```swift
class AnimationViewController: UIViewController {
var simpleVM = DotLottieAnimation(webURL: "https://lottie.host/link.lottie", autoplay: true, loop: false)
var simpleVM = DotLottieAnimation(webURL: "https://lottie.host/link.lottie", playbackConfig: PlaybackConfig(autoplay: true, loop: false))

override func viewWillAppear(_ animated: Bool) {
let dotLottieView = simpleVM.createDotLottieView()
Expand Down
181 changes: 181 additions & 0 deletions Sources/DotLottie/Private/DotLottieManager.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
//
// File.swift
//
//
// Created by Sam on 18/12/2023.
//

import Foundation

enum DotLottieManagerErrors: Error {
case missingAnimationInsideManifest
case invalidURL
case missingManifestFile
}

class DotLottieManager {
// id : path on disk
private var filePaths: [String: URL] = [:]

// path on disk
private var manifestFilePath: URL?

// Object representation of manifest
public private(set) var manifest: ManifestModel?

public private(set) var currentAnimationId: String = ""

// If an error occurs whilst loading
private var errorMessage: String?

public func initFromWebUrl(url: URL) async throws {
// Fetch the file data
let urlBook = try await fetchDotLottieAndWriteToDisk(url: url)

try loadDotLottiePipeline(urlBook: urlBook)
}

/// Initiliaze the DotLottieManager from an animation inside the main asset bundle.
/// - Parameter assetName: Name of the animation inside the asset bundle.
public func initFromBundle(assetName: String) throws {
let fileData = try fetchFileFromBundle(animationName: assetName,
extensionName: "lottie")
let urlBook = try writeDotLottieToDisk(dotLottie: fileData, fileName: assetName)

try loadDotLottiePipeline(urlBook: urlBook)
}

/// Initialize the DotLottieManager with the dotLottie information already written to disk.
/// - Parameter urlBook: A dictionnary containg the id of the animation as key, and its path to disk as URL.
private func loadDotLottiePipeline(urlBook: [String:URL]) throws {
guard let manifestPath = urlBook["manifest"] else { throw DotLottieManagerErrors.missingManifestFile }

// Extract out a ManifestModel
let manifestObject = try extractManifest(manifestFilePath: manifestPath)

// Set properties
self.filePaths = urlBook
self.manifest = manifestObject
self.manifestFilePath = urlBook["manifest"]

// If theres a default animation set the current animation id to that
if let aaId = self.manifest?.activeAnimationId {
if containsAnimation(animationId: aaId) {
self.currentAnimationId = aaId
}
} else if let manifest = self.manifest {
if let firstAnimation = manifest.animations.first {
self.currentAnimationId = firstAnimation.id
}
}
}

/// Check if an animation is inside the loaded dotLottie.
/// - Parameter animationId: Id of the animation, available from the manifest.json file of the dotLottie.
/// - Returns: True if contains the animation, otherwise false
public func containsAnimation(animationId: String) -> Bool {
if filePaths[animationId] != nil {
return true
}

return false
}

/// Get the playback settings of a specific animation.
/// - Parameter animationId: Id of the animation.
/// - Returns: ManifestAnimationModel object containing the playback settings of the desired animation. Constructed from information inside the manifest file.
public func getPlaybackSettings(animationId: String) throws -> ManifestAnimationModel {
if let manifest = self.manifest {
for animation in manifest.animations {
if animation.id == animationId {
return animation
}
}
}

throw DotLottieManagerErrors.missingAnimationInsideManifest
}

/// Get the playback settings of the current active animation.
/// - Returns: ManifestAnimationModel object containing the playback settings of the current active animation. Constructed from information inside the manifest file.
public func currentAnimationPlaybackSettings() throws -> ManifestAnimationModel {
return try getPlaybackSettings(animationId: self.currentAnimationId)
}

/// Loads the next animation from the dotLottie.
/// - Returns: ManifestAnimationModel object containing the playback settings of the n+1 animation. Constructed from information inside the manifest file.
public func nextAnimation() throws -> ManifestAnimationModel {
if let manifest = self.manifest {

let index = manifest.animations.firstIndex { animation in
if animation.id == self.currentAnimationId {
return true
} else {
return false
}
}

if var checkedIndex = index {
if checkedIndex < manifest.animations.count - 1 {
checkedIndex += 1

self.currentAnimationId = manifest.animations[checkedIndex].id
}

return manifest.animations[checkedIndex]
}
}

throw DotLottieManagerErrors.missingAnimationInsideManifest
}

/// Loads the previous animation from the dotLottie.
/// - Returns: ManifestAnimationModel object containing the playback settings of the n-1 animation. Constructed from information inside the manifest file.
public func prevAnimation() throws -> ManifestAnimationModel {
if let manifest = self.manifest {

let index = manifest.animations.firstIndex { animation in
if animation.id == self.currentAnimationId {
return true
} else {
return false
}
}

if var checkedIndex = index {
if checkedIndex > 0 {
checkedIndex -= 1

self.currentAnimationId = manifest.animations[checkedIndex].id
}

return manifest.animations[checkedIndex]
}
}

throw DotLottieManagerErrors.missingAnimationInsideManifest
}


/// Set the current playing animation.
/// - Parameter animationId: Desired animation to play.
public func setActiveAnimation(animationId: String) throws {
if !self.containsAnimation(animationId: animationId) {
throw DotLottieManagerErrors.missingAnimationInsideManifest
}

self.currentAnimationId = animationId
}


/// Returns the path on disk to the desired animation.
/// - Parameter animationId: Animation to search for.
/// - Returns: URL to a path on disk where the animation data in .json format is located.
public func getAnimationPath(_ animationId: String) throws -> URL {
if !self.containsAnimation(animationId: animationId) {
throw DotLottieManagerErrors.missingAnimationInsideManifest
}

return filePaths[animationId]!
}
}
11 changes: 0 additions & 11 deletions Sources/DotLottie/Public/Coordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,19 +50,11 @@ public class Coordinator : NSObject, MTKViewDelegate {
var filteredImage = inputImage.transformed(by: CGAffineTransform(
scaleX: size.size.width / inputImage.extent.size.width,
y: size.size.height / inputImage.extent.size.height))

#if targetEnvironment(simulator)
// filteredImage = filteredImage.transformed(by: CGAffineTransform(scaleX: 1, y: -1))
// .transformed(by: CGAffineTransform(translationX: 0, y: filteredImage.extent.height))
#endif

let x = -size.origin.x
let y = -size.origin.y

// Blend the image over an opaque background image.
// This is needed if the image is smaller than the view, or if it has transparent

// Commented out for the moment due to memory errors
filteredImage = filteredImage.composited(over: parent.dotLottieViewModel.backgroundColor())

self.mtlTexture = drawable.texture
Expand All @@ -79,9 +71,6 @@ public class Coordinator : NSObject, MTKViewDelegate {
parent.dotLottieViewModel.tick()

} else {
print("NIL frame")

parent.dotLottieViewModel.pause()
return ;
}
}
Expand Down
Loading

0 comments on commit d3f48e9

Please sign in to comment.