85 lines
2.9 KiB
Swift
85 lines
2.9 KiB
Swift
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
|