CollectionView 与 touchesBegan 和 touchesEnded 方法中的动画混淆
CollectionView messes with animations in touchesBegan and touchesEnded methods
我有一个集合视图,其中包含带有图像视图的单元格。当用户按下图像视图(使用 touchesBegan)时触发动画,当用户释放时触发另一个动画(使用 touchesEnded)。
仅当我按住单击然后松开(延迟单击)时动画才能完美运行,但是当我快速单击时,动画会像持续时间设置为 0 一样跳跃。
我认为问题是因为集合视图是滚动视图的子类,而 scrollViews "temporarily intercepts a touch-down event by starting a timer and, before the timer fires, seeing if the touching finger makes any movement. If the timer fires without a significant change in position, the scroll view sends tracking events to the touched subview of the content view."
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
}
}
我在 scrollviews
中播放动画时遇到了同样的问题。
我通过在动画中使用该参数修复了它:UIViewAnimationOptions.allowUserInteraction
所以你的动画块最终会变成这样:
UIView.animate(withDuration: duration,
delay: 0,
usingSpringWithDamping: CGFloat(0.30),
initialSpringVelocity: CGFloat(10.0),
options: UIViewAnimationOptions.allowUserInteraction,
animations: {
applyWhatEverTransform()
},completion: completion)
我有点猜测你的问题,因为我们没有你的代码。但是当然,如果您使用不同类型的动画,这将无法工作;)
此外,如果这没有帮助。为了帮助您进行调试,您可以尝试禁用集合视图的滚动以查看该事件是否拦截了您的触摸事件。
作为替代方案,您可以考虑挂接到按钮的 beginTracking
和 endTracking
。
,而不是自己做 touchesBegan
和 touchesEnded
例如,您可以将按钮子类化并提供您想要的任何动画:
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
}
}
}
产生:
或者,如果您想根据单元格本身的部分来执行此操作,您可以挂钩到集合视图的 "highlighting" 机制,如 中所示。
顺便说一句,您假设问题是容器视图与触摸混淆。你 100% 确定这就是问题所在吗?这可能是一个基本的动画问题。例如
例如,如果您在第一个动画还在 运行 时开始第二个动画,它不会完成第一个动画,而是会立即从它所在的位置开始第二个动画mid-animation(在现代 iOS 版本中,使用当前行驶的任何速度)并过渡到新目的地。
例如,这里有两个视图,我正在为它们设置动画,它们向下移动一秒钟,然后再向上移动一秒钟。但是对于右边的视图,我在第一个动画开始 0.1 秒后开始第二个动画(即打断第一个动画):
如您所见,因为第二个示例打断了您的动画,所以看起来它只是快速返回。
我认为在这种情况下不太可能,但如果您使用自动布局,则必须小心。如果您执行任何触发自动布局引擎 re-apply 其约束的操作(这实际上可以是任何 UI 操作,例如在一些不相关的标签中设置文本这样无伤大雅的操作),它将停止无论您正在进行什么动画,都会将视图捕捉到约束规定的位置。
如果视图是使用自动布局布局的,您可能还想考虑使用自动布局为它们设置动画。例如,不是调整 frame
(或其他),而是为您的约束创建 IBOutlet
,更改该约束的 constant
,然后将调用动画化为 layoutIfNeeded
。
最重要的是,您可能想看看是否可以验证问题是否真的与触摸事件有关,而不是一些不相关的动画问题。
不幸的是,您的示例中的内容不足以让我们诊断问题的根源。您可以考虑创建一个 MVCE, a minimal, verifiable, and complete example of the problem。我建议创建一个不带集合视图的 MVCE,再创建一个带集合视图的 MVCE,这样您就可以确认集合视图是否真的是问题的根源。但是,归根结底,在我们能够重现您的问题之前,我们很难帮助您解决问题。
你试过了吗delaysContentTouches = false
?
https://developer.apple.com/documentation/uikit/uiscrollview/1619398-delayscontenttouches
告诉 scrollView/collectionView 不要延迟对单元格的触摸。当我开始点击它们时,帮助我立即制作响应单元格。也没有让我的滚动越野车。
我有一个集合视图,其中包含带有图像视图的单元格。当用户按下图像视图(使用 touchesBegan)时触发动画,当用户释放时触发另一个动画(使用 touchesEnded)。
仅当我按住单击然后松开(延迟单击)时动画才能完美运行,但是当我快速单击时,动画会像持续时间设置为 0 一样跳跃。
我认为问题是因为集合视图是滚动视图的子类,而 scrollViews "temporarily intercepts a touch-down event by starting a timer and, before the timer fires, seeing if the touching finger makes any movement. If the timer fires without a significant change in position, the scroll view sends tracking events to the touched subview of the content view."
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
}
}
我在 scrollviews
中播放动画时遇到了同样的问题。
我通过在动画中使用该参数修复了它:UIViewAnimationOptions.allowUserInteraction
所以你的动画块最终会变成这样:
UIView.animate(withDuration: duration,
delay: 0,
usingSpringWithDamping: CGFloat(0.30),
initialSpringVelocity: CGFloat(10.0),
options: UIViewAnimationOptions.allowUserInteraction,
animations: {
applyWhatEverTransform()
},completion: completion)
我有点猜测你的问题,因为我们没有你的代码。但是当然,如果您使用不同类型的动画,这将无法工作;)
此外,如果这没有帮助。为了帮助您进行调试,您可以尝试禁用集合视图的滚动以查看该事件是否拦截了您的触摸事件。
作为替代方案,您可以考虑挂接到按钮的 beginTracking
和 endTracking
。
touchesBegan
和 touchesEnded
例如,您可以将按钮子类化并提供您想要的任何动画:
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
}
}
}
产生:
或者,如果您想根据单元格本身的部分来执行此操作,您可以挂钩到集合视图的 "highlighting" 机制,如
顺便说一句,您假设问题是容器视图与触摸混淆。你 100% 确定这就是问题所在吗?这可能是一个基本的动画问题。例如
例如,如果您在第一个动画还在 运行 时开始第二个动画,它不会完成第一个动画,而是会立即从它所在的位置开始第二个动画mid-animation(在现代 iOS 版本中,使用当前行驶的任何速度)并过渡到新目的地。
例如,这里有两个视图,我正在为它们设置动画,它们向下移动一秒钟,然后再向上移动一秒钟。但是对于右边的视图,我在第一个动画开始 0.1 秒后开始第二个动画(即打断第一个动画):
如您所见,因为第二个示例打断了您的动画,所以看起来它只是快速返回。
我认为在这种情况下不太可能,但如果您使用自动布局,则必须小心。如果您执行任何触发自动布局引擎 re-apply 其约束的操作(这实际上可以是任何 UI 操作,例如在一些不相关的标签中设置文本这样无伤大雅的操作),它将停止无论您正在进行什么动画,都会将视图捕捉到约束规定的位置。
如果视图是使用自动布局布局的,您可能还想考虑使用自动布局为它们设置动画。例如,不是调整
frame
(或其他),而是为您的约束创建IBOutlet
,更改该约束的constant
,然后将调用动画化为layoutIfNeeded
。
最重要的是,您可能想看看是否可以验证问题是否真的与触摸事件有关,而不是一些不相关的动画问题。
不幸的是,您的示例中的内容不足以让我们诊断问题的根源。您可以考虑创建一个 MVCE, a minimal, verifiable, and complete example of the problem。我建议创建一个不带集合视图的 MVCE,再创建一个带集合视图的 MVCE,这样您就可以确认集合视图是否真的是问题的根源。但是,归根结底,在我们能够重现您的问题之前,我们很难帮助您解决问题。
你试过了吗delaysContentTouches = false
?
https://developer.apple.com/documentation/uikit/uiscrollview/1619398-delayscontenttouches
告诉 scrollView/collectionView 不要延迟对单元格的触摸。当我开始点击它们时,帮助我立即制作响应单元格。也没有让我的滚动越野车。