如何在SwiftUI中暂停动画?

时间:2019-09-21 15:31:26

标签: swift animation swiftui

启动withAnimation(_:)方法后我不知道如何停止。

我正在用SwiftUI编写我的第一个应用程序,我想创建一个进度圈,该进度圈将由用户通过按钮进行控制-开始按钮,该按钮将开始播放动画,其中圆圈将在末尾填充并且停止按钮将具有保存填充点的实际状态并停止动画。

我的主视图:

struct MainView: View {
  @State private var fillPoint = 1.0 
  @State private var animationDuration = 10.0

  private var ring: Ring {
    let ring = Ring(fillPoint: self.fillPoint)
    return ring
  }

  var body: some View {
    VStack {
      ring.stroke(Color.red, lineWidth: 15.0)
        .frame(width: 200, height: 200)
        .padding(40)
      HStack {
        Button(action: {
          withAnimation(.easeIn(duration: self.animationDuration)) {
              self.fillPoint = 0
          }
        }) {
          Text("Start")
        }
        Button(action: {
          // what should I do here?
        }) {
          Text("Stop")
        }
      }
    }
  }
}

Ring的结构:

struct Ring: Shape {
  var startArcAngle: Double = 360.0

  var fillPoint: Double {
    willSet {
      startArcAngle = 360 * newValue
    }
  }

  internal var animatableData: Double {
    get { return fillPoint }
    set { fillPoint = newValue }
  }

  internal func path(in rect: CGRect) -> Path {
    let endArcAngle = 0.0

    var path = Path()

    path.addArc(center: CGPoint(x: rect.size.width / 2,
                                y: rect.size.height / 2),
                radius: rect.size.width / 2,
                startAngle: .degrees(startArcAngle - 90),
                endAngle: .degrees(endArcAngle - 90),
                clockwise: true)

    return path
  }
}

我尝试操纵animatableData的值,但是在Ring的结构之外,它总是返回0.0(值是动画开始时在动画结束时要实现的值),并且在Ring的结构中它会按我的方式打印(0.96xxxx-> 0.94xxxx等),但是将其取自Ring的结构之外总是返回1.0或0.0。

2 个答案:

答案 0 :(得分:1)

似乎没有停止动画的控件。由于您的要求是在进度中间开始和停止抽奖,因此另一种解决方案是使用计时器。棘手的一点是根据计时器的持续时间清除电弧。这是我在MainView中进行更改的代码:

注意:根据您的选择调整动画持续时间。

struct MainView: View {
  @State private var fillPoint = 1.0
  @State private var animationDuration = 10.0
  @State private var  stopAnimation = true
  @State private var countdownTimer: Timer?

  private var ring: Ring {
    let ring = Ring(fillPoint: self.fillPoint)
    return ring
  }

  var body: some View {
    VStack {
      ring.stroke(Color.red, lineWidth: 15.0)
        .frame(width: 200, height: 200)
        .padding(40)
        .animation(self.stopAnimation ? nil : .easeIn(duration: 0.1))
      HStack {
        Button(action: {
            self.stopAnimation = false
            self.countdownTimer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true, block: { _ in
                guard self.animationDuration > 0 else {
                    self.countdownTimer?.invalidate()
                    return
                }
                self.fillPoint = self.animationDuration/10
                self.animationDuration -= 0.1
            })
        }) {
          Text("Start")
        }
        Button(action: {
             self.countdownTimer?.invalidate()
            self.stopAnimation = true

        }) {
            Text("Stop")
        }
      }
    }
  }
}    

答案 1 :(得分:0)

你不能打断动画,但你可以让它看起来像是打断。 您可以使用 Group 并且当视图没有动画时,您可以使用静态视图。 这是一个详细说明的例子;

struct RingView: View {
@ObservedObject var viewModel: ViewModel

@State private var animatedBonusRemaining: Double = 1

private func startBonusTimeAnimation() {
    animatedBonusRemaining = viewModel.remainingTimeRatio
    withAnimation(.linear(duration: viewModel.remainingDuration)) {
        animatedBonusRemaining = 0
    }
}

var body: some View {
    ZStack {
        Pie()
            .opacity(0.4)
        Group {
            if viewModel.isRunning {
                Pie(startAngle: Angle(degrees: -90),
                    endAngle: Angle(degrees: (-360 * animatedBonusRemaining) - 90))
            } else {
                Pie(startAngle: Angle(degrees: -90),
                    endAngle: Angle(degrees: (-360 * viewModel.remainingTimeRatio) - 90))
            }
        }
    }
    .aspectRatio(1.0, contentMode: .fill)
    .onTapGesture {
        routine.toggleIsRunning()
        if routine.isRunning {
            startBonusTimeAnimation()
        }
    }
}

剩余比率是一个计算属性,可以通过剩余时间与总持续时间轻松计算。 我在我的视图模型中使用了一个计时器,但没有将它公开给 View,因为我认为视图本身没有计时器的工作。它可以通过 withAnimation 闭包进行动画处理。我使用计时器仅用于检测完成。