import SwiftUI import Lottie /// 仅播放一次并在完成时回调的 Lottie 视图 struct BlindBoxLottieOnceView: UIViewRepresentable { let name: String var animationSpeed: CGFloat = 1.0 let onCompleted: () -> Void func makeUIView(context: Context) -> UIView { // 与通用 LottieView 一致:用容器承载并通过约束填充,避免尺寸偏差 let container = UIView() container.clipsToBounds = true let animationView = LottieAnimationView() animationView.translatesAutoresizingMaskIntoConstraints = false if let animation = LottieAnimation.named(name) { animationView.animation = animation } else if let path = Bundle.main.path(forResource: name, ofType: "json") { let animation = LottieAnimation.filepath(path) animationView.animation = animation } animationView.loopMode = .playOnce animationView.animationSpeed = animationSpeed animationView.contentMode = .scaleAspectFit animationView.backgroundBehavior = .pauseAndRestore container.addSubview(animationView) NSLayoutConstraint.activate([ animationView.leadingAnchor.constraint(equalTo: container.leadingAnchor), animationView.trailingAnchor.constraint(equalTo: container.trailingAnchor), animationView.topAnchor.constraint(equalTo: container.topAnchor), animationView.bottomAnchor.constraint(equalTo: container.bottomAnchor) ]) // 开始播放一次并在完成后回调 animationView.play { _ in onCompleted() } return container } func updateUIView(_ uiView: UIView, context: Context) { // 单次播放,不需要在更新时重复触发 } } #if DEBUG struct BlindBoxLottieOnceView_Previews: PreviewProvider { static var previews: some View { Group { VStack(spacing: 16) { Text("Opening • x1.0") .font(.caption) .foregroundColor(.gray) BlindBoxLottieOnceView(name: "opening", animationSpeed: 1.0) { print("Opening completed") } .frame(width: 300, height: 300) .background(Color.themeTextWhiteSecondary) } .previewDisplayName("Opening • 1.0x") VStack(spacing: 16) { Text("Opening • x1.5") .font(.caption) .foregroundColor(.gray) BlindBoxLottieOnceView(name: "opening", animationSpeed: 1.5) { print("Opening completed (1.5x)") } .frame(width: 300, height: 300) .background(Color.themeTextWhiteSecondary) } .previewDisplayName("Opening • 1.5x") } .padding() .background(Color.themeTextWhiteSecondary) } } #endif