I have my LottieView
struct LottieView: UIViewRepresentable {
var name: String
var loopMode: LottieLoopMode = .loop
var contentMode: UIView.ContentMode = .scaleAspectFit
var paused: Bool = false
var shouldPlay: Bool = true
var animationView = AnimationView()
func makeUIView(context: UIViewRepresentableContext<LottieView>) -> UIView {
let view = UIView(frame: .zero)
animationView.animation = Animation.named(name)
animationView.contentMode = contentMode
animationView.loopMode = loopMode
animationView.backgroundBehavior = .pauseAndRestore
if shouldPlay {
animationView.play()
}
animationView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(animationView)
NSLayoutConstraint.activate([
animationView.heightAnchor.constraint(equalTo: view.heightAnchor),
animationView.widthAnchor.constraint(equalTo: view.widthAnchor)
])
return view
}
func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<LottieView>) {
if shouldPlay {
context.coordinator.parent.animationView.play { finished in
if context.coordinator.parent.animationView.loopMode == .playOnce && finished {
context.coordinator.parent.animationView.play()
DispatchQueue.main.asyncAfter(deadline: .now() 0.3) {
context.coordinator.parent.animationView.pause()
}
}
}
} else {
context.coordinator.parent.animationView.pause()
}
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject {
var parent: LottieView
init(_ parent: LottieView) {
self.parent = parent
}
}
}
which animation's start depends on it's property shouldPlay
passed in view's initializer.
I use LottieView in custom AddToCartButton structure:
struct AddToCartButton: View {
let onAdd: () -> Void
@State private var shouldPresentAddAnimation: Bool = false
var body: some View {
Button {
withAnimation {
shouldPresentAddAnimation = true
onAdd()
}
} label: {
HStack(spacing: 0) {
LottieView(name: "add_to_cart_2",
loopMode: .playOnce,
contentMode: .scaleAspectFit,
shouldPlay: shouldPresentAddAnimation)
.frame(minWidth: 50, maxHeight: 40)
Text("Add to Cart")
.font(.ssButton)
.foregroundColor(.ssWhite)
.padding(.all, 10)
}
.padding(.vertical, 5)
.background {
RoundedRectangle(cornerRadius: 5)
}
.fixedSize(horizontal: true, vertical: false)
}
}
}
Clicking on this button should play the animation once and then return to initial animation state thanks to the code in LottieView's updateUIView
method.
I have my main view in which there are many AddToCartButton structures created like this:
AddToCartButton {
[some code...]
}
And the effect is that at first button click, it animates properly. On second different button click the second and the first one animation fires. When clicking on third button, all three buttons fire animations. Sample in the photos attached:
Firstly, the initial state of buttons:
Secondly, after one button click:
Finally, after second button click (two of them fire animation):
What I want is that only the button that is clicked fire it's animation.
CodePudding user response:
I think the problem is that you don’t set shouldPresentAddAnimation
back to false
after animation is played. So every time you press the button the whole list is rendered and since you state property is true
, it fires animation.