在SwiftUI中使用合并更新Lottie动画时,无法在正确的时间更新,并产生意外结果

xt0899hw  于 2022-11-28  发布在  Swift
关注(0)|答案(1)|浏览(148)

我有一个使用合并和计时器的ViewModel,我希望这个ViewModel用一个新的动画更新LottieView和文件名。当计时器倒计时时,我希望它发布并发送特定的字符串,这些字符串将是json Lottie文件名。当我的ContentView收到这些文件名时,我希望它动态更新LottieViews动画。
因此,我在ContentView中创建了一个名为name的@State变量,并使其等于传入的接收值。但是,令我困惑的是,ViewModels计时器在10秒标记处发布和发送的文件名应该在LottieView中接收和使用(文件名:名称)。
然而,当我启动应用程序时,LottieView会立即运行该文件。为什么会这样?整个应用程序中唯一存在该文件名的地方是当计时器达到10秒时,它甚至不应该存在(name)。它也会忽略之前应该在19秒时执行的文件名称。如果我忽略LottieView(name)全部放在一起,并运行一个文本视图代替,所以在这种情况下文本(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>) {
  }
    
}

uqjltbpv

uqjltbpv1#

经过一天的搜索,没有找到一个答案,我可能想出了一个不是很正确的解决方案,但它似乎工作(保存临时fileName和更新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
        }
    }
}

相关问题