GestureRecognizers与触摸事件分发

一.Gesture Recognizers

Gesture

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

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

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

UIPanGestureRecognizer:用来识别拖动手势。

UISwipeGestureRecognizer:用来识别Swipe手势。

UIRotationGestureRecognizer:用来识别旋转手势。

UILongPressGestureRecognizer:用来识别长按手势。

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

注:图中view与Gesture Recognizer的关系是,GestureRecognizer关联在view或view的superview(可能多级)上。

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

Gesture

Recognizers可能会延迟将触摸事件发送到hit-test

view上,默认情况下,当GestureRecognizers识别到手势后,会向hit-test

view发送cancel消息,来取消之前发给hit-testview的事件。控制这个流程的是UIGestureRecognizer的三个属性

cancelsTouchesInView (默认为YES)

delaysTouchesBegan (默认为NO)

delaysTouchesEnded (默认为YES)

cancelsTouchesInView为YES,表示当GestureRecognizers识别到手势后,会向hit-test

view发送touchesCancelled:withEvent:消息来取消hit-test

view对此触摸序列的处理,这样只有GestureRecognizers能响应此触摸序列,hit-testview不再响应。如果为NO,则不发送touchesCancelled:withEvent:消息给hit-testview,这样会使Gesture

Recognizers和hit-test view同时响应触摸序列。

delaysTouchesBegan为NO,表示触摸序列开始时,而手势识别器还未识别出此手势时,touch事件会同时发向hit-testview,这样在手势识别器还未识别出此手势,hit-testview同时也可以收到同样的触摸事件。如果为YES,则在手势识别器在识别手势的过程中,不会有任何触摸事件发送给hit-testview,如果手势识别器最终识别到了手势,则也不会发送任何消息(包括touchesCancelled:withEvent:)给hit-testview;若干手势识别最终没有识别到手势,则所有的触摸事件在发给hit-test

view处理。关于这个特性,可参考UIScrollView的delaysContentTouches属性。这样属性也谨慎使用,使用不当会导致UI无响应。

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

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:

这个问题还没搞清楚!

三.多个Gesture Recognizer之间的关系

在一个view上可以绑定多个GestureRecognizer,在默认情况下,触摸序列中的触摸事件会以不确定的次序在各个gesturerecognizer中传递,直到事件最终发送给hit-test

view(如果中间没被GestureRecognizer识别出并截获的话)。多个GestureRecognizer之间的关系也可以根据需要定制,主要有下面几种行为

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

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

[singleTapGesture requireGestureRecognizerToFail:doubleTapGesture];

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

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

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

gestureRecognizerShouldBegin:

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

gestureRecognizer:shouldReceiveTouch:

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

另外,在UIGestureRecognizer类中也有两个可以重写的方法来完成与Delegate方法中相同的功能

- (BOOL)canPreventGestureRecognizer:(UIGestureRecognizer*)preventedGestureRecognizer;

- (BOOL)canBePreventedByGestureRecognizer:(UIGestureRecognizer*)preventingGestureRecognizer;

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

默认情况下,两个gesturerecognizers不会同时识别它们的手势,但是你可以实现UIGestureRecognizerDelegate协议中的

gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:方法对其进行控制。这个方法在这两个gesturerecognizers中的任意一个将block另一个的触摸事件时调用,如果返回YES,则两个gesturerecognizers可同时识别,如果返回NO,则并不保证两个gesturerecognizers必不能同时识别,因为另外一个gesturerecognizer的此方法可能返回YES。也就是说两个gesturerecognizers的delegate方法只要任意一个返回YES,则这两个就可以同时识别;只有两个都返回NO的时候,才是互斥的。默认情况下是返回NO。

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

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {    return YES;}

四.UIScrollView的类似行为

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

– touchesShouldBegin:withEvent:inContentView:

– touchesShouldCancelInContentView:

canCancelContentTouches

delaysContentTouches

参考:

Event Handling Guide for iOS – Gesture Recognizers

UIGestureRecognizer Class Reference

UIGestureRecognizerDelegate Protocol Reference

Detecting all touches in an app

UIScrollView Class Reference

How to recognize swipe gesture in UIScrollView

UIGestureRecognizer blocks subview for handling touchevents

UIButton touch is delayed when in UIScrollView

Why is scrolling a UITableView much more responsive than scrollinga UIScrollView?

How to cancel touches exactly like UIScrollView?

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349

推荐阅读更多精彩内容