CollectionView混淆了touchesBegan和touchesEnded方法中的动画

时间:2018-03-29 22:34:28

标签: ios swift uiview uicollectionview uiviewanimation

我有一个集合视图,用于保存带有图像视图的单元格。当用户按下图像视图(使用touchesBegan)时触发动画,并在用户释放时触发另一个动画(使用touchesEnded)。

只有当我按下我的点击然后释放(延迟点击)时,动画才能完美运行,但是当我快速点击时,动画会跳跃,就好像持续时间设置为0一样。

我认为问题是因为集合视图是滚动视图的子类,而scrollViews“通过启动计时器暂时拦截触摸事件,并且在计时器触发之前,查看触摸手指是否进行任何移动。如果计时器在没有显着位置变化的情况下触发,滚动视图将跟踪事件发送到内容视图的触摸子视图。“

https://developer.apple.com/documentation/uikit/uiscrollview#//apple_ref/doc/uid/TP40006922

从我可以收集到的内容来看,我认为如果点击比触摸计时器更快,则从集合视图中触摸拦截会导致动画出现问题。当我使用常规视图而不是集合视图作为超级视图进行测试时,动画效果很好,不需要延迟点击。

如果是这种情况,那么动画是如何触发快速点击的呢?而且,如何在不使用延迟点击的情况下触发动画?

如果不是这种情况,那么这个问题可能是什么原因?

这是我的动画和触摸代码:

func animateClickerAndBallPoint(newXpositionForClicker: CGFloat, newXpositionForBallPoint: CGFloat, ballPoint: UIImageView) {
    UIView.animate(withDuration: 0.1) {
        self.frame.origin.x = newXpositionForClicker
        ballPoint.frame.origin.x = newXpositionForBallPoint
    }
}

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    guard let ballPoint = self.ballPoint else {return}
    self.animateClickerAndBallPoint(newXpositionForClicker: 288, newXpositionForBallPoint: 11, ballPoint: ballPoint)
}

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
    guard let ballPoint = self.ballPoint else {return}

    if isInWritingMode == true {
        animateClickerAndBallPoint(newXpositionForClicker: 306, newXpositionForBallPoint: 26,  ballPoint: ballPoint)
        isInWritingMode = false

    } else {
        animateClickerAndBallPoint(newXpositionForClicker: 297, newXpositionForBallPoint: 17, ballPoint: ballPoint)
        isInWritingMode = true
    }
}

3 个答案:

答案 0 :(得分:2)

作为替代方案,您可以考虑挂钩按钮touchesBegantouchesEnded,而不是自己做beginTrackingendTracking

例如,您可以将按钮子类化并提供您想要的任何动画:

class AnimatedButton: UIButton {

    override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
        UIView.animate(withDuration: 0.25) {
            self.transform = .init(scaleX: 0.8, y: 0.8)
        }

        return true
    }

    override func endTracking(_ touch: UITouch?, with event: UIEvent?) {
        UIView.animate(withDuration: 0.25) {
            self.transform = .identity
        }
    }

}

产量:

enter image description here

或者,如果你想根据单元格本身的部分来做,你可以挂钩到集合视图的“突出显示”机制,如https://stackoverflow.com/a/45664054/1271826所示。

顺便说一下,你假设问题是容器视图弄乱了。你100%确定这是问题吗?这可能是一个基本的动画问题。 E.g。

  • 例如,如果您在第一个动画仍在运行时开始第二个动画,它将无法完成第一个动画,而是立即从动画中期的任何地方立即开始第二个动画(并且在现代动画中) iOS版本,使用当前正在运行的任何速度)并转换到新目的地。

    例如,这里有两个视图,我正在以相同的距离向下播放一秒钟,然后再向上播放另一秒钟。但是对于右边的视图,我在第一个动画中开始第二个动画0.1秒(即中断第一个动画):

    enter image description here

    正如您所看到的,因为第二个示例正在中断您的动画,所以它看起来只是抢回来。

  • 我认为在这种情况下不太可能,但如果您使用自动布局,则必须小心。如果您执行任何触发自动布局引擎重新应用其约束的事情(这实际上可能是任何UI操作,例如在某些不相关的标签中设置文本一样无害),这将阻止您正在进行的任何动画和将视图捕捉回到受限制的位置。

    如果使用自动布局布置视图,您可能还需要考虑使用自动布局设置动画。例如,不是调整frame(或其他),而是为约束创建IBOutlet,更改该约束的constant,然后将调用设置为layoutIfNeeded

最重要的是,您可能想知道是否可以验证问题是否与触摸事件有关,而不是某些无关的动画问题。

不幸的是,您的示例中没有足够的内容来诊断问题的根源。您可以考虑创建MVCE, a minimal, verifiable, and complete example of the problem。我建议创建一个没有集合视图和另一个集合视图的MVCE,这样你就可以确认集合视图是否真的是问题的根源。但是,直到我们能够重现您的问题,我们很难帮您解决问题。

答案 1 :(得分:1)

scrollviews中播放动画时遇到了同样的问题。 我通过在动画中使用该参数来修复它:UIViewAnimationOptions.allowUserInteraction

所以你的动画块会像那样结束:

UIView.animate(withDuration: duration,
                                   delay: 0,
                                   usingSpringWithDamping: CGFloat(0.30),
                                   initialSpringVelocity: CGFloat(10.0),
                                   options: UIViewAnimationOptions.allowUserInteraction,
                                   animations: {

                                    applyWhatEverTransform()

                    },completion: completion)

我有点猜测你的问题,因为我们没有你的代码。但是,当然,如果您使用不同类型的动画,则无法使用;)

此外,如果这没有帮助。为了帮助您进行调试,您可以尝试禁用滚动集合视图以查看拦截触摸事件的事件。

答案 2 :(得分:1)

您是否尝试过delaysContentTouches = false

https://developer.apple.com/documentation/uikit/uiscrollview/1619398-delayscontenttouches

告诉scrollView / collectionView不要延迟对单元格的触摸。当我开始点击它们时,我立即为我做了响应细胞。也没有制作我的滚动车。