Hot questions for Using Lottie in swift

Question:

Whether the view I'm creating is a LOTAnimatedSwitch or View, the image of the animation always appears very small. The lottie animation doesn't take up the size of the view that I create. Is this an issue with downloading the animation from LottieFiles? The dimensions of the file are 600x600 pixels. I'm using Lottie version 2.5.0 and Swift 4. For example:

    let animatedSwitch = LOTAnimatedSwitch(named: "toggle_switch")
    animatedSwitch.frame.origin = CGPoint(x: 8, y: separatorLineView.frame.height + separatorLineView.frame.origin.y + 8)
    animatedSwitch.frame.size = CGSize(width: dialogViewWidth - 16, height: 40)
    animatedSwitch.setProgressRangeForOnState(fromProgress: 0.5, toProgress: 1)
    animatedSwitch.setProgressRangeForOffState(fromProgress: 0, toProgress: 0.5)
    animatedSwitch.contentMode = .scaleAspectFill
    animatedSwitch.clipsToBounds = true
    animatedSwitch.backgroundColor = .purple

Answer:

Try this code i am not sure this will help in your case

let animatedSwitch = LOTAnimatedSwitch(named: "toggle_switch")
animatedSwitch.frame = CGRect(x: 0, y: 0, width: 200, height: 200)
animatedSwitch.center = self.view.center
animatedSwitch.setProgressRangeForOnState(fromProgress: 0.5, toProgress: 1)
animatedSwitch.setProgressRangeForOffState(fromProgress: 0, toProgress: 0.5)
self.view.addSubview(animatedSwitch)
self.view.backgroundColor = UIColor.lightGray

Question:

I'm using Lottie (animation framework) and one of the delegates I'm trying to use is expecting me to return an Unmanaged<CGColor>!

This is the definition:

color(forFrame currentFrame: CGFloat, startKeyframe: CGFloat, endKeyframe: CGFloat, interpolatedProgress: CGFloat, start startColor: CGColor!, end endColor: CGColor!, currentColor interpolatedColor: CGColor!) -> Unmanaged<CGColor>!

If I just try to return UIColor.white.cgColor I get an error that says

Cannot convert return expression of type 'CGColor' to return type 'Unmanaged<CGColor>!'

I already tried going through their documentation but their example only shows to use it like this:

let colorBlock = LOTColorBlockCallback { (currentFrame, startKeyFrame, endKeyFrame, interpolatedProgress, startColor, endColor, interpolatedColor) -> Unmanaged<CGColor> in
    return aColor
}

So how can I return the proper type?

Thanks


Answer:

You can create Unmanaged instance by calling passRetained or passUnretained static function like this:

Unmanaged.passRetained(UIColor.white.cgColor)

But keep in mind, that for future usage of this variable should be handled with takeRetainedValue if you decided to use passRetained() or with takeUnretainedValue() function if you will use passUnretained. If that would not be done - you would have memory leak or possible crash.

Question:

My goal is to replace the default spinner of the UIRefreshControl with a Lottie animation.

My issue is that the animation does not play immediately when I pull down my UICollectionView whose subview is the UIRefreshControl. The animation only plays when I scroll down slightly and pause my finger. The moment I again start moving the scrolling position it immediately goes back to its initial starting state not playing.

Any guidance would be appreciated, below is relevant code..

func setupRefreshControl() {
    guard let refreshContents = Bundle.main.loadNibNamed("RefreshControlView", owner: self, options: nil), let refreshView = refreshContents[0] as? RefreshControlView else { return }

    refreshView.frame = refreshControl.frame
    refreshControl.addSubview(refreshView)

    refreshControl.tintColor = UIColor.clear
    refreshControl.backgroundColor = UIColor.clear
    refreshControl.addTarget(self, action: #selector(exampleFunction), for: .valueChanged)      
}

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    for subview in refreshControl.subviews {
        if let refreshControlView = subview as? RefreshControlView {
            refreshControlView.activityIndicatorControl.animationView.setAnimation(named: "lottieJson")                
            refreshControlView.activityIndicatorControl.animationView.loopAnimation = true
            refreshControlView.activityIndicatorControl.animationView.play()
            break
        } else {
            continue
        }
    }
}

Answer:

You don't see the animation playing while you are scrolling because every time scrollViewDidScroll is called you restart the animation by calling play. And because that function is called almost constantly while you scroll the animation does not have a chance to play more than its first frame. So it looks paused.

When implementing a custom Refresh Control you have to implement 3 phases:

1. User scrolls but the refresh has not been triggered yet

In this phase you probably want to show a progress in your animation depending on how far the user has scrolled. To do this you calculate the progress in scrollViewDidScroll and pass it to the LOTAnimationView's animationProgress property.

To calculate this progress you have to know how far the user has to scroll down until the refresh is triggered. In my experience this happens at a contentOffset.y of approximately 150.

2. Refresh is triggered

When the user has scrolled down enough a .valueChanged ControlEvent is triggered. When this happens you start the looping animation by calling play() on the LOTAnimationView

3. Refresh is done

When the refreshing is completed you call endRefreshing() on your custom Refresh Control. When this happens you stop the animation by calling stop() on the LOTAnimationView

Check out this small example of a LottieRefreshControl that I use in one of my projects:

import UIKit
import Lottie

class LottieRefreshControl: UIRefreshControl {
    fileprivate let animationView = LOTAnimationView(name: "refresh")
    fileprivate var isAnimating = false

    fileprivate let maxPullDistance: CGFloat = 150

    override init() {
        super.init(frame: .zero)
        setupView()
        setupLayout()
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func updateProgress(with offsetY: CGFloat) {
        guard !isAnimating else { return }
        let progress = min(abs(offsetY / maxPullDistance), 1)
        animationView.animationProgress = progress
    }

    override func beginRefreshing() {
        super.beginRefreshing()
        isAnimating = true
        animationView.animationProgress = 0
        animationView.play()
    }

    override func endRefreshing() {
        super.endRefreshing()
        animationView.stop()
        isAnimating = false
    }
}

private extension LottieRefreshControl {
    func setupView() {
        // hide default indicator view
        tintColor = .clear
        animationView.loopAnimation = true
        addSubview(animationView)

        addTarget(self, action: #selector(beginRefreshing), for: .valueChanged)
    }

    func setupLayout() {
        animationView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            animationView.centerXAnchor.constraint(equalTo: centerXAnchor),
            animationView.centerYAnchor.constraint(equalTo: centerYAnchor),
            animationView.widthAnchor.constraint(equalToConstant: 50),
            animationView.heightAnchor.constraint(equalToConstant: 32)
        ])
    }
}

Now you only have to call updateProgress on the Refresh Control from scrollViewDidScroll:

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    refreshControl.updateProgress(with: scrollView.contentOffset.y)
}

Question:

I'm trying to use Lottie animations inside my ios app but I can't get them to work. I've seen people use LOTAnimationViews but I can't import or use this class. I've added an Animation folder with my animations in.

ViewController:

class CardStackViewController: UIViewController, CLLocationManagerDelegate, NetworkManagerDelegate {
    ...
    private let animationView = AnimationView()
    ...

    ...
    func didFail(with error: Error) {
        if let animation = Animation.named("error", subdirectory: "Animations") {
            animationView.animation = animation
            animationView.loopMode = .loop
            view.addSubview(animationView)
            animationView.play()
        }
        print("---DIDFAIL WITH ERROR---", error.localizedDescription, error)
    }
    ...
}

Answer:

Above

import lottie

and give the view a frame

animationView.animation = animation
animationView.frame = ///

Question:

Am try to set lottie animation in Navigation bar button of UInavigation controller and i don't know how to set animation to UIbarButtons


Answer:

Just make use of UIBarButtonItem(customView: yourView)

//Initialise a Lottie view with frame
let customAnimationView = AnimationView(name: "yourLottieFileName")
customAnimationView.frame = CGRect(x: 0, y: 0, width: 30, height: 30)

//Do your configurations
customAnimationView.loopMode = .loop
customAnimationView.backgroundBehavior = .pauseAndRestore

//Add a tap gesture
customAnimationView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(yourViewTapped(_:)))) //Declare an @objc function to handle your view's tap 
//And play
customAnimationView.play()

//Initilise a bar button with this custom Lottie view and use it
yourBarButton = UIBarButtonItem(customView: customAnimationView)
navigationItem.setLeftBarButton(yourBarButton, animated: true)

Tested this code and it works fine for me.

Question:

Im using Lottie animation in my app and im trying to keep the animation running in the background when i exit the app and open it again (not force close) ..

I was able to do so successfully, but the problem is the animation stops when i select different tab bar item and go back to the tab bar item that has the animation view.

this is my code.

import UIKit
import Lottie
import UserNotifications
import NotificationCenter

class HomeViewController: UIViewController {

    @IBOutlet weak var animationView: UIView!
    var animation : AnimationView?

    override func viewDidLoad() {
        super.viewDidLoad()
        setupAnimation()
        NotificationCenter.default.addObserver(self, selector: #selector(applicationEnterInForground), name: UIApplication.willEnterForegroundNotification, object: nil)
    }

    func setupAnimation() {
        animation = AnimationView(name: "cong")
        animation?.frame = self.animationView.bounds
        self.animationView.addSubview(animation!)
        animation?.loopMode = .loop
        animation?.contentMode = .scaleAspectFit
        animation?.play()
    }

    @objc func applicationEnterInForground() {
        if animation != nil {
            if !(self.animation?.isAnimationPlaying)! {self.animation?.play()}}
    }
}

Answer:

Better, use viewWillAppear/viewDidAppear to start the animation again and remove observing the willEnterForegroundNotification.

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    if self.animation?.isAnimationPlaying == false {
       self.animation?.play()
    }
}

Question:

I'm making a simple app testing Lottie animation library in swift. The code is perfectly running when i lunch the app for the first time. But if i relaunch it from the background, the animation stops.

How do i keep the animation running in the background so when i switch to another app and back again, i see the animation working.

I just started learning swift, so i apologize in advance.

import UIKit
import Lottie

class ViewController: UIViewController {
    @IBOutlet weak var animationView: AnimationView!

    override func viewDidLoad() {
        super.viewDidLoad()
        SetupAnimation()
    }

func SetupAnimation() {
        let animationView = AnimationView(name: "kiss")
        animationView.frame = self.animationView.frame
        self.animationView.addSubview(animationView)
        animationView.loopMode = .autoReverse
        animationView.contentMode = .scaleAspectFit
        animationView.play()
   }
}

Answer:

There are one simple thing which will help you to play your animation.

Add following notification of willEnterForegroundNotification in your view controller where you have added animation.

Another thing, Declare Lottie's AnimationView's instance globally. See following code

class MyClassVC: UIViewController {

    @IBOutlet weak var animationView : UIView!
    var animation : AnimationView?

    func setupAnimation() {
        animation = AnimationView(name: "kiss")
        animation?.frame = self.animationView.bounds
        self.animationView.addSubview(animation!)
        animation?.loopMode = .autoReverse
        animation?.contentMode = .scaleAspectFit
        animation?.play()
    }

    @objc func applicationEnterInForground() {
        if animation != nil {
            if !(self.animation?.isAnimationPlaying)! {
                self.animation?.play()
            }
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        self.setupAnimation()
        NotificationCenter.default.addObserver(self, selector: #selector(applicationEnterInForground), name: UIApplication.willEnterForegroundNotification, object: nil)
    }
}

This will play animation when application come in foreground from background.