关于hit-test用法,视图事件传递链的控制

来源:互联网 发布:女s网络调教男m步骤 编辑:程序博客网 时间:2024/04/20 10:30

今天,群里有人问了这个问题:添加了touch事件之后怎么阻止touch事件传递到子view。其实看了官方的文档Event Handling Guide for iOS的童鞋,应该是没有问题的。但是自己还是总结一下。

触摸之后,主要的步骤如下:

(1), 事件分发:如何确定当前点击的点由哪个view来处理?
hit-test来确定hit-view
(2), 事件响应:确定hit-view之后,如何处理事件? 

当确定了hit-view之后,第一响应者就是当前的hit-view,然后就会根据响应者链来处理触摸事件。

有手势的先处理手势,手势识别失败后,执行touch系列回调处理。


情景应用
问题1:如果父视图userInteractionEnable是NO,这时候子视图能接收touch事件吗?
分析:不能
因为在hit-testing的时候父视图返回nil了,那么就轮不到子视图来hit-testing了。
这也是为何在imgView上面加载UIButton的时候,button无法响应的原因

问题2:如果一个视图A(A上面加载了手势处理)被视图B盖住了,A与B都是视图X的子视图,那么怎样让A的手势能响应?
分析: 因为B盖住了A,所以hit-test的结果之后,hit-view肯定是B,A的手势无法响应,
可以这么做:
  1, 设置B.userInteractionEnable = NO;
  2, B.hidden = YES;
  3, B.alpha = 0;
上面的3种情况下,A都可以响应手势了。
因为这么设置之后,在hit-testing的时候,B视图的hitTest方法返回的是nil,最终的hit-view是A,所以触摸事件就轮到了A来处理。

问题3:如果一个viewA不希望它的subView来处理touch事件,而是由自己处理,怎么办?
分析:
viewA不希望触摸事件传递到它的subView, 也就是viewA自己阻断触摸事件的传递,只要让触摸后最终的hit-view是他自己就可以了。
比如viewA的subView为YLViewSub1
有如下2种方法:
方法一:不推荐
在viewA的.m文件中重载hitTest(注意:viewA是一个自定义的UIView才能重载此方法),如下
-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
  UIView *hitView = [super hitTest:point withEvent:event];
  // 此时hitView是已经检测出的hit-view,self or subViews(hitted subView)
  
  
/* 注意:
   * 
如果想要阻断触摸事件传递给subView,下面的2种做法是不太合理的:
   */
 
  /* 方法1:
   * 不管3721的直接返回self也是不对的,因为当没有点击在self(包括它的subView)的时候,self都成了hit-view
   */

  
return self;
 
  /* 方法2:
   * 因为hitView可能返回的是它的subViewsubViewsubView...
   * 
所以不能这么做如果能确定selfsubView只有一级,这么做也是可以的.
   */

  
if (hitView.superview == self && hitView == self) {
    
return self// 点击在它的subView上也由它自己来处理,subView永远不是hit-view(永远不会是第一响应者,不处理触摸)
  }
 
  /* 正确的做法:也就是下面的方法二,在subView中重载hitTest
   * 1,可以在selfsubView中重载hitTest方法,直接返回nil,那么点击在selfsubView上的时候,最后hit-view还是self
   * 
所以在重载此方法的时候一定要搞清楚具体的应用场景.
   */

  
  
return nil;
}
可以看到直接在viewA中重载还是不太好的实现,而且如果viewA是一个vc.view,那么就没办法重载hitTest方法了。

方法二:推荐
在viewA的subView(YLViewSub1)的类中重载hitTest,
在YLViewSub1的.m文件中,
-(id)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
  
UIView *hitView = [super hitTest:point withEvent:event];

  if (hitView == self) {
    
return nil;
  } 
else {
    
return hitView;
  }
}
有人可能有疑问了:上面说过直接在这里返回nil不就可以了,为什么还要分情况处理。
其实这要看具体的情况了,如果YLViewSub1上面还有subView,直接的返回nil,那么就会忽略掉,所以如果你想全部忽略掉就直接返回nil,不然可以像上面这么处理。
另外,还有一种更简单的做法,直接让viewA的subView的userInteraction为NO,那么subView就不会受到触摸消息了。

扩展:
hit-test还有另外一个场景,比如viewA有B,C两个subView,但是他们2个有重合的部分,点击重合部分,那如何指定让B响应还是C来响应。
分析:
默认情况下,hit-test的时候,是从subViews的最顶上的subView开始执行hit-test,即假如先加B,再加载C,那么hit-test就是先C先B后,这样点击重合部分,那就是C就是hit-view,那么由C来响应。
如何变成让B先响应呢?
-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
  
UIView *hitView = [super hitTest:point withEvent:event];
  if (hitView == viewC) { // 点击在C上的时候,让hit-viewB,那么B就是第一响应者
    return viewB;
  } 
else {
    
return hitView;
  }
}
这样,当点击重合部分就由B响应,点击B,C非重合部分由他们各自响应.

问题4:如果一个view自己不愿意处理touch事件,但是希望它的subViews处理,怎么办?
应用场景:这个问题有点sb,因为默认情况下就是subView来先处理,应用场景在哪?
可能是如果点击view自己,让他的父视图处理,点击view上面的subView由subView响应更合理?
分析:
设置view.userInteractionEnable = NO;之后,虽然自己不会响应touch事件,但是它的子view也不会响应了,
所以不能这么做。这时候就需要使用hitTest来处理,
-( UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
  UIView *hitView = [super hitTest:point withEvent:event]; 
  if (hitView == self) {
    return nil// self的时候不做处理,由它的父视图处理 
  } else {
   
return hitView; // subView的时候,subView去处理
  }
}

问题5:如果一个view自己不想处理,也不愿意往它的响应者链传递让别人处理,怎么办?
分析:首先,确定处理对象的时候必须是自己,然后,在自己这里处理的时候丢弃,
也就是自己重载响应函数,然后响应函数里面不做任何事,这样就不会继续向上传递了,也就是在自己这里做一个空处理来截止响应的处理.

问题6:一个全屏的UIView上加载了tap手势,在此view上加载一个UITableView,点击cell的时候没有执行tableview的didSelected:方法,而是响应了_onTap手势,如何处理?
解决方法:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
  
// tianyi memo:
  
// 点击在tableView上时,因为tableView自己不响应tap,所以会交给它的父视图self来响应,也就是响应_onTap:,但这不是我们想要的
  
// 我们需要点击tableView上面时,响应tableViewdidSelectRowAtIndexPath方法.点击其他空白地方相应_onTap:
  
// 返回NO表示,tap手势不会根据响应者链传递了,当前的touch对象会被忽略,也就是丢弃这个手势,
  
// 丢弃手势之后,相当于手势识别失败,然后就会走默认的touch系列回调方法,我猜测在这个时候UITableView执行了自己默认的选择cell的流程.
  
if ([touch.viewisDescendantOfView:_tableView]) {
    
returnNO;
  }
  returnYES;
}


如果有不正确或者考虑不完善的地方,欢迎指正交流。



本文转自:http://blog.csdn.net/itianyi/article/details/50550099
0 0
原创粉丝点击