iOS总结-有关UITouch事件(三)之 UIResponder和响应者链条

参考: https://blog.csdn.net/zeng_zhiming/article/details/71747881
 https://www.jianshu.com/p/44a5b59e7e85
https://www.jianshu.com/p/4ad8b71246f7

UIResponder

UIResponder类是专门用来响应用户的操作处理各种事件的,触摸事件Touch Events 、运动事件Motion Events、远程控制事件 Remote Control Events ,只有继承了UIResponder才能响应事件, 如UIApplication / UIView/ UIViewController

事件传递和响应者链条
事件传递中UIWindow会根据不同的事件类型,用不同的方式寻找initial object.对于Touch Event,其实就是hit-testView,对于Motion/Remote Event. UIWindow会把例如震动/远程控制的事件传递给当前的firstResponder.

事件的触发流程:

IOKit.framework是系统内核的库

SpringBoard.app相当于手机的桌面

Source1 是runloop响应自动唤醒的资源  source0 非主动唤醒的资源

Hit-Test的目的就是找到手指点击到的最外层的那个view,它进行类似于探测的工作,判断是否点击在某个视图上.

什么时候Hit-Test

   与Hit-Test相关的两个方法:
   - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
  - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event;

runLoop
 发生触摸事件后,系统会将该事件加入到一个由UIApplication管理的事件队列中,UIApplication会从事件队列中取出最前面的事件并将其分发处理,通常,先发送时间给应用程序的主窗口UIWindow.UIWindow会调hitTest:withEvent:方法, 从后往前遍历subviews数组,找到点击的点在哪个subview,然后继续调用subView的hitTest:withEvent:方法,直到在视图继承树中找到一个最合适的子视图来处理触摸事件,该子视图即为hit-test view.

这个view和上面依附的手势,都会和一个UITouch的对象关联起来, 这个UITouch会作为事件传递的参数之一.我们可以看到UITouch里面有个一个view和gestureRecognizers属性,就是Hit-Test view和它的手势.

hitTest:withEvent: 方法 如何找到最合适的控件来处理事件

 1.判断自己是否能接受触摸事件 ,能否与用户交互

 2.触摸点是否在自己身上?  调用pointInside:withEvent:

 3.从后往前遍历子控件数组,重复前面的两个步骤(从后往前: 按照addsubview的顺序, 越晚添加的越先访问)

 4.如果没有符合条件的子控件,那么 就自己最适合处理
   找到合适的视图控件后, 就会调用视图控件的touches方法来作具体的事件处理.

要拦截事件传递,可以使用pointInside:withEvent: 方法,在实现里面直接return NO即可.那么hitTest:withEvent:方法返回nil. 或者在hitTest:withEvent:直接return self, 不传递给子视图.

可以用来扩大view的点击区域,如通过重写button的子类的hitTest:withEvent:方法,判断point在button的frame之外10pt内,就返回button自己.

时间响应

第一响应者是一个UIWindow对象接收到一个事件后, 第一个来响应的该事件的对象.
如果hit-test视图不处理收到的事件消息,UIKit则将事件转发到响应者链中的下一个响应者,看其是否能对改消息进行处理.

所有视图按照树状层次结构组织,每个view都有自己的superView, 包括vc的self.view:

1.当一个view被添加到superView上的时候, 它的nextResponder就会被指向所在controller

2.当vc被初始化的时候,self.view的nextResponder会被指向所在的controller
    如果当前这个view是控制器的self.view,那么控制器就是上一个响应者,如果当前这个view不是控制器的view,那么父控件就是上一个响应者)

3.vc的nextResponder会被指向self.view的superView

4.最顶级的vc的nextResponder指向UIWindow

5.UIWindow的nextResponder指向UIApplication
 这就形成了响应链,通过UIResponder串连起来的

touches方法实际上没做什么,UIView继承了它并重写,把事件传递给nextResponder,相当于[self.nextResponder touchBegan:touches withEvent:event]. 当一个view没有重写touch事件,那么这个事件就会一直传递下去, 直到UIApplication. 如果重写了touch方法,这个view响应了事件,事件就被拦截了, 它的nextResponder不会收到这个事件.

响应链事件传递 向上传递:

1.如果view的控制器存在, 就传递给控制器,如果控制器不存在,则将其传递给它的父视图.

2.在视图层次结构的最顶级视图,如果不能处理收到的事件/消息,则将事件/消息传递给window对象进行处理

3.如果window对象不处理,则将其事件/消息传递给UIApplication对象

4.如果UIApplication不处理事件/消息,则将其丢弃

监听事件的基本流程:

 1.当应用程序启动以后创建UIApplication对象

 2.然后启动消息循环监听所有事件

 3.当用户触摸屏幕的时候,消息循环监听到这个触摸事件

 4.消息循环首先把监听到的触摸事件传递给UIApplication对象

 5.UIApplication对象再传递给UIWindow对象

 6.UIWindow对象再传递给UIWindow的根控制器rootViewController

 7.控制器再传递给控制器所管理的view

 8.控制器所管理的view在其内部搜索看本次触摸的点在哪个控件的范围内

 9. 找到某个控件以后,调用这个控件的touchBegan方法,再一次向上返回,最终返回给消息循环

10.消息循环知道哪个按钮被点击后, 在搜索这个按钮是否注册了对应的事件,如果注册了,就调用这个事件处理程序.(一般就是执行控制器中的事件处理方法)

手势

手势是被和触摸事件是两个独立的事,.

  通过touches方法监听view触摸事件, 缺点: 必须自定义view,由于是在view内部的touches方法中监听触摸事件,  因此默认情况下,无法让其他外界对象监听view的触摸事件,不容易区分用户的具体手势行为.

  Tap 敲击  Press 长按  Swipe轻扫  Rotation 旋转   Pinch 捏合  Pan 拖拽

  UIControl类型的控件也是通过UIApplication对象进行转发       调用方法是sendAction:to:forEvent:

猜你喜欢

转载自blog.csdn.net/qq_28551705/article/details/84981556