在有些开发场景下,我们在scrollView上放置了视图,我们想未按住子视图时拖动scrollView,按住子视图时拖动子视图, 但是实际会发现按住视图 ,无法拖动视图,拖动的还是scrollView。
原因是:
手势识别器比UIResponder具有更高的事件响应优先级。
Window在将事件传递给hit-tested view之前,会先将事件传递给相关的手势识别器并由手势识别器优先识别,而scrollView上系统就添加了手势。若手势识别器成功识别了事件,就会取消hit-tested view对事件的响应;若手势识别器没能识别事件,hit-tested view才完全接手事件的响应权。
解决办法: 设置delaysContentTouches = NO. 继承 scrollView 重写下面方法
called before scrolling begins if touches have already been delivered to a subview of the scroll view. if it returns NO the touches will continue to be delivered to the subview and scrolling will not occur
not called if canCancelContentTouches is NO. default returns YES if view isn't a UIControl
this has no effect on presses
- (BOOL)touchesShouldCancelInContentView:(UIView *)view {
if ([view isKindOfClass:NSClassFromString(@"TestView")]) { //判断是否是需要拖动的子视图,
return NO;
}
return [super touchesShouldCancelInContentView:view];
}
scrollView.delaysContentTouches = NO;
为什么这样做?
touchesShouldCancelInContentView解读:是否取消给定视图上的touches事件传递。
- 该方法会在scrollView滚动前调用,前提是touches事件已经传递到子视图上面, 如果返回NO,那么事件就会继续传递到子视图上面,scrollView就不会响应滚动了。
- canCancelContentTouches=YES时该方法才会调用。canCancelContentTouches:
default is YES. if NO, then once we start tracking, we don't try to drag if the touch moves.
canCancelContentTouches设置为NO时,表示拖动视图时,不再去drag scrollView。看解释,感觉我们这个需求直接设置这个参数为NO即可,经过我的测试,确实是可以的,但是需要拖动视图时,需要按住一段时间后,才能拖动视图,否则还是会拖动scrollView,这个涉及到手势的识别过程,长按过程中scrollView的手势识别完成后,才会将事件交由hit-tested view。所以设置这个参数为NO是不够达到所需效果的。 - default returns YES if view isn't a UIControl:在子视图不是UIControl是默认返回YES, 意思子视图是UIControl的话,事件会传递到UIControl上,另一个方面说明UIControl事件优先级更高。
scrollView.delaysContentTouches 解读:这个字段描述的是子控件响应触摸事件是否立即响应触摸事件,默认为YES。
- 该设置应该是一种优化,因为系统识别手势需要一定的时间判断, 该值默认YES,给手势识别预留了0.15s的时间,在这个时间内,hit-tested view对事件的响应就被延迟,如果直接scrollView手势直接识别了,那么subView的touchBegin都不会执行了。
- 如果默认为YES的话,会发现按住就马上开始移动手指时还是scrollView在滚动。
这是因为subView的touchBegin
都没执行,也就是说touches事件没有传递到子视图上面touchesShouldCancelInContentView
由于条件未达到不会触发。 反而要按住一段时间(0.15s),scrollView的拖动手势没能识别事件后,touches事件传递到subView上,subView的touchesBegin
被触发, 再发生移动时触发touchesShouldCancelInContentView
调用, 最终subView的touchesMove
触发, subView才能拖动。 - 所以设置为YES,让按下时touches事件就直接传递到subView上,移动时直接触发重写的touchesShouldCancelInContentView。
最后
开发中,响应链、手势、target-action这一系列响应触摸事件,往往混合在一起,出现的问题让人头大,如果不是对iOS触发事件十分深入了解,很难很好的处理到问题,以及处理完了也不知道具体为什么,这里推荐这表文章iOS触摸事件全家桶,这篇文章很深入浅出的讲解了iOS触摸事件的一系列机制。