OC学习01:事件传递链和响应链
前言
在 iOS
中只有继承 UIResponder
的 对象
才能够 接收并处理事件
,UIResponder
是所有响应对象的 基类
。继承关系如下:
- UIApplication -> UIResponder -> NSObject
- UIViewController -> UIResponder -> NSObject
- UIView -> UIResponder -> NSObject
- UIWindow -> UIViww -> UIResponder -> NSObject
- UIButton -> UIControl -> UIView -> UIResponder -> NSObject
事件链
传递链
:由系统向离用户最近的view传递。
顺序:Appdelegate -> UIApplication -> UIWindow -> UIViewController -> subViews响应链
:由离用户最近的view向系统传递。
顺序: suberViews –> UIViewController –> UIWindow –> UIApplication –> AppDelegate
传递链
- 事件传递的两个核心方法
1 | // 返回哪个视图进行事件响应 |
其中
UIView
不接受事件处理的情况有- hidden = YES 视图被隐藏
- userInteractionEnabled = NO 不接受响应事件
- alpha <= 0.01,透明视图不接收响应事件
- 子视图超出父视图范围
- 需响应视图被其他视图盖住
- 是否重写了其父视图以及自身的hitTest方法
- 是否重写了其父视图以及自身的pointInside方法
流程描述
- 当iOS程序发生触摸事件后,系统会利用
Runloop
将事件加入到UIApplication
的任务队列中 UIApplication
分发触摸事件到UIWindow
- 然后
UIWindow
依次向下分发给UIView
UIView
调用hitTest:withEvent:
方法返回一个最终响应的视图- 在
hitTest:withEvent:
方法中就会去调用pointInside: withEvent:
去判断当前点击的point
是否在UIView
范围内,如果是的话,就会去逆序遍历
它的子视图来查找最终响应的子视图
- 遍历的方式是使用
倒序
的方式来遍历子视图,也就是说最后添加的子视图会最先遍历,在每一个视图中都会去调用它的hitTest:withEvent:
方法,可以理解为是一个递归调用
- 最终会返回一个响应视图,如果返回视图有值,那么这个视图就作为最终响应视图,结束整个事件传递;如果没有值,那么就会将UIView作为响应者
- 当iOS程序发生触摸事件后,系统会利用
响应链
响应者链的事件传递过程:
- 如果
view
的控制器存在,就传递给控制器处理;如果控制器不存在,则传递给它的父视图
- 在视图层次结构的最顶层,如果也不能处理收到的事件,则将事件传递给
UIWindow
对象进行处理 - 如果
UIWindow
对象也不处理,则将事件传递给UIApplication
对象 - 如果
UIApplication
也不能处理该事件,则将该事件丢弃
面试题
- 实现一个按钮的点击范围扩大效果
思路:自定义一个按钮继承自UIButton,重写 poinstInSide
方法,增大内边距,返回一个新的bounds
1 | #import "ZJBtn.h" |
- 子视图超过父视图部分仍然能响应
思路:正常情况下子视图超出部分是不能响应事件的,需重写 hitTest:withEvent
方法,指定 子视图
可点击
1 | #import "VA.h" |
- Post title:OC学习01:事件传递链和响应链
- Post author:张建
- Create time:2023-03-01 17:30:34
- Post link:https://redefine.ohevan.com/2023/03/01/OC/OC学习01:事件传递链和响应链/
- Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.