Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI playing Lottie animation

I was playing with the Lottie animations, these days, but I have some trouble how to stop them from the View.

Here is my ContentView, where I am playing the animation. I have one button which shows the boolean value in real-time:

struct ContentView: View {

@State var isShowing : Bool = true

var body: some View {
    ZStack {
         Color.green.opacity(0.3)
        VStack {
            VStack {
                LottieDosi(isShowing: .constant(isShowing)){Text("")}
                Button(action: {self.isShowing.toggle()}) {
                    
                    Text("Button is \(String(isShowing))")
                    .padding(10)
                    .background(Color.white)
                    .cornerRadius(40)
                }
            }.padding(.horizontal, 30)
        }
    }.edgesIgnoringSafeArea(.all)
}
} 

and here is my LottieView:

struct LottieView: UIViewRepresentable {

 @Binding var isAnimating: Bool
 @State   var name  : String

 var loopMode: LottieLoopMode = .loop
 var animationView = AnimationView()

 func makeUIView(context: UIViewRepresentableContext<LottieView>) -> UIView {
   let view = UIView()

   animationView.animation = Animation.named(name)
   animationView.contentMode = .scaleAspectFill
   animationView.loopMode = .loop
   animationView.play()

   animationView.translatesAutoresizingMaskIntoConstraints = false
   view.addSubview(animationView)

   NSLayoutConstraint.activate([
       animationView.widthAnchor.constraint(equalTo: view.widthAnchor),
       animationView.heightAnchor.constraint(equalTo: view.heightAnchor)
   ])

return view
}

func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<LottieView>) {
    isAnimating ? animationView.play() : animationView.stop()
}
}

struct LottieDosi<Content>:  View where Content: View {

@Binding var isShowing: Bool
var content: () -> Content

var body: some View {
    
    VStack {

          LottieView(isAnimating: .constant(isShowing), name: "13728-sticker-4")
            .frame(width: 175, height: 175)
        
    }
}
}

I have a binding bool which has to change itself either to true or false, whenever I tap on the button. In other words, when I press the button I should be able to play and stop it. But somehow, this boolean value is not shared between the structs and the animation is constantly playing. I am new to swift UI, so I assume I am doing something very wrong, therefore I will appreciate some help.

Here you can see my animation

like image 553
Dakata Avatar asked Mar 02 '23 06:03

Dakata


1 Answers

You've almost got it. You need to construct a Coordinator so that you can access the values inside your LottieView. It's straight forward to do.

  1. Create a Coordinator class that conforms to NSObject
  2. Pass in your LottieView.
  3. Add the function makeCoordinator to your LottieView
  4. Create an instance of your Coordinator in the makeCoordinator function
  5. In your updateUIView access the animationView from the coordinator.

You shouldn't need a binding for the isAnimating as the LottieView is not passing any information back to your ContentView. You also don't need that @State for the name

Here is how I have gotten Lottie to play and pause in my apps. Note that some of the variable names are different to yours but it should be enough to get you to where you need to be.

import SwiftUI
import Lottie

struct LottieView: UIViewRepresentable {
    typealias UIViewType = UIView
    let filename: String
    let animationView = AnimationView()
    let isPaused: Bool

    func makeUIView(context: UIViewRepresentableContext<LottieView>) -> UIView {
        let view = UIView(frame: .zero)

        let animation = Animation.named(filename)
        animationView.animation = animation
        animationView.contentMode = .scaleAspectFit
        animationView.loopMode = .loop

        animationView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(animationView)

        NSLayoutConstraint.activate([
            animationView.widthAnchor.constraint(equalTo: view.widthAnchor),
            animationView.heightAnchor.constraint(equalTo: view.heightAnchor),
        ])

        return view
    }

    func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<LottieView>) {
        if isPaused {
            context.coordinator.parent.animationView.pause()
        } else {
            context.coordinator.parent.animationView.play()

        }
    }

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    class Coordinator: NSObject {
        var parent: LottieView

        init(_ parent: LottieView) {
            self.parent = parent
        }
    }
}

My ContentView looks like the following:

struct ContentView: View {

    @State private var isPaused: Bool = true

    var body: some View {
        VStack {
            LottieView(filename: "loading", isPaused: isPaused)
                .frame(width: 200, height: 200)
            Button(action: {
                self.isPaused.toggle()
            }, label: {
                Text(isPaused ? "Play" : "Pause")
            })
        }

    }
}

Here it is working:

Animation playing and pausing on button click

like image 89
Andrew Avatar answered Mar 11 '23 15:03

Andrew