在运行动画中检索点击的 UIButton,该动画可能会稍微离开屏幕 [英] Retrieve tapped UIButton in a running animation which can be slightly off the screen

查看:22
本文介绍了在运行动画中检索点击的 UIButton,该动画可能会稍微离开屏幕的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是代码(如下)的样子:

This is what the code (below) can look like:

import UIKit

class TornadoButton: UIButton {

    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        let pres = self.layer.presentation()!
        let suppt = self.convert(point, to: self.superview!)
        let prespt = self.superview!.layer.convert(suppt, to: pres)

        return super.hitTest(prespt, with: event)
    }
}

class TestViewController: UIViewController {

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        let greySubview = UIView()
        greySubview.backgroundColor = .red
        greySubview.translatesAutoresizingMaskIntoConstraints = false
        self.view.addSubview(greySubview)

        greySubview.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
        greySubview.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true
        greySubview.widthAnchor.constraint(equalTo: self.view.widthAnchor).isActive = true
        greySubview.heightAnchor.constraint(equalTo: greySubview.widthAnchor).isActive = true

        let button = TornadoButton()
        greySubview.addSubview(button)
        button.translatesAutoresizingMaskIntoConstraints = false
        button.widthAnchor.constraint(equalTo: greySubview.widthAnchor, multiplier: 0.09).isActive = true
        button.heightAnchor.constraint(equalTo: greySubview.heightAnchor, multiplier: 0.2).isActive = true

        //below constrains are needed, else the origin of the UIBezierPath is wrong
        button.centerXAnchor.constraint(equalTo: greySubview.centerXAnchor).isActive = true
        button.centerYAnchor.constraint(equalTo: greySubview.centerYAnchor, constant: self.view.frame.height * 0.35).isActive = true
        self.view.layoutIfNeeded()

        button.addTarget(self, action: #selector(tappedOnCard(_:)), for: .touchUpInside)

        let circlePath = UIBezierPath(arcCenter: greySubview.frame.origin, radius: CGFloat(greySubview.frame.width * 0.5), startAngle: 0, endAngle: CGFloat.pi * 2, clockwise: true)

        let orbit = CAKeyframeAnimation(keyPath: "position")
        orbit.duration = 12
        orbit.path = circlePath.cgPath
        orbit.isAdditive = true
        orbit.repeatCount = Float.greatestFiniteMagnitude
        orbit.calculationMode = kCAAnimationPaced
        orbit.rotationMode = kCAAnimationRotateAuto

        button.layer.add(orbit, forKey: "orbit")
        button.backgroundColor = .blue

        let gr = UITapGestureRecognizer(target: self, action: #selector(onTap(gesture:)))
        greySubview.addGestureRecognizer(gr)
    }

    @objc func onTap(gesture:UITapGestureRecognizer) {
        let p = gesture.location(in: gesture.view)
        let v = gesture.view?.hitTest(p, with: nil)
    }

    @IBAction func tappedOnCard(_ sender: UIButton) {
        print(sender)
    }
}

这几乎有效,但是:

如果按钮在屏幕上 100% 可见,我可以检索该按钮.例如,如果它是 50% 可见的(和 50% 的屏幕外(看下图)),我不会检索按钮点击(也就是不检索打印).

I can retrieve the button if the button is 100% visible on the screen. If it is, for example, 50% visible (and 50% off the screen (look picture below)), I do not retrieve the buttons tap (aka not retrieve the print).

当按钮处于运行动画中并且它可能稍微离开屏幕时,如何检索按钮?

推荐答案

原答案:

很可能是 greySubview 中的 UITapGestureRecognizer 正在消耗触摸而不是让它传递到按钮.试试这个:

It is very likely that the UITapGestureRecognizer in greySubview is consuming the touch and not letting it pass through to the button. Try this:

gr.cancelsTouchesInView = NO;

在将 UITapGestureRecognizer 添加到 greySubview 之前.

before adding the UITapGestureRecognizer to greySubview.

编辑答案:

请忽略我之前的回答,如果您这样做了,请还原更改.我的原始答案没有意义,因为在您的情况下,按钮是 grayView 的子视图,而 cancelsTouchesInView 在视图层次结构中向上工作,而不是向下工作.

Please ignore my earlier answer and revert the change if you did it. My original answer did not make sense because in your case, the button is the subview of the grayView and cancelsTouchesInView works upwards in the view hierarchy, not downwards.

经过大量挖掘,我能够弄清楚为什么会发生这种情况.这是因为动画期间 TornadoButton 中的命中测试不正确.由于您正在为按钮设置动画,因此您需要覆盖 TornadoButtonhitTestpointInside 方法,以便它们说明动画位置而不是点击测试时按钮的实际位置.

After a lot of digging, I was able to figure out why this was happening. It was because of incorrect hit testing in the TornadoButton during animation. Since you are animating the button, you will need to override the hitTest and the pointInside methods of the TornadoButton so that they account for the animated position instead of the actual position of the button when hit testing.

pointInside 方法的默认实现没有考虑按钮的动画表现层,只是尝试检查矩形内的触摸点(这将失败,因为触摸是别的地方).

The default implementation of the pointInside method does not take into account the animated presentation layer of the button, and just tries to check for the touch point within the rect (which will fail because the touch was somewhere else).

这些方法的以下实现似乎可以解决问题:

The following implementations of the methods seem to do the trick:

class TornadoButton: UIButton{
    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        let pres = self.layer.presentation()!
        let suppt = self.convert(point, to: self.superview!)
        let prespt = self.superview!.layer.convert(suppt, to: pres)
        if (pres.hitTest(suppt)) != nil{
            return self
        }
        return super.hitTest(prespt, with: event)
    }

    override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
        let pres = self.layer.presentation()!
        let suppt = self.convert(point, to: self.superview!)
        return (pres.hitTest(suppt)) != nil
    }
}

这篇关于在运行动画中检索点击的 UIButton,该动画可能会稍微离开屏幕的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆