Gesture Recognizers与触摸事件分发

Gesture Recognizers与触摸事件分发

转自: http://blog.csdn.net/chun799/article/details/8194893

另外,若不理解响应者链条 可阅读: http://blog.csdn.net/mobanchengshuang/article/details/11858217

.Gesture Recognizers

Gesture Recognizers是在iOS3.2引入的,可以用来识别手势、简化定制视图事件处理的对象。Gesture Recognizers的基类为UIGestureRecognizer,这一个抽象基类,定义了实现底层手势识别行为的编程接口。在UIKit框架中提供了6个具体的手势识别类,用来识别常见的手势。这6个手势识别器类为:

§ UITapGestureRecognizer:用来识别点击手势,包括单击双击甚至三击等

§ UIPinchGestureRecognizer:用来识别手指捏合手势

§ UIPanGestureRecognizer:用来识别拖动手势

§ UISwipeGestureRecognizer:用来识别Swipe手势

§ UIRotationGestureRecognizer:用来识别旋转手势

§ UILongPressGestureRecognizer:用来识别长按手势


为了识别手势,需要将Gesture Recognizers关联到其检测触摸事件的view,可以使用UIViewaddGestureRecognizer:方法将手势识别器绑定到视图上。Gesture Recognizers在触摸事件处理流程中,处于观察者的角色,其不是view层级结构的一部分,所以也不参与responder chain。在将触摸事件发送给hit-test view之前,系统会先将触摸事件发送到hit-test view上绑定的或hit-test view父视图(superview)上绑定的Gesture Recognizers上。其流程大概如下图所示:

:图中viewGesture Recognizer的关系是,Gesture Recognizer关联在viewviewsuperview(可能多级)上。


.Gesture Recognizers与事件分发路径的关系

Gesture Recognizers可能会延迟将触摸事件发送到hit-test view,默认情况下,当Gesture Recognizers识别到手势后,会向hit-test view发送cancel消息,来取消之前发给hit-test view的事件。控制这个流程的是UIGestureRecognizer的三个属性

§ cancelsTouchesInView (默认为YES)

§ delaysTouchesBegan (默认为NO)

§ delaysTouchesEnded (默认为YES)

cancelsTouchesInView为YES,表示当GestureRecognizers识别到手势后,会向hit-test view发送 touchesCancelled:withEvent:消息来取消hit-test view对此触摸序列的处理,这样只有GestureRecognizers能响应此触摸序列,hit-test view不再响应。如果为NO,则不发送touchesCancelled:withEvent:消息给hit-test view,这样会使Gesture Recognizers和hit-test view同时响应触摸序列。

delaysTouchesBegan为NO,表示触摸序列开始时,而手势识别器还未识别出此手势时,touch事件会同时发向hit-testview,这样在手势识别器还未识别出此手势,hit-testview同时也可以收到同样的触摸事件。如果为YES,则在手势识别器在识别手势的过程中,不会有任何触摸事件发送给hit-test view,如果手势识别器最终识别到了手势,则也不会发送任何消息(包括touchesCancelled:withEvent:)给hit-test view;若干手势识别最终没有识别到手势,则所有的触摸事件在发给hit-test view处理。关于这个特性,可参考UIScrollViewdelaysContentTouches属性。这样属性也谨慎使用,使用不当会导致UI无响应。

delaysTouchesEnded,在文档上的解释是,当手势识别器在识别手势时,对于UITouchPhaseEnded阶段的touch会延迟发送给hit-testview,在手势识别成功后,发送给hit-test view cancel消息,手势识别失败时,发送原来的end消息。其给出了了这样的例子识别双击操作的UITapGestureRecognizer对象,其numberOfTapsRequired设为2,在用户进行双击操作时,如果delaysTouchesEnded为NO,则hit-test view中的调用序列为
touchesBegan:withEvent:,
touchesEnded:withEvent:,
touchesBegan:withEvent:,
and touchesCancelled:withEvent:
如果delaysTouchesEnded为YES,则调用序列为:
touchesBegan:withEvent:,
touchesBegan:withEvent:,
touchesCancelled:withEvent:,
touchesCancelled:withEvent:


但我在实际测试时,并非如此,实际测试的结果是,如果delaysTouchesEnded为NO,则调用序列为:
touchesBegan:withEvent:,
touchesEnded:withEvent:,
TapGestureRecognizer 检测到双击

如果delaysTouchesEnded为YES,则调用序列为:
touchesBegan:withEvent:,
touchesEnded:withEvent:,
TapGestureRecognizer 检测到双击
touchesCancelled:withEvent:
这个问题还没搞清楚!


三.多个GestureRecognizer之间的关系

在一个view上可以绑定多个Gesture Recognizer,在默认情况下,触摸序列中的触摸事件会以不确定的次序在各个gesture recognizer中传递,直到事件最终发送给hit-test view(如果中间没被Gesture Recognizer识别出并截获的话)。多个Gesture Recognizer之间的关系也可以根据需要定制,主要有下面几种行


1.使其中一个gesture recognizer失败的情况下,另一个gesture recognizer才能分析事件。

以同时识别单击操作和双击操作为例,两个gesture recognizers分别用来识别单击和双击,分别为singleTapGesturedoubleTapGesture。在默认情况下,当用户进行单击操作时,singleTapGesture会识别出一个单击操作,doubleTapGesture也会识别出一个双击动作,但我们的意图是,这仅仅是一个双击操作。在这种情况下我们可以使用UIGestureRecognizerrequireGestureRecognizerToFail:方法来使singleTapGesturedoubleTapGesture识别识别的时候才分析事件,如果doubleTapGesture识别出双击事件,则singleTapGesture不会有任何动作。

[singleTapGesturerequireGestureRecognizerToFail:doubleTapGesture];

需要注意的是,在这种情况下,如果用户进行单击操作,需要一段延时(doubleTapGesture识别失败)singleTapGesture才会识别出单击动作,进行单击处理,这段时间很多,对实际使用几乎没有影响。


2.精确控制gesture recognizer是否响应某个事件或事件序列.

UIGestureRecognizerDelegate协议中有两个可选方法可以控制gesture recognizer是否需要识别某些事件

 gestureRecognizerShouldBegin:
此方法在gesture recognizer视图转出UIGestureRecognizerStatePossible状态时调用如果返回NO,则转换到UIGestureRecognizerStateFailed;如果返回YES,则继续识别触摸序列.(默认情况下为YES)

 gestureRecognizer:shouldReceiveTouch:
此方法在window对象在有触摸事件发生时调用gesture recognizertouchesBegan:withEvent:方法之前调用如果返回NO,gesturerecognizer不会看到此触摸事件(默认情况下为YES).

另外,在UIGestureRecognizer类中也有两个可以重写的方法来完成与Delegate方法中相同的功能
- (BOOL)canPreventGestureRecognizer:(UIGestureRecognizer*)preventedGestureRecognizer;
- (BOOL)canBePreventedByGestureRecognizer:(UIGestureRecognizer*)preventingGestureRecognizer;


3.允许多个手势识别器共同识别

默认情况下,两个gesture recognizers不会同时识别它们的手势,但是你可以实现UIGestureRecognizerDelegate协议中的
gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:
方法对其进行控制。这个方法在这两个gesture recognizers中的任意一个将block另一个的触摸事件时调用,如果返回YES,则两个gesture recognizers可同时识别,如果返回NO,则并不保证两个gesture recognizers必不能同时识别,因为另外一个gesture recognizer的此方法可能返回YES。也就是说两个gesture recognizersdelegate方法只要任意一个返回YES,则这两个就可以同时识别;只有两个都返回NO的时候,才是互斥的。默认情况下是返回NO

有这样一个例子,如果要侦测在window上的所有触摸事件,可以将gesture recognizer关联到window上,默认情况下如果手势被window识别,则子视图中的gesture recognizer就失效了,而我们在window上的gesture recognizer的目的只是监控所有事件,但并不处理这些事件,具体事件的处理还需要子视图中的各个gesture recognizer去处理,这样我们可以实现window上绑定gesture recognizerdelegate方法,使gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:返回YES即可。

- (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizershouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer*)otherGestureRecognizer {     returnYES; }


四.UIScrollView的类似行为

scroll view没有滚动栏,当在scroll view上有触摸行为时其要识别出触摸行为的目的是scroll view本身还是其内容子视图。定制scrollview如何处理这种情况,看查看UIScrollView类的下列属性和方法。
– touchesShouldBegin:withEvent:inContentView:
– touchesShouldCancelInContentView:
canCancelContentTouches
delaysContentTouches

这里把UIScrollView的几个要点总结下:

从你的手指touch屏幕开始,scrollView开始一个timer,如果:

1.  150ms内如果你的手指没有任何动作,消息就会传给subView。

2.  150ms内手指有明显的滑动(一个swipe动作),scrollView就会滚动,消息不会传给subView,这里就是产生问题二的原因。

3. 150ms内手指没有滑动,scrollView将消息传给subView,但是之后手指开始滑动,scrollView传送touchesCancelled消息给subView,然后开始滚动。

观察下tableView的情况,你先按住一个cell,cell开始高亮,手不要放开,开始滑动,tableView开始滚动,高亮取消。

delaysContentTouches的作用:

这个标志默认是YES,使用上面的150ms的timer,如果设置为NO,touch事件立即传递给subView,不会有150ms的等待。

cancelsTouches的作用:

这个标准默认为YES,如果设置为NO,这消息一旦传递给subView,这scroll事件不会再发生。

简单地说:

delaysContentTouches属性默认为YES,这种情况下150ms内如果手指不滑动,触摸就会传给subView;当设置为NO时,这种情况下touch直接传递给subView,不会等待150ms

canCancelsTouches属性默认为YES,当设置为NO时,触摸一旦传递给subView,不会再触发滚动事件。


 

你可能感兴趣的:(ios开发,gesture,Recognizers)