Prevent updateUIView() from being called in UIRepresentableView?

666 views Asked by At

I think I need to change a boolean in a UIRepresentableView before removing it from the ContentView but can't find a way, since I can't mutate the struct properties.

Why do I need that? Because I need to prevent updateUIView() from being called one last time before the view being released.

So probably this is an XY problem... anyway, I'm lost.


I have a model like this:

class PlayerModel: NSObject, ObservableObject {

    @Published var lottie: [LottieView]

Where LottieView is this:

struct LottieView: UIViewRepresentable {
    var name: String
    var loopMode: LottieLoopMode = .playOnce
    
    func makeUIView(context: UIViewRepresentableContext<LottieView>) -> UIView {
        let view = UIView()
        return view
    }

    func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<LottieView>) {
        uiView.subviews.forEach({ sub in
            sub.removeFromSuperview()
        })
        
        let animationView = AnimationView()
        animationView.translatesAutoresizingMaskIntoConstraints = false
        uiView.addSubview(animationView)

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

        animationView.animation = Animation.named(name, animationCache: LRUAnimationCache.sharedCache)
        animationView.contentMode = .scaleAspectFit
        animationView.loopMode = loopMode
        animationView.play()
    }
}

In my ContentView I only display a LottieView if there's a recent one in the published array:

struct ContentView: View {

    @ObservedObject var model: PlayerModel
    
    var body: some View {
        NavigationView {
            VStack {
                // ...
                
                if let lottie = model.lottie.last {
                    lottie
                }
                
                // ...
            }
        }
    }

}

Now this works ok, but when in the model I remove the last LottieView from the published array, the updateUIView() func is called one last time by SwiftUI, resulting in the animation being recreated again just before the LottieView is removed from the view, resulting in a visible flicker.

So I thought I'd add a boolean that I could update just before removing the last LottieView from the published array, and do an if/else in the updateUIView(), but... is this even possible? I can't find a way.

Ideally I would just have the animation view created in makeUIView() and nothing done in updateUIView() but this is not possible, I need to remove the subviews first, otherwise the previous shown animation is not replaced by the new one.

1

There are 1 answers

0
Butterfly Ball On

While I would still love to have someone give me a proper solution to my issue, or tell me an alternative way of doing things, I found a workaround.

In the model, I was previously emptying the published array by doing

lottie = []

But if instead we do this:

lottie.removeAll()

We see no flicker anymore (however updateView() is still called!!).