128 lines
3.7 KiB
Swift
128 lines
3.7 KiB
Swift
import SwiftUI
|
|
import Lottie
|
|
|
|
// MARK: - Animation Cache
|
|
|
|
final class LottieAnimationCache {
|
|
static let shared = LottieAnimationCache()
|
|
private var cache: [String: LottieAnimation] = [:]
|
|
private let lock = NSLock()
|
|
|
|
private init() {}
|
|
|
|
func animation(named name: String) -> LottieAnimation? {
|
|
lock.lock()
|
|
defer { lock.unlock() }
|
|
if let cached = cache[name] {
|
|
return cached
|
|
}
|
|
if let animation = LottieAnimation.named(name) {
|
|
cache[name] = animation
|
|
return animation
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func preload(_ names: [String]) {
|
|
for name in names {
|
|
_ = animation(named: name)
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - LottieView
|
|
|
|
struct LottieView: UIViewRepresentable, Equatable {
|
|
let animationName: String
|
|
var loopMode: LottieLoopMode = .playOnce
|
|
var animationSpeed: CGFloat = 1.5
|
|
var isPlaying: Bool = true
|
|
|
|
static func == (lhs: LottieView, rhs: LottieView) -> Bool {
|
|
lhs.animationName == rhs.animationName &&
|
|
lhs.loopMode == rhs.loopMode &&
|
|
lhs.animationSpeed == rhs.animationSpeed &&
|
|
lhs.isPlaying == rhs.isPlaying
|
|
}
|
|
|
|
func makeCoordinator() -> Coordinator {
|
|
Coordinator()
|
|
}
|
|
|
|
final class Coordinator {
|
|
var didPlayOnce = false
|
|
var lastAnimationName = ""
|
|
}
|
|
|
|
func makeUIView(context: Context) -> LottieAnimationView {
|
|
let animationView: LottieAnimationView
|
|
if let cached = LottieAnimationCache.shared.animation(named: animationName) {
|
|
animationView = LottieAnimationView(animation: cached)
|
|
} else {
|
|
animationView = LottieAnimationView(name: animationName)
|
|
}
|
|
animationView.contentMode = .scaleAspectFit
|
|
animationView.loopMode = loopMode
|
|
animationView.animationSpeed = animationSpeed
|
|
animationView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
|
animationView.setContentCompressionResistancePriority(.defaultLow, for: .vertical)
|
|
context.coordinator.lastAnimationName = animationName
|
|
playIfNeeded(animationView, coordinator: context.coordinator)
|
|
return animationView
|
|
}
|
|
|
|
func updateUIView(_ uiView: LottieAnimationView, context: Context) {
|
|
if context.coordinator.lastAnimationName != animationName {
|
|
if let cached = LottieAnimationCache.shared.animation(named: animationName) {
|
|
uiView.animation = cached
|
|
} else {
|
|
uiView.animation = LottieAnimation.named(animationName)
|
|
}
|
|
context.coordinator.lastAnimationName = animationName
|
|
context.coordinator.didPlayOnce = false
|
|
}
|
|
|
|
uiView.loopMode = loopMode
|
|
uiView.animationSpeed = animationSpeed
|
|
|
|
if !isPlaying {
|
|
context.coordinator.didPlayOnce = false
|
|
if uiView.isAnimationPlaying {
|
|
uiView.stop()
|
|
}
|
|
return
|
|
}
|
|
|
|
playIfNeeded(uiView, coordinator: context.coordinator)
|
|
}
|
|
|
|
private func playIfNeeded(_ view: LottieAnimationView, coordinator: Coordinator) {
|
|
if loopMode == .playOnce {
|
|
guard !coordinator.didPlayOnce else { return }
|
|
coordinator.didPlayOnce = true
|
|
view.play()
|
|
return
|
|
}
|
|
if !view.isAnimationPlaying {
|
|
view.play()
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Crossfading Lottie Container
|
|
|
|
struct CrossfadingLottieView: View {
|
|
let animationName: String
|
|
let animationID: Int
|
|
|
|
var body: some View {
|
|
LottieView(
|
|
animationName: animationName,
|
|
loopMode: .playOnce,
|
|
animationSpeed: 1.5
|
|
)
|
|
.id(animationID)
|
|
.transition(.opacity.animation(.easeInOut(duration: 0.4)))
|
|
}
|
|
}
|