在动画 UIView 上点击手势不起作用
Tap Gesture on animating UIView not working
我在正在动画化翻译的 UILabel 上有一个点击手势。每当您在动画期间点击标签时,点击手势都没有响应。
这是我的代码:
label.addGestureRecognizer(tapGesture)
label.userInteractionEnabled = true
label.transform = CGAffineTransformMakeTranslation(0, 0)
UIView.animateWithDuration(12, delay: 0, options: UIViewAnimationOptions.AllowUserInteraction, animations: { () -> Void in
label.transform = CGAffineTransformMakeTranslation(0, 900)
}, completion: nil)
手势代码:
func setUpRecognizers() {
tapGesture = UITapGestureRecognizer(target: self, action: "onTap:")
}
func onTap(sender : AnyObject) {
print("Tapped")
}
有什么想法吗?谢谢:)
为 2021 年添加的注释:
现在这非常简单,您只需重写 hitTest。
要使点击手势生效,您必须设置点击次数。添加此行:
tapGesture.numberOfTapsRequired = 1
(我假设 tapGesture
与您调用的 label.addGestureRecognizer(tapGesture)
相同)
如果你想看到动画,你需要把它放在onTap
处理程序中。
let gesture = UITapGestureRecognizer(target: self, action: #selector(onTap(sender:))
gesture.numberOfTapsRequired = 1
label.addGestureRecognizer(gesture)
label.userInteractionEnabled = true
label.transform = CGAffineTransformMakeTranslation(0, 0)
UIView.animateWithDuration(12, delay: 3, options: [.allowUserInteraction], animations: { () -> Void in
label.transform = CGAffineTransformMakeTranslation(0, 900)
}, completion: nil)
func onTap(sender: AnyObject)
{
print("Tapped")
}
出于 1 个重要原因,您将无法在使用敲击手势后完成现在的工作。 tapgesture 与标签的框架相关联。标签最终帧在开始动画时立即更改,您只是在看假电影(动画)。如果您能够触摸屏幕上的 (0,900),它会在动画发生时正常触发。有一种方法可以做到这一点,但有点不同。最好的办法是使用 touchesBegan。这是我刚刚写的一个扩展来测试我的理论,但可以进行调整以适合您的 needs.For 示例,您可以使用您的实际子类并访问标签属性而无需循环。
extension UIViewController{
public override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
guard let touch = touches.first else{return}
let touchLocation = touch.locationInView(self.view)
for subs in self.view.subviews{
guard let ourLabel = subs as? UILabel else{return}
print(ourLabel.layer.presentationLayer())
if ourLabel.layer.presentationLayer()!.hitTest(touchLocation) != nil{
print("Touching")
UIView.animateWithDuration(0.4, animations: {
self.view.backgroundColor = UIColor.redColor()
}, completion: {
finished in
UIView.animateWithDuration(0.4, animations: {
self.view.backgroundColor = UIColor.whiteColor()
}, completion: {
finished in
})
})
}
}
}
}
您可以看到它正在测试 CALayer.presentationLayer() 的坐标。这就是我所说的电影。老实说,我还没有完全了解表示层及其工作原理。
以下是基于@agibson007 在Swift 3.
中的回答的更通用的回答
这并没有立即解决我的问题,因为我有额外的子视图覆盖了我的视图。如果遇到问题,请尝试更改扩展类型并为 touchLocation
编写打印语句以查明函数何时触发。接受的答案中的描述很好地解释了这个问题。
extension UIViewController {
open override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
let touchLocation = touch.location(in: self.view)
for subview in self.view.subviews {
if subview.tag == VIEW_TAG_HERE && subview.layer.presentation()?.hitTest(touchLocation) != nil {
print("[UIViewController] View Touched!")
// Handle Action Here
}
}
}
}
如何在移动的视图中检测触摸
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let pf = layer.presentation()!.frame
// note, that is in the space of our superview
let p = self.convert(point, to: superview!)
if pf.contains(p) { return self }
return nil
}
就这么简单
相关提示 -
不要忘记,在大多数情况下,如果动画是 运行,您当然几乎肯定会想要取消它。因此,假设有一个 "moving target" 并且您希望能够用手指抓住它并将其滑动到其他地方,自然地在该用例中,您的视图控制器中的代码将类似于 ..
func sliderTouched() {
if alreadyMoving {
yourPropertyAnimator?.stopAnimation(true)
yourPropertyAnimator = nil
}
etc ...
}
我在这个问题上被困了几个小时,无法理解为什么在延迟 3 秒后滑出屏幕的动画标签上点击不起作用。
说得好agibson007,关于动画就像假电影的播放,3秒的延迟控制了电影的回报,但是标签的帧在动画开始时立即改变,没有延迟.所以敲击(取决于标签在其原始位置的框架)将不起作用。
我的解决方案是将 3 秒延迟更改为超时函数,例如 -
DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
[weak self] in
self?.hideLabel()
}
这样可以在延迟期间保持点击有效,并允许动画在延迟后的 hideLabel() 调用中运行。
我在正在动画化翻译的 UILabel 上有一个点击手势。每当您在动画期间点击标签时,点击手势都没有响应。
这是我的代码:
label.addGestureRecognizer(tapGesture)
label.userInteractionEnabled = true
label.transform = CGAffineTransformMakeTranslation(0, 0)
UIView.animateWithDuration(12, delay: 0, options: UIViewAnimationOptions.AllowUserInteraction, animations: { () -> Void in
label.transform = CGAffineTransformMakeTranslation(0, 900)
}, completion: nil)
手势代码:
func setUpRecognizers() {
tapGesture = UITapGestureRecognizer(target: self, action: "onTap:")
}
func onTap(sender : AnyObject) {
print("Tapped")
}
有什么想法吗?谢谢:)
为 2021 年添加的注释:
现在这非常简单,您只需重写 hitTest。
要使点击手势生效,您必须设置点击次数。添加此行:
tapGesture.numberOfTapsRequired = 1
(我假设 tapGesture
与您调用的 label.addGestureRecognizer(tapGesture)
相同)
如果你想看到动画,你需要把它放在onTap
处理程序中。
let gesture = UITapGestureRecognizer(target: self, action: #selector(onTap(sender:))
gesture.numberOfTapsRequired = 1
label.addGestureRecognizer(gesture)
label.userInteractionEnabled = true
label.transform = CGAffineTransformMakeTranslation(0, 0)
UIView.animateWithDuration(12, delay: 3, options: [.allowUserInteraction], animations: { () -> Void in
label.transform = CGAffineTransformMakeTranslation(0, 900)
}, completion: nil)
func onTap(sender: AnyObject)
{
print("Tapped")
}
出于 1 个重要原因,您将无法在使用敲击手势后完成现在的工作。 tapgesture 与标签的框架相关联。标签最终帧在开始动画时立即更改,您只是在看假电影(动画)。如果您能够触摸屏幕上的 (0,900),它会在动画发生时正常触发。有一种方法可以做到这一点,但有点不同。最好的办法是使用 touchesBegan。这是我刚刚写的一个扩展来测试我的理论,但可以进行调整以适合您的 needs.For 示例,您可以使用您的实际子类并访问标签属性而无需循环。
extension UIViewController{
public override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
guard let touch = touches.first else{return}
let touchLocation = touch.locationInView(self.view)
for subs in self.view.subviews{
guard let ourLabel = subs as? UILabel else{return}
print(ourLabel.layer.presentationLayer())
if ourLabel.layer.presentationLayer()!.hitTest(touchLocation) != nil{
print("Touching")
UIView.animateWithDuration(0.4, animations: {
self.view.backgroundColor = UIColor.redColor()
}, completion: {
finished in
UIView.animateWithDuration(0.4, animations: {
self.view.backgroundColor = UIColor.whiteColor()
}, completion: {
finished in
})
})
}
}
}
}
您可以看到它正在测试 CALayer.presentationLayer() 的坐标。这就是我所说的电影。老实说,我还没有完全了解表示层及其工作原理。
以下是基于@agibson007 在Swift 3.
中的回答的更通用的回答这并没有立即解决我的问题,因为我有额外的子视图覆盖了我的视图。如果遇到问题,请尝试更改扩展类型并为 touchLocation
编写打印语句以查明函数何时触发。接受的答案中的描述很好地解释了这个问题。
extension UIViewController {
open override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
let touchLocation = touch.location(in: self.view)
for subview in self.view.subviews {
if subview.tag == VIEW_TAG_HERE && subview.layer.presentation()?.hitTest(touchLocation) != nil {
print("[UIViewController] View Touched!")
// Handle Action Here
}
}
}
}
如何在移动的视图中检测触摸
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let pf = layer.presentation()!.frame
// note, that is in the space of our superview
let p = self.convert(point, to: superview!)
if pf.contains(p) { return self }
return nil
}
就这么简单
相关提示 -
不要忘记,在大多数情况下,如果动画是 运行,您当然几乎肯定会想要取消它。因此,假设有一个 "moving target" 并且您希望能够用手指抓住它并将其滑动到其他地方,自然地在该用例中,您的视图控制器中的代码将类似于 ..
func sliderTouched() {
if alreadyMoving {
yourPropertyAnimator?.stopAnimation(true)
yourPropertyAnimator = nil
}
etc ...
}
我在这个问题上被困了几个小时,无法理解为什么在延迟 3 秒后滑出屏幕的动画标签上点击不起作用。
说得好agibson007,关于动画就像假电影的播放,3秒的延迟控制了电影的回报,但是标签的帧在动画开始时立即改变,没有延迟.所以敲击(取决于标签在其原始位置的框架)将不起作用。
我的解决方案是将 3 秒延迟更改为超时函数,例如 -
DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
[weak self] in
self?.hideLabel()
}
这样可以在延迟期间保持点击有效,并允许动画在延迟后的 hideLabel() 调用中运行。