Hot questions for Using Lottie in swiftui

Question:

I'm trying to implement an animationView using Lottie & SwiftUI.

this is my code :

import SwiftUI
import Lottie

struct ContentView: View {
var body: some View {

    AnimationsView()
    }
}

struct ContentView_Previews: PreviewProvider {
static var previews: some View {
    ContentView()
}
}
struct AnimationsView : UIViewRepresentable {

func makeUIView(context: UIViewRepresentableContext<AnimationsView>) -> 
AnimationView {
   let aniView = AnimationView()
    let animation = Animation.named("Switch", subdirectory: "TestAnimations")
    aniView.animation = animation
    aniView.play()
    return aniView
    }
func updateUIView(_ uiView: AnimationView, context: 
UIViewRepresentableContext<AnimationsView>) {

    }
}

I've added the last version of Lottie as Swift Package dependencies. The preview in SwiftUI show me the animation and everything is OK at this state. I'm not using Storyboard it should open the View and the lottie animation inside.

When I run the app, The app crash and I have this message code : Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)

As I understand it there's something not initialized and the return value is null ... I'm trying to do the same thing as in this tutorial : https://www.youtube.com/watch?v=iuEqGyBYaE4

What's wrong?

thank you in advance


Answer:

This is a known issue. You can see here and here

There is a workaround by setting dead code stripping to no in your app's target

DEAD_CODE_STRIPPING = NO

Similarly you may need to also change how you access the lottie file from the bundle.

struct AnimationsView: UIViewRepresentable {

    func makeUIView(context: UIViewRepresentableContext<AnimationsView>) -> AnimationView {
        let aniView = AnimationView()
        // its always a good idea to check that the file exists and throw an error if it doesn't. 
        guard let animation = Animation.named("Switch", bundle: .main) else {
            fatalError("Couldn't load animation")
        }
        aniView.animation = animation
        aniView.loopMode = .repeat(10.0)
        return aniView
    }

    func updateUIView(_ uiView: AnimationsView.UIViewType, context: UIViewRepresentableContext<AnimationsView>) {

    }

}

Question:

today I'm trying to navigate between 2 different views but I want to use the same view cleaning the info from the previous view. I created a MotherView to invoke 2 different view. Here's my code

import SwiftUI

struct MotherView: View {
    @ObservedObject var viewRouter: ViewRouter

    var body: some View {

        VStack {
            if viewRouter.currentPage == "splash" {
                SplashView()
            } else{
                ContentView()
            }
        }
    }
}


struct MotherView_Previews : PreviewProvider {
    static var previews: some View {
        MotherView(viewRouter: ViewRouter())
    }
}

This is my SplashScreen:

    struct SplashView: View {
    @State private var isAnimated = true
    @EnvironmentObject var pageSettings: PageSettings

    var body: some View {
            VStack{

                LottieView(filename:"Logo")
                pageSettings.currentPage = "content"
            }
    }


}

NOTE SplashView() is a view with an animation using Lottie and ContentView() is a view with my login. I want to start my app using my SplashView() and then clean the VStack and run the ContentView().

Is That possible? Thank you!

This is the last update from this code (I got this error: "Argument type '()' does not conform to expected type 'View'")

    struct SplashView: View {
    @State private var animated = false
    @EnvironmentObject var pageSettings: PageSettings
    var body: some View {
        VStack{
            if self.animated{
                pageSettings.currentPage = "content"
            }else{
                LottieView()
            }
        }
    }
}

Answer:

I use a similar technic to show a login view and then the content view after the user logged in. Here's a way you can achieve that:

Create an ObservableObject

class pageSettings: ObservableObject {
    @Published var currentPage: String = "splash"
}

Modify your SceneDelegate

Add following line after var window: UIWindow? :

var pageSettings = PageSettings()

Then replace the call of UIHostingController:

window.rootViewController = UIHostingController(rootView: contentView)

by this call:

window.rootViewController = UIHostingController(rootView: contentView.environmentObject(pageSettings))

Use your ObservableObject

Inside your MotherView, add this property:

@EnvironmentObject var pageSettings: PageSettings

Replace your test by this:

if pageSettings.currentPage == "splash" 

Inside your SplashScreen

You also need to call the EnvironmentObject. You can then update the value of currentPage like this (it will instantly switch view):

pageSettings.currentPage = "content"

You can use your environment object to pass data between all your views.