iOS触摸事件处理详解

iOS触摸事件处理详解,第1张

1.发生触摸事件后,系统会将该事件加入到一个由UIApplication管理的队列事件中

2.UIApplication会从事件队列中取出最前面的事件,并将事件分发下去以便处理,通常会先发送事件给应用程序的主窗口(keyWindow)

3.主窗口会在视图层次结构中找到一个最合适的视图来处理触摸事件

事件的具体传递过程,如图:

一般事件的传递是从父控件传递到子控件的

例如:

点击了绿色的View,传递过程如下:UIApplication->Window->白色View->绿色View

点击蓝色的View,传递过程如下:UIApplication->Window->白色View->橙色View->蓝色View

如果父控件接受不到触摸事件,那么子控件就不可能接收到触摸事件

应用如何找到最合适的控件来处理事件?有以下准则

详述:

1.主窗口接收到应用程序传递过来的事件后,首先判断自己能否接手触摸事件。如果能,那么在判断触摸点在不在窗口自己身上

2.如果触摸点也在窗口身上,那么窗口会从后往前遍历自己的子控件(遍历自己的子控件只是为了寻找出来最合适的view)

3.遍历到每一个子控件后,又会重复上面的两个步骤(传递事件给子控件,1.判断子控件能否接受事件,2.点在不在子控件上)

4.如此循环遍历子控件,直到找到最合适的view,如果没有更合适的子控件,那么自己就成为最合适的view。

UIView不能接收触摸事件的三种情况:

寻找最合适的view过程,如图:

这里点击了橙色的那块区域,事件传递判断过程如下:

1.UIApplication从事件队列中取出事件分发给UIWindow

2.UIWindow判断自己是否能接受触摸事件,可以

3.UIWindow判断触摸点是否在自己身上,是的。

4.UIWindow从后往前便利自己的子控件,取出白1(a.UIWindow的子控件只有一个,那就是白1)

5.白1都满足最上面两个条件,遍历子控件橙2

特别说明:

( a.白1的子控件有两个,绿2和橙2 )

( b.添加顺序是,先添加绿2,后添加橙2 )

( c.根据后添加的子控件先遍历的原则,肯定是先遍历橙2子控件 )

6.橙2都满足最上面两个条件,遍历子控件,先取出红3

( a.橙2的子控件有两个,蓝3和红3,注意黄4不属于橙2的子控件而是蓝3的子控件 )

( b.添加顺序是,先添加蓝3,后添加红3 )

( c.根据后添加的子控件先遍历的原则,肯定是先遍历红3子控件 )

7.红3不满足条件2,取出蓝3

8.蓝3也不满足条件2,最后最合适的控件是橙2

寻找合适的View用到两个重要方法:

什么时候调用?

只要事件一传递给一个控件,这个控件就会调用他自己的hitTest:withEvent:方法寻找合适的View

作用

寻找并返回最合适的view(能够响应事件的那个最合适的view)

注 意:不管这个控件能不能处理事件,也不管触摸点在不在这个控件上,

事件都会先传递给这个控件,随后再调用hitTest:withEvent:方法

hitTest:withEvent:底层调用流程:

事件传递给窗口或控件后,就调用hitTest:withEvent:方法寻找更合适的view。所以是,先传递事件,再根据事件在自己身上找更合适的view。

不管子控件是不是最合适的view,系统默认都要先把事件传递给子控件,经过子控件调用自己的hitTest:withEvent:方法验证后才知道有没有更合适的view。即便父控件是最合适的view了,子控件的hitTest:withEvent:方法还是会调用,不然怎么知道有没有更合适的!即,如果确定最终父控件是最合适的view,那么该父控件的子控件的hitTest:withEvent:方法也是会被调用的。

以上是事件传递的顺序:

上文介绍了事件的传递过程,找到合适的View之后就会调用该view的touches方法要进行响应处理具体的事件,找不到最合适的view,就不会调用touches方法进行事件处理。

这里先介绍一下响应者链条:响应者链条其实就是很多响应者对象(继承自UIResponder的对象)一起组合起来的链条称之为响应者链条

一般默认做法是控件将事件顺着响应者链条向上传递,将事件交给上一个响应者进行处理 (即调用super的touches方法)。

那么如何判断当前响应者的上一个响应者是谁呢?有以下两个规则:

1.判断当前是否是控制器的View,如果是控制器的View,上一个响应者就是控制器

2.如果不是控制器的View,上一个响应者就是父控件

响应过程如下图:

touch响应:

如果控制器也不响应响应touches方法,就交给UIWindow。如果UIWindow也不响应,交给UIApplication,如果都不响应事件就作废了。

最后总结来说一次完整的触摸事件的传递响应过程为:

UIApplication-->UIWindow-->递归找到最合适处理的控件-->控件调用touches方法-->判断是否实现touches方法-->没有实现默认会将事件传递给上一个响应者-->找到上一个响应者-->找不到方法作废

一句话总结整个过程是:触摸或者点击一个控件,然后这个事件会从上向下(从父->子)找最合适的view处理,找到这个view之后看他能不能处理,能就处理,不能就按照事件响应链向上(从子->父)传递给父控件

额外添加:

有了响应网为基础,事件的传递就比较简单,只需要选择其中一条响应链,但是选择那一条响应链来传递呢?为了弄清真个过程,我们先来查看一下从触摸硬件事件转化为UIEvent消息。

首先用户触摸屏幕,系统的硬件进程会获取到这个点击事件,将事件简单处理封装后存到系统中,由于硬件检测进程和当前运行的APP是两个进程,所以进程两者之间传递事件用的是端口通信。硬件检测进程会将事件放入到APP检测的那个端口。

其次,APP启动主线程RunLoop会注册一个端口事件,来检测触摸事件的发生。当时事件到达,系统会唤起当前APP主线程的Runloop。唤起原因就是端口触摸事件,主线程会分析这个事件。

最后,系统判断该次触摸是否导致了一个新的事件, 也就是说是否是第一个手指开始触碰,如果是,系统会先从响应网中 寻找响应链。如果不是,说明该事件是当前正在进行中的事件产生的一个Touch message, 也就是说已经有保存好的响应链。

1、Touch事件是由硬件捕获到触摸后由系统传递给应用的ViewRoot,再由ViewRoot往下一层一层传递.

2、处理过程都是自上而下的分发,可以看成是由布局的“包含”关系,自顶向下的方式

3、事件存在消耗,事件的处理方法都会返回一个boolean值,如果该值为true,则本次事件下发将会被消费掉,而不会继续往下一层传递.

4、Touch事件从ACTION_DOWN开始,也就是按下的这个action开始算起,到ACTION_UP抬起时结束;但是如果在ACTION_DOWN的时候,没有接受事件,那么后续的其他动作也将不会被接受

5、dispatchTouchEvent方法,是用来分发上层传递过来的事件的,它在View和ViewGroup中都有实现

6、onInterceptTouchEvent方法,是用来拦截事件传递的,它只在ViewGroup中有实现,在View中没有

7、view对象的TouchLitener中的onTouch方法总是会先于view自己的onTouchEvent(event)方法被执行,这是由View中的dispatchEvent方法决定。

8、Activity中的onTouchEvent只会在能响应的所有view都响应完事件,且都没有消费掉事件之后才会被调用。

9、如果一个ViewGroup被点击的地方,有多个子View/ViewGroup可以响应点击事件,那么它们响应的顺序是:后addView进来的子view/ViewGroup先响应事件或者是xml布局文件中后被添加的view先 响应触摸事件


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/bake/11757582.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-18
下一篇 2023-05-18

发表评论

登录后才能评论

评论列表(0条)

保存