SwiftUI withAnimation 内部条件不起作用
SwiftUI withAnimation inside conditional not working
我想要一个仅在有条件的情况下可见的动画视图。当我这样做时,我会出现不可预测的行为。特别是,在以下情况下,我希望在 onAppear 中调用一个永远重复的动画,无论它在何时何地初始化,它总是可以工作,但实际上它的行为是不稳定的。我应该如何理解这种行为?我应该如何为有条件出现的视图内的值设置动画?
情况 1:当示例开始时,没有圆圈(如预期的那样),当单击按钮时圆圈开始动画(如预期的那样),如果单击关闭则标签保持动画(它应该' 因为动画值在 false if 语句后面),如果再次单击,则圆圈保持全尺寸,而标签保持动画效果
struct TestButton: View {
@State var radius = 50.0
@State var running = false
let animation = Animation.linear(duration: 1).repeatForever(autoreverses: false)
var body: some View {
VStack {
Button(running ? "Stop" : "Start") {
running.toggle()
}
if running {
Circle()
.fill(.blue)
.frame(width: radius * 2, height: radius * 2)
.onAppear {
withAnimation(animation) {
self.radius = 100
}
}
}
}
}
}
情况2:无论点击多少次都没有动画显示。
struct TestButton: View {
@State var radius = 50.0
@State var running = false
let animation = Animation.linear(duration: 1).repeatForever(autoreverses: false)
var body: some View {
VStack {
Button(running ? "Stop" : "Start") {
running.toggle()
}
if running {
Circle()
.fill(.blue.opacity(0.2))
.frame(width: radius * 2, height: radius * 2)
}
}
// `onAppear` moved from `Circle` to `VStack`.
.onAppear {
withAnimation(animation) {
self.radius = 100
}
}
}
}
案例 3:动画运行就像在案例 1 中单击第一个按钮后一样。
struct TestButton: View {
@State var radius = 50.0
@State var running = true // This now starts as `true`
let animation = Animation.linear(duration: 1).repeatForever(autoreverses: false)
var body: some View {
VStack {
Button(running ? "Stop" : "Start") {
running.toggle()
}
if running {
Circle()
.fill(.blue.opacity(0.2))
.frame(width: radius * 2, height: radius * 2)
}
}
.onAppear {
withAnimation(animation) {
self.radius = 100
}
}
}
}
最好将 animation
与您想要动画的值连接起来,在您的情况下它是 radius
,明确地在包含动画视图的容器上。
这是方法演示。使用 Xcode 13.2 / iOS 15.2
测试
struct TestButton: View {
@State var radius = 50.0
@State var running = false
let animation = Animation.linear(duration: 1).repeatForever(autoreverses: false)
var body: some View {
VStack {
Button(running ? "Stop" : "Start") {
running.toggle()
}
VStack { // responsible for animation of
// conditionally appeared/disappeared view
if running {
Circle()
.fill(.blue)
.frame(width: radius * 2, height: radius * 2)
.onAppear {
self.radius = 100
}
.onDisappear {
self.radius = 50
}
}
}
.animation(animation, value: radius) // << here !!
}
}
}
我想要一个仅在有条件的情况下可见的动画视图。当我这样做时,我会出现不可预测的行为。特别是,在以下情况下,我希望在 onAppear 中调用一个永远重复的动画,无论它在何时何地初始化,它总是可以工作,但实际上它的行为是不稳定的。我应该如何理解这种行为?我应该如何为有条件出现的视图内的值设置动画?
情况 1:当示例开始时,没有圆圈(如预期的那样),当单击按钮时圆圈开始动画(如预期的那样),如果单击关闭则标签保持动画(它应该' 因为动画值在 false if 语句后面),如果再次单击,则圆圈保持全尺寸,而标签保持动画效果
struct TestButton: View {
@State var radius = 50.0
@State var running = false
let animation = Animation.linear(duration: 1).repeatForever(autoreverses: false)
var body: some View {
VStack {
Button(running ? "Stop" : "Start") {
running.toggle()
}
if running {
Circle()
.fill(.blue)
.frame(width: radius * 2, height: radius * 2)
.onAppear {
withAnimation(animation) {
self.radius = 100
}
}
}
}
}
}
情况2:无论点击多少次都没有动画显示。
struct TestButton: View {
@State var radius = 50.0
@State var running = false
let animation = Animation.linear(duration: 1).repeatForever(autoreverses: false)
var body: some View {
VStack {
Button(running ? "Stop" : "Start") {
running.toggle()
}
if running {
Circle()
.fill(.blue.opacity(0.2))
.frame(width: radius * 2, height: radius * 2)
}
}
// `onAppear` moved from `Circle` to `VStack`.
.onAppear {
withAnimation(animation) {
self.radius = 100
}
}
}
}
案例 3:动画运行就像在案例 1 中单击第一个按钮后一样。
struct TestButton: View {
@State var radius = 50.0
@State var running = true // This now starts as `true`
let animation = Animation.linear(duration: 1).repeatForever(autoreverses: false)
var body: some View {
VStack {
Button(running ? "Stop" : "Start") {
running.toggle()
}
if running {
Circle()
.fill(.blue.opacity(0.2))
.frame(width: radius * 2, height: radius * 2)
}
}
.onAppear {
withAnimation(animation) {
self.radius = 100
}
}
}
}
最好将 animation
与您想要动画的值连接起来,在您的情况下它是 radius
,明确地在包含动画视图的容器上。
这是方法演示。使用 Xcode 13.2 / iOS 15.2
测试struct TestButton: View {
@State var radius = 50.0
@State var running = false
let animation = Animation.linear(duration: 1).repeatForever(autoreverses: false)
var body: some View {
VStack {
Button(running ? "Stop" : "Start") {
running.toggle()
}
VStack { // responsible for animation of
// conditionally appeared/disappeared view
if running {
Circle()
.fill(.blue)
.frame(width: radius * 2, height: radius * 2)
.onAppear {
self.radius = 100
}
.onDisappear {
self.radius = 50
}
}
}
.animation(animation, value: radius) // << here !!
}
}
}