From 7f1b50c5cc432b7262707454255ab060e97e2594 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Fri, 8 Jul 2022 22:22:53 +0800 Subject: [PATCH 1/6] Now it's time to move to Lottie 3.4, which solve the performance issue Pick back the code, and implements the bitmap frame extract using the new API --- .../contents.xcworkspacedata | 7 + Cartfile | 2 +- Cartfile.resolved | 4 +- .../AppDelegate.swift | 14 +- .../ViewController.swift | 36 ++- .../project.pbxproj | 2 + .../SDWebImageLottiePlugin/AppDelegate.swift | 1 + .../ViewController.swift | 27 +-- Example/Tests/Tests.swift | 36 +-- Package.resolved | 25 ++ Package.swift | 36 +++ README.md | 40 +--- SDWebImageLottiePlugin.podspec | 6 +- .../Classes/AnimatedControl+WebCache.swift | 45 ++++ .../Classes/AnimationView+WebCache.swift | 43 ++++ .../CompatibleAnimationView+WebCache.swift | 87 +++++++ .../Classes/LOTAnimatedControl+WebCache.h | 167 ------------- .../Classes/LOTAnimatedControl+WebCache.m | 83 ------- .../Classes/LOTAnimatedImage.h | 48 ---- .../Classes/LOTAnimatedImage.m | 222 ------------------ .../Classes/LOTAnimationView+WebCache.h | 163 ------------- .../Classes/LOTAnimationView+WebCache.m | 79 ------- .../Classes/LottieCompositionLayer.swift | 37 +++ .../Classes/LottieImage.swift | 181 ++++++++++++++ .../Module/SDWebImageLottiePlugin.h | 21 -- SDWebImageLottiePlugin/Private/LOTAsset.h | 38 --- .../Private/LOTAssetGroup.h | 28 --- .../Private/LOTBezierData.h | 27 --- .../Private/LOTCompositionContainer.h | 46 ---- SDWebImageLottiePlugin/Private/LOTKeyframe.h | 49 ---- SDWebImageLottiePlugin/Private/LOTLayer.h | 76 ------ .../Private/LOTLayerContainer.h | 37 --- .../Private/LOTLayerGroup.h | 30 --- .../Private/LOTPlatformCompat.h | 37 --- 34 files changed, 530 insertions(+), 1250 deletions(-) create mode 100644 .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata create mode 100644 Package.resolved create mode 100644 Package.swift create mode 100644 SDWebImageLottiePlugin/Classes/AnimatedControl+WebCache.swift create mode 100644 SDWebImageLottiePlugin/Classes/AnimationView+WebCache.swift create mode 100644 SDWebImageLottiePlugin/Classes/CompatibleAnimationView+WebCache.swift delete mode 100644 SDWebImageLottiePlugin/Classes/LOTAnimatedControl+WebCache.h delete mode 100644 SDWebImageLottiePlugin/Classes/LOTAnimatedControl+WebCache.m delete mode 100644 SDWebImageLottiePlugin/Classes/LOTAnimatedImage.h delete mode 100644 SDWebImageLottiePlugin/Classes/LOTAnimatedImage.m delete mode 100644 SDWebImageLottiePlugin/Classes/LOTAnimationView+WebCache.h delete mode 100644 SDWebImageLottiePlugin/Classes/LOTAnimationView+WebCache.m create mode 100644 SDWebImageLottiePlugin/Classes/LottieCompositionLayer.swift create mode 100644 SDWebImageLottiePlugin/Classes/LottieImage.swift delete mode 100644 SDWebImageLottiePlugin/Module/SDWebImageLottiePlugin.h delete mode 100644 SDWebImageLottiePlugin/Private/LOTAsset.h delete mode 100644 SDWebImageLottiePlugin/Private/LOTAssetGroup.h delete mode 100644 SDWebImageLottiePlugin/Private/LOTBezierData.h delete mode 100644 SDWebImageLottiePlugin/Private/LOTCompositionContainer.h delete mode 100644 SDWebImageLottiePlugin/Private/LOTKeyframe.h delete mode 100644 SDWebImageLottiePlugin/Private/LOTLayer.h delete mode 100644 SDWebImageLottiePlugin/Private/LOTLayerContainer.h delete mode 100644 SDWebImageLottiePlugin/Private/LOTLayerGroup.h delete mode 100644 SDWebImageLottiePlugin/Private/LOTPlatformCompat.h diff --git a/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Cartfile b/Cartfile index 83c24c0..2537e9b 100644 --- a/Cartfile +++ b/Cartfile @@ -1,2 +1,2 @@ github "SDWebImage/SDWebImage" ~> 5.10 -github "airbnb/lottie-ios" ~> 2.5 +github "airbnb/lottie-ios" ~> 3.4 diff --git a/Cartfile.resolved b/Cartfile.resolved index fbe6b37..c85873a 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,2 +1,2 @@ -github "SDWebImage/SDWebImage" "5.5.2" -github "airbnb/lottie-ios" "2.5.3" +github "SDWebImage/SDWebImage" "5.13.0" +github "airbnb/lottie-ios" "3.4.0" diff --git a/Example/SDWebImageLottiePlugin Example macOS/AppDelegate.swift b/Example/SDWebImageLottiePlugin Example macOS/AppDelegate.swift index ff03677..d489f77 100644 --- a/Example/SDWebImageLottiePlugin Example macOS/AppDelegate.swift +++ b/Example/SDWebImageLottiePlugin Example macOS/AppDelegate.swift @@ -1,10 +1,10 @@ -// -// AppDelegate.swift -// SDWebImageLottiePlugin_Example macOS -// -// Created by 李卓立 on 2020/2/29. -// Copyright © 2020 CocoaPods. All rights reserved. -// +/* +* This file is part of the SDWebImage package. +* (c) DreamPiggy +* +* For the full copyright and license information, please view the LICENSE +* file that was distributed with this source code. +*/ import Cocoa diff --git a/Example/SDWebImageLottiePlugin Example macOS/ViewController.swift b/Example/SDWebImageLottiePlugin Example macOS/ViewController.swift index 5d21fc9..ed60c40 100644 --- a/Example/SDWebImageLottiePlugin Example macOS/ViewController.swift +++ b/Example/SDWebImageLottiePlugin Example macOS/ViewController.swift @@ -1,10 +1,10 @@ -// -// ViewController.swift -// SDWebImageLottiePlugin_Example macOS -// -// Created by 李卓立 on 2020/2/29. -// Copyright © 2020 CocoaPods. All rights reserved. -// +/* +* This file is part of the SDWebImage package. +* (c) DreamPiggy +* +* For the full copyright and license information, please view the LICENSE +* file that was distributed with this source code. +*/ import Cocoa import Lottie @@ -12,7 +12,7 @@ import SDWebImageLottiePlugin class ViewController: NSViewController { - let animationView = LOTAnimationView() + let animationView = AnimationView() override func viewDidLoad() { super.viewDidLoad() @@ -22,17 +22,15 @@ class ViewController: NSViewController { view.addSubview(animationView) let lottieUrl = URL(string: "https://raw.githubusercontent.com/airbnb/lottie-web/master/demo/adrock/data.json") - animationView.sd_setImage(with: lottieUrl) { (image, error, cacheType, url) in - self.animationView.play(fromProgress: 0, - toProgress: 1, - withCompletion: { (finished) in - if finished { - print("Animation Complete") - } else { - print("Animation cancelled") - } - }) - } + animationView.sd_setImage(with: lottieUrl, completed: { _,_,_,_ in + self.animationView.play(fromProgress: 0, toProgress: 1, loopMode: .repeat(5)) { finished in + if finished { + print("Animation Complete") + } else { + print("Animation cancelled") + } + } + }) } } diff --git a/Example/SDWebImageLottiePlugin.xcodeproj/project.pbxproj b/Example/SDWebImageLottiePlugin.xcodeproj/project.pbxproj index 0e52de1..09a492f 100644 --- a/Example/SDWebImageLottiePlugin.xcodeproj/project.pbxproj +++ b/Example/SDWebImageLottiePlugin.xcodeproj/project.pbxproj @@ -763,6 +763,7 @@ "$(inherited)", ); INFOPLIST_FILE = Tests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -781,6 +782,7 @@ "$(inherited)", ); INFOPLIST_FILE = Tests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/Example/SDWebImageLottiePlugin/AppDelegate.swift b/Example/SDWebImageLottiePlugin/AppDelegate.swift index d1b68b4..9b1fbbe 100644 --- a/Example/SDWebImageLottiePlugin/AppDelegate.swift +++ b/Example/SDWebImageLottiePlugin/AppDelegate.swift @@ -7,6 +7,7 @@ */ import UIKit +import Lottie @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { diff --git a/Example/SDWebImageLottiePlugin/ViewController.swift b/Example/SDWebImageLottiePlugin/ViewController.swift index 7104ab3..1be7a29 100644 --- a/Example/SDWebImageLottiePlugin/ViewController.swift +++ b/Example/SDWebImageLottiePlugin/ViewController.swift @@ -9,11 +9,12 @@ import UIKit import Lottie import SDWebImageLottiePlugin +import SDWebImage class ViewController: UIViewController { - let animationView = LOTAnimationView() - + let animationView = AnimationView() + override func viewDidLoad() { super.viewDidLoad() animationView.contentMode = .scaleAspectFit @@ -22,18 +23,16 @@ class ViewController: UIViewController { view.addSubview(animationView) let lottieUrl = URL(string: "https://raw.githubusercontent.com/airbnb/lottie-web/master/demo/gatin/data.json") - animationView.sd_setImage(with: lottieUrl) { (image, error, cacheType, url) in - self.animationView.play(fromProgress: 0, - toProgress: 1, - withCompletion: { (finished) in - if finished { - print("Animation Complete") - } else { - print("Animation cancelled") - } - }) - } + animationView.sd_setImage(with: lottieUrl, completed: { _,_,_,_ in + self.animationView.play(fromProgress: 0, toProgress: 1, loopMode: .repeat(5)) { finished in + if finished { + print("Animation Complete") + } else { + print("Animation cancelled") + } + } + }) } - + } diff --git a/Example/Tests/Tests.swift b/Example/Tests/Tests.swift index 43b55bd..97d9dad 100644 --- a/Example/Tests/Tests.swift +++ b/Example/Tests/Tests.swift @@ -37,44 +37,32 @@ class Tests: XCTestCase { func testAnimatedImageViewLoad() { let exception = self.expectation(description: "AnimationView load lottie URL") - let animationView = LOTAnimationView() + let animationView = AnimationView() let lottieURL = URL(string: "https://raw.githubusercontent.com/airbnb/lottie-web/master/demo/happy2016/data.json") - animationView.sd_setImage(with: lottieURL) { (image, error, cacheType, url) in + animationView.sd_setImage(with: lottieURL, completed: { (image, error, cacheType, url) in XCTAssertNil(error) let lottieImage = try! XCTUnwrap(image) - XCTAssertTrue(lottieImage.isKind(of: LOTAnimatedImage.self)) - let animation = try! XCTUnwrap((lottieImage as! LOTAnimatedImage).composition) - XCTAssertEqual(animation.compBounds.size, CGSize(width: 1920, height: 1080)) + XCTAssertTrue(lottieImage.isKind(of: LottieImage.self)) + let animation = try! XCTUnwrap((lottieImage as! LottieImage).animation) + XCTAssertEqual(animation.size, CGSize(width: 1920, height: 1080)) exception.fulfill() - } + }) self.waitForExpectations(timeout: 5, handler: nil) } func testAnimatedControlLoad() { let exception = self.expectation(description: "AnimatedControl load lottie URL") - let animationView = LOTAnimatedSwitch() + let animationView = AnimatedSwitch() let lottieURL = URL(string: "https://raw.githubusercontent.com/airbnb/lottie-web/master/demo/adrock/data.json") - animationView.sd_setImage(with: lottieURL) { (image, error, cacheType, url) in + animationView.sd_setImage(with: lottieURL, completed: { (image, error, cacheType, url) in XCTAssertNil(error) let lottieImage = try! XCTUnwrap(image) - XCTAssertTrue(lottieImage.isKind(of: LOTAnimatedImage.self)) - let animation = try! XCTUnwrap((lottieImage as! LOTAnimatedImage).composition) - XCTAssertEqual(animation.compBounds.size, CGSize(width: 690, height: 913)) + XCTAssertTrue(lottieImage.isKind(of: LottieImage.self)) + let animation = try! XCTUnwrap((lottieImage as! LottieImage).animation) + XCTAssertEqual(animation.size, CGSize(width: 690, height: 913)) exception.fulfill() - } + }) self.waitForExpectations(timeout: 5, handler: nil) } - func testLottieImageWithBundle() throws { - let bundle = Bundle(for: type(of: self)) - let fileURL = bundle.url(forResource: "Assets", withExtension: "json")! - let lottieData = try Data(contentsOf: fileURL) - let context = [SDWebImageContextOption.lottieBundle : bundle] - let lottieImage = LOTAnimatedImage(data: lottieData, scale: 1, options: [.webImageContext: context])! - let posterFrame = try XCTUnwrap(lottieImage.animatedImageFrame(at: 0)) - // Pick the color to check - let color = try XCTUnwrap(posterFrame.sd_color(at: CGPoint(x: 150, y: 150))) - XCTAssertEqual(color.toHexString(), "#00d1c1"); - } - } diff --git a/Package.resolved b/Package.resolved new file mode 100644 index 0000000..658efc8 --- /dev/null +++ b/Package.resolved @@ -0,0 +1,25 @@ +{ + "object": { + "pins": [ + { + "package": "Lottie", + "repositoryURL": "https://github.com/airbnb/lottie-ios.git", + "state": { + "branch": null, + "revision": "246bab7ef72bad56abefb88e84a08871cecf9cb8", + "version": "3.4.0" + } + }, + { + "package": "SDWebImage", + "repositoryURL": "https://github.com/SDWebImage/SDWebImage.git", + "state": { + "branch": null, + "revision": "c4b8660bb3ef543fe4bdcaac0db956b32dc5583f", + "version": "5.13.0" + } + } + ] + }, + "version": 1 +} diff --git a/Package.swift b/Package.swift new file mode 100644 index 0000000..de8b910 --- /dev/null +++ b/Package.swift @@ -0,0 +1,36 @@ +// swift-tools-version:5.1 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "SDWebImageLottiePlugin", + platforms: [ + .iOS(.v11), + .tvOS(.v11), + .macOS(.v10_11) + ], + products: [ + // Products define the executables and libraries produced by a package, and make them visible to other packages. + .library( + name: "SDWebImageLottiePlugin", + targets: ["SDWebImageLottiePlugin"]), + ], + dependencies: [ + // Dependencies declare other packages that this package depends on. + // .package(url: /* package url */, from: "1.0.0"), + .package(url: "https://github.com/SDWebImage/SDWebImage.git", from: "5.10.0"), + .package(url: "https://github.com/airbnb/lottie-ios.git", from: "3.4.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 which this package depends on. + .target( + name: "SDWebImageLottiePlugin", + dependencies: ["SDWebImage", "Lottie"], + path: ".", + sources: ["SDWebImageLottiePlugin/Classes"], + publicHeadersPath: "SDWebImageLottiePlugin/Classes" + ) + ] +) diff --git a/README.md b/README.md index a3468ff..13a5e00 100644 --- a/README.md +++ b/README.md @@ -14,9 +14,9 @@ You can find more resource about Lottie in their [Official Site](https://airbnb. ## Requirements -+ iOS 9+ ++ iOS 11+ + macOS 10.11+ -+ tvOS 9+ ++ tvOS 11+ + Xcode 11+ ## Installation @@ -38,30 +38,22 @@ SDWebImageLottiePlugin is available through [Carthage](https://github.com/Cartha github "SDWebImage/SDWebImageLottiePlugin" ``` -## Lottie 2 && 3 +## Lottie 2 && Objective-C -Although Lottie already release 3.x with the full Swift-rewritten code, however, during the performance testing of demo project, the Lottie 3 render performance is 60% slower than Lottie 2, many animation can not render as 60 FPS, while Lottie 2 did. See compare result at [here](https://github.com/SDWebImage/SDWebImageLottiePlugin/issues/1). +Lottie 3.4 version's new `Lottie.RenderingEngine = .coreAnimation` solve the huge performance regression in the issue [here](https://github.com/airbnb/lottie-ios/issues/895) 🚀 -So, to provide better performance on user, this plugin was written to support Lottie 2 currently, until Lottie community fix the performance problem. Track the issue [here](https://github.com/airbnb/lottie-ios/issues/895). +So from SDWebImageLottiePlugin v1.0.0, we drop the Lottie 2 support, as well as the Objective-C support because Lottie 3 use the pure Swift. -If you really want Lottie 3 support, please checkout [1.x branch](https://github.com/SDWebImage/SDWebImageLottiePlugin/tree/1.x), which provide the Lottie 3 and fully written in Swift. Once Lottie 3 fix the performance issue, we will upgrade this plugin's major version to 1.0 and release with Lottie 3 support. +For user who still use Lottie 2 and Objective-C, please check the 0.x version updated to [0.3.0](https://github.com/SDWebImage/SDWebImageLottiePlugin/releases/tag/0.3.0) ## Usage ### Load Lottie from remote JSON -+ Objective-C - -```objective-c -LOTAnimationView *animationView; -NSURL *lottieJSONURL; -[animationView sd_setImageWithURL:lottieJSONURL]; -``` - + Swift ```swift -let animationView: LOTAnimationView +let animationView: Lottie.AnimationView let lottieJSONURL: URL animationView.sd_setImage(with: lottieJSONURL) ``` @@ -73,30 +65,20 @@ Note: ### Advanced usage -This Lottie plugin use a wrapper class `LOTAnimatedImage` because of SDWebImage's [customization architecture design](https://github.com/SDWebImage/SDWebImage/wiki/Advanced-Usage#customization). Typically you should not care about this, however this can allows some advanced usage. - -+ Objective-C - -```objective-c -LOTComposition *composition = [LOTComposition animationFromJSON:jsonDict]; -LOTAnimatedImage *animatedImage = [[LOTAnimatedImage alloc] initWithComposition:composition]; -// Snapshot Lottie animation frame -UIImage *posterFrame = [animatedImage animatedImageAtIndex:0]; -NSTimeInterval duration = [animatedImage animatedImageDurationAtIndex: 0]; -``` +This Lottie plugin use a wrapper class `LottieImage` because of SDWebImage's [customization architecture design](https://github.com/SDWebImage/SDWebImage/wiki/Advanced-Usage#customization). Typically you should not care about this, however this can allows some advanced usage. + Swift ```swift -let composition = LOTComposition(json: jsonDict) -let animatedImage = LOTAnimatedImage(composition: composition) +let animation = try? JSONDecoder().decode(Animation.self, from: data) +let animatedImage = LottieImage(animation: animation) // Snapshot Lottie animation frame UIImage *posterFrame = animatedImage.animatedImageFrame(at: 0) TimeInterval duration = animatedImage.animatedImageDuration(at: 0) ``` Note: -+ The snapshot is a bitmap version and used for special cases, like thumbnail poster. You'd better not play it on SDAnimatedImageView. Because Lottie is a vector animation and LOTAnimationView use Core Animation for rendering, which is faster. ++ The snapshot is a bitmap version and used for special cases, like thumbnail poster. You'd better not play it on `SDAnimatedImageView`. Because Lottie is a vector animation and `Lottie.AnimationView` use Core Animation for rendering, which is faster. ## Demo diff --git a/SDWebImageLottiePlugin.podspec b/SDWebImageLottiePlugin.podspec index c620b67..290aa4e 100644 --- a/SDWebImageLottiePlugin.podspec +++ b/SDWebImageLottiePlugin.podspec @@ -20,9 +20,9 @@ SDWebImageLottiePlugin is a plugin for SDWebImage framework, which provide the L s.author = { 'DreamPiggy' => 'lizhuoli1126@126.com' } s.source = { :git => 'https://github.com/SDWebImage/SDWebImageLottiePlugin.git', :tag => s.version.to_s } - s.ios.deployment_target = '9.0' + s.ios.deployment_target = '11.0' s.osx.deployment_target = '10.11' - s.tvos.deployment_target = '9.0' + s.tvos.deployment_target = '11.0' s.source_files = 'SDWebImageLottiePlugin/Classes/**/*', 'SDWebImageLottiePlugin/Module/SDWebImageLottiePlugin.h' @@ -32,5 +32,5 @@ SDWebImageLottiePlugin is a plugin for SDWebImage framework, which provide the L } s.dependency 'SDWebImage', '~> 5.10' - s.dependency 'lottie-ios', '~> 2.5' + s.dependency 'lottie-ios', '~> 3.4' end diff --git a/SDWebImageLottiePlugin/Classes/AnimatedControl+WebCache.swift b/SDWebImageLottiePlugin/Classes/AnimatedControl+WebCache.swift new file mode 100644 index 0000000..8d1c8ce --- /dev/null +++ b/SDWebImageLottiePlugin/Classes/AnimatedControl+WebCache.swift @@ -0,0 +1,45 @@ +/* +* This file is part of the SDWebImage package. +* (c) DreamPiggy +* +* For the full copyright and license information, please view the LICENSE +* file that was distributed with this source code. +*/ + +import SDWebImage +import Lottie + +#if os(iOS) || os(tvOS) +extension AnimatedControl { + /** + * Set the imageView `image` with an `url`, placeholder, custom options and context. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param placeholder The image to be set initially, until the image request finishes. + * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. + * @param context A context contains different options to perform specify changes or processes, see `SDWebImageContextOption`. This hold the extra objects which `options` enum can not hold. + * @param progressBlock A block called while image is downloading + * @note the progress block is executed on a background queue + * @param completedBlock A block called when operation has been completed. This block has no return value + * and takes the requested UIImage as first parameter. In case of error the image parameter + * is nil and the second parameter may contain an NSError. The third parameter is a Boolean + * indicating if the image was retrieved from the local cache or from the network. + * The fourth parameter is the original image url. + */ + public func sd_setImage(with url: URL?, placeholderImage placeholder: UIImage? = nil, options: SDWebImageOptions = [], context: [SDWebImageContextOption : Any]? = nil, progress progressBlock: SDImageLoaderProgressBlock? = nil, completed completedBlock: SDExternalCompletionBlock? = nil) { + var context = context ?? [:] + context[.animatedImageClass] = LottieImage.self + self.sd_internalSetImage(with: url, placeholderImage: placeholder, options: options, context: context, setImageBlock: { [weak self] (image, data, cacheType, url) in + if let lottieImage = image as? LottieImage { + self?.animation = lottieImage.animation + } else { + self?.animation = nil + } + }, progress: progressBlock) { (image, data, error, cacheType, finiseh, url) in + completedBlock?(image, error, cacheType, url) + } + } +} +#endif diff --git a/SDWebImageLottiePlugin/Classes/AnimationView+WebCache.swift b/SDWebImageLottiePlugin/Classes/AnimationView+WebCache.swift new file mode 100644 index 0000000..7391f1a --- /dev/null +++ b/SDWebImageLottiePlugin/Classes/AnimationView+WebCache.swift @@ -0,0 +1,43 @@ +/* +* This file is part of the SDWebImage package. +* (c) DreamPiggy +* +* For the full copyright and license information, please view the LICENSE +* file that was distributed with this source code. +*/ + +import SDWebImage +import Lottie + +extension AnimationView { + /** + * Set the imageView `image` with an `url`, placeholder, custom options and context. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param placeholder The image to be set initially, until the image request finishes. + * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. + * @param context A context contains different options to perform specify changes or processes, see `SDWebImageContextOption`. This hold the extra objects which `options` enum can not hold. + * @param progressBlock A block called while image is downloading + * @note the progress block is executed on a background queue + * @param completedBlock A block called when operation has been completed. This block has no return value + * and takes the requested UIImage as first parameter. In case of error the image parameter + * is nil and the second parameter may contain an NSError. The third parameter is a Boolean + * indicating if the image was retrieved from the local cache or from the network. + * The fourth parameter is the original image url. + */ + public func sd_setImage(with url: URL?, placeholderImage placeholder: PlatformImage? = nil, options: SDWebImageOptions = [], context: [SDWebImageContextOption : Any]? = nil, progress progressBlock: SDImageLoaderProgressBlock? = nil, completed completedBlock: SDExternalCompletionBlock? = nil) { + var context = context ?? [:] + context[.animatedImageClass] = LottieImage.self + self.sd_internalSetImage(with: url, placeholderImage: placeholder, options: options, context: context, setImageBlock: { [weak self] (image, data, cacheType, url) in + if let lottieImage = image as? LottieImage { + self?.animation = lottieImage.animation + } else { + self?.animation = nil + } + }, progress: progressBlock) { (image, data, error, cacheType, finiseh, url) in + completedBlock?(image, error, cacheType, url) + } + } +} diff --git a/SDWebImageLottiePlugin/Classes/CompatibleAnimationView+WebCache.swift b/SDWebImageLottiePlugin/Classes/CompatibleAnimationView+WebCache.swift new file mode 100644 index 0000000..b6581ac --- /dev/null +++ b/SDWebImageLottiePlugin/Classes/CompatibleAnimationView+WebCache.swift @@ -0,0 +1,87 @@ +/* +* This file is part of the SDWebImage package. +* (c) DreamPiggy +* +* For the full copyright and license information, please view the LICENSE +* file that was distributed with this source code. +*/ + +import SDWebImage +import Lottie + +#if os(iOS) || os(tvOS) +/// These code is from https://github.com/airbnb/lottie-ios/pull/1123/files +extension CompatibleAnimationView { + @objc + public var animationView: AnimationView { + self.subviews.first as! AnimationView + } + + @objc + public var isAnimationPlaying: Bool { + return animationView.isAnimationPlaying + } + + @objc + public var animationDuration: TimeInterval { + return animationView.animation?.duration ?? -1; + } + + @objc + public func setFloatValue(_ value: CGFloat, forKeypath keypath: CompatibleAnimationKeypath) { + + let valueProvider = FloatValueProvider(value) + animationView.setValueProvider(valueProvider, keypath: keypath.animationKeypath) + } + + @objc + public func getFloatValue(for keypath: CompatibleAnimationKeypath, atFrame: CGFloat) -> NSNumber? { + let value = animationView.getValue(for: keypath.animationKeypath, atFrame: atFrame) + return value as? NSNumber + } + + @objc + public override func sizeThatFits(_ size: CGSize) -> CGSize { + return animationView.intrinsicContentSize + } + + @objc + public override var intrinsicContentSize: CGSize { + get { return animationView.intrinsicContentSize } + } +} + +extension CompatibleAnimationView { + /** + * Set the imageView `image` with an `url`, placeholder, custom options and context. + * + * The download is asynchronous and cached. + * + * @param url The url for the image. + * @param placeholder The image to be set initially, until the image request finishes. + * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. + * @param context A context contains different options to perform specify changes or processes, see `SDWebImageContextOption`. This hold the extra objects which `options` enum can not hold. + * @param progressBlock A block called while image is downloading + * @note the progress block is executed on a background queue + * @param completedBlock A block called when operation has been completed. This block has no return value + * and takes the requested UIImage as first parameter. In case of error the image parameter + * is nil and the second parameter may contain an NSError. The third parameter is a Boolean + * indicating if the image was retrieved from the local cache or from the network. + * The fourth parameter is the original image url. + */ + @objc + public func sd_setImage(with url: URL?, placeholderImage placeholder: UIImage? = nil, options: SDWebImageOptions = [], context: [SDWebImageContextOption : Any]? = nil, progress progressBlock: SDImageLoaderProgressBlock? = nil, completed completedBlock: SDExternalCompletionBlock? = nil) { + var context = context ?? [:] + context[.animatedImageClass] = LottieImage.self + self.sd_internalSetImage(with: url, placeholderImage: placeholder, options: options, context: context, setImageBlock: { [weak self] (image, data, cacheType, url) in + if let lottieImage = image as? LottieImage { + self?.animationView.animation = lottieImage.animation + } else { + self?.animationView.animation = nil + } + }, progress: progressBlock) { (image, data, error, cacheType, finiseh, url) in + completedBlock?(image, error, cacheType, url) + } + } +} +#endif diff --git a/SDWebImageLottiePlugin/Classes/LOTAnimatedControl+WebCache.h b/SDWebImageLottiePlugin/Classes/LOTAnimatedControl+WebCache.h deleted file mode 100644 index e4a6026..0000000 --- a/SDWebImageLottiePlugin/Classes/LOTAnimatedControl+WebCache.h +++ /dev/null @@ -1,167 +0,0 @@ -/* -* This file is part of the SDWebImage package. -* (c) DreamPiggy -* -* For the full copyright and license information, please view the LICENSE -* file that was distributed with this source code. -*/ - -#import -#import - -#if SD_UIKIT - -/** - * Integrates SDWebImage async downloading and caching of remote images with LOTAnimatedControl. - */ -@interface LOTAnimatedControl (WebCache) - -/** - * Set the imageView `image` with an `url`. - * - * The download is asynchronous and cached. - * - * @param url The url for the image. - */ -- (void)sd_setImageWithURL:(nullable NSURL *)url NS_REFINED_FOR_SWIFT; - -/** - * Set the imageView `image` with an `url` and a placeholder. - * - * The download is asynchronous and cached. - * - * @param url The url for the image. - * @param placeholder The image to be set initially, until the image request finishes. - * @see sd_setImageWithURL:placeholderImage:options: - */ -- (void)sd_setImageWithURL:(nullable NSURL *)url - placeholderImage:(nullable UIImage *)placeholder NS_REFINED_FOR_SWIFT; - -/** - * Set the imageView `image` with an `url`, placeholder and custom options. - * - * The download is asynchronous and cached. - * - * @param url The url for the image. - * @param placeholder The image to be set initially, until the image request finishes. - * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. - */ -- (void)sd_setImageWithURL:(nullable NSURL *)url - placeholderImage:(nullable UIImage *)placeholder - options:(SDWebImageOptions)options NS_REFINED_FOR_SWIFT; - -/** - * Set the imageView `image` with an `url`, placeholder, custom options and context. - * - * The download is asynchronous and cached. - * - * @param url The url for the image. - * @param placeholder The image to be set initially, until the image request finishes. - * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. - * @param context A context contains different options to perform specify changes or processes, see `SDWebImageContextOption`. This hold the extra objects which `options` enum can not hold. - */ -- (void)sd_setImageWithURL:(nullable NSURL *)url - placeholderImage:(nullable UIImage *)placeholder - options:(SDWebImageOptions)options - context:(nullable SDWebImageContext *)context; - -/** - * Set the imageView `image` with an `url`. - * - * The download is asynchronous and cached. - * - * @param url The url for the image. - * @param completedBlock A block called when operation has been completed. This block has no return value - * and takes the requested UIImage as first parameter. In case of error the image parameter - * is nil and the second parameter may contain an NSError. The third parameter is a Boolean - * indicating if the image was retrieved from the local cache or from the network. - * The fourth parameter is the original image url. - */ -- (void)sd_setImageWithURL:(nullable NSURL *)url - completed:(nullable SDExternalCompletionBlock)completedBlock; - -/** - * Set the imageView `image` with an `url`, placeholder. - * - * The download is asynchronous and cached. - * - * @param url The url for the image. - * @param placeholder The image to be set initially, until the image request finishes. - * @param completedBlock A block called when operation has been completed. This block has no return value - * and takes the requested UIImage as first parameter. In case of error the image parameter - * is nil and the second parameter may contain an NSError. The third parameter is a Boolean - * indicating if the image was retrieved from the local cache or from the network. - * The fourth parameter is the original image url. - */ -- (void)sd_setImageWithURL:(nullable NSURL *)url - placeholderImage:(nullable UIImage *)placeholder - completed:(nullable SDExternalCompletionBlock)completedBlock NS_REFINED_FOR_SWIFT; - -/** - * Set the imageView `image` with an `url`, placeholder and custom options. - * - * The download is asynchronous and cached. - * - * @param url The url for the image. - * @param placeholder The image to be set initially, until the image request finishes. - * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. - * @param completedBlock A block called when operation has been completed. This block has no return value - * and takes the requested UIImage as first parameter. In case of error the image parameter - * is nil and the second parameter may contain an NSError. The third parameter is a Boolean - * indicating if the image was retrieved from the local cache or from the network. - * The fourth parameter is the original image url. - */ -- (void)sd_setImageWithURL:(nullable NSURL *)url - placeholderImage:(nullable UIImage *)placeholder - options:(SDWebImageOptions)options - completed:(nullable SDExternalCompletionBlock)completedBlock; - -/** - * Set the imageView `image` with an `url`, placeholder and custom options. - * - * The download is asynchronous and cached. - * - * @param url The url for the image. - * @param placeholder The image to be set initially, until the image request finishes. - * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. - * @param progressBlock A block called while image is downloading - * @note the progress block is executed on a background queue - * @param completedBlock A block called when operation has been completed. This block has no return value - * and takes the requested UIImage as first parameter. In case of error the image parameter - * is nil and the second parameter may contain an NSError. The third parameter is a Boolean - * indicating if the image was retrieved from the local cache or from the network. - * The fourth parameter is the original image url. - */ -- (void)sd_setImageWithURL:(nullable NSURL *)url - placeholderImage:(nullable UIImage *)placeholder - options:(SDWebImageOptions)options - progress:(nullable SDImageLoaderProgressBlock)progressBlock - completed:(nullable SDExternalCompletionBlock)completedBlock; - -/** - * Set the imageView `image` with an `url`, placeholder, custom options and context. - * - * The download is asynchronous and cached. - * - * @param url The url for the image. - * @param placeholder The image to be set initially, until the image request finishes. - * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. - * @param context A context contains different options to perform specify changes or processes, see `SDWebImageContextOption`. This hold the extra objects which `options` enum can not hold. - * @param progressBlock A block called while image is downloading - * @note the progress block is executed on a background queue - * @param completedBlock A block called when operation has been completed. This block has no return value - * and takes the requested UIImage as first parameter. In case of error the image parameter - * is nil and the second parameter may contain an NSError. The third parameter is a Boolean - * indicating if the image was retrieved from the local cache or from the network. - * The fourth parameter is the original image url. - */ -- (void)sd_setImageWithURL:(nullable NSURL *)url - placeholderImage:(nullable UIImage *)placeholder - options:(SDWebImageOptions)options - context:(nullable SDWebImageContext *)context - progress:(nullable SDImageLoaderProgressBlock)progressBlock - completed:(nullable SDExternalCompletionBlock)completedBlock; - -@end - -#endif diff --git a/SDWebImageLottiePlugin/Classes/LOTAnimatedControl+WebCache.m b/SDWebImageLottiePlugin/Classes/LOTAnimatedControl+WebCache.m deleted file mode 100644 index afcba5f..0000000 --- a/SDWebImageLottiePlugin/Classes/LOTAnimatedControl+WebCache.m +++ /dev/null @@ -1,83 +0,0 @@ -/* -* This file is part of the SDWebImage package. -* (c) DreamPiggy -* -* For the full copyright and license information, please view the LICENSE -* file that was distributed with this source code. -*/ - -#import "LOTAnimatedControl+WebCache.h" -#import "LOTAnimatedImage.h" - -#if SD_UIKIT - -@implementation LOTAnimatedControl (WebCache) - -- (void)sd_setImageWithURL:(nullable NSURL *)url { - [self sd_setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:nil]; -} - -- (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder { - [self sd_setImageWithURL:url placeholderImage:placeholder options:0 progress:nil completed:nil]; -} - -- (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options { - [self sd_setImageWithURL:url placeholderImage:placeholder options:options progress:nil completed:nil]; -} - -- (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options context:(nullable SDWebImageContext *)context { - [self sd_setImageWithURL:url placeholderImage:placeholder options:options context:context progress:nil completed:nil]; -} - -- (void)sd_setImageWithURL:(nullable NSURL *)url completed:(nullable SDExternalCompletionBlock)completedBlock { - [self sd_setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:completedBlock]; -} - -- (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder completed:(nullable SDExternalCompletionBlock)completedBlock { - [self sd_setImageWithURL:url placeholderImage:placeholder options:0 progress:nil completed:completedBlock]; -} - -- (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options completed:(nullable SDExternalCompletionBlock)completedBlock { - [self sd_setImageWithURL:url placeholderImage:placeholder options:options progress:nil completed:completedBlock]; -} - -- (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options progress:(nullable SDImageLoaderProgressBlock)progressBlock completed:(nullable SDExternalCompletionBlock)completedBlock { - [self sd_setImageWithURL:url placeholderImage:placeholder options:options context:nil progress:progressBlock completed:completedBlock]; -} - -- (void)sd_setImageWithURL:(nullable NSURL *)url - placeholderImage:(nullable UIImage *)placeholder - options:(SDWebImageOptions)options - context:(nullable SDWebImageContext *)context - progress:(nullable SDImageLoaderProgressBlock)progressBlock - completed:(nullable SDExternalCompletionBlock)completedBlock { - SDWebImageMutableContext *mutableContext; - if (context) { - mutableContext = [context mutableCopy]; - } else { - mutableContext = [NSMutableDictionary dictionary]; - } - mutableContext[SDWebImageContextAnimatedImageClass] = LOTAnimatedImage.class; - __weak typeof(self) wself = self; - [self sd_internalSetImageWithURL:url - placeholderImage:placeholder - options:options - context:[mutableContext copy] - setImageBlock:^(UIImage * _Nullable image, NSData * _Nullable imageData, SDImageCacheType cacheType, NSURL * _Nullable imageURL) { - if ([image isKindOfClass:LOTAnimatedImage.class]) { - wself.animationComp = ((LOTAnimatedImage *)image).composition; - } else { - wself.animationComp = nil; - } - } - progress:progressBlock - completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) { - if (completedBlock) { - completedBlock(image, error, cacheType, imageURL); - } - }]; -} - -@end - -#endif diff --git a/SDWebImageLottiePlugin/Classes/LOTAnimatedImage.h b/SDWebImageLottiePlugin/Classes/LOTAnimatedImage.h deleted file mode 100644 index c697b70..0000000 --- a/SDWebImageLottiePlugin/Classes/LOTAnimatedImage.h +++ /dev/null @@ -1,48 +0,0 @@ -/* -* This file is part of the SDWebImage package. -* (c) DreamPiggy -* -* For the full copyright and license information, please view the LICENSE -* file that was distributed with this source code. -*/ - -#import -#import - -/** - * The asset bundle used for lottie animation to load bitmap images in the animation. If you don't provide this context option, use main bundle instead. - * Defaults to nil, means main bundle. (NSBundle) - */ -FOUNDATION_EXPORT SDWebImageContextOption _Nonnull const SDWebImageContextLottieBundle; - -/** - A wrapper class to allow `LOTComposition` to be compatible for SDWebImage loading/cache/rendering system. The lottie json loading from `LOTCompositionView+WebCache` category, will use this subclass instead of `UIImage`. - - @note Though this class conforms to `SDAnimatedImage` protocol, so it's compatible to be used for `SDAnimatedImageView`. But it's normally discouraged to do so. Because it does not provide optimization for animation rendering. Instead, use `SDAnimatedImage` class with `SDAnimatedImageView`. - @note If you want to get the bitmap version of Lottie animation at specify frame, call `animatedImageFrameAtIndex:` will result a snapshot for frame. -*/ -@interface LOTAnimatedImage : UIImage - -/** - The `LOTComposition` instance for Lottie representation. This property typically be nonnull if you init the image with the following methods. However, it will be null when you call super method like `initWithCGImage:` -*/ -@property (nonatomic, strong, readonly, nullable) LOTComposition *composition; - -/** - Create the wrapper with specify `LOTComposition` instance. The instance should be nonnull. - This is a convenience method for some use cases, for example, create a placeholder with `LOTComposition`. - @param composition The `LOTComposition` instance - @return An initialized object -*/ -- (nonnull instancetype)initWithComposition:(nonnull LOTComposition *)composition; - -// This class override these methods from UIImage, and it supports NSSecureCoding. -// You should use these methods to create a new animated image. Use other methods just call super instead. -+ (nullable instancetype)imageWithContentsOfFile:(nonnull NSString *)path; -+ (nullable instancetype)imageWithData:(nonnull NSData *)data; -+ (nullable instancetype)imageWithData:(nonnull NSData *)data scale:(CGFloat)scale; -- (nullable instancetype)initWithContentsOfFile:(nonnull NSString *)path; -- (nullable instancetype)initWithData:(nonnull NSData *)data; -- (nullable instancetype)initWithData:(nonnull NSData *)data scale:(CGFloat)scale; - -@end diff --git a/SDWebImageLottiePlugin/Classes/LOTAnimatedImage.m b/SDWebImageLottiePlugin/Classes/LOTAnimatedImage.m deleted file mode 100644 index 6a7ec1c..0000000 --- a/SDWebImageLottiePlugin/Classes/LOTAnimatedImage.m +++ /dev/null @@ -1,222 +0,0 @@ -/* -* This file is part of the SDWebImage package. -* (c) DreamPiggy -* -* For the full copyright and license information, please view the LICENSE -* file that was distributed with this source code. -*/ - -#import "LOTAnimatedImage.h" -#undef UIColor -#import "LOTCompositionContainer.h" - -SDWebImageContextOption _Nonnull const SDWebImageContextLottieBundle = @"lottieBundle"; - -@interface LOTAnimatedImage () - -@property (nonatomic, copy, nullable) NSData *animatedImageData; -@property (nonatomic, strong, nullable) NSBundle *assetBundle; -@property (nonatomic, strong, nullable) LOTCompositionContainer *compositionContainer; - -@end - -@implementation LOTAnimatedImage - -- (instancetype)initWithComposition:(LOTComposition *)composition { -#if SD_UIKIT - self = [super init]; -#else - self = [super initWithSize:composition.compBounds.size]; -#endif - if (self) { - _composition = composition; - } - return self; -} - -+ (instancetype)imageWithContentsOfFile:(NSString *)path { - return [[self alloc] initWithContentsOfFile:path]; -} - -+ (instancetype)imageWithData:(NSData *)data { - return [[self alloc] initWithData:data]; -} - -+ (instancetype)imageWithData:(NSData *)data scale:(CGFloat)scale { - return [[self alloc] initWithData:data scale:scale]; -} - -- (instancetype)initWithData:(NSData *)data { - return [self initWithData:data scale:1]; -} - -- (instancetype)initWithContentsOfFile:(NSString *)path { - NSData *data = [NSData dataWithContentsOfFile:path]; - return [self initWithData:data]; -} - -- (instancetype)initWithData:(NSData *)data scale:(CGFloat)scale { - return [self initWithData:data scale:scale options:nil]; -} - -- (instancetype)initWithData:(NSData *)data scale:(CGFloat)scale options:(SDImageCoderOptions *)options { - NSError *error; - NSDictionary *jsonObject = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error]; - if (error) { - NSLog(@"%@", error); - return nil; - } - if (![jsonObject isKindOfClass:NSDictionary.class]) { - return nil; - } - // Parse lottie bundle - NSBundle *bundle; - SDWebImageContext *context = options[SDImageCoderWebImageContext]; - if (context[SDWebImageContextLottieBundle]) { - bundle = context[SDWebImageContextLottieBundle]; - } - if (!bundle) { - bundle = [NSBundle mainBundle]; - } - - LOTComposition *composition = [LOTComposition animationFromJSON:jsonObject inBundle:bundle]; - if (!composition) { - return nil; - } -#if SD_UIKIT - self = [super init]; -#else - self = [super initWithSize:composition.compBounds.size]; -#endif - if (self) { - _composition = composition; - _animatedImageData = data; - _assetBundle = bundle; - } - return self; -} - -- (instancetype)initWithAnimatedCoder:(id)animatedCoder scale:(CGFloat)scale { - // Does not support progressive load for Lottie images at all - return nil; -} - -#if SD_UIKIT -- (CGSize)size { - LOTComposition *composition = self.composition; - if (composition) { - return composition.compBounds.size; - } - return [super size]; -} -#endif - -#pragma mark - NSSecureCoding - -- (instancetype)initWithCoder:(NSCoder *)aDecoder { - self = [super initWithCoder:aDecoder]; - if (self) { - NSData *animatedImageData = [aDecoder decodeObjectOfClass:[NSData class] forKey:NSStringFromSelector(@selector(animatedImageData))]; - if (!animatedImageData) { - return self; - } - NSError *error; - NSDictionary *jsonObject = [NSJSONSerialization JSONObjectWithData:animatedImageData options:0 error:&error]; - if (error) { - NSLog(@"%@", error); - return self; - } - // Parse lottie bundle - NSBundle *bundle; - NSString *bundlePath = [aDecoder decodeObjectOfClass:[NSString class] forKey:NSStringFromSelector(@selector(assetBundle))]; - if (bundlePath.length > 0) { - bundle = [NSBundle bundleWithPath:bundlePath]; - } - if (!bundle) { - bundle = [NSBundle mainBundle]; - } - LOTComposition *composition = [LOTComposition animationFromJSON:jsonObject inBundle:bundle]; - if (!composition) { - return self; - } - _composition = composition; - _animatedImageData = animatedImageData; - _assetBundle = bundle; -#if SD_MAC - self.size = composition.compBounds.size; -#endif - } - return self; -} - -- (void)encodeWithCoder:(NSCoder *)aCoder { - [super encodeWithCoder:aCoder]; - NSData *animatedImageData = self.animatedImageData; - if (animatedImageData) { - [aCoder encodeObject:animatedImageData forKey:NSStringFromSelector(@selector(animatedImageData))]; - } - NSBundle *bundle = self.assetBundle; - NSString *bundlePath = bundle.bundlePath; - if (bundle && bundlePath.length > 0 && bundle != [NSBundle mainBundle]) { - [aCoder encodeObject:bundlePath forKey:NSStringFromSelector(@selector(assetBundle))]; - } -} - -#pragma mark - Helper - -- (LOTCompositionContainer *)compositionContainer { - if (!_compositionContainer) { - _compositionContainer = [[LOTCompositionContainer alloc] initWithModel:nil inLayerGroup:nil withLayerGroup:self.composition.layerGroup withAssestGroup:self.composition.assetGroup]; - } - return _compositionContainer; -} - -- (UIImage *)createFrameWithComposition:(nonnull LOTComposition *)composition frameIndex:(NSUInteger)frameIndex { - LOTCompositionContainer *container = self.compositionContainer; - if (!container) { - return nil; - } - [container displayWithFrame:@(frameIndex) forceUpdate:YES]; - SDGraphicsImageRenderer *renderer = [[SDGraphicsImageRenderer alloc] initWithSize:composition.compBounds.size]; - UIImage *image = [renderer imageWithActions:^(CGContextRef _Nonnull context) { - [container renderInContext:context]; - }]; - return image; -} - -#pragma mark - SDAnimatedImage - -- (UIImage *)animatedImageFrameAtIndex:(NSUInteger)index { - LOTComposition *composition = self.composition; - if (!composition) { - return nil; - } - UIImage *frame = [self createFrameWithComposition:composition frameIndex:index]; - return frame; -} - -- (NSTimeInterval)animatedImageDurationAtIndex:(NSUInteger)index { - LOTComposition *composition = self.composition; - if (!composition) { - return 0; - } - NSUInteger frameCount = self.animatedImageFrameCount; - if (frameCount == 0) { - return 0; - } - return composition.timeDuration / frameCount; -} - -- (NSUInteger)animatedImageLoopCount { - return 0; -} - -- (NSUInteger)animatedImageFrameCount { - LOTComposition *composition = self.composition; - if (!composition) { - return 0; - } - return (composition.endFrame.unsignedIntegerValue - composition.startFrame.unsignedIntegerValue); -} - -@end diff --git a/SDWebImageLottiePlugin/Classes/LOTAnimationView+WebCache.h b/SDWebImageLottiePlugin/Classes/LOTAnimationView+WebCache.h deleted file mode 100644 index dfe3171..0000000 --- a/SDWebImageLottiePlugin/Classes/LOTAnimationView+WebCache.h +++ /dev/null @@ -1,163 +0,0 @@ -/* -* This file is part of the SDWebImage package. -* (c) DreamPiggy -* -* For the full copyright and license information, please view the LICENSE -* file that was distributed with this source code. -*/ - -#import -#import - -/** - * Integrates SDWebImage async downloading and caching of remote images with LOTAnimationView. - */ -@interface LOTAnimationView (WebCache) - -/** - * Set the imageView `image` with an `url`. - * - * The download is asynchronous and cached. - * - * @param url The url for the image. - */ -- (void)sd_setImageWithURL:(nullable NSURL *)url NS_REFINED_FOR_SWIFT; - -/** - * Set the imageView `image` with an `url` and a placeholder. - * - * The download is asynchronous and cached. - * - * @param url The url for the image. - * @param placeholder The image to be set initially, until the image request finishes. - * @see sd_setImageWithURL:placeholderImage:options: - */ -- (void)sd_setImageWithURL:(nullable NSURL *)url - placeholderImage:(nullable UIImage *)placeholder NS_REFINED_FOR_SWIFT; - -/** - * Set the imageView `image` with an `url`, placeholder and custom options. - * - * The download is asynchronous and cached. - * - * @param url The url for the image. - * @param placeholder The image to be set initially, until the image request finishes. - * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. - */ -- (void)sd_setImageWithURL:(nullable NSURL *)url - placeholderImage:(nullable UIImage *)placeholder - options:(SDWebImageOptions)options NS_REFINED_FOR_SWIFT; - -/** - * Set the imageView `image` with an `url`, placeholder, custom options and context. - * - * The download is asynchronous and cached. - * - * @param url The url for the image. - * @param placeholder The image to be set initially, until the image request finishes. - * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. - * @param context A context contains different options to perform specify changes or processes, see `SDWebImageContextOption`. This hold the extra objects which `options` enum can not hold. - */ -- (void)sd_setImageWithURL:(nullable NSURL *)url - placeholderImage:(nullable UIImage *)placeholder - options:(SDWebImageOptions)options - context:(nullable SDWebImageContext *)context; - -/** - * Set the imageView `image` with an `url`. - * - * The download is asynchronous and cached. - * - * @param url The url for the image. - * @param completedBlock A block called when operation has been completed. This block has no return value - * and takes the requested UIImage as first parameter. In case of error the image parameter - * is nil and the second parameter may contain an NSError. The third parameter is a Boolean - * indicating if the image was retrieved from the local cache or from the network. - * The fourth parameter is the original image url. - */ -- (void)sd_setImageWithURL:(nullable NSURL *)url - completed:(nullable SDExternalCompletionBlock)completedBlock; - -/** - * Set the imageView `image` with an `url`, placeholder. - * - * The download is asynchronous and cached. - * - * @param url The url for the image. - * @param placeholder The image to be set initially, until the image request finishes. - * @param completedBlock A block called when operation has been completed. This block has no return value - * and takes the requested UIImage as first parameter. In case of error the image parameter - * is nil and the second parameter may contain an NSError. The third parameter is a Boolean - * indicating if the image was retrieved from the local cache or from the network. - * The fourth parameter is the original image url. - */ -- (void)sd_setImageWithURL:(nullable NSURL *)url - placeholderImage:(nullable UIImage *)placeholder - completed:(nullable SDExternalCompletionBlock)completedBlock NS_REFINED_FOR_SWIFT; - -/** - * Set the imageView `image` with an `url`, placeholder and custom options. - * - * The download is asynchronous and cached. - * - * @param url The url for the image. - * @param placeholder The image to be set initially, until the image request finishes. - * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. - * @param completedBlock A block called when operation has been completed. This block has no return value - * and takes the requested UIImage as first parameter. In case of error the image parameter - * is nil and the second parameter may contain an NSError. The third parameter is a Boolean - * indicating if the image was retrieved from the local cache or from the network. - * The fourth parameter is the original image url. - */ -- (void)sd_setImageWithURL:(nullable NSURL *)url - placeholderImage:(nullable UIImage *)placeholder - options:(SDWebImageOptions)options - completed:(nullable SDExternalCompletionBlock)completedBlock; - -/** - * Set the imageView `image` with an `url`, placeholder and custom options. - * - * The download is asynchronous and cached. - * - * @param url The url for the image. - * @param placeholder The image to be set initially, until the image request finishes. - * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. - * @param progressBlock A block called while image is downloading - * @note the progress block is executed on a background queue - * @param completedBlock A block called when operation has been completed. This block has no return value - * and takes the requested UIImage as first parameter. In case of error the image parameter - * is nil and the second parameter may contain an NSError. The third parameter is a Boolean - * indicating if the image was retrieved from the local cache or from the network. - * The fourth parameter is the original image url. - */ -- (void)sd_setImageWithURL:(nullable NSURL *)url - placeholderImage:(nullable UIImage *)placeholder - options:(SDWebImageOptions)options - progress:(nullable SDImageLoaderProgressBlock)progressBlock - completed:(nullable SDExternalCompletionBlock)completedBlock; - -/** - * Set the imageView `image` with an `url`, placeholder, custom options and context. - * - * The download is asynchronous and cached. - * - * @param url The url for the image. - * @param placeholder The image to be set initially, until the image request finishes. - * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. - * @param context A context contains different options to perform specify changes or processes, see `SDWebImageContextOption`. This hold the extra objects which `options` enum can not hold. - * @param progressBlock A block called while image is downloading - * @note the progress block is executed on a background queue - * @param completedBlock A block called when operation has been completed. This block has no return value - * and takes the requested UIImage as first parameter. In case of error the image parameter - * is nil and the second parameter may contain an NSError. The third parameter is a Boolean - * indicating if the image was retrieved from the local cache or from the network. - * The fourth parameter is the original image url. - */ -- (void)sd_setImageWithURL:(nullable NSURL *)url - placeholderImage:(nullable UIImage *)placeholder - options:(SDWebImageOptions)options - context:(nullable SDWebImageContext *)context - progress:(nullable SDImageLoaderProgressBlock)progressBlock - completed:(nullable SDExternalCompletionBlock)completedBlock; - -@end diff --git a/SDWebImageLottiePlugin/Classes/LOTAnimationView+WebCache.m b/SDWebImageLottiePlugin/Classes/LOTAnimationView+WebCache.m deleted file mode 100644 index f16dc65..0000000 --- a/SDWebImageLottiePlugin/Classes/LOTAnimationView+WebCache.m +++ /dev/null @@ -1,79 +0,0 @@ -/* -* This file is part of the SDWebImage package. -* (c) DreamPiggy -* -* For the full copyright and license information, please view the LICENSE -* file that was distributed with this source code. -*/ - -#import "LOTAnimationView+WebCache.h" -#import "LOTAnimatedImage.h" - -@implementation LOTAnimationView (WebCache) - -- (void)sd_setImageWithURL:(nullable NSURL *)url { - [self sd_setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:nil]; -} - -- (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder { - [self sd_setImageWithURL:url placeholderImage:placeholder options:0 progress:nil completed:nil]; -} - -- (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options { - [self sd_setImageWithURL:url placeholderImage:placeholder options:options progress:nil completed:nil]; -} - -- (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options context:(nullable SDWebImageContext *)context { - [self sd_setImageWithURL:url placeholderImage:placeholder options:options context:context progress:nil completed:nil]; -} - -- (void)sd_setImageWithURL:(nullable NSURL *)url completed:(nullable SDExternalCompletionBlock)completedBlock { - [self sd_setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:completedBlock]; -} - -- (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder completed:(nullable SDExternalCompletionBlock)completedBlock { - [self sd_setImageWithURL:url placeholderImage:placeholder options:0 progress:nil completed:completedBlock]; -} - -- (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options completed:(nullable SDExternalCompletionBlock)completedBlock { - [self sd_setImageWithURL:url placeholderImage:placeholder options:options progress:nil completed:completedBlock]; -} - -- (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options progress:(nullable SDImageLoaderProgressBlock)progressBlock completed:(nullable SDExternalCompletionBlock)completedBlock { - [self sd_setImageWithURL:url placeholderImage:placeholder options:options context:nil progress:progressBlock completed:completedBlock]; -} - -- (void)sd_setImageWithURL:(nullable NSURL *)url - placeholderImage:(nullable UIImage *)placeholder - options:(SDWebImageOptions)options - context:(nullable SDWebImageContext *)context - progress:(nullable SDImageLoaderProgressBlock)progressBlock - completed:(nullable SDExternalCompletionBlock)completedBlock { - SDWebImageMutableContext *mutableContext; - if (context) { - mutableContext = [context mutableCopy]; - } else { - mutableContext = [NSMutableDictionary dictionary]; - } - mutableContext[SDWebImageContextAnimatedImageClass] = LOTAnimatedImage.class; - __weak typeof(self) wself = self; - [self sd_internalSetImageWithURL:url - placeholderImage:placeholder - options:options - context:[mutableContext copy] - setImageBlock:^(UIImage * _Nullable image, NSData * _Nullable imageData, SDImageCacheType cacheType, NSURL * _Nullable imageURL) { - if ([image isKindOfClass:LOTAnimatedImage.class]) { - wself.sceneModel = ((LOTAnimatedImage *)image).composition; - } else { - wself.sceneModel = nil; - } - } - progress:progressBlock - completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) { - if (completedBlock) { - completedBlock(image, error, cacheType, imageURL); - } - }]; -} - -@end diff --git a/SDWebImageLottiePlugin/Classes/LottieCompositionLayer.swift b/SDWebImageLottiePlugin/Classes/LottieCompositionLayer.swift new file mode 100644 index 0000000..394535a --- /dev/null +++ b/SDWebImageLottiePlugin/Classes/LottieCompositionLayer.swift @@ -0,0 +1,37 @@ +/* +* This file is part of the SDWebImage package. +* (c) DreamPiggy +* +* For the full copyright and license information, please view the LICENSE +* file that was distributed with this source code. +*/ + +import SDWebImage +@testable import Lottie + + +/// This layer is used if as Lottie 2's`LOTCompositionContainer`. +/// Which can render the specify frame into CGBitmapContext, used for debugging or some snapshot use case +class LottieCompositionLayer : CALayer { + var animationLayers: [CompositionLayer]? + init(animation: Animation) { + let layerImageProvider = LayerImageProvider(imageProvider: BundleImageProvider(bundle: .main, searchPath: nil).cachedImageProvider, assets: animation.assetLibrary?.imageAssets) + let layers = animation.layers.initializeCompositionLayers(assetLibrary: animation.assetLibrary, layerImageProvider: layerImageProvider, textProvider: DefaultTextProvider(), fontProvider: DefaultFontProvider(), frameRate: CGFloat(animation.framerate)) + super.init() + bounds = animation.bounds + + for layer in layers.reversed() { + layer.bounds = bounds + addSublayer(layer) + } + animationLayers = layers + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func displayWithFrame(frame: CGFloat, forceUpdates: Bool) { + animationLayers?.forEach { $0.displayWithFrame(frame: frame, forceUpdates: forceUpdates) } + } +} diff --git a/SDWebImageLottiePlugin/Classes/LottieImage.swift b/SDWebImageLottiePlugin/Classes/LottieImage.swift new file mode 100644 index 0000000..c8f9d27 --- /dev/null +++ b/SDWebImageLottiePlugin/Classes/LottieImage.swift @@ -0,0 +1,181 @@ +/* +* This file is part of the SDWebImage package. +* (c) DreamPiggy +* +* For the full copyright and license information, please view the LICENSE +* file that was distributed with this source code. +*/ + +import SDWebImage +@testable import Lottie + +#if os(macOS) +public typealias PlatformImage = NSImage +#else +public typealias PlatformImage = UIImage +#endif + +/// Used for Swift when subclassing UIImage, workaround for `required' initializer 'init(imageLiteralResourceName:)' must be provided by subclass of 'UIImage'` +extension PlatformImage { + private convenience init!(failableImageLiteral name: String) { + self.init(named: name) + } + + public convenience init(imageLiteralResourceName name: String) { + self.init(failableImageLiteral: name) + } +} + +/// A UIImage subclass wrapper for Lottie animation +public class LottieImage : PlatformImage, SDAnimatedImageProtocol { + + /// The lottie animation model + public var animation: Animation? + + /// Init the LottieImage with lottie animation model + /// - Parameter animation: animation + public required init?(animation: Animation) { + #if os(iOS) || os(tvOS) + super.init() + #else + super.init(size: animation.size) + #endif + self.animation = animation + } + + public required init?(data: Data, scale: CGFloat, options: [SDImageCoderOption : Any]? = nil) { + let animation: Animation + do { + animation = try JSONDecoder().decode(Animation.self, from: data) + } catch let error { + print(error) + return nil + } + #if os(iOS) || os(tvOS) + super.init() + #else + super.init(size: animation.size) + #endif + self.animation = animation + self.animatedImageData = data + } + + #if os(iOS) || os(tvOS) + public override var size: CGSize { + if let animation = animation { + return animation.size + } + return super.size + } + #endif + + public required convenience init?(animatedCoder: SDAnimatedImageCoder, scale: CGFloat) { + guard let data = animatedCoder.animatedImageData else { + return nil + } + self.init(data: data, scale: scale, options: nil) + } + + //MARK: - NSSecureCoding + #if os(iOS) || os(tvOS) + required public init?(coder: NSCoder) { + super.init(coder: coder) + decode(with: coder) + } + #endif + + #if os(macOS) + required public init(coder: NSCoder) { + // Hack: Why NSImage on AppKit use another non-optional NSCoding ? + super.init(coder: coder) + decode(with: coder) + } + #endif + + private func decode(with coder: NSCoder) { + if let data = coder.decodeObject(of: NSData.self, forKey: "animatedImageData") as? Data { + let animation: Animation + do { + animation = try JSONDecoder().decode(Animation.self, from: data) + } catch let error { + print(error) + return + } + self.animation = animation + self.animatedImageData = data + #if os(macOS) + self.size = animation.size + #endif + } + } + + public override func encode(with coder: NSCoder) { + super.encode(with: coder) + if let animatedImageData = animatedImageData { + coder.encode(animatedImageData, forKey: "animatedImageData") + } + } + + #if os(macOS) + required init?(pasteboardPropertyList propertyList: Any, ofType type: NSPasteboard.PasteboardType) { + super.init(pasteboardPropertyList: propertyList, ofType: type) + } + #endif + + // MARK: - Helper + lazy var compositionLayer: LottieCompositionLayer? = { + guard let animation = animation else { + return nil + } + let compositionLayer = LottieCompositionLayer(animation: animation) + return compositionLayer + }() + + func createFrame(with animation: Animation, frameIndex: UInt) -> PlatformImage? { + // We use AnimationFrameTime(Seconds * Framerate) as frame count, so this is 1:1 mapping + let frame = CGFloat(frameIndex) + guard let compositionLayer = compositionLayer else { + return nil + } + compositionLayer.displayWithFrame(frame: frame, forceUpdates: true) + let renderer = SDGraphicsImageRenderer(size: animation.size) + let image = renderer.image { context in + // Render CALayer for current frame + compositionLayer.render(in: context) + } + return image + } + + // MARK: - SDAnimatedImageProvider + public private(set) var animatedImageData: Data? + + public var animatedImageFrameCount: UInt { + guard let animation = animation else { + return 0 + } + return UInt(animation.endFrame - animation.startFrame) + } + + public var animatedImageLoopCount: UInt { + return 0 + } + + public func animatedImageFrame(at index: UInt) -> PlatformImage? { + guard let animation = animation else { + return nil + } + let image = createFrame(with: animation, frameIndex: index) + return image + } + + public func animatedImageDuration(at index: UInt) -> TimeInterval { + guard let animation = animation else { + return 0 + } + let frameCount = self.animatedImageFrameCount + if frameCount == 0 { + return 0 + } + return animation.duration / Double(frameCount) + } +} diff --git a/SDWebImageLottiePlugin/Module/SDWebImageLottiePlugin.h b/SDWebImageLottiePlugin/Module/SDWebImageLottiePlugin.h deleted file mode 100644 index e4ac398..0000000 --- a/SDWebImageLottiePlugin/Module/SDWebImageLottiePlugin.h +++ /dev/null @@ -1,21 +0,0 @@ -/* -* This file is part of the SDWebImage package. -* (c) DreamPiggy -* -* For the full copyright and license information, please view the LICENSE -* file that was distributed with this source code. -*/ - -#import - -//! Project version number for SDWebImageLottiePlugin. -FOUNDATION_EXPORT double SDWebImageLottiePluginVersionNumber; - -//! Project version string for SDWebImageLottiePlugin. -FOUNDATION_EXPORT const unsigned char SDWebImageLottiePluginVersionString[]; - -// In this header, you should import all the public headers of your framework using statements like #import - -#import -#import -#import diff --git a/SDWebImageLottiePlugin/Private/LOTAsset.h b/SDWebImageLottiePlugin/Private/LOTAsset.h deleted file mode 100644 index 0455c7f..0000000 --- a/SDWebImageLottiePlugin/Private/LOTAsset.h +++ /dev/null @@ -1,38 +0,0 @@ -// -// LOTAsset.h -// Pods -// -// Created by Brandon Withrow on 2/16/17. -// -// - -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@class LOTLayerGroup; -@class LOTLayer; -@class LOTAssetGroup; - -@interface LOTAsset : NSObject - -- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary - withAssetGroup:(LOTAssetGroup * _Nullable)assetGroup - withAssetBundle:(NSBundle *_Nonnull)bundle - withFramerate:(NSNumber *)framerate; - -@property (nonatomic, readonly, nullable) NSString *referenceID; -@property (nonatomic, readonly, nullable) NSNumber *assetWidth; -@property (nonatomic, readonly, nullable) NSNumber *assetHeight; - -@property (nonatomic, readonly, nullable) NSString *imageName; -@property (nonatomic, readonly, nullable) NSString *imageDirectory; - -@property (nonatomic, readonly, nullable) LOTLayerGroup *layerGroup; - -@property (nonatomic, readwrite) NSString *rootDirectory; -@property (nonatomic, readonly) NSBundle *assetBundle; -@end - -NS_ASSUME_NONNULL_END diff --git a/SDWebImageLottiePlugin/Private/LOTAssetGroup.h b/SDWebImageLottiePlugin/Private/LOTAssetGroup.h deleted file mode 100644 index dcae55a..0000000 --- a/SDWebImageLottiePlugin/Private/LOTAssetGroup.h +++ /dev/null @@ -1,28 +0,0 @@ -// -// LOTAssetGroup.h -// Pods -// -// Created by Brandon Withrow on 2/17/17. -// -// - -#import -#import - -@class LOTAsset; -@class LOTLayerGroup; -@interface LOTAssetGroup : NSObject -@property (nonatomic, readwrite) NSString * _Nullable rootDirectory; -@property (nonatomic, readonly, nullable) NSBundle *assetBundle; - -- (instancetype _Nonnull)initWithJSON:(NSArray * _Nonnull)jsonArray - withAssetBundle:(NSBundle *_Nullable)bundle - withFramerate:(NSNumber * _Nonnull)framerate; - -- (void)buildAssetNamed:(NSString * _Nonnull)refID withFramerate:(NSNumber * _Nonnull)framerate; - -- (void)finalizeInitializationWithFramerate:(NSNumber * _Nonnull)framerate; - -- (LOTAsset * _Nullable)assetModelForID:(NSString * _Nonnull)assetID; - -@end diff --git a/SDWebImageLottiePlugin/Private/LOTBezierData.h b/SDWebImageLottiePlugin/Private/LOTBezierData.h deleted file mode 100644 index 132d100..0000000 --- a/SDWebImageLottiePlugin/Private/LOTBezierData.h +++ /dev/null @@ -1,27 +0,0 @@ -// -// LOTBezierData.h -// Lottie -// -// Created by brandon_withrow on 7/10/17. -// Copyright © 2017 Airbnb. All rights reserved. -// - -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface LOTBezierData : NSObject - -- (instancetype)initWithData:(NSDictionary *)bezierData; - -@property (nonatomic, readonly) NSInteger count; -@property (nonatomic, readonly) BOOL closed; - -- (CGPoint)vertexAtIndex:(NSInteger)index; -- (CGPoint)inTangentAtIndex:(NSInteger)index; -- (CGPoint)outTangentAtIndex:(NSInteger)index; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SDWebImageLottiePlugin/Private/LOTCompositionContainer.h b/SDWebImageLottiePlugin/Private/LOTCompositionContainer.h deleted file mode 100644 index b52e0d6..0000000 --- a/SDWebImageLottiePlugin/Private/LOTCompositionContainer.h +++ /dev/null @@ -1,46 +0,0 @@ -// -// LOTCompositionContainer.h -// Lottie -// -// Created by brandon_withrow on 7/18/17. -// Copyright © 2017 Airbnb. All rights reserved. -// - -#import "LOTLayerContainer.h" -#import "LOTAssetGroup.h" - -@interface LOTCompositionContainer : LOTLayerContainer - -- (instancetype _Nonnull)initWithModel:(LOTLayer * _Nullable)layer - inLayerGroup:(LOTLayerGroup * _Nullable)layerGroup - withLayerGroup:(LOTLayerGroup * _Nullable)childLayerGroup - withAssestGroup:(LOTAssetGroup * _Nullable)assetGroup; - -- (nullable NSArray *)keysForKeyPath:(nonnull LOTKeypath *)keypath; - -- (CGPoint)convertPoint:(CGPoint)point - toKeypathLayer:(nonnull LOTKeypath *)keypath - withParentLayer:(CALayer *_Nonnull)parent; - -- (CGRect)convertRect:(CGRect)rect - toKeypathLayer:(nonnull LOTKeypath *)keypath - withParentLayer:(CALayer *_Nonnull)parent; - -- (CGPoint)convertPoint:(CGPoint)point - fromKeypathLayer:(nonnull LOTKeypath *)keypath - withParentLayer:(CALayer *_Nonnull)parent; - -- (CGRect)convertRect:(CGRect)rect - fromKeypathLayer:(nonnull LOTKeypath *)keypath - withParentLayer:(CALayer *_Nonnull)parent; - -- (void)addSublayer:(nonnull CALayer *)subLayer - toKeypathLayer:(nonnull LOTKeypath *)keypath; - -- (void)maskSublayer:(nonnull CALayer *)subLayer - toKeypathLayer:(nonnull LOTKeypath *)keypath; - -@property (nonatomic, readonly, nonnull) NSArray *childLayers; -@property (nonatomic, readonly, nonnull) NSDictionary *childMap; - -@end diff --git a/SDWebImageLottiePlugin/Private/LOTKeyframe.h b/SDWebImageLottiePlugin/Private/LOTKeyframe.h deleted file mode 100644 index c2e3778..0000000 --- a/SDWebImageLottiePlugin/Private/LOTKeyframe.h +++ /dev/null @@ -1,49 +0,0 @@ -// -// LOTKeyframe.h -// Pods -// -// Created by brandon_withrow on 7/10/17. -// -// - -#import -#import -#import "LOTPlatformCompat.h" -#import "LOTBezierData.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface LOTKeyframe : NSObject - -- (instancetype)initWithKeyframe:(NSDictionary *)keyframe; -- (instancetype)initWithValue:(id)value; -- (void)remapValueWithBlock:(CGFloat (^)(CGFloat inValue))remapBlock; -- (LOTKeyframe *)copyWithData:(id)data; - -@property (nonatomic, readonly) NSNumber *keyframeTime; -@property (nonatomic, readonly) BOOL isHold; -@property (nonatomic, readonly) CGPoint inTangent; -@property (nonatomic, readonly) CGPoint outTangent; -@property (nonatomic, readonly) CGPoint spatialInTangent; -@property (nonatomic, readonly) CGPoint spatialOutTangent; - -@property (nonatomic, readonly) CGFloat floatValue; -@property (nonatomic, readonly) CGPoint pointValue; -@property (nonatomic, readonly) CGSize sizeValue; -@property (nonatomic, readonly) UIColor *colorValue; -@property (nonatomic, readonly, nullable) LOTBezierData *pathData; -@property (nonatomic, readonly) NSArray *arrayValue; - -@end - -@interface LOTKeyframeGroup : NSObject - -- (instancetype)initWithData:(id)data; - -- (void)remapKeyframesWithBlock:(CGFloat (^)(CGFloat inValue))remapBlock; - -@property (nonatomic, readonly) NSArray *keyframes; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SDWebImageLottiePlugin/Private/LOTLayer.h b/SDWebImageLottiePlugin/Private/LOTLayer.h deleted file mode 100644 index aedb84b..0000000 --- a/SDWebImageLottiePlugin/Private/LOTLayer.h +++ /dev/null @@ -1,76 +0,0 @@ -// -// LOTLayer.h -// LottieAnimator -// -// Created by Brandon Withrow on 12/14/15. -// Copyright © 2015 Brandon Withrow. All rights reserved. -// - -#import -#import "LOTPlatformCompat.h" -#import "LOTKeyframe.h" - -@class LOTShapeGroup; -@class LOTMask; -@class LOTAsset; -@class LOTAssetGroup; - -typedef enum : NSInteger { - LOTLayerTypePrecomp, - LOTLayerTypeSolid, - LOTLayerTypeImage, - LOTLayerTypeNull, - LOTLayerTypeShape, - LOTLayerTypeUnknown -} LOTLayerType; - -typedef enum : NSInteger { - LOTMatteTypeNone, - LOTMatteTypeAdd, - LOTMatteTypeInvert, - LOTMatteTypeUnknown -} LOTMatteType; - -NS_ASSUME_NONNULL_BEGIN - -@interface LOTLayer : NSObject - -- (instancetype)initWithJSON:(NSDictionary *)jsonDictionary - withAssetGroup:(LOTAssetGroup * _Nullable)assetGroup - withFramerate:(NSNumber *)framerate; - -@property (nonatomic, readonly) NSString *layerName; -@property (nonatomic, readonly, nullable) NSString *referenceID; -@property (nonatomic, readonly) NSNumber *layerID; -@property (nonatomic, readonly) LOTLayerType layerType; -@property (nonatomic, readonly, nullable) NSNumber *parentID; -@property (nonatomic, readonly) NSNumber *startFrame; -@property (nonatomic, readonly) NSNumber *inFrame; -@property (nonatomic, readonly) NSNumber *outFrame; -@property (nonatomic, readonly) NSNumber *timeStretch; -@property (nonatomic, readonly) CGRect layerBounds; - -@property (nonatomic, readonly, nullable) NSArray *shapes; -@property (nonatomic, readonly, nullable) NSArray *masks; - -@property (nonatomic, readonly, nullable) NSNumber *layerWidth; -@property (nonatomic, readonly, nullable) NSNumber *layerHeight; -@property (nonatomic, readonly, nullable) UIColor *solidColor; -@property (nonatomic, readonly, nullable) LOTAsset *imageAsset; - -@property (nonatomic, readonly) LOTKeyframeGroup *opacity; -@property (nonatomic, readonly, nullable) LOTKeyframeGroup *timeRemapping; -@property (nonatomic, readonly) LOTKeyframeGroup *rotation; -@property (nonatomic, readonly, nullable) LOTKeyframeGroup *position; - -@property (nonatomic, readonly, nullable) LOTKeyframeGroup *positionX; -@property (nonatomic, readonly, nullable) LOTKeyframeGroup *positionY; - -@property (nonatomic, readonly) LOTKeyframeGroup *anchor; -@property (nonatomic, readonly) LOTKeyframeGroup *scale; - -@property (nonatomic, readonly) LOTMatteType matteType; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SDWebImageLottiePlugin/Private/LOTLayerContainer.h b/SDWebImageLottiePlugin/Private/LOTLayerContainer.h deleted file mode 100644 index d5b0409..0000000 --- a/SDWebImageLottiePlugin/Private/LOTLayerContainer.h +++ /dev/null @@ -1,37 +0,0 @@ -// -// LOTLayerContainer.h -// Lottie -// -// Created by brandon_withrow on 7/18/17. -// Copyright © 2017 Airbnb. All rights reserved. -// - -#import "LOTPlatformCompat.h" -#import "LOTLayer.h" -#import "LOTLayerGroup.h" -#import -#import - -@class LOTValueCallback; - -@interface LOTLayerContainer : CALayer - -- (instancetype _Nonnull)initWithModel:(LOTLayer * _Nullable)layer - inLayerGroup:(LOTLayerGroup * _Nullable)layerGroup; - -@property (nonatomic, readonly, strong, nullable) NSString *layerName; -@property (nonatomic, nullable) NSNumber *currentFrame; -@property (nonatomic, readonly, nonnull) NSNumber *timeStretchFactor; -@property (nonatomic, assign) CGRect viewportBounds; -@property (nonatomic, readonly, nonnull) CALayer *wrapperLayer; -@property (nonatomic, readonly, nonnull) NSDictionary *valueInterpolators; - -- (void)displayWithFrame:(NSNumber * _Nonnull)frame; -- (void)displayWithFrame:(NSNumber * _Nonnull)frame forceUpdate:(BOOL)forceUpdate; - -- (void)searchNodesForKeypath:(LOTKeypath * _Nonnull)keypath; - -- (void)setValueDelegate:(id _Nonnull)delegate - forKeypath:(LOTKeypath * _Nonnull)keypath; - -@end diff --git a/SDWebImageLottiePlugin/Private/LOTLayerGroup.h b/SDWebImageLottiePlugin/Private/LOTLayerGroup.h deleted file mode 100644 index f6952b5..0000000 --- a/SDWebImageLottiePlugin/Private/LOTLayerGroup.h +++ /dev/null @@ -1,30 +0,0 @@ -// -// LOTLayerGroup.h -// Pods -// -// Created by Brandon Withrow on 2/16/17. -// -// - -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@class LOTLayer; -@class LOTAssetGroup; - -@interface LOTLayerGroup : NSObject - -- (instancetype)initWithLayerJSON:(NSArray *)layersJSON - withAssetGroup:(LOTAssetGroup * _Nullable)assetGroup - withFramerate:(NSNumber *)framerate; - -@property (nonatomic, readonly) NSArray *layers; - -- (LOTLayer *)layerModelForID:(NSNumber *)layerID; -- (LOTLayer *)layerForReferenceID:(NSString *)referenceID; - -@end - -NS_ASSUME_NONNULL_END diff --git a/SDWebImageLottiePlugin/Private/LOTPlatformCompat.h b/SDWebImageLottiePlugin/Private/LOTPlatformCompat.h deleted file mode 100644 index eea9184..0000000 --- a/SDWebImageLottiePlugin/Private/LOTPlatformCompat.h +++ /dev/null @@ -1,37 +0,0 @@ -// -// LOTPlatformCompat.h -// Lottie -// -// Created by Oleksii Pavlovskyi on 2/2/17. -// Copyright (c) 2017 Airbnb. All rights reserved. -// - -#ifndef LOTPlatformCompat_h -#define LOTPlatformCompat_h - -#include - -#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR - -#import - -#else - -#import -#import "UIColor.h" -#import "CALayer+Compat.h" -#import "NSValue+Compat.h" -#import "UIBezierPath.h" - -NS_INLINE NSString *NSStringFromCGRect(CGRect rect) { - return NSStringFromRect(rect); -} - -NS_INLINE NSString *NSStringFromCGPoint(CGPoint point) { - return NSStringFromPoint(point); -} - -typedef NSEdgeInsets UIEdgeInsets; - -#endif -#endif From 97bf55590f0cc0b7733134a0843b94400f41fa7e Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Fri, 8 Jul 2022 22:59:59 +0800 Subject: [PATCH 2/6] Added test case for frame extracting from LottieImage --- Example/Tests/Tests.swift | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/Example/Tests/Tests.swift b/Example/Tests/Tests.swift index 97d9dad..3975b9a 100644 --- a/Example/Tests/Tests.swift +++ b/Example/Tests/Tests.swift @@ -65,4 +65,25 @@ class Tests: XCTestCase { self.waitForExpectations(timeout: 5, handler: nil) } + func testLottieImageExtractFrame() { + let exception = self.expectation(description: "LottieImage extract frame") + let lottieUrl = URL(string: "https://raw.githubusercontent.com/airbnb/lottie-web/master/demo/gatin/data.json")! + let task = URLSession.shared.dataTask(with: lottieUrl) { data, _, _ in + if let data = data, let animation = try? JSONDecoder().decode(Animation.self, from: data) { + let lottieImage = LottieImage(animation: animation) + let frameCount = lottieImage?.animatedImageFrameCount ?? 0 + XCTAssertEqual(frameCount, 80) + let posterFrame = lottieImage?.animatedImageFrame(at: 0) + let lastFrame = lottieImage?.animatedImageFrame(at: frameCount - 1) + XCTAssertNotNil(posterFrame) + XCTAssertNotNil(lastFrame) + XCTAssertNotEqual(posterFrame, lastFrame) + exception.fulfill() + } else { + XCTFail() + } + } + task.resume() + self.waitForExpectations(timeout: 5, handler: nil) + } } From 4a4cd8af72da51ec42deaae2d11a278415823dc2 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Fri, 8 Jul 2022 23:01:43 +0800 Subject: [PATCH 3/6] Remove the conflict extension API --- .../SDWebImageLottiePlugin Example macOS/AppDelegate.swift | 2 ++ .../ViewController.swift | 1 + Example/SDWebImageLottiePlugin/AppDelegate.swift | 1 + .../Classes/CompatibleAnimationView+WebCache.swift | 5 ----- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Example/SDWebImageLottiePlugin Example macOS/AppDelegate.swift b/Example/SDWebImageLottiePlugin Example macOS/AppDelegate.swift index d489f77..657b4a9 100644 --- a/Example/SDWebImageLottiePlugin Example macOS/AppDelegate.swift +++ b/Example/SDWebImageLottiePlugin Example macOS/AppDelegate.swift @@ -7,6 +7,7 @@ */ import Cocoa +import Lottie @NSApplicationMain class AppDelegate: NSObject, NSApplicationDelegate { @@ -15,6 +16,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { func applicationDidFinishLaunching(_ aNotification: Notification) { // Insert code here to initialize your application + LottieConfiguration.shared.renderingEngine = .coreAnimation } func applicationWillTerminate(_ aNotification: Notification) { diff --git a/Example/SDWebImageLottiePlugin Example macOS/ViewController.swift b/Example/SDWebImageLottiePlugin Example macOS/ViewController.swift index ed60c40..b4f39c8 100644 --- a/Example/SDWebImageLottiePlugin Example macOS/ViewController.swift +++ b/Example/SDWebImageLottiePlugin Example macOS/ViewController.swift @@ -9,6 +9,7 @@ import Cocoa import Lottie import SDWebImageLottiePlugin +import SDWebImage class ViewController: NSViewController { diff --git a/Example/SDWebImageLottiePlugin/AppDelegate.swift b/Example/SDWebImageLottiePlugin/AppDelegate.swift index 9b1fbbe..c76878e 100644 --- a/Example/SDWebImageLottiePlugin/AppDelegate.swift +++ b/Example/SDWebImageLottiePlugin/AppDelegate.swift @@ -16,6 +16,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { + LottieConfiguration.shared.renderingEngine = .coreAnimation // Override point for customization after application launch. return true } diff --git a/SDWebImageLottiePlugin/Classes/CompatibleAnimationView+WebCache.swift b/SDWebImageLottiePlugin/Classes/CompatibleAnimationView+WebCache.swift index b6581ac..5e6f4da 100644 --- a/SDWebImageLottiePlugin/Classes/CompatibleAnimationView+WebCache.swift +++ b/SDWebImageLottiePlugin/Classes/CompatibleAnimationView+WebCache.swift @@ -17,11 +17,6 @@ extension CompatibleAnimationView { self.subviews.first as! AnimationView } - @objc - public var isAnimationPlaying: Bool { - return animationView.isAnimationPlaying - } - @objc public var animationDuration: TimeInterval { return animationView.animation?.duration ?? -1; From 9887d2ab1107b6f2219f71752e7551de34f493d2 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Sat, 9 Jul 2022 00:16:33 +0800 Subject: [PATCH 4/6] Fix the imageProvider in frame extracting --- Example/Tests/Tests.swift | 29 +++++++++- README.md | 24 +++++++- .../Classes/LottieCompositionLayer.swift | 57 +++++++++++++++---- .../Classes/LottieImage.swift | 15 ++++- 4 files changed, 105 insertions(+), 20 deletions(-) diff --git a/Example/Tests/Tests.swift b/Example/Tests/Tests.swift index 3975b9a..d850b49 100644 --- a/Example/Tests/Tests.swift +++ b/Example/Tests/Tests.swift @@ -65,16 +65,39 @@ class Tests: XCTestCase { self.waitForExpectations(timeout: 5, handler: nil) } + func testLottieImageWithBundle() throws { + let bundle = Bundle(for: type(of: self)) + let fileURL = bundle.url(forResource: "Assets", withExtension: "json")! + let lottieData = try Data(contentsOf: fileURL) + let animation = try JSONDecoder().decode(Animation.self, from: lottieData) + let animationView = AnimationView(animation: animation, imageProvider: BundleImageProvider(bundle: bundle, searchPath: nil)) + let renderer = SDGraphicsImageRenderer(size: animation.size) + let viewImage = renderer.image { context in + animationView.drawHierarchy(in: CGRect(origin: CGPoint(x: 0, y: 0), size: animation.size), afterScreenUpdates: true) + } + // Pick the color to check + let color1 = try XCTUnwrap(viewImage.sd_color(at: CGPoint(x: 150, y: 150))) + XCTAssertEqual(color1.toHexString(), "#00d1c1"); + + let lottieImage = LottieImage(animation: animation) + lottieImage.imageProvider = BundleImageProvider(bundle: bundle, searchPath: nil) + let posterFrame = try XCTUnwrap(lottieImage.animatedImageFrame(at: 0)) + // Pick the color to check + let color2 = try XCTUnwrap(posterFrame.sd_color(at: CGPoint(x: 150, y: 150))) + XCTAssertEqual(color2.toHexString(), "#00d1c1"); + } + + func testLottieImageExtractFrame() { let exception = self.expectation(description: "LottieImage extract frame") let lottieUrl = URL(string: "https://raw.githubusercontent.com/airbnb/lottie-web/master/demo/gatin/data.json")! let task = URLSession.shared.dataTask(with: lottieUrl) { data, _, _ in if let data = data, let animation = try? JSONDecoder().decode(Animation.self, from: data) { let lottieImage = LottieImage(animation: animation) - let frameCount = lottieImage?.animatedImageFrameCount ?? 0 + let frameCount = lottieImage.animatedImageFrameCount XCTAssertEqual(frameCount, 80) - let posterFrame = lottieImage?.animatedImageFrame(at: 0) - let lastFrame = lottieImage?.animatedImageFrame(at: frameCount - 1) + let posterFrame = lottieImage.animatedImageFrame(at: 0) + let lastFrame = lottieImage.animatedImageFrame(at: frameCount - 1) XCTAssertNotNil(posterFrame) XCTAssertNotNil(lastFrame) XCTAssertNotEqual(posterFrame, lastFrame) diff --git a/README.md b/README.md index 13a5e00..e778e68 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ github "SDWebImage/SDWebImageLottiePlugin" Lottie 3.4 version's new `Lottie.RenderingEngine = .coreAnimation` solve the huge performance regression in the issue [here](https://github.com/airbnb/lottie-ios/issues/895) 🚀 -So from SDWebImageLottiePlugin v1.0.0, we drop the Lottie 2 support, as well as the Objective-C support because Lottie 3 use the pure Swift. +So from SDWebImageLottiePlugin v1.0.0, we drop the Lottie 2 support, as well as the Objective-C support because Lottie 3 use pure Swift. And therefore, we drop the iOS 9-10 support because the upstream dependency need iOS 11+. For user who still use Lottie 2 and Objective-C, please check the 0.x version updated to [0.3.0](https://github.com/SDWebImage/SDWebImageLottiePlugin/releases/tag/0.3.0) @@ -59,9 +59,25 @@ animationView.sd_setImage(with: lottieJSONURL) ``` Note: -+ You can also load lottie json files on `LOTAnimatedControl`, like switch button. ++ You can also load lottie json files on `AnimatedControl`, like switch button. + Lottie animation does not start automatically, you can use the completion block, or UITableView/UICollectionView's will display timing to play. -+ If your Lottie json files contains references to App bundle images, you can use `SDWebImageContextLottieBundle` context option to pass the NSBundle object to load it. + +```swift +animationView.sd_setImage(with: lottieUrl, completed: { _,_,_,_ in + self.animationView.play(fromProgress: 0, toProgress: 1, loopMode: .repeat(5)) { finished in + // ... + } +} +``` + + ++ If your Lottie json files contains references to App bundle images, just set the `imageProvider` before the lottie animation start. + +```swift +let bundle = Bundle(for: MyBundleClass.self) +animationView.imageProvider = BundleImageProvider(bundle: bundle, searchPath: nil) +animationView.sd_setImage(with: lottieUrl) +``` ### Advanced usage @@ -72,6 +88,8 @@ This Lottie plugin use a wrapper class `LottieImage` because of SDWebImage's [cu ```swift let animation = try? JSONDecoder().decode(Animation.self, from: data) let animatedImage = LottieImage(animation: animation) +// Optional, custom image bundle +LottieImage.imageProvider = BundleImageProvider(bundle: bundle, searchPath: nil) // Snapshot Lottie animation frame UIImage *posterFrame = animatedImage.animatedImageFrame(at: 0) TimeInterval duration = animatedImage.animatedImageDuration(at: 0) diff --git a/SDWebImageLottiePlugin/Classes/LottieCompositionLayer.swift b/SDWebImageLottiePlugin/Classes/LottieCompositionLayer.swift index 394535a..de11a21 100644 --- a/SDWebImageLottiePlugin/Classes/LottieCompositionLayer.swift +++ b/SDWebImageLottiePlugin/Classes/LottieCompositionLayer.swift @@ -9,29 +9,64 @@ import SDWebImage @testable import Lottie - -/// This layer is used if as Lottie 2's`LOTCompositionContainer`. -/// Which can render the specify frame into CGBitmapContext, used for debugging or some snapshot use case -class LottieCompositionLayer : CALayer { - var animationLayers: [CompositionLayer]? - init(animation: Animation) { - let layerImageProvider = LayerImageProvider(imageProvider: BundleImageProvider(bundle: .main, searchPath: nil).cachedImageProvider, assets: animation.assetLibrary?.imageAssets) - let layers = animation.layers.initializeCompositionLayers(assetLibrary: animation.assetLibrary, layerImageProvider: layerImageProvider, textProvider: DefaultTextProvider(), fontProvider: DefaultFontProvider(), frameRate: CGFloat(animation.framerate)) +/// This layer is used like Lottie 2's`LOTCompositionContainer`. Typically private, use with caution :) +/// Which can render the specify frame into CGBitmapContext by calling `layer.render(in:)`, used for debugging or some snapshot use case +public class LottieCompositionLayer : CALayer { + + /// All composition sublayers, in hierarchy (reverse order) unlike `sublayers` + private var animationLayers: [CompositionLayer] = [] + + /// Initializes an LottieCompositionLayer with an animation. + /// - Parameters: + /// - animation: animation + /// - imageProvider: imageProvider + /// - textProvider: textProvider + /// - fontProvider: fontProvider + public init(animation: Animation, + imageProvider: AnimationImageProvider? = nil, + textProvider: AnimationTextProvider? = nil, + fontProvider: AnimationFontProvider? = nil) { + // Steal from MainThreadAnimationLayer.swift + let layerImageProvider = LayerImageProvider(imageProvider: (imageProvider ?? BundleImageProvider(bundle: .main, searchPath: nil)).cachedImageProvider, assets: animation.assetLibrary?.imageAssets) + let layerTextProvider = LayerTextProvider(textProvider: textProvider ?? DefaultTextProvider()) + let layerFontProvider = LayerFontProvider(fontProvider: fontProvider ?? DefaultFontProvider()) + + let layers = animation.layers.initializeCompositionLayers(assetLibrary: animation.assetLibrary, layerImageProvider: layerImageProvider, textProvider: layerTextProvider.textProvider, fontProvider: layerFontProvider.fontProvider, frameRate: CGFloat(animation.framerate)) super.init() bounds = animation.bounds + var imageLayers = [ImageCompositionLayer]() + var textLayers = [TextCompositionLayer]() + for layer in layers.reversed() { layer.bounds = bounds + animationLayers.append(layer) + if let imageLayer = layer as? ImageCompositionLayer { + imageLayers.append(imageLayer) + } + if let textLayer = layer as? TextCompositionLayer { + textLayers.append(textLayer) + } addSublayer(layer) } - animationLayers = layers + + layerImageProvider.addImageLayers(imageLayers) + layerImageProvider.reloadImages() + layerTextProvider.addTextLayers(textLayers) + layerTextProvider.reloadTexts() + layerFontProvider.addTextLayers(textLayers) + layerFontProvider.reloadTexts() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - func displayWithFrame(frame: CGFloat, forceUpdates: Bool) { - animationLayers?.forEach { $0.displayWithFrame(frame: frame, forceUpdates: forceUpdates) } + /// Display the layer tree to specify frame time, then call `render(in:)` to draw on CGBitmapContext + /// - Parameters: + /// - frame: The AnimationFrameTime(Seconds * Framerate) + /// - forceUpdates: Whether to force update the tree node, true need time but more accurate rendering result + public func displayWithFrame(frame: CGFloat, forceUpdates: Bool) { + animationLayers.forEach { $0.displayWithFrame(frame: frame, forceUpdates: forceUpdates) } } } diff --git a/SDWebImageLottiePlugin/Classes/LottieImage.swift b/SDWebImageLottiePlugin/Classes/LottieImage.swift index c8f9d27..f7f02a6 100644 --- a/SDWebImageLottiePlugin/Classes/LottieImage.swift +++ b/SDWebImageLottiePlugin/Classes/LottieImage.swift @@ -32,9 +32,18 @@ public class LottieImage : PlatformImage, SDAnimatedImageProtocol { /// The lottie animation model public var animation: Animation? + /// The lottie image provider, used for frame extracting + public var imageProvider: AnimationImageProvider? + + /// The lottie text provider, used for frame extracting + public var textProvider: AnimationTextProvider? + + /// The lottie font provider, used for frame extracting + public var fontProvider: AnimationFontProvider? + /// Init the LottieImage with lottie animation model /// - Parameter animation: animation - public required init?(animation: Animation) { + public required init(animation: Animation) { #if os(iOS) || os(tvOS) super.init() #else @@ -127,7 +136,7 @@ public class LottieImage : PlatformImage, SDAnimatedImageProtocol { guard let animation = animation else { return nil } - let compositionLayer = LottieCompositionLayer(animation: animation) + let compositionLayer = LottieCompositionLayer(animation: animation, imageProvider: imageProvider, textProvider: textProvider, fontProvider: fontProvider) return compositionLayer }() @@ -146,7 +155,7 @@ public class LottieImage : PlatformImage, SDAnimatedImageProtocol { return image } - // MARK: - SDAnimatedImageProvider + // MARK: - SDAnimatedImageProvider && Frame Extracting public private(set) var animatedImageData: Data? public var animatedImageFrameCount: UInt { From 85190a32054473e623711bdc617871237bad3c79 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Sat, 9 Jul 2022 01:28:18 +0800 Subject: [PATCH 5/6] Remove CompatibleAnimationView extension, which is un-usable at all (upstream bug) Seems a lie that you can use Lottie 3 from Objc --- .../CompatibleAnimationView+WebCache.swift | 82 ------------------- 1 file changed, 82 deletions(-) delete mode 100644 SDWebImageLottiePlugin/Classes/CompatibleAnimationView+WebCache.swift diff --git a/SDWebImageLottiePlugin/Classes/CompatibleAnimationView+WebCache.swift b/SDWebImageLottiePlugin/Classes/CompatibleAnimationView+WebCache.swift deleted file mode 100644 index 5e6f4da..0000000 --- a/SDWebImageLottiePlugin/Classes/CompatibleAnimationView+WebCache.swift +++ /dev/null @@ -1,82 +0,0 @@ -/* -* This file is part of the SDWebImage package. -* (c) DreamPiggy -* -* For the full copyright and license information, please view the LICENSE -* file that was distributed with this source code. -*/ - -import SDWebImage -import Lottie - -#if os(iOS) || os(tvOS) -/// These code is from https://github.com/airbnb/lottie-ios/pull/1123/files -extension CompatibleAnimationView { - @objc - public var animationView: AnimationView { - self.subviews.first as! AnimationView - } - - @objc - public var animationDuration: TimeInterval { - return animationView.animation?.duration ?? -1; - } - - @objc - public func setFloatValue(_ value: CGFloat, forKeypath keypath: CompatibleAnimationKeypath) { - - let valueProvider = FloatValueProvider(value) - animationView.setValueProvider(valueProvider, keypath: keypath.animationKeypath) - } - - @objc - public func getFloatValue(for keypath: CompatibleAnimationKeypath, atFrame: CGFloat) -> NSNumber? { - let value = animationView.getValue(for: keypath.animationKeypath, atFrame: atFrame) - return value as? NSNumber - } - - @objc - public override func sizeThatFits(_ size: CGSize) -> CGSize { - return animationView.intrinsicContentSize - } - - @objc - public override var intrinsicContentSize: CGSize { - get { return animationView.intrinsicContentSize } - } -} - -extension CompatibleAnimationView { - /** - * Set the imageView `image` with an `url`, placeholder, custom options and context. - * - * The download is asynchronous and cached. - * - * @param url The url for the image. - * @param placeholder The image to be set initially, until the image request finishes. - * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. - * @param context A context contains different options to perform specify changes or processes, see `SDWebImageContextOption`. This hold the extra objects which `options` enum can not hold. - * @param progressBlock A block called while image is downloading - * @note the progress block is executed on a background queue - * @param completedBlock A block called when operation has been completed. This block has no return value - * and takes the requested UIImage as first parameter. In case of error the image parameter - * is nil and the second parameter may contain an NSError. The third parameter is a Boolean - * indicating if the image was retrieved from the local cache or from the network. - * The fourth parameter is the original image url. - */ - @objc - public func sd_setImage(with url: URL?, placeholderImage placeholder: UIImage? = nil, options: SDWebImageOptions = [], context: [SDWebImageContextOption : Any]? = nil, progress progressBlock: SDImageLoaderProgressBlock? = nil, completed completedBlock: SDExternalCompletionBlock? = nil) { - var context = context ?? [:] - context[.animatedImageClass] = LottieImage.self - self.sd_internalSetImage(with: url, placeholderImage: placeholder, options: options, context: context, setImageBlock: { [weak self] (image, data, cacheType, url) in - if let lottieImage = image as? LottieImage { - self?.animationView.animation = lottieImage.animation - } else { - self?.animationView.animation = nil - } - }, progress: progressBlock) { (image, data, error, cacheType, finiseh, url) in - completedBlock?(image, error, cacheType, url) - } - } -} -#endif From bd3f5b64ad9e3fcaa4c44c8313dd41f0aba44ddf Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Sat, 9 Jul 2022 01:35:31 +0800 Subject: [PATCH 6/6] Try fixing Carthage, though not work on macOS (upstream bug) --- README.md | 21 +- .../project.pbxproj | 186 ++++-------------- 2 files changed, 60 insertions(+), 147 deletions(-) diff --git a/README.md b/README.md index e778e68..d0f9295 100644 --- a/README.md +++ b/README.md @@ -21,23 +21,38 @@ You can find more resource about Lottie in their [Official Site](https://airbnb. ## Installation +#### Swift Package Manager + +SDWebImageWebPCoder is available through [Swift Package Manager](https://swift.org/package-manager). + +```swift +let package = Package( + dependencies: [ + .package(url: "https://github.com/SDWebImage/SDWebImageLottiePlugin.git", from: "1.0.0") + ] +) +``` + #### CocoaPods SDWebImageLottiePlugin is available through [CocoaPods](https://cocoapods.org). To install it, simply add the following line to your Podfile: ```ruby -pod 'SDWebImageLottiePlugin' +pod 'SDWebImageLottiePlugin', '~> 1.0' ``` -#### Carthage +#### Carthage (Deprecated) SDWebImageLottiePlugin is available through [Carthage](https://github.com/Carthage/Carthage). ``` -github "SDWebImage/SDWebImageLottiePlugin" +github "SDWebImage/SDWebImageLottiePlugin", ~> 1.0 ``` +Note: +1. Carthage macOS integration contains issue, because the module name is `Lottie_macOS` but not `Lottie`, wait the issue [here](https://github.com/airbnb/lottie-ios/issues/1638) been fixed 👀 + ## Lottie 2 && Objective-C Lottie 3.4 version's new `Lottie.RenderingEngine = .coreAnimation` solve the huge performance regression in the issue [here](https://github.com/airbnb/lottie-ios/issues/895) 🚀 diff --git a/SDWebImageLottiePlugin.xcodeproj/project.pbxproj b/SDWebImageLottiePlugin.xcodeproj/project.pbxproj index e39e8ab..c2bede3 100644 --- a/SDWebImageLottiePlugin.xcodeproj/project.pbxproj +++ b/SDWebImageLottiePlugin.xcodeproj/project.pbxproj @@ -7,65 +7,28 @@ objects = { /* Begin PBXBuildFile section */ - 3249A4F2240A54FB00AB1888 /* SDWebImageLottiePlugin.h in Headers */ = {isa = PBXBuildFile; fileRef = 3249A4F0240A54FB00AB1888 /* SDWebImageLottiePlugin.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3249A527240A58C800AB1888 /* SDWebImage.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3249A526240A58C800AB1888 /* SDWebImage.framework */; }; 3249A52B240A58D100AB1888 /* Lottie.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3249A52A240A58D100AB1888 /* Lottie.framework */; }; 3249A52E240A58E000AB1888 /* SDWebImage.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3249A52D240A58E000AB1888 /* SDWebImage.framework */; }; 3249A532240A58EA00AB1888 /* Lottie.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3249A531240A58EA00AB1888 /* Lottie.framework */; }; 3249A535240A58FB00AB1888 /* SDWebImage.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3249A534240A58FB00AB1888 /* SDWebImage.framework */; }; 3249A539240A590600AB1888 /* Lottie.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3249A538240A590600AB1888 /* Lottie.framework */; }; - 32A1472D240B59BB003CB0C5 /* LOTAnimationView+WebCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A1472B240B59BB003CB0C5 /* LOTAnimationView+WebCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 32A1472E240B59BB003CB0C5 /* LOTAnimationView+WebCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A1472B240B59BB003CB0C5 /* LOTAnimationView+WebCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 32A1472F240B59BB003CB0C5 /* LOTAnimationView+WebCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A1472B240B59BB003CB0C5 /* LOTAnimationView+WebCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 32A14730240B59BB003CB0C5 /* LOTAnimationView+WebCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 32A1472C240B59BB003CB0C5 /* LOTAnimationView+WebCache.m */; }; - 32A14731240B59BB003CB0C5 /* LOTAnimationView+WebCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 32A1472C240B59BB003CB0C5 /* LOTAnimationView+WebCache.m */; }; - 32A14732240B59BB003CB0C5 /* LOTAnimationView+WebCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 32A1472C240B59BB003CB0C5 /* LOTAnimationView+WebCache.m */; }; - 32A14735240B5A2F003CB0C5 /* LOTAnimatedControl+WebCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A14733240B5A2F003CB0C5 /* LOTAnimatedControl+WebCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 32A14736240B5A2F003CB0C5 /* LOTAnimatedControl+WebCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A14733240B5A2F003CB0C5 /* LOTAnimatedControl+WebCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 32A14737240B5A2F003CB0C5 /* LOTAnimatedControl+WebCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A14733240B5A2F003CB0C5 /* LOTAnimatedControl+WebCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 32A14738240B5A2F003CB0C5 /* LOTAnimatedControl+WebCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 32A14734240B5A2F003CB0C5 /* LOTAnimatedControl+WebCache.m */; }; - 32A14739240B5A2F003CB0C5 /* LOTAnimatedControl+WebCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 32A14734240B5A2F003CB0C5 /* LOTAnimatedControl+WebCache.m */; }; - 32A1473A240B5A2F003CB0C5 /* LOTAnimatedControl+WebCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 32A14734240B5A2F003CB0C5 /* LOTAnimatedControl+WebCache.m */; }; - 32A1473D240B5A98003CB0C5 /* LOTAnimatedImage.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A1473B240B5A98003CB0C5 /* LOTAnimatedImage.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 32A1473E240B5A98003CB0C5 /* LOTAnimatedImage.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A1473B240B5A98003CB0C5 /* LOTAnimatedImage.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 32A1473F240B5A98003CB0C5 /* LOTAnimatedImage.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A1473B240B5A98003CB0C5 /* LOTAnimatedImage.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 32A14740240B5A98003CB0C5 /* LOTAnimatedImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 32A1473C240B5A98003CB0C5 /* LOTAnimatedImage.m */; }; - 32A14741240B5A98003CB0C5 /* LOTAnimatedImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 32A1473C240B5A98003CB0C5 /* LOTAnimatedImage.m */; }; - 32A14742240B5A98003CB0C5 /* LOTAnimatedImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 32A1473C240B5A98003CB0C5 /* LOTAnimatedImage.m */; }; - 32A14746240B72EE003CB0C5 /* LOTCompositionContainer.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A14744240B72EE003CB0C5 /* LOTCompositionContainer.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 32A14747240B72EE003CB0C5 /* LOTCompositionContainer.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A14744240B72EE003CB0C5 /* LOTCompositionContainer.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 32A14748240B72EE003CB0C5 /* LOTCompositionContainer.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A14744240B72EE003CB0C5 /* LOTCompositionContainer.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 32A14749240B72EE003CB0C5 /* LOTLayerContainer.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A14745240B72EE003CB0C5 /* LOTLayerContainer.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 32A1474A240B72EE003CB0C5 /* LOTLayerContainer.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A14745240B72EE003CB0C5 /* LOTLayerContainer.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 32A1474B240B72EE003CB0C5 /* LOTLayerContainer.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A14745240B72EE003CB0C5 /* LOTLayerContainer.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 32A1474C240B7305003CB0C5 /* SDWebImageLottiePlugin.h in Headers */ = {isa = PBXBuildFile; fileRef = 3249A4F0240A54FB00AB1888 /* SDWebImageLottiePlugin.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 32A1474D240B7305003CB0C5 /* SDWebImageLottiePlugin.h in Headers */ = {isa = PBXBuildFile; fileRef = 3249A4F0240A54FB00AB1888 /* SDWebImageLottiePlugin.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 32A14750240B7392003CB0C5 /* LOTLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A1474E240B7392003CB0C5 /* LOTLayer.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 32A14751240B7392003CB0C5 /* LOTLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A1474E240B7392003CB0C5 /* LOTLayer.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 32A14752240B7392003CB0C5 /* LOTLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A1474E240B7392003CB0C5 /* LOTLayer.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 32A14753240B7392003CB0C5 /* LOTPlatformCompat.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A1474F240B7392003CB0C5 /* LOTPlatformCompat.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 32A14754240B7392003CB0C5 /* LOTPlatformCompat.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A1474F240B7392003CB0C5 /* LOTPlatformCompat.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 32A14755240B7392003CB0C5 /* LOTPlatformCompat.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A1474F240B7392003CB0C5 /* LOTPlatformCompat.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 32A14758240B73E0003CB0C5 /* LOTKeyframe.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A14756240B73E0003CB0C5 /* LOTKeyframe.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 32A14759240B73E0003CB0C5 /* LOTKeyframe.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A14756240B73E0003CB0C5 /* LOTKeyframe.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 32A1475A240B73E0003CB0C5 /* LOTKeyframe.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A14756240B73E0003CB0C5 /* LOTKeyframe.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 32A1475B240B73E0003CB0C5 /* LOTBezierData.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A14757240B73E0003CB0C5 /* LOTBezierData.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 32A1475C240B73E0003CB0C5 /* LOTBezierData.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A14757240B73E0003CB0C5 /* LOTBezierData.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 32A1475D240B73E0003CB0C5 /* LOTBezierData.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A14757240B73E0003CB0C5 /* LOTBezierData.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 32A1475F240B73FF003CB0C5 /* LOTLayerGroup.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A1475E240B73FF003CB0C5 /* LOTLayerGroup.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 32A14760240B73FF003CB0C5 /* LOTLayerGroup.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A1475E240B73FF003CB0C5 /* LOTLayerGroup.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 32A14761240B73FF003CB0C5 /* LOTLayerGroup.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A1475E240B73FF003CB0C5 /* LOTLayerGroup.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 32A14764240B7497003CB0C5 /* LOTAsset.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A14762240B7497003CB0C5 /* LOTAsset.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 32A14765240B7497003CB0C5 /* LOTAsset.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A14762240B7497003CB0C5 /* LOTAsset.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 32A14766240B7497003CB0C5 /* LOTAsset.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A14762240B7497003CB0C5 /* LOTAsset.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 32A14767240B7497003CB0C5 /* LOTAssetGroup.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A14763240B7497003CB0C5 /* LOTAssetGroup.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 32A14768240B7497003CB0C5 /* LOTAssetGroup.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A14763240B7497003CB0C5 /* LOTAssetGroup.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 32A14769240B7497003CB0C5 /* LOTAssetGroup.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A14763240B7497003CB0C5 /* LOTAssetGroup.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 32FBF6922878941700A0FA93 /* LottieImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32FBF68C2878941700A0FA93 /* LottieImage.swift */; }; + 32FBF6932878941700A0FA93 /* LottieImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32FBF68C2878941700A0FA93 /* LottieImage.swift */; }; + 32FBF6942878941700A0FA93 /* LottieImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32FBF68C2878941700A0FA93 /* LottieImage.swift */; }; + 32FBF6982878941700A0FA93 /* AnimationView+WebCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32FBF68E2878941700A0FA93 /* AnimationView+WebCache.swift */; }; + 32FBF6992878941700A0FA93 /* AnimationView+WebCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32FBF68E2878941700A0FA93 /* AnimationView+WebCache.swift */; }; + 32FBF69A2878941700A0FA93 /* AnimationView+WebCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32FBF68E2878941700A0FA93 /* AnimationView+WebCache.swift */; }; + 32FBF69E2878941700A0FA93 /* AnimatedControl+WebCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32FBF6902878941700A0FA93 /* AnimatedControl+WebCache.swift */; }; + 32FBF69F2878941700A0FA93 /* AnimatedControl+WebCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32FBF6902878941700A0FA93 /* AnimatedControl+WebCache.swift */; }; + 32FBF6A02878941700A0FA93 /* AnimatedControl+WebCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32FBF6902878941700A0FA93 /* AnimatedControl+WebCache.swift */; }; + 32FBF6A12878941700A0FA93 /* LottieCompositionLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32FBF6912878941700A0FA93 /* LottieCompositionLayer.swift */; }; + 32FBF6A22878941700A0FA93 /* LottieCompositionLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32FBF6912878941700A0FA93 /* LottieCompositionLayer.swift */; }; + 32FBF6A32878941700A0FA93 /* LottieCompositionLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32FBF6912878941700A0FA93 /* LottieCompositionLayer.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ 3249A4ED240A54FB00AB1888 /* SDWebImageLottiePlugin.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SDWebImageLottiePlugin.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 3249A4F0240A54FB00AB1888 /* SDWebImageLottiePlugin.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDWebImageLottiePlugin.h; path = Module/SDWebImageLottiePlugin.h; sourceTree = ""; }; 3249A4F1240A54FB00AB1888 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Module/Info.plist; sourceTree = ""; }; 3249A508240A562B00AB1888 /* SDWebImageLottiePlugin.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SDWebImageLottiePlugin.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3249A515240A563500AB1888 /* SDWebImageLottiePlugin.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SDWebImageLottiePlugin.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -75,21 +38,10 @@ 3249A531240A58EA00AB1888 /* Lottie.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Lottie.framework; path = Carthage/Build/tvOS/Lottie.framework; sourceTree = ""; }; 3249A534240A58FB00AB1888 /* SDWebImage.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SDWebImage.framework; path = Carthage/Build/Mac/SDWebImage.framework; sourceTree = ""; }; 3249A538240A590600AB1888 /* Lottie.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Lottie.framework; path = Carthage/Build/Mac/Lottie.framework; sourceTree = ""; }; - 32A1472B240B59BB003CB0C5 /* LOTAnimationView+WebCache.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "LOTAnimationView+WebCache.h"; sourceTree = ""; }; - 32A1472C240B59BB003CB0C5 /* LOTAnimationView+WebCache.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "LOTAnimationView+WebCache.m"; sourceTree = ""; }; - 32A14733240B5A2F003CB0C5 /* LOTAnimatedControl+WebCache.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "LOTAnimatedControl+WebCache.h"; sourceTree = ""; }; - 32A14734240B5A2F003CB0C5 /* LOTAnimatedControl+WebCache.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "LOTAnimatedControl+WebCache.m"; sourceTree = ""; }; - 32A1473B240B5A98003CB0C5 /* LOTAnimatedImage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LOTAnimatedImage.h; sourceTree = ""; }; - 32A1473C240B5A98003CB0C5 /* LOTAnimatedImage.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LOTAnimatedImage.m; sourceTree = ""; }; - 32A14744240B72EE003CB0C5 /* LOTCompositionContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LOTCompositionContainer.h; sourceTree = ""; }; - 32A14745240B72EE003CB0C5 /* LOTLayerContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LOTLayerContainer.h; sourceTree = ""; }; - 32A1474E240B7392003CB0C5 /* LOTLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LOTLayer.h; sourceTree = ""; }; - 32A1474F240B7392003CB0C5 /* LOTPlatformCompat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LOTPlatformCompat.h; sourceTree = ""; }; - 32A14756240B73E0003CB0C5 /* LOTKeyframe.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LOTKeyframe.h; sourceTree = ""; }; - 32A14757240B73E0003CB0C5 /* LOTBezierData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LOTBezierData.h; sourceTree = ""; }; - 32A1475E240B73FF003CB0C5 /* LOTLayerGroup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LOTLayerGroup.h; sourceTree = ""; }; - 32A14762240B7497003CB0C5 /* LOTAsset.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LOTAsset.h; sourceTree = ""; }; - 32A14763240B7497003CB0C5 /* LOTAssetGroup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LOTAssetGroup.h; sourceTree = ""; }; + 32FBF68C2878941700A0FA93 /* LottieImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LottieImage.swift; sourceTree = ""; }; + 32FBF68E2878941700A0FA93 /* AnimationView+WebCache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AnimationView+WebCache.swift"; sourceTree = ""; }; + 32FBF6902878941700A0FA93 /* AnimatedControl+WebCache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AnimatedControl+WebCache.swift"; sourceTree = ""; }; + 32FBF6912878941700A0FA93 /* LottieCompositionLayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LottieCompositionLayer.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -145,27 +97,12 @@ 3249A4EF240A54FB00AB1888 /* SDWebImageLottiePlugin */ = { isa = PBXGroup; children = ( - 32A14743240B72EE003CB0C5 /* Private */, - 3249A4F8240A554300AB1888 /* Classes */, - 3249A4F0240A54FB00AB1888 /* SDWebImageLottiePlugin.h */, + 32FBF68B2878941700A0FA93 /* Classes */, 3249A4F1240A54FB00AB1888 /* Info.plist */, ); path = SDWebImageLottiePlugin; sourceTree = ""; }; - 3249A4F8240A554300AB1888 /* Classes */ = { - isa = PBXGroup; - children = ( - 32A1472B240B59BB003CB0C5 /* LOTAnimationView+WebCache.h */, - 32A1472C240B59BB003CB0C5 /* LOTAnimationView+WebCache.m */, - 32A14733240B5A2F003CB0C5 /* LOTAnimatedControl+WebCache.h */, - 32A14734240B5A2F003CB0C5 /* LOTAnimatedControl+WebCache.m */, - 32A1473B240B5A98003CB0C5 /* LOTAnimatedImage.h */, - 32A1473C240B5A98003CB0C5 /* LOTAnimatedImage.m */, - ); - path = Classes; - sourceTree = ""; - }; 3249A525240A58C800AB1888 /* Frameworks */ = { isa = PBXGroup; children = ( @@ -179,20 +116,15 @@ name = Frameworks; sourceTree = ""; }; - 32A14743240B72EE003CB0C5 /* Private */ = { + 32FBF68B2878941700A0FA93 /* Classes */ = { isa = PBXGroup; children = ( - 32A14762240B7497003CB0C5 /* LOTAsset.h */, - 32A14763240B7497003CB0C5 /* LOTAssetGroup.h */, - 32A14757240B73E0003CB0C5 /* LOTBezierData.h */, - 32A14744240B72EE003CB0C5 /* LOTCompositionContainer.h */, - 32A14756240B73E0003CB0C5 /* LOTKeyframe.h */, - 32A1474E240B7392003CB0C5 /* LOTLayer.h */, - 32A14745240B72EE003CB0C5 /* LOTLayerContainer.h */, - 32A1475E240B73FF003CB0C5 /* LOTLayerGroup.h */, - 32A1474F240B7392003CB0C5 /* LOTPlatformCompat.h */, - ); - path = Private; + 32FBF68C2878941700A0FA93 /* LottieImage.swift */, + 32FBF68E2878941700A0FA93 /* AnimationView+WebCache.swift */, + 32FBF6902878941700A0FA93 /* AnimatedControl+WebCache.swift */, + 32FBF6912878941700A0FA93 /* LottieCompositionLayer.swift */, + ); + path = Classes; sourceTree = ""; }; /* End PBXGroup section */ @@ -202,19 +134,6 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - 32A14735240B5A2F003CB0C5 /* LOTAnimatedControl+WebCache.h in Headers */, - 32A1475F240B73FF003CB0C5 /* LOTLayerGroup.h in Headers */, - 32A1475B240B73E0003CB0C5 /* LOTBezierData.h in Headers */, - 32A14764240B7497003CB0C5 /* LOTAsset.h in Headers */, - 32A14753240B7392003CB0C5 /* LOTPlatformCompat.h in Headers */, - 32A1473D240B5A98003CB0C5 /* LOTAnimatedImage.h in Headers */, - 32A14767240B7497003CB0C5 /* LOTAssetGroup.h in Headers */, - 32A14746240B72EE003CB0C5 /* LOTCompositionContainer.h in Headers */, - 32A14750240B7392003CB0C5 /* LOTLayer.h in Headers */, - 32A1472D240B59BB003CB0C5 /* LOTAnimationView+WebCache.h in Headers */, - 32A14749240B72EE003CB0C5 /* LOTLayerContainer.h in Headers */, - 3249A4F2240A54FB00AB1888 /* SDWebImageLottiePlugin.h in Headers */, - 32A14758240B73E0003CB0C5 /* LOTKeyframe.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -222,19 +141,6 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - 32A1474C240B7305003CB0C5 /* SDWebImageLottiePlugin.h in Headers */, - 32A14760240B73FF003CB0C5 /* LOTLayerGroup.h in Headers */, - 32A1475C240B73E0003CB0C5 /* LOTBezierData.h in Headers */, - 32A14765240B7497003CB0C5 /* LOTAsset.h in Headers */, - 32A14754240B7392003CB0C5 /* LOTPlatformCompat.h in Headers */, - 32A1472E240B59BB003CB0C5 /* LOTAnimationView+WebCache.h in Headers */, - 32A14768240B7497003CB0C5 /* LOTAssetGroup.h in Headers */, - 32A14747240B72EE003CB0C5 /* LOTCompositionContainer.h in Headers */, - 32A14751240B7392003CB0C5 /* LOTLayer.h in Headers */, - 32A14736240B5A2F003CB0C5 /* LOTAnimatedControl+WebCache.h in Headers */, - 32A1474A240B72EE003CB0C5 /* LOTLayerContainer.h in Headers */, - 32A1473E240B5A98003CB0C5 /* LOTAnimatedImage.h in Headers */, - 32A14759240B73E0003CB0C5 /* LOTKeyframe.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -242,19 +148,6 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - 32A1474D240B7305003CB0C5 /* SDWebImageLottiePlugin.h in Headers */, - 32A14761240B73FF003CB0C5 /* LOTLayerGroup.h in Headers */, - 32A1475D240B73E0003CB0C5 /* LOTBezierData.h in Headers */, - 32A14766240B7497003CB0C5 /* LOTAsset.h in Headers */, - 32A14755240B7392003CB0C5 /* LOTPlatformCompat.h in Headers */, - 32A1472F240B59BB003CB0C5 /* LOTAnimationView+WebCache.h in Headers */, - 32A14769240B7497003CB0C5 /* LOTAssetGroup.h in Headers */, - 32A14748240B72EE003CB0C5 /* LOTCompositionContainer.h in Headers */, - 32A14752240B7392003CB0C5 /* LOTLayer.h in Headers */, - 32A14737240B5A2F003CB0C5 /* LOTAnimatedControl+WebCache.h in Headers */, - 32A1474B240B72EE003CB0C5 /* LOTLayerContainer.h in Headers */, - 32A1473F240B5A98003CB0C5 /* LOTAnimatedImage.h in Headers */, - 32A1475A240B73E0003CB0C5 /* LOTKeyframe.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -384,9 +277,10 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 32A14740240B5A98003CB0C5 /* LOTAnimatedImage.m in Sources */, - 32A14738240B5A2F003CB0C5 /* LOTAnimatedControl+WebCache.m in Sources */, - 32A14730240B59BB003CB0C5 /* LOTAnimationView+WebCache.m in Sources */, + 32FBF6922878941700A0FA93 /* LottieImage.swift in Sources */, + 32FBF69E2878941700A0FA93 /* AnimatedControl+WebCache.swift in Sources */, + 32FBF6A12878941700A0FA93 /* LottieCompositionLayer.swift in Sources */, + 32FBF6982878941700A0FA93 /* AnimationView+WebCache.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -394,9 +288,10 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 32A14741240B5A98003CB0C5 /* LOTAnimatedImage.m in Sources */, - 32A14739240B5A2F003CB0C5 /* LOTAnimatedControl+WebCache.m in Sources */, - 32A14731240B59BB003CB0C5 /* LOTAnimationView+WebCache.m in Sources */, + 32FBF6932878941700A0FA93 /* LottieImage.swift in Sources */, + 32FBF69F2878941700A0FA93 /* AnimatedControl+WebCache.swift in Sources */, + 32FBF6A22878941700A0FA93 /* LottieCompositionLayer.swift in Sources */, + 32FBF6992878941700A0FA93 /* AnimationView+WebCache.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -404,9 +299,10 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 32A14742240B5A98003CB0C5 /* LOTAnimatedImage.m in Sources */, - 32A1473A240B5A2F003CB0C5 /* LOTAnimatedControl+WebCache.m in Sources */, - 32A14732240B59BB003CB0C5 /* LOTAnimationView+WebCache.m in Sources */, + 32FBF6942878941700A0FA93 /* LottieImage.swift in Sources */, + 32FBF6A02878941700A0FA93 /* AnimatedControl+WebCache.swift in Sources */, + 32FBF6A32878941700A0FA93 /* LottieCompositionLayer.swift in Sources */, + 32FBF69A2878941700A0FA93 /* AnimationView+WebCache.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -464,15 +360,16 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MACOSX_DEPLOYMENT_TARGET = 10.11; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; + OTHER_SWIFT_FLAGS = "-Xfrontend -disable-testable-attr-requires-testable-module"; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - TVOS_DEPLOYMENT_TARGET = 9.0; + TVOS_DEPLOYMENT_TARGET = 11.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; WATCHOS_DEPLOYMENT_TARGET = 2.0; @@ -524,14 +421,15 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MACOSX_DEPLOYMENT_TARGET = 10.11; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; + OTHER_SWIFT_FLAGS = "-Xfrontend -disable-testable-attr-requires-testable-module"; SDKROOT = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; - TVOS_DEPLOYMENT_TARGET = 9.0; + TVOS_DEPLOYMENT_TARGET = 11.0; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = "";