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

Adds .lottie file load capability to LottieAnimation #1785

Merged
merged 53 commits into from
Nov 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
f756c65
[FEAT] Added dotLottie files and starting point
eharrison Oct 19, 2022
a66d60b
[FEAT] Added support for .lottie and data load fallback to try .lottie
eharrison Oct 19, 2022
44de21e
[CHORE] Removed public from ZIPFoundation to avoid conflicts
eharrison Oct 19, 2022
ad119a7
[CHORE] Cleanup dotLottie files spacing and simplified file loading
eharrison Oct 19, 2022
7d68391
[CHORE] Removes unused file after decompressing
eharrison Oct 19, 2022
841ef30
[FEAT] Added dotlottie autoconfiguration for LottieAnimationView.
eharrison Oct 19, 2022
377f5e9
[CHORE] Added ZIPFoundation as a dependency
eharrison Oct 20, 2022
1b0fd91
[CHORE] Cleanup changes on lottie animation
eharrison Oct 20, 2022
cd0ce33
[FEAT] Separated dotlottie implementation to it's own classes
eharrison Oct 20, 2022
09f27b1
[FEAT] Updated example to support .lottie
eharrison Oct 20, 2022
c70fc14
[CHORE] Renamed DotLottieFile to DotLottie
eharrison Oct 20, 2022
7d4dd91
[CHORE] Cleanup and code format
eharrison Oct 20, 2022
d9eaee6
[CHORE] Moved dotLottie folders to upper level
eharrison Oct 20, 2022
1fe94b9
[FEAT] Cleanup .lottie file after decompress and use filename for ani…
eharrison Oct 20, 2022
0d5eaa3
[FIX] Input image provider before animation to stop warning
eharrison Oct 20, 2022
fedf4a0
[FEAT] Setup new image provider to keep images in memory and remove t…
eharrison Oct 20, 2022
5ccb007
[FEAT] Added support for macOS
eharrison Oct 20, 2022
4b08939
[FEAT] Updated image cache to use NSCache
eharrison Oct 20, 2022
93ab322
[CHORE] Updated snapshot tests
eharrison Oct 21, 2022
6acd2d8
[FEAT] Added Carthage dependency for ZipFoundation
eharrison Oct 21, 2022
5401dc8
[CHORE] Updated os deployment version to 10.13 to support ZipFoundation
eharrison Oct 21, 2022
e3ce3d1
[FIX] Changed CGFloat to Double
eharrison Oct 21, 2022
293d774
[CHORE] Updated podspec
eharrison Oct 21, 2022
8be4568
[FIX] Fixed guard usage
eharrison Oct 21, 2022
a729792
[CHORE] Updated carthage and podspec
eharrison Oct 21, 2022
d325d26
[FIX] Fixed speed conversion
eharrison Oct 21, 2022
c34d33a
[FIX] Fixed issue converting speed
eharrison Oct 21, 2022
cd8b4c7
[FIX] Removed usage of CGFloat from LottieAnimationViewInitializers
eharrison Oct 21, 2022
1a54087
[FEAT] Added support to use multiple animations from dotLottieFile
eharrison Oct 21, 2022
c138554
[FEAT] UPdated example
eharrison Oct 21, 2022
1a4dbcb
Update Sources/Public/Animation/LottieAnimationView.swift
eharrison Oct 21, 2022
3e6e932
[FIX] Updated dotLottieData name
eharrison Oct 21, 2022
bd96111
Merge remote-tracking branch 'origin/dotlottie' into dotlottie
eharrison Oct 21, 2022
47f0c79
[FEAT] Updated DotLottie Cache to use NSCache
eharrison Oct 21, 2022
5b97ffe
[CHORE] Updated DotLottieFile name
eharrison Oct 21, 2022
045e7db
[CHORE] Removed public from DotLottie files
eharrison Oct 21, 2022
184ca97
Update Sources/Public/Animation/LottieAnimationView.swift
eharrison Oct 21, 2022
92d3501
Merge remote-tracking branch 'origin/dotlottie' into dotlottie
eharrison Oct 21, 2022
db9d43b
[FIX] Updated loadAnimation(from lottie function
eharrison Oct 21, 2022
7118864
[FEAT] Clieanup dependencies and zip extensions
eharrison Oct 27, 2022
78c7510
[CHORE] Removed unused dependency
eharrison Oct 31, 2022
e072754
[FEAT] Updated macos to 10.12 and lint files
eharrison Oct 31, 2022
e555b81
[CHORE] lint code
eharrison Oct 31, 2022
fe82f31
[CHORE] Removed copy right mentions
eharrison Oct 31, 2022
60343ad
Update Sources/Public/DotLottie/Cache/DotLottieCacheProvider.swift
eharrison Oct 31, 2022
13cceac
[CHORE] Removed public from dotlottiefile
eharrison Oct 31, 2022
e426b48
Merge remote-tracking branch 'origin/dotlottie' into dotlottie
eharrison Oct 31, 2022
2271084
[CHORE] Reorganized files
eharrison Oct 31, 2022
dd51f5b
[CHORE] Code cleanup
eharrison Oct 31, 2022
d8258a1
[FEAT] Made dotLottie usage async
eharrison Oct 31, 2022
93d0318
[CHORE] Removed unused initializer
eharrison Oct 31, 2022
4d7507a
[CHORE] Resolve package
eharrison Oct 31, 2022
cadb7fd
[FEAT] Put dotLottie loading in background thread.
eharrison Oct 31, 2022
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
10 changes: 8 additions & 2 deletions Example/iOS/ViewControllers/AnimationPreviewViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,15 @@ class AnimationPreviewViewController: UIViewController {
super.viewDidLoad()
view.backgroundColor = .systemBackground

let animation = LottieAnimation.named(animationName)
if let animation = LottieAnimation.named(animationName) {
animationView.animation = animation
} else {
DotLottieFile.named(animationName) { [animationView] result in
guard case Result.success(let lottie) = result else { return }
animationView.loadAnimation(from: lottie)
}
}

animationView.animation = animation
animationView.contentMode = .scaleAspectFit
view.addSubview(animationView)

Expand Down
4 changes: 3 additions & 1 deletion Example/iOS/ViewControllers/SampleListViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@ final class SampleListViewController: CollectionViewController {

@ItemModelBuilder
private var sampleAnimationLinks: [ItemModeling] {
(Bundle.main.urls(forResourcesWithExtension: "json", subdirectory: directory) ?? [])
(
(Bundle.main.urls(forResourcesWithExtension: "json", subdirectory: directory) ?? []) +
(Bundle.main.urls(forResourcesWithExtension: "lottie", subdirectory: directory) ?? []))
.map { $0.lastPathComponent.replacingOccurrences(of: ".json", with: "") }
.sorted(by: { $0.localizedCompare($1) == .orderedAscending })
.map { (name: $0, path: "\(directory)/\($0)") }
Expand Down
15 changes: 12 additions & 3 deletions Example/iOS/Views/LinkView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,18 @@ final class LinkView: UIView, EpoxyableView {
return animationView
},
setContent: { context, _ in
context.constrainable.animation = .named(animationName)
context.constrainable.contentMode = .scaleAspectFit
context.constrainable.currentProgress = 0.5
if let animation = LottieAnimation.named(animationName) {
context.constrainable.animation = animation
context.constrainable.contentMode = .scaleAspectFit
context.constrainable.currentProgress = 0.5
} else {
DotLottieFile.named(animationName) { result in
guard case Result.success(let lottie) = result else { return }
context.constrainable.loadAnimation(from: lottie)
context.constrainable.contentMode = .scaleAspectFit
context.constrainable.currentProgress = 0.5
}
}
})
}

Expand Down
166 changes: 164 additions & 2 deletions Lottie.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ let package = Package(
name: "Lottie",
platforms: [.iOS("11.0"), .macOS("10.10"), .tvOS("11.0")],
products: [.library(name: "Lottie", targets: ["Lottie"])],
targets: [.target(name: "Lottie", path: "Sources")])
targets: [.target(name: "Lottie", path: "Sources")])
52 changes: 52 additions & 0 deletions Sources/Private/Model/DotLottie/DotLottieAnimation.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//
// DotLottieAnimation.swift
// Pods
//
// Created by Evandro Harrison Hoffmann on 28/06/2021.
//

import Foundation

struct DotLottieAnimation: Codable {
/// Id of Animation
var id: String

/// Loop enabled
var loop: Bool

// appearance color in HEX
var themeColor: String

/// Animation Playback Speed
var speed: Double

/// 1 or -1
var direction: Int? = 1

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

/// URL to animation, to be set internally
var animationUrl: URL?

/// Loop mode for animation
var loopMode: LottieLoopMode {
mode == "bounce" ? .autoReverse : (loop ? .loop : .playOnce)
}

/// Animation speed
var animationSpeed: Double {
speed * Double(direction ?? 1)
}

/// Loads `LottieAnimation` from `animationUrl`
/// - Returns: Deserialized `LottieAnimation`. Optional.
func animation() throws -> LottieAnimation {
guard let animationUrl = animationUrl else {
throw DotLottieError.animationNotAvailable
}
let data = try Data(contentsOf: animationUrl)
return try LottieAnimation.from(data: data)
}

}
15 changes: 15 additions & 0 deletions Sources/Private/Model/DotLottie/DotLottieConfiguration.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//
// DotLottieSettings.swift
// Lottie
//
// Created by Evandro Hoffmann on 19/10/22.
//

import Foundation

struct DotLottieConfiguration {
var id: String
var imageProvider: AnimationImageProvider?
var loopMode: LottieLoopMode
var speed: Double
}
68 changes: 68 additions & 0 deletions Sources/Private/Model/DotLottie/DotLottieImageProvider.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//
// DotLottieImageProvider.swift
// Lottie
//
// Created by Evandro Hoffmann on 20/10/22.
//

import Foundation
#if os(iOS) || os(tvOS) || os(watchOS) || targetEnvironment(macCatalyst)
import UIKit
#elseif os(macOS)
import AppKit
#endif

// MARK: - DotLottieImageProvider

/// Provides an image for a lottie animation from a provided Bundle.
class DotLottieImageProvider: AnimationImageProvider {

// MARK: Lifecycle

/// Initializes an image provider with a specific filepath.
///
/// - Parameter filepath: The absolute filepath containing the images.
///
init(filepath: String) {
self.filepath = URL(fileURLWithPath: filepath)
loadImages()
}

init(filepath: URL) {
self.filepath = filepath
loadImages()
}

// MARK: Internal

let filepath: URL

func imageForAsset(asset: ImageAsset) -> CGImage? {
imageCache.object(forKey: asset.name as NSString)
}

// MARK: Private

private var imageCache: NSCache<NSString, CGImage> = .init()

private func loadImages() {
filepath.urls.forEach {
#if os(iOS) || os(tvOS) || os(watchOS) || targetEnvironment(macCatalyst)
if
let data = try? Data(contentsOf: $0),
let image = UIImage(data: data)?.cgImage
{
imageCache.setObject(image, forKey: $0.lastPathComponent as NSString)
}
#elseif os(macOS)
if
let data = try? Data(contentsOf: $0),
let image = NSImage(data: data)?.lottie_CGImage
{
imageCache.setObject(image, forKey: $0.lastPathComponent as NSString)
}
#endif
}
}

}
53 changes: 53 additions & 0 deletions Sources/Private/Model/DotLottie/DotLottieManifest.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//
// DotLottieManifest.swift
// Lottie
//
// Created by Evandro Harrison Hoffmann on 27/06/2020.
//

import Foundation

/// Manifest model for .lottie File
struct DotLottieManifest: Codable {

// MARK: Lifecycle

init(animations: [DotLottieAnimation], version: String, author: String, generator: String) {
self.animations = animations
self.version = version
self.author = author
self.generator = generator
}

// MARK: Internal

var animations: [DotLottieAnimation]
var version: String
var author: String
var generator: String

/// Decodes data to Manifest model
/// - Parameter data: Data to decode
/// - Throws: Error
/// - Returns: .lottie Manifest model
static func decode(from data: Data) throws -> DotLottieManifest {
try JSONDecoder().decode(DotLottieManifest.self, from: data)
}

/// Loads manifest from given URL
/// - Parameter path: URL path to Manifest
/// - Returns: Manifest Model
static func load(from url: URL) throws -> DotLottieManifest {
let data = try Data(contentsOf: url)
return try decode(from: data)
}

/// Encodes to data
/// - Parameter encoder: JSONEncoder
/// - Throws: Error
/// - Returns: encoded Data
func encode(with encoder: JSONEncoder = JSONEncoder()) throws -> Data {
try encoder.encode(self)
}

}
58 changes: 58 additions & 0 deletions Sources/Private/Model/DotLottie/DotLottieUtils.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//
// DotLottieUtils.swift
// Lottie
//
// Created by Evandro Harrison Hoffmann on 27/06/2020.
//

import Foundation

// MARK: - DotLottieUtils

struct DotLottieUtils {
static let dotLottieExtension = "lottie"
static let jsonExtension = "json"

/// Temp folder to app directory
static var tempDirectoryURL: URL {
if #available(iOS 10.0, *) {
return FileManager.default.temporaryDirectory
}
return URL(fileURLWithPath: NSTemporaryDirectory())
}
}

extension URL {
/// Checks if url is a lottie file
var isDotLottie: Bool {
pathExtension == DotLottieUtils.dotLottieExtension
}

/// Checks if url is a json file
var isJsonFile: Bool {
pathExtension == DotLottieUtils.jsonExtension
}

var urls: [URL] {
FileManager.default.urls(for: self) ?? []
}
}

extension FileManager {
/// Lists urls for all files in a directory
/// - Parameters:
/// - url: URL of directory to search
/// - skipsHiddenFiles: If should or not show hidden files
/// - Returns: Returns urls of all files matching criteria in the directory
func urls(for url: URL, skipsHiddenFiles: Bool = true) -> [URL]? {
try? contentsOfDirectory(at: url, includingPropertiesForKeys: nil, options: skipsHiddenFiles ? .skipsHiddenFiles : [])
}
}

// MARK: - DotLottieError

public enum DotLottieError: Error {
case invalidFileFormat
case invalidData
case animationNotAvailable
}
Loading