问题:页面事件传递的过程是什么?


  1. 首先用户触摸屏幕生成一个电信号; 交由IOKit.framework生成IOHIDEvent事件.

    当一个硬件事件(触摸/晃动/锁屏等)发生之后,首先由IOKit.framework生成IOHIDEvent事件,并且传递给SpringBoard,SpingBoard只能接收按键, 触摸, 加速, 接近传感器等几类Event事件.

    应用程序主RunLoop会申请一个 mach port 用于监听 IOHIDEventSources1 事件 回调方法是 __IOHIDEventSystemClientQueueCallBack();

    回调函数会进一步分发 source0 事件. 它的回调函数是 __UIApplicationHandleEventQueue(). 然后将接收到的 IOHIDEvent 封装成我们所熟知的 UIEvent 事件.

  2. 取出队列中最前面的事件, UIApplication 通过 sendEvent: 往下传递事件,交给主窗口 UIWindow 对象处理事件.

  3. UIWindow 会根据 Hit-Test 找到合适的视图View.

  4. 最终找到合适的视图View来响应该事件.


问题: 简述一下hit-Test相关内容.


如果一个OC对象想要响应事件,那么它必须要继承于 UIResponder ,例如 UIApplication、UIWindow、UIViewController、UIView等.

响应者链核心机制是 hit-Test 机制.

响应者链核心的两个方法如下所示.

- (void)pointInside:(CGPoint)point withEvent:(UIEvent *)event;
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;

流程如下所示.

  1. 在顶级视图(rootView)中, 调用hitTest:withEvent:方法, 内部调用 pointInside:withEvent: 是否触摸点在当前视图内.

  2. 如果返回NO, hitTest:withEvent: 返回nil,如果返回YE,那么按照子视图数组倒序的顺序发送 hitTest:withEvent: 事件.

  3. 如果子视图返回非空对象,则返回该对象,处理结束.

  4. 如果所有子视图都返回nil,那么则返回自身self,处理结束.

  5. 在 pointInside:withEvent: 过程中, 如果视图isHidden = YES, alpha小于0.1, userInteractionEnabled=NO 都会导致该视图被忽略.


问题: 简述一下事件响应相关内容.


响应者链中各个响应者都是继承于UIResponder, 常见的UIView, UIViewController, UIWindow, UIAppDelegate 都是继承于UIResponder. 响应者链响应者通过 hit-test 机制就已经确定了, 可以通过迭代 nextResponder 来获取所有的响应者.

事件响应的过程是这样的.

  1. 通过 hit-test 返回的view为第一响应者, nextResponder为上一响应者.

  2. 如果当前view默认不去重写,或者重写调用了父类的实现,也就是当前view不能响应,那么响应就会顺着响应者链进行传递 (通过nextResponder查找到).

  3. 如果上一个响应者是viewcontroller,那么会交给view进行处理响应,如果view处理不了,那么会把响应交由viewcontroller处理.

  4. 重复上面过程,当传递到window时,如果window对象也处理不了响应,那么就会交给UIApplication,UIApplication的delegate也是继承于UIResponder,如果UIApplication的delegate也处理不了响应,那么该响应就会被抛弃.


IT界无底坑洞栋主 欢迎加Q骚扰:676758285