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