如何在 SwiftUI 中使用 Lottie Button 动画启用动作?
How to enable action with Lottie Button animation in SwiftUI?
我有一个简单的项目,它是 运行 点击按钮时的 Lottie 动画。
struct ContentView: View {
@State var number = 0
@State var toogleValue : Bool = false
var body: some View {
ZStack {
Color.green.opacity(0.2)
ZStack {
LottieButton(filename: "17247-happy-flower")
.frame(width: 200)
.onTapGesture {
self.number += 1
}
Text("number = \(self.number)")
.offset( y: 150)
}
}.edgesIgnoringSafeArea(.all)
}
}
我想要实现的是:每当用户点击按钮时,动画应该开始并且数字计数器应该增加 1。
现在发生了什么:计数器加 1 但动画没有播放。
我尝试了什么:当我评论 // onTapGesture {self.number += 1} 动画工作正常,但计数器不工作。
这是 Lottie 按钮:
struct LottieButton: UIViewRepresentable {
/// Create a button.
let animationButton = AnimatedButton()
var filename = "LottieLogo2"
func makeUIView(context: UIViewRepresentableContext<LottieButton>) -> UIView {
let view = UIView()
let animation = Animation.named(filename)
animationButton.animation = animation
animationButton.contentMode = .scaleAspectFit
animationButton.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(animationButton)
animationButton.clipsToBounds = false
/// Set animation play ranges for touch states
animationButton.setPlayRange(fromMarker: "touchDownStart", toMarker: "touchDownEnd", event: .touchUpInside)
// animationButton.setPlayRange(fromMarker: "touchDownEnd", toMarker: "touchUpCancel", event: .touchUpOutside)
// animationButton.setPlayRange(fromMarker: "touchDownEnd", toMarker: "touchUpEnd", event: .touchUpInside)
NSLayoutConstraint.activate([
animationButton.heightAnchor.constraint(equalTo: view.heightAnchor),
animationButton.widthAnchor.constraint(equalTo: view.widthAnchor),
])
return view
}
func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<LottieButton>) {
}
}
我假设问题是由于视图的层次结构引起的,但我不确定。
您需要接受可以与 UIViewRepresentable
一起使用的 Coordinator
。
通过 Coordinator
class 它允许您将目标添加到 animationView
。目标和 Objective-C 暴露的函数只能添加到 classes。这意味着您可以在点击动画时执行任何操作。
我们向 LottieButton
结构传递两个参数 filename
和 action
。该动作基本上是一个闭包,允许我们传递一段稍后将执行的代码。
我们通过传递对我们正在创建的 LottieButton
的引用来设置 Coordinator
,然后我们添加目标。这需要在调用 super.init
之后添加,因为我们需要用户 self
.
import SwiftUI
import Lottie
struct LottieButton: UIViewRepresentable {
typealias UIViewType = UIView
let animationView = AnimatedButton()
let filename: String
let action: () -> Void
func makeUIView(context: UIViewRepresentableContext<LottieButton>) -> UIView {
let view = UIView()
let animation = Animation.named(filename)
animationView.animation = animation
animationView.contentMode = .scaleAspectFit
animationView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(animationView)
NSLayoutConstraint.activate([
animationView.widthAnchor.constraint(equalTo: view.widthAnchor),
animationView.heightAnchor.constraint(equalTo: view.heightAnchor),
])
return view
}
func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<LottieButton>) {
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject {
let parent: LottieButton
init(_ parent: LottieButton) {
self.parent = parent
super.init()
parent.animationView.addTarget(self, action: #selector(touchUpInside), for: .touchUpInside)
}
// this function can be called anything, but it is best to make the names clear
@objc func touchUpInside() {
parent.action()
}
}
}
这意味着我们可以在 ContentView 中执行以下操作。我们的动作参数现在可以在点击按钮时执行我们想要的任何操作。
struct ContentView: View {
@State private var tapped: Int = 0
var body: some View {
VStack {
LottieButton(filename: "loading", action: {
print("tapped")
tapped += 1
})
.frame(width: 200, height: 200)
Text("tapped count \(tapped)")
}
}
}
它正在运行:
我有一个简单的项目,它是 运行 点击按钮时的 Lottie 动画。
struct ContentView: View {
@State var number = 0
@State var toogleValue : Bool = false
var body: some View {
ZStack {
Color.green.opacity(0.2)
ZStack {
LottieButton(filename: "17247-happy-flower")
.frame(width: 200)
.onTapGesture {
self.number += 1
}
Text("number = \(self.number)")
.offset( y: 150)
}
}.edgesIgnoringSafeArea(.all)
}
}
我想要实现的是:每当用户点击按钮时,动画应该开始并且数字计数器应该增加 1。
现在发生了什么:计数器加 1 但动画没有播放。
我尝试了什么:当我评论 // onTapGesture {self.number += 1} 动画工作正常,但计数器不工作。
这是 Lottie 按钮:
struct LottieButton: UIViewRepresentable {
/// Create a button.
let animationButton = AnimatedButton()
var filename = "LottieLogo2"
func makeUIView(context: UIViewRepresentableContext<LottieButton>) -> UIView {
let view = UIView()
let animation = Animation.named(filename)
animationButton.animation = animation
animationButton.contentMode = .scaleAspectFit
animationButton.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(animationButton)
animationButton.clipsToBounds = false
/// Set animation play ranges for touch states
animationButton.setPlayRange(fromMarker: "touchDownStart", toMarker: "touchDownEnd", event: .touchUpInside)
// animationButton.setPlayRange(fromMarker: "touchDownEnd", toMarker: "touchUpCancel", event: .touchUpOutside)
// animationButton.setPlayRange(fromMarker: "touchDownEnd", toMarker: "touchUpEnd", event: .touchUpInside)
NSLayoutConstraint.activate([
animationButton.heightAnchor.constraint(equalTo: view.heightAnchor),
animationButton.widthAnchor.constraint(equalTo: view.widthAnchor),
])
return view
}
func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<LottieButton>) {
}
}
我假设问题是由于视图的层次结构引起的,但我不确定。
您需要接受可以与 UIViewRepresentable
一起使用的 Coordinator
。
通过 Coordinator
class 它允许您将目标添加到 animationView
。目标和 Objective-C 暴露的函数只能添加到 classes。这意味着您可以在点击动画时执行任何操作。
我们向 LottieButton
结构传递两个参数 filename
和 action
。该动作基本上是一个闭包,允许我们传递一段稍后将执行的代码。
我们通过传递对我们正在创建的 LottieButton
的引用来设置 Coordinator
,然后我们添加目标。这需要在调用 super.init
之后添加,因为我们需要用户 self
.
import SwiftUI
import Lottie
struct LottieButton: UIViewRepresentable {
typealias UIViewType = UIView
let animationView = AnimatedButton()
let filename: String
let action: () -> Void
func makeUIView(context: UIViewRepresentableContext<LottieButton>) -> UIView {
let view = UIView()
let animation = Animation.named(filename)
animationView.animation = animation
animationView.contentMode = .scaleAspectFit
animationView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(animationView)
NSLayoutConstraint.activate([
animationView.widthAnchor.constraint(equalTo: view.widthAnchor),
animationView.heightAnchor.constraint(equalTo: view.heightAnchor),
])
return view
}
func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<LottieButton>) {
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject {
let parent: LottieButton
init(_ parent: LottieButton) {
self.parent = parent
super.init()
parent.animationView.addTarget(self, action: #selector(touchUpInside), for: .touchUpInside)
}
// this function can be called anything, but it is best to make the names clear
@objc func touchUpInside() {
parent.action()
}
}
}
这意味着我们可以在 ContentView 中执行以下操作。我们的动作参数现在可以在点击按钮时执行我们想要的任何操作。
struct ContentView: View {
@State private var tapped: Int = 0
var body: some View {
VStack {
LottieButton(filename: "loading", action: {
print("tapped")
tapped += 1
})
.frame(width: 200, height: 200)
Text("tapped count \(tapped)")
}
}
}
它正在运行: