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

Animation 来自@Published属性的SwiftUI动画从视图外部更改,animation,swiftui,swiftui-animation,Animation,Swiftui,Swiftui Animation,SwiftUI在绑定上提供了.animation(),这些绑定将为视图中的更改设置动画。但是,如果来自@observedobject的@Published属性“自动”更改(例如,来自计时器),而视图将根据更改进行更新,则没有明显的方法获取视图来设置更改的动画 在下面的示例中,当从切换更改isOn时,它会设置动画,但从计时器更改时,它不会设置动画。有趣的是,如果我在这里使用三元条件,而不是if/else,那么即使切换也不会触发动画 struct ContentView: View { @O

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

在下面的示例中,当从切换更改
isOn
时,它会设置动画,但从
计时器更改时,它不会设置动画。有趣的是,如果我在这里使用三元条件,而不是
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()
        })
    }
}

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

最简单的选项是在计时器关闭内添加
带动画的
块:

withAnimation(.easeIn(duration: 0.5)) {
  isOn.toggle()
}
如果您无法更改@observeObject闭包,可以添加一个局部变量来镜像更改:

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
            }
        }
    }
}
您还可以使用ObserveObject中的镜像变量执行类似的操作:


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
            }
        })
    }
}