如何通过更改非状态变量来触发 SwiftUI 动画
How to trigger SwiftUI animation via change of non-State variable
使用 .onAppear()
可以很容易地在视图出现时开始播放动画。但我想在视图的非状态变量之一发生变化时执行重复动画。一个例子:
这是一个视图,每当它的 throbbing
外部设置的参数是 true
:
时,我都想“跳动”
struct MyCircle: View {
var throbbing: Bool
@State var scale = 1.0
var body: some View {
Circle()
.frame(width: 100 * scale, height: 100 * scale)
.foregroundColor(.blue)
.animation(.easeInOut.repeatForever(), value: scale)
.onAppear { scale = 1.2 }
}
}
目前代码立即开始跳动,与 throbbing
变量无关。
但想象一下这个场景:
struct ContentView: View {
@State var throb: Bool = false
var body: some View {
VStack {
Button("Throb: \(throb ? "ON" : "OFF")") { throb.toggle() }
MyCircle(throbbing: throb)
}
}
}
看起来像这样:
知道如何修改 MyCircle 以便在点击按钮时开始跳动并在再次点击时结束吗?
您可以使用onChange
观看throbbing
然后分配一个动画。如果为 true,则添加一个重复动画,如果为 false,则仅动画回到原始比例大小:
struct MyCircle: View {
var throbbing: Bool
@State private var scale : CGFloat = 0
var body: some View {
Circle()
.frame(width: 100, height: 100)
.scaleEffect(1 + scale)
.foregroundColor(.blue)
.onChange(of: throbbing) { newValue in
if newValue {
withAnimation(.easeInOut.repeatForever()) {
scale = 0.2
}
} else {
withAnimation {
scale = 0
}
}
}
}
}
我刚刚在尝试实现您的目标时发现了两件有趣的事情。
- 当
value
改变 时将添加动画
- 无法删除添加到视图的动画,我们需要通过删除它来从视图层次结构中删除动画视图
id
,将创建具有零动画的新视图。
import SwiftUI
import PlaygroundSupport
struct ContentView: View {
@State var throbbling = false
@State var circleId = UUID()
var body: some View {
VStack {
Toggle("Throbbling", isOn: $throbbling)
circle.id(circleId)
.scaleEffect(throbbling ? 1.2 : 1.0)
.animation(.easeInOut.repeatForever(), value: throbbling)
.onChange(of: throbbling) { newValue in
if newValue == false {
circleId = UUID()
}
}
}.padding()
}
@ViewBuilder
var circle: some View {
Circle().fill(.green).frame(width: 100, height: 100)
}
}
PlaygroundPage.current.setLiveView(ContentView())
使用 .onAppear()
可以很容易地在视图出现时开始播放动画。但我想在视图的非状态变量之一发生变化时执行重复动画。一个例子:
这是一个视图,每当它的 throbbing
外部设置的参数是 true
:
struct MyCircle: View {
var throbbing: Bool
@State var scale = 1.0
var body: some View {
Circle()
.frame(width: 100 * scale, height: 100 * scale)
.foregroundColor(.blue)
.animation(.easeInOut.repeatForever(), value: scale)
.onAppear { scale = 1.2 }
}
}
目前代码立即开始跳动,与 throbbing
变量无关。
但想象一下这个场景:
struct ContentView: View {
@State var throb: Bool = false
var body: some View {
VStack {
Button("Throb: \(throb ? "ON" : "OFF")") { throb.toggle() }
MyCircle(throbbing: throb)
}
}
}
看起来像这样:
知道如何修改 MyCircle 以便在点击按钮时开始跳动并在再次点击时结束吗?
您可以使用onChange
观看throbbing
然后分配一个动画。如果为 true,则添加一个重复动画,如果为 false,则仅动画回到原始比例大小:
struct MyCircle: View {
var throbbing: Bool
@State private var scale : CGFloat = 0
var body: some View {
Circle()
.frame(width: 100, height: 100)
.scaleEffect(1 + scale)
.foregroundColor(.blue)
.onChange(of: throbbing) { newValue in
if newValue {
withAnimation(.easeInOut.repeatForever()) {
scale = 0.2
}
} else {
withAnimation {
scale = 0
}
}
}
}
}
我刚刚在尝试实现您的目标时发现了两件有趣的事情。
- 当
value
改变 时将添加动画
- 无法删除添加到视图的动画,我们需要通过删除它来从视图层次结构中删除动画视图
id
,将创建具有零动画的新视图。
import SwiftUI
import PlaygroundSupport
struct ContentView: View {
@State var throbbling = false
@State var circleId = UUID()
var body: some View {
VStack {
Toggle("Throbbling", isOn: $throbbling)
circle.id(circleId)
.scaleEffect(throbbling ? 1.2 : 1.0)
.animation(.easeInOut.repeatForever(), value: throbbling)
.onChange(of: throbbling) { newValue in
if newValue == false {
circleId = UUID()
}
}
}.padding()
}
@ViewBuilder
var circle: some View {
Circle().fill(.green).frame(width: 100, height: 100)
}
}
PlaygroundPage.current.setLiveView(ContentView())