无法在动画移动对象(UIView)上检测到点击手势

时间:2019-01-23 10:11:06

标签: ios swift animation uiview uitapgesturerecognizer

我尝试使用动画简单地移动UIView。这是我的示例代码。

let tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.tap1(gesture:)))
    tapGesture.numberOfTapsRequired = 1
    tapGesture.numberOfTouchesRequired = 1
    self.tempView.addGestureRecognizer(tapGesture)

    self.tempView.frame = CGRect(x: self.tempView.frame.origin.x, y: 0, width: self.tempView.frame.width, height: self.tempView.frame.height)

    UIView.animate(withDuration: 15, delay: 0.0, options: [.allowUserInteraction, .allowAnimatedContent], animations: {
      self.tempView.frame = CGRect(x: self.tempView.frame.origin.x, y: UIScreen.main.bounds.height - 100, width: self.tempView.frame.width, height: self.tempView.frame.height)


    }) { (_) in

    }

这里我提到点击手势方法:

@objc func tap1(gesture: UITapGestureRecognizer) {
    print("tap with object")
}

并且当UIView处于动画状态并且此时从一个位置移动到另一位置时,点击手势不起作用。那是我的问题。 请帮我。
谢谢你。

1 个答案:

答案 0 :(得分:3)

尽管从视觉上查看更改位置,但不会这样做。如果在动画中检查帧,则该帧将始终是您的最终帧。因此,您可以在最终位置将其轻按。 (这还假设您已在进行动画时启用了用户交互)。

对于您的情况,您需要使用计时器手动移动视图。这需要花费更多的精力,但可以轻松实现。计时器应该以一些不错的FPS(如60)触发,并且每次触发时,帧都应更新到其内插位置。

要进行插值,只需按组件进行插值:

func interpolateRect(from: CGRect, to: CGRect, scale: CGFloat) -> CGRect {
    return CGRect(x: from.minX + (to.minX - from.minX) * scale, y: from.minY + (to.minY - from.minY) * scale, width: from.width + (to.width - from.width) * scale, height: from.height + (to.height - from.height) * scale)
}

在大多数情况下,比例自然必须在0到1之间。

现在,您需要一种使用计时器进行动画处理的方法,例如:

func animateFrame(to frame: CGRect, animationDuration duration: TimeInterval) {
    self.animationStartFrame = tempView.frame // Assign new values
    self.animationEndFrame = frame // Assign new values
    self.animationStartTime = Date() // Assign new values

    self.currentAnimationTimer.invalidate() // Remove current timer if any

    self.currentAnimationTimer = Timer.scheduledTimer(withTimeInterval: 1.0/60.0, repeats: true) { timer in
        let timeElapsed = Date().timeIntervalSince(self.animationStartTime)
        let scale = timeElapsed/duration
        self.tempView.frame = self.interpolateRect(from: self.animationStartFrame, to: self.animationEndFrame, scale: CGFloat(max(0.0, min(scale, 1.0))))
        if(scale >= 1.0) { // Animation ended. Remove timer
            timer.invalidate()
            self.currentAnimationTimer = nil
        }
     }
 }

为了公平起见,由于我们使用带块的计时器,因此仍可以减少此代码:

func animateFrame(to frame: CGRect, animationDuration duration: TimeInterval) {
    let startFrame = tempView.frame
    let endFrame = frame
    let animationStartTime = Date()

    self.currentAnimationTimer.invalidate() // Remove current timer if any

    self.currentAnimationTimer = Timer.scheduledTimer(withTimeInterval: 1.0/60.0, repeats: true) { timer in
        let timeElapsed = Date().timeIntervalSince(animationStartTime)
        let scale = timeElapsed/duration
        self.tempView.frame = self.interpolateRect(from: startFrame, to: endFrame, scale: CGFloat(max(0.0, min(scale, 1.0))))
        if(scale >= 1.0) { // Animation ended. Remove timer
            timer.invalidate()
            if(timer === self.currentAnimationTimer) {
                self.currentAnimationTimer = nil // Remove reference only if this is the current timer
            }
        }
     }
 }

这样,我们不需要在类中存储太多值,而是将所有内容保留在方法中。

提及宽松政策可能会很有趣。诀窍只是操纵我们的scale。考虑这样的事情:

func easeInScaleFromLinearScale(_ scale: CGFloat, factor: CGFloat = 0.2) -> CGFloat {
    return pow(scale, 1.0+factor)
}
func easeOutScaleFromLinearScale(_ scale: CGFloat, factor: CGFloat = 0.2) -> CGFloat {
    return pow(scale, 1.0/(1.0+factor))
}

现在我们要做的就是在插值时使用它:

self.tempView.frame = self.interpolateRect(from: startFrame, to: endFrame, scale: easeInScaleFromLinearScale(CGFloat(max(0.0, min(scale, 1.0)))))

修改因子将改变效果。系数越高,效果越高。而且使用零因子表示线性曲线。

您几乎可以执行任何类型的动画。唯一的规则是您的函数必须遵循f(0) = 0f(1) = 1。这意味着它从开始位置开始,到结束位置结束。

有些曲线有些棘手。 EaseInOut看起来很简单,但事实并非如此。我们可能希望在范围sin中实现类似[-PI/2, PI/2]的东西。然后将此函数与线性函数平衡。这是我发现的实现之一:

return (sin((inputScale-0.5) * .pi)+1.0)/2.0 * (magnitude) + (inputScale * (1.0-magnitude))

如果您使用一些“在线图形计算器”来查找方程式,然后将结果转换为函数,将很有帮助。