You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI中Lottie动画随视图更新停止播放的问题求助

问题描述

我尝试在SwiftUI视图状态更新时播放Lottie动画。点击播放按钮可正常播放动画,但一旦引入会触发视图刷新的@State属性(比如示例中的value),动画就会停止播放。

原LottieView代码

import SwiftUI
import Lottie

struct LottieView: UIViewRepresentable {
    let animationName:String
    
    @Binding var play: Bool
    var animationView:LottieAnimationView
    
    init(animationName:String,
         play: Binding<Bool> = .constant(true)
    ) {
        self.animationName = animationName
        self.animationView = LottieAnimationView(name: animationName)
        self._play = play
    }
    class Coordinator: NSObject {
        @Binding var play: Bool
        let parent: LottieView
        init(parent: LottieView, play: Binding<Bool>) {
            self.parent = parent
            _play = play
        }  
    }
    
    func makeCoordinator() -> Coordinator {
        return Coordinator(parent: self, play: $play)
    }
    
    func makeUIView(context: Context) -> UIView {
        let view = UIView(frame: .zero)
        view.addSubview(animationView)
        animationView.translatesAutoresizingMaskIntoConstraints = false
        animationView.heightAnchor.constraint(equalTo: view.heightAnchor).isActive = true
        animationView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
        return view
    }
    
    func updateUIView(_ uiView: UIView, context: Context) {
        if play {
            animationView.play { _ in
                play.toggle()
            }
        }
    }
}

原ContentView代码

struct ContentView: View {
    @State private var play = false
    @State private var value = 0
    var body: some View {
        ZStack {
            VStack {
                Button("play") {
                    value += 1
                    play.toggle()
                }
                // 取消注释后动画失效
//                Text("The Value is \(value)")
                Rectangle()
                    .frame(width: 400, height: 400)
                    .foregroundColor(.clear)
                    .overlay {
                        LottieView(animationName: "confetti",
                                    play: $play)
                    }
            }
        }
    }
}
问题原因
  1. 动画实例被重复创建:LottieAnimationView在LottieView的init方法中初始化,当ContentView因value更新而刷新时,LottieView会被重新实例化,旧的动画实例被丢弃,导致动画中断。
  2. 播放逻辑重复触发:updateUIView中只要play为true就调用play(),视图刷新时play可能仍处于true状态,重复调用会重置动画,导致播放停止。
解决方案

修改LottieView,将LottieAnimationView的创建移至makeUIView方法,确保实例与UIKit视图生命周期绑定;同时优化播放逻辑,仅在play状态从false切换到true时触发播放。

修复后的LottieView代码

import SwiftUI
import Lottie

struct LottieView: UIViewRepresentable {
    let animationName: String
    @Binding var play: Bool
    
    init(animationName: String, play: Binding<Bool> = .constant(true)) {
        self.animationName = animationName
        self._play = play
    }
    
    class Coordinator: NSObject {
        @Binding var play: Bool
        weak var animationView: LottieAnimationView?
        
        init(play: Binding<Bool>) {
            self._play = play
            super.init()
        }
        
        func triggerAnimation() {
            guard let animationView = animationView, play, !animationView.isAnimationPlaying else { return }
            animationView.play { _ in
                play.toggle()
            }
        }
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(play: $play)
    }
    
    func makeUIView(context: Context) -> UIView {
        let view = UIView(frame: .zero)
        // 在makeUIView中创建动画实例,确保生命周期稳定
        let animationView = LottieAnimationView(name: animationName)
        view.addSubview(animationView)
        animationView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            animationView.heightAnchor.constraint(equalTo: view.heightAnchor),
            animationView.widthAnchor.constraint(equalTo: view.widthAnchor)
        ])
        context.coordinator.animationView = animationView
        return view
    }
    
    func updateUIView(_ uiView: UIView, context: Context) {
        // 仅当play状态切换为true且动画未播放时触发
        context.coordinator.triggerAnimation()
    }
}

修复后的ContentView代码

struct ContentView: View {
    @State private var play = false
    @State private var value = 0
    
    var body: some View {
        ZStack {
            VStack {
                Button("play") {
                    value += 1
                    play.toggle()
                }
                // 现在取消注释也不会影响动画播放
                Text("The Value is \(value)")
                Rectangle()
                    .frame(width: 400, height: 400)
                    .foregroundColor(.clear)
                    .overlay {
                        LottieView(animationName: "confetti", play: $play)
                    }
            }
        }
    }
}
关键改进点
  • 将LottieAnimationView的创建移至makeUIView,确保实例在UIKit视图生命周期内保持稳定,不会因SwiftUI视图刷新而被替换。
  • 通过Coordinator关联动画实例,避免在updateUIView中引用错误的实例。
  • 优化播放触发逻辑,仅在动画未播放且play为true时调用play(),防止重复触发导致的动画中断。

内容的提问来源于stack exchange,提问作者Stewart Lynch

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 13:41:23