[英].repeatForever() does not work when value is updated in SwiftUI
I'm trying to make an idle animation in SwiftUI
that gets triggered if there's no touch in the screen for 3 seconds.我正在尝试在SwiftUI
中创建一个空闲的 animation,如果屏幕上没有触摸 3 秒,它就会被触发。 I made a little animation that goes up and down (y offset 15) when there's no touch for 3 seconds and goes back to its original position when a touch occurs.我做了一个小的 animation,当没有触摸 3 秒时,它会上下移动(y 偏移量 15),并在发生触摸时返回到其原始的 position。 But the thing is, when it goes to its original positon, autoreverses doesn't get triggered.但问题是,当它回到原来的位置时,自动反转不会被触发。 Here's how it looks like:它是这样的:
Go Live button: Go 直播按钮:
struct GoLiveButton: View {
@State private var animationOffset: CGFloat = 0
@Binding var isIdle: Bool
var body: some View {
ZStack {
Button(action: {} ) {
Text("Go Live")
.frame(width: 120, height: 40)
.background(Color.black)
.foregroundColor(.white)
.clipShape(Capsule())
.font(.system(size: 20))
.shadow(color: .black, radius: 4, x: 4, y: 4)
}
.offset(y: animationOffset)
.animation(.timingCurve(0.38, 0.07, 0.12, 0.93, duration: 2).repeatForever(autoreverses: true), value: isIdle)
.animation(.timingCurve(0.38, 0.07, 0.12, 0.93, duration: 2), value: !isIdle)
}
.onAppear {
self.isIdle = true
self.animationOffset = 15
}
.onChange(of: isIdle) { newValue in
if newValue {
self.animationOffset = 15
}
else {
self.animationOffset = 0
}
}
}
}
Here is the idle view:这是空闲视图:
struct StackOverflowView: View {
@State private var timer: Timer?
@State private var isIdle = false
var body: some View {
GeometryReader { geo in
GoLiveButton(isIdle: $isIdle)
}
.onTapGesture {
print("DEBUG: CustomTabView OnTapGesture Triggered")
self.isIdle = false
self.timer?.invalidate()
self.timer = Timer.scheduledTimer(withTimeInterval: 3, repeats: false) { _ in
self.isIdle = true
}
}
.gesture(
DragGesture().onEnded { _ in
self.isIdle = false
self.timer?.invalidate()
self.timer = Timer.scheduledTimer(withTimeInterval: 3, repeats: false) { _ in
self.isIdle = true
}
}
)
}
}
Here is an approach without AnimatableData:这是一种没有 AnimatableData 的方法:
I added a second timer that just triggers the animations by offset (0, -15, 0, -15...) every 2 seconds, repeating forever.我添加了第二个计时器,它每 2 秒通过偏移量 (0、-15、0、-15...) 触发动画,永远重复。
If isIdle
changes to false, we just set offset to 0, and this will be animated too.如果isIdle
变为 false,我们只需将 offset 设置为 0,这也会被激活。 We reset all timers.我们重置所有计时器。 And again set the idle timer (3 secs) which when fires will start the animation timer (2 secs).并再次设置空闲计时器(3 秒),当触发时将启动 animation 计时器(2 秒)。 voila.瞧。
(I also restructured the GoLiveButton
a little bit so it holds all relevant states in itself, and the parent view only has to control isIdle
) (我还对GoLiveButton
进行了一些重组,使其自身拥有所有相关状态,而父视图只需控制isIdle
)
struct GoLiveButton: View {
@Binding var isIdle: Bool
@State private var timer: Timer?
@State private var animationTimer: Timer?
@State private var animationOffset: CGFloat = 0
var body: some View {
ZStack {
Button(action: {} ) {
Text("Go Live")
.frame(width: 120, height: 40)
.background(Color.black)
.foregroundColor(.white)
.clipShape(Capsule())
.font(.system(size: 20))
.shadow(color: .black, radius: 4, x: 4, y: 4)
}
.offset(y: animationOffset)
.animation(.timingCurve(0.38, 0.07, 0.12, 0.93, duration: 2), value: animationOffset)
}
.onAppear {
self.timer = Timer.scheduledTimer(withTimeInterval: 3, repeats: false) { _ in
self.isIdle = true
animationTimer = Timer.scheduledTimer(withTimeInterval: 2, repeats: true) { _ in
animationOffset = (animationOffset == 15) ? 0 : 15
}
}
}
.onChange(of: isIdle) { newValue in
if newValue == false {
// reset all
self.animationTimer?.invalidate()
self.timer?.invalidate()
animationOffset = 0
self.timer = Timer.scheduledTimer(withTimeInterval: 3, repeats: false) { _ in
self.isIdle = true
animationTimer = Timer.scheduledTimer(withTimeInterval: 2, repeats: true) { _ in
animationOffset = (animationOffset == 15) ? 0 : 15
}
}
}
}
}
}
struct ContentView: View {
@State private var isIdle = false
var body: some View {
ZStack {
// for background tap only
Color.gray.opacity(0.2)
.onTapGesture {
print("tap")
self.isIdle = false
}
GoLiveButton(isIdle: $isIdle)
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.