swift - 在 SwiftUI 中使用 combine 更新 Lottie Animation 时,它未能在正确的时间更新并给出意外的结果
问题描述
我有这个 ViewModel 使用 combine 和一个计时器,我希望这个 ViewModel 使用新动画更新 LottieView 和文件名。当计时器倒计时时,我希望它发布和发送特定的字符串,这些字符串将是 json Lottie 文件名。当我的 ContentView 收到这些文件名时,我希望它动态更新 LottieViews 动画。
因此,我在 ContentView 中创建了一个名为 name 的 @State 变量,并使其等于传入的接收值。但是,让我感到困惑的是,在 10 秒标记处从 ViewModels 计时器发布和发送的文件名是假设在 LottieView(文件名:名称)中接收和使用。
但是,当我启动应用程序时,这个 LottieView 会立即运行这个文件。怎么会这样?文件名在整个应用程序中唯一存在的地方是当计时器达到 10 秒时,它甚至不应该在调用 LottieView(name) 时存在。它还会忽略应该在 19 秒标记处运行的前一个文件名。如果我要一起忽略 LottieView(name) 并运行文本视图,那么在本例中为 Text(name),当我运行应用程序时,文本会在计时器达到 10 时正确更改。
那么 LottieView(name) 怎么会这样运行呢?我验证了这些文件也正确匹配它们的动画。
import SwiftUI
import Combine
class ViewModel: ObservableObject {
var anyCancellable: AnyCancellable?
let publish = PassthroughSubject<String, Never>()
private var timer: Timer?
private var scheduleTime = 20
init() {
fire()
anyCancellable = publish.sink { str in
print("Value that is being passed over: \(str)")
}
}
func fire() {
print("Fire timer")
timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
self.scheduleTime -= 1
print(self.scheduleTime)
if self.scheduleTime == 19 {
self.publish.send("13865-sign-for-error-flat-style")
}
if self.scheduleTime == 10 {
self.publish.send("4174-unlock-to-premium")
timer.invalidate()
}
}
}
}
import SwiftUI
import Combine
struct ContentView: View {
@ObservedObject var vm = ViewModel()
@State var name: String = ""
var body: some View {
VStack {
LottieView(filename: $name)
Text(name)
}
.onReceive(vm.publish, perform: { value in
print("passed over : \(value)")
name = value
print(name)
})
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
import SwiftUI
import Lottie
struct LottieView: UIViewRepresentable {
typealias UIViewType = UIView
@Binding var filename: String
func makeUIView(context: UIViewRepresentableContext<LottieView>) -> UIView {
let view = UIView(frame: .zero)
let animationView = AnimationView()
let animation = Animation.named(filename)
animationView.animation = animation
animationView.contentMode = .scaleAspectFit
animationView.play()
animationView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(animationView)
NSLayoutConstraint.activate([
animationView.widthAnchor.constraint(equalTo: view.widthAnchor),
animationView.heightAnchor.constraint(equalTo: view.heightAnchor),
animationView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
animationView.centerYAnchor.constraint(equalTo: view.centerYAnchor)
])
return view
}
func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<LottieView>) {
}
}
解决方案
经过一天的搜索,没有找到答案,我可能想出了一个不太正确的解决方案,但它似乎有效(保存临时文件名并在更新 LottieView 检查名称后):
import SwiftUI
import Lottie
struct LottieView: UIViewRepresentable {
typealias UIViewType = UIView
var filename: String
var animationView = AnimationView()
let isPaused: Bool
var needUpdate: Bool = false {
didSet {
if needUpdate {
let animation = Animation.named(filename)
animationView.animation = animation
animationView.contentMode = .scaleAspectFit
animationView.loopMode = .loop
animationView.play()
needUpdate = false
}
}
}
func makeUIView(context: UIViewRepresentableContext<LottieView>) -> UIView {
let view = UIView(frame: .zero)
let animation = Animation.named(filename)
animationView.animation = animation
animationView.contentMode = .scaleAspectFit
animationView.loopMode = .loop
animationView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(animationView)
NSLayoutConstraint.activate([
animationView.widthAnchor.constraint(equalTo: view.widthAnchor),
animationView.heightAnchor.constraint(equalTo: view.heightAnchor),
])
animationView.play()
return view
}
func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<LottieView>) {
let tempFileName = context.coordinator.parent.filename
DispatchQueue.main.async {
context.coordinator.parent.filename = filename
if tempFileName != filename {
context.coordinator.parent.needUpdate = true
}
}
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject {
var parent: LottieView
init(_ parent: LottieView) {
self.parent = parent
}
}
}
推荐阅读
- c# - 是否可以在没有 ASP.NET 的情况下创建“用户帐户”Windows 应用程序?
- security - Neo4j Bolt:由于身份验证失败,客户端未经授权
- amazon-web-services - 访问 AWS 为已部署资源自动生成的 URL
- spss - 向 SPSS 数据集中的所有变量添加值 + 值标签
- r - 需要 R 函数来比较一个文件中的缺失值并在另一个文件中更新
- python - 使用 psycopg2 的“ELF 文件操作系统 ABI 无效”
- ffi - 如何将函数分配给 Rebol 结构成员
- javascript - js切换多个div
- android-tv - 将 android TV 应用程序部署到 beta 测试人员的好方法是什么
- css - 在角度模态动画中实现缩放效果