iOS开发之-事件的传递和响应

2021-03-27  本文已影响0人  剑老师

本文首发于公众号【一个老码农】

注:在查找响应者过程当中,hitTest和pointInside方法其实是会调用两次的,两次为同一个事件,只是事件的状态不同。一次为事件的begain,一次为事件的end。

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
}
    
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
}    

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
}

view上有一个button,view和button分别绑定了事件。但是我在点击button的时候,想忽略掉button点击事件,转而让view响应。

分析:在Button的hitTest方法中,如果super.hitTest返回的对象是自己,则返回nil。这样就可以忽略掉button的点击事件。

代码如下:

override func viewDidLoad() {
        super.viewDidLoad()        
        let bgView = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
        bgView.backgroundColor = UIColor.blue        
        let tap = UITapGestureRecognizer(target: self, action: #selector(viewTap))
        view.addGestureRecognizer(tap)
        view.addSubview(bgView)
        
        let button = CustomButton(type: .custom)
        button.frame = CGRect(x: 100, y: 100, width: 50, height: 50)
        button.setTitle("Click", for: .normal)
        button.setTitleColor(.blue, for: .normal)
        button.addTarget(self, action: #selector(click), for: .touchUpInside)
        button.backgroundColor = UIColor.red
        bgView.addSubview(button)        
    }

@objc private func viewTap() {
     print("调用的是view")
}
            
@objc private func click() {
     print("点击")
}

//重写button的hitTest方法
class CustomButton: UIButton {
    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        print("CustomButton hitTest")
        let testView = super.hitTest(point, with: event)
        if testView == self {
            return nil
        }
        return testView 
    }
}

点击Button之后控制台打印效果:


实例2:Button超过父View的范围,点击范围外的部分,仍然让其响应事件

如图,点击蓝色外的红色部分,仍然让其响应Click事件。


分析:在父view的pointInside里面,判断子view是否为Button类型,如果是,则判断是否点击在button范围内。

如下代码,点击蓝色区域外的button区域,则可以响应button事件:

class CustomView: UIView {
    override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
        for subView in subviews {
            if subView.classForCoder == CustomButton.classForCoder() {
                let subPoint = self.convert(point, to: subView)
                if subView.point(inside: subPoint, with: event) {
                    return true
                }
            }
        }
        return super.point(inside: point, with: event)
    }
}

实例3:让UIButton的响应区域变大
如,我想让工程中所有的UIButton响应区域都至少为50*50pt。
思路:重写UIButton的pointInside方法,在此方法内判断button的响应区域,若在button的50pt范围内的,则返回true。代码如下:

extension UIButton {
    ///所有按钮的可点击区域不小于50*50
    open override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
        var bounds = self.bounds
        let widthDelta = max(50.0 - bounds.size.width, 0)
        let heightDelta = max(50.0 - bounds.size.height, 0)
        bounds = bounds.insetBy(dx: -0.5 * widthDelta, dy: -0.5 * heightDelta)
        return bounds.contains(point)
    }
}

总结:
1.事件的传递是由UIApplication、UIWindow、UIView...从父view至子view递归的传递,直至找到最佳响应者。事件的响应是从子view至父view一层一层的传递,直至找到可以处理响应事件的view。

2.我们可以通过重写hitTest和pointInside来改变事件的响应链或修改view的响应区域。

关注公众号【一个老码农】免费获取iOS进阶学习视频

原文地址:事件的传递和响应

上一篇 下一篇

猜你喜欢

热点阅读