您的位置:首页 > 其它

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

2016-05-20 16:03 351 查看
今天,群里有人问了这个问题:添加了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可能返回的是它的subView的subView的subView...

   * 所以不能这么做. 如果能确定self的subView只有一级,这么做也是可以的.

   */

  if (hitView.superview == self &&
hitView == self) {

    return self; // 点击在它的subView上也由它自己来处理,subView永远不是hit-view(永远不会是第一响应者,不处理触摸)

  }
 
  /* 正确的做法:也就是下面的方法二,在subView中重载hitTest
   * 1,可以在self的subView中重载hitTest方法,直接返回nil,那么点击在self的subView上的时候,最后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-view为B,那么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上面时,响应tableView的didSelectRowAtIndexPath方法.点击其他空白地方相应_onTap:

  // 返回NO表示,tap手势不会根据响应者链传递了,当前的touch对象会被忽略,也就是丢弃这个手势,

  // 丢弃手势之后,相当于手势识别失败,然后就会走默认的touch系列回调方法,我猜测在这个时候UITableView执行了自己默认的选择cell的流程.

  if ([touch.viewisDescendantOfView:_tableView])
{

    returnNO;

  }
  returnYES;
}

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

本文转自:http://blog.csdn.net/itianyi/article/details/50550099
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: