如何在 SwiftUI 中暂停动画?
Posted
技术标签:
【中文标题】如何在 SwiftUI 中暂停动画?【英文标题】:How to pause an animation in SwiftUI? 【发布时间】:2019-09-21 15:31:26 【问题描述】: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。
【问题讨论】:
【参考方案1】:好像没有停止动画的控件。
由于您的要求是在进行中开始和停止抽签,因此另一种解决方案是使用Timer
。棘手的一点是根据计时器持续时间清除电弧。
这是我在您的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")
【讨论】:
效果几乎很好!但是如果它必须是动态的(用户将多次更改计时器的持续时间,但仅在进度环结束或重置之后)countdownTimer
中的self.fillPoint
必须除以总时间值(这里它将是 10 , 但在其他情况下 10 将是无效值)。无论如何,感谢您的帮助,这确实是我想要的。【参考方案2】:
你不能打断一个动画,但你可以让它看起来像一个打断。 您可以使用组,当视图没有动画时,您可以使用静态视图。 这是一个详细说明的例子;
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 闭包进行动画处理。我只使用计时器来检测完成情况。
【讨论】:
以上是关于如何在 SwiftUI 中暂停动画?的主要内容,如果未能解决你的问题,请参考以下文章