标签:
class MyButton: UIButton {
override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
if !self.userInteractionEnabled || self.hidden || self.alpha == 0 {
return nil
}
if CGRectContainsPoint(CGRectInset(self.bounds, -40, -40), point){
for subview in self.subviews.reverse() {
let convertPoint = subview.convertPoint(point, fromView:self)
if let sview = subview.hitTest(convertPoint, withEvent: event) {
return sview
}
}
return self
}
return nil
}
}
hitTest:withEvent:方法首先检查视图是否允许接收触摸事件。视图允许接收触摸事件的条件是:
视图不是隐藏的:self.hidden == NO
视图是允许交互的:self.userInteractionEnabled ==true
视图透明度大于0.01:self.alpha > 0.01
视图包含这个点: pointInside:withEvent: ==true
然后,如果视图允许接收触摸事件,这个方法通过从后往前发送hitTest:withEvent:消息给每一个子视图来穿过接收者的子树,直到子视图中的一个返回nil。这些子视图中的第一个返回的非nil就是在触摸点下面的最前面的视图,被接收者返回。如果所有的子视图都返回nil或者接收者没有子视图返回接收者自己。否则,如果视图不允许接收触摸事件,这个方法返回nil而根本不会传递到接收者的子树。因此,hit-test可能不会访问所有的视图体系结构中的视图。
测试代码和效果如下:
func testExpandButtonClickArea(){
//为了便于观察,添加一个背景视图,大小正好为100*100
let backgroundView = UIView(frame: CGRect(x: 60, y: 160, width: 100, height: 100))
backgroundView.backgroundColor = UIColor.purpleColor()
view.addSubview(backgroundView)
let btn = MyButton(type: .Custom)
btn.frame = CGRect(x: 100, y: 200, width: 20, height: 20)
btn.backgroundColor = UIColor.redColor()
btn.setTitle("btn", forState: .Normal)
btn.addTarget(self, action: #selector(UIButtonViewController.tapButton), forControlEvents: .TouchUpInside)
view.addSubview(btn)
}
func tapButton(){
print("button has been pressed!");
}
点击紫色区域内容,同样可以响应点击事件,可以在console看到打印输出:button has been pressed!
2:实现传递事件到点击视图之下的视图
有的时候对于一个视图忽略触摸事件并传递给下面的视图是很重要的。例如,假设一个透明的视图覆盖在应用内所有视图的最上面。覆盖层有子视图应该相应触摸事件的一些控件和按钮。但是触摸覆盖层的其他区域应该传递给覆盖层下面的视图。为了完成这个行为,覆盖层需要覆盖hitTest:withEvent:方法来返回包含触摸点的子视图中的一个,然后其他情况返回nil,包括覆盖层包含触摸点的情况:
class SHView: UIView {
override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
var hitTestView = super.hitTest(point, withEvent:event)
if hitTestView == self{
hitTestView = nil
}
return hitTestView;
}
}
测试部分代码:
func testCoverView(){
let btn1 = UIButton(type: .Custom)
btn1.frame = CGRect(x: 80, y: 200, width: 20, height: 20)
btn1.backgroundColor = UIColor.redColor()
btn1.setTitle("btn1", forState: .Normal)
btn1.addTarget(self, action: #selector(OverSuperViewController.tapButton(_:)), forControlEvents: .TouchUpInside)
view.addSubview(btn1)
let btn2 = UIButton(type: .Custom)
btn2.frame = CGRect(x: 120, y: 200, width: 20, height: 20)
btn2.backgroundColor = UIColor.yellowColor()
btn2.setTitle("btn2", forState: .Normal)
btn2.addTarget(self, action: #selector(OverSuperViewController.tapButton(_:)), forControlEvents: .TouchUpInside)
view.addSubview(btn2)
//添加一个覆盖层
let backgroundView = SHView(frame: CGRect(x: 60, y: 160, width: 100, height: 100))
backgroundView.backgroundColor = UIColor.purpleColor()
backgroundView.alpha = 0.75;
view.addSubview(backgroundView)
}
func tapButton(button:UIButton){
print("button = %@,title = %@",button,button.currentTitle);
}当点击覆盖层的时候,如果点击的位置属于对应的按钮的区域,将响应对应的触发事件,点击btn1将打印按钮1的相关信息,点击按钮2将打印按钮2的相关信息。页面效果如下:
3:超出父视图区域部分响应事件
首先看一下页面效果:当前页面上有3个控件,紫色视图是红色视图的子视图,红色视图是灰色视图的子视图。最上面是一个按钮,方便我们进行测试:现在我们要实现点击红色视图之外的紫色区域能够响应事件。
实现代码:自定义TestView实现hitTest方法,并调用我们对UIView的扩展方法
class TestView: UIView {
override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
super.hitTest(point, withEvent: event)
return overlapHitTest(point, withEvent: event)
}
}
extension UIView{
func overlapHitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
// We should not send touch events for hidden or transparent views, or views with userInteractionEnabled set to NO;
if !self.userInteractionEnabled || self.hidden || self.alpha == 0 {
return nil
}
// If touch is inside self, self will be considered as potential result.
var hitView: UIView? = self
if !self.pointInside(point, withEvent: event) {
if self.clipsToBounds {
return nil
} else {
hitView = nil
}
}
// Check recursively all subviews for hit. If any, return it.
for subview in self.subviews.reverse() {
let insideSubview = self.convertPoint(point, toView: subview)
if let sview = subview.overlapHitTest(insideSubview, withEvent: event) {
return sview
}
}
// Else return self or nil depending on result from step 2.
return hitView
}
}
测试部分代码:
func testOverSuperview(){
let view1 = TestView(frame:CGRect(x: 100, y: 100, width: 200, height: 200))
view1.backgroundColor = UIColor.lightGrayColor()
view.addSubview(view1)
let view2 = UIView(frame: CGRect(x: 40, y: 40, width: 100, height: 100))
view2.backgroundColor = UIColor.redColor()
view1.addSubview(view2)
let view3 = UIButton(type: .Custom)
view3.frame = (frame: CGRect(x: 10, y: 10, width: 200, height: 80))
view3.backgroundColor = UIColor.purpleColor()
view3.addTarget(self, action: #selector(ThirdViewController.tapButton), forControlEvents: .TouchUpInside)
view2.addSubview(view3)
}
func tapButton(){
print("button has been pressed!");
}
参考文章:
标签:
原文地址:http://blog.csdn.net/longshihua/article/details/51952824