How Offsetting works?
首先,这是一种expected行为.因为当你用offset
,SwiftUI
shifts the displayed contents的时候.简而言之,这意味着SwiftUI
shifts the 104 itself
既然是onTapGesture
only recognizes the touches on the view,这也解释了为什么您可以click到偏移视图
How Animation Works?
在您的代码中,您首先是offsetting
/View
,然后是应用动画.当您使用withAnimation
、SwiftUI recomputes the view's body with provided animation,但请记住,它does not会更改预先应用于View
的任何内容.
请注意当进入红色矩形时,Click Me
是如何变为可点击的.这是因为红色矩形表示Click Me
按钮的最终偏移量.(因此它只是一个占位符)
所以View
本身和offset
必须匹配,因为当您首先偏移您的视图时,SwiftUI
需要您的视图there来触发点击手势.
Possible solution
既然我们了解了问题,我们就能解决它.所以,问题发生是因为我们是offsetting our view first, then applying animation岁.
因此,如果这不起作用,一种可能的解决方案是使用动画以周期为单位更改偏移量(例如,每个周期使用0.1秒),因为这将导致SwiftUI在每次更改偏移量时重新定位视图,因此应该不会发生奇怪的错误.
代码:
struct ContentView: View {
@State private var increment : CGFloat = 1
@State private var offset : CGFloat = 0
var body: some View {
ZStack {
Button("Click Me") {
print("Click")
}
.fontWeight(.black)
}
.tappableOffsetAnimation(offset: $offset, animation: .linear, duration: 5, finalOffsetAmount: 300)
}
}
struct TappableAnimationModifier : ViewModifier {
@Binding var offset : CGFloat
var duration : Double
var finalOffsetAmount : Double
var animation : Animation
let timer = Timer.publish(every: 0.1, on: .main, in: .common).autoconnect()
func body(content: Content) -> some View {
content
.animation(animation, value: offset)
.offset(x: offset)
.onReceive(timer) { input in
/*
* a simple math here, we're dividing duration by 0.1 because our timer gets triggered
* in every 0.1 seconds, so dividing this result will always produce the
* proper value to finish offset animation in `x` seconds
* example: 300 / (5 / 0.1) = 300 / 50 = 6 increment per 0.1 second
*/
if (offset >= finalOffsetAmount) {
// you could implement autoReverses by not canceling the timer here
// and substracting finalOffsetAmount / (duration / 0.1) until it reaches zero
// then you can again start incrementing it.
timer.upstream.connect().cancel()
return
}
offset += finalOffsetAmount / (duration / 0.1)
}
}
}
extension View {
func tappableOffsetAnimation(offset: Binding<CGFloat>, animation: Animation, duration: Double, finalOffsetAmount: Double) -> some View {
modifier(TappableAnimationModifier(offset: offset, duration: duration, finalOffsetAmount: finalOffsetAmount, animation: animation))
}
}
下面是它看起来的样子:
您的视图正在运行,请将其捕捉到x)