来自 @Published 属性的 SwiftUI 动画从视图外部更改

Posted

技术标签:

【中文标题】来自 @Published 属性的 SwiftUI 动画从视图外部更改【英文标题】:SwiftUI Animation from @Published property changing from outside the View 【发布时间】:2021-04-22 20:03:51 【问题描述】:

SwiftUI 提供.animation() 绑定,这些绑定将为视图中的更改设置动画。但是,如果来自@ObserveredObject@Published 属性“自动”更改(例如,来自计时器),而视图将根据更改进行更新,则没有明显的方法可以让视图为更改设置动画。

在下面的示例中,当 isOn 从 Toggle 更改时,它会动画,但从 Timer 更改时不会。有趣的是,如果我在这里使用三元条件而不是if/else,即使切换也不会触发动画。

struct ContentView: View 
    @ObservedObject var model: Model
    var body: some View 
        VStack 
            if model.isOn 
                MyImage(color: .blue)
             else 
                MyImage(color: .clear)
            
            Spacer()
            Toggle("switch", isOn: $model.isOn.animation(.easeIn(duration: 0.5)))
            Spacer()
        
    


struct MyImage: View 
    var color: Color
    var body: some View 
        Image(systemName: "pencil.circle.fill")
            .resizable()
            .frame(width: 100, height: 100)
            .foregroundColor(color)
    


class Model: ObservableObject 
    @Published var isOn: Bool = false
    var timer = Timer()
    init() 
        timer = Timer.scheduledTimer(withTimeInterval: 5, repeats: true, block:  [unowned self] _ in
            isOn.toggle()
        )
    

当值更改不是来自绑定时,如何触发动画?

【问题讨论】:

【参考方案1】:

最简单的选择是在你的计时器闭包中添加一个withAnimation 块:

withAnimation(.easeIn(duration: 0.5)) 
  isOn.toggle()

如果您无法更改 @ObservableObject 闭包,您可以添加一个局部变量来反映更改:

struct ContentView: View 
    @ObservedObject var model: Model
    @State var localIsOn = false
    var body: some View 
        VStack 
            if localIsOn 
                MyImage(color: .blue)
             else 
                MyImage(color: .clear)
            
            Spacer()
            Toggle("switch", isOn: $model.isOn.animation(.easeIn(duration: 0.5)))
            Spacer()
        .onChange(of: model.isOn)  (on) in
            withAnimation 
                localIsOn = on
            
        
    

您也可以在 ObservableObject 中使用镜像变量执行类似的技巧:


struct ContentView: View 
    @ObservedObject var model: Model
    var body: some View 
        VStack 
            if model.animatedOn 
                MyImage(color: .blue)
             else 
                MyImage(color: .clear)
            
            Spacer()
            Toggle("switch", isOn: $model.isOn.animation(.easeIn(duration: 0.5)))
            Spacer()
        
    



class Model: ObservableObject 
    @Published var isOn: Bool = false
    @Published var animatedOn : Bool = false
    
    var cancellable : AnyCancellable?
    
    var timer = Timer()
    init() 
        timer = Timer.scheduledTimer(withTimeInterval: 5, repeats: true, block:  [unowned self] _ in
                isOn.toggle()
        )
        cancellable = $isOn.sink(receiveValue:  (on) in
            withAnimation 
                self.animatedOn = on
            
        )
    

【讨论】:

以上是关于来自 @Published 属性的 SwiftUI 动画从视图外部更改的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI:“@Published”属性更改时未刷新“带有组的动态列表”

当我通过鼠标单击更改 SwiftUI 列表的选择时,@Published 属性的 didSet 被调用了两次

SwiftUI:属性装饰器的理解@State,@Binding,@ObservedObject,@Published,@Environment,@EnvironmentObject

SwiftUI:属性装饰器的理解@State,@Binding,@ObservedObject,@Published,@Environment,@EnvironmentObject

SwiftUI:如何使用 UserDefaults 持久化 @Published 变量?

在需要@Binding 的地方传递@Published(SwiftUI、Combine)