Skip to content

Instantly share code, notes, and snippets.

@yucelokan
Last active October 19, 2022 13:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yucelokan/01212dfbf7a188523fd74ae06fd09875 to your computer and use it in GitHub Desktop.
Save yucelokan/01212dfbf7a188523fd74ae06fd09875 to your computer and use it in GitHub Desktop.
A general hack to prevent Lottie animations from stopping playing after pushing/popping/presenting/dismissing.
// A general hack to prevent Lottie animations from stopping playing after pushing/popping/presenting/dismissing.
// Usage: LottieFinder.setupLottieFinder()
import UIKit
import Lottie
class LottieFinder {
static private var operationQueue = OperationQueue()
static private var operations: [Operation] = []
static func setupLottieFinder() {
UIViewController.subscribeLotties()
}
static func operation(from: UIViewController) {
operationQueue.cancelAllOperations()
operations.removeAll()
let operation = LottieFinderOperation(viewController: from)
operations.append(contentsOf: [operation])
operationQueue.addOperation(operation)
}
}
extension UIViewController {
@objc private dynamic func swizzledLottieViewWillAppear(_ animated: Bool) {
swizzledLottieViewWillAppear(animated)
LottieFinder.operation(from: self)
}
internal static func subscribeLotties() {
swizzleLottieViewWillAppear()
}
private static func swizzleLottieViewWillAppear() {
let selector1 = #selector(UIViewController.viewWillAppear(_:))
let selector2 = #selector(UIViewController.swizzledLottieViewWillAppear(_:))
let originalMethod = class_getInstanceMethod(UIViewController.self, selector1)!
let swizzleMethod = class_getInstanceMethod(UIViewController.self, selector2)!
method_exchangeImplementations(originalMethod, swizzleMethod)
}
}
class LottieFinderOperation: Operation {
init(viewController: UIViewController) {
self.viewController = viewController
}
weak private var viewController: UIViewController?
override func main() {
guard !isCancelled else { return }
DispatchQueue.main.async { [weak self] in
guard let `self` = self, let view = self.viewController?.view else { return }
for lottie in self.subviews(
in: view, ofType: AnimationView.self
) where lottie.superview != nil {
guard !self.isCancelled else { break }
lottie.play()
}
}
}
private func subviews<T: UIView>(in view: UIView, ofType whatType: T.Type) -> [T] {
var tViews: [T] = []
for subview in view.subviews {
guard !isCancelled else { break }
if let tView = subview as? T {
tViews.append(tView)
} else {
tViews.append(contentsOf: subviews(in: subview, ofType: whatType))
}
}
return isCancelled ? [] : tViews
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment