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) } } } } }
问题原因
- 动画实例被重复创建:
LottieAnimationView在LottieView的init方法中初始化,当ContentView因value更新而刷新时,LottieView会被重新实例化,旧的动画实例被丢弃,导致动画中断。 - 播放逻辑重复触发:
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
相关产品推荐
相关产品推荐

