[关闭]
@qidiandasheng 2016-09-02T18:12:13.000000Z 字数 1394 阅读 2197

iOS响应链

iOS理论


在一个app中间有一个button,在你手触摸屏幕点击后,到这个button收到点击事件,中间发生了什么

当我们点击屏幕的时候,操作系统把包含这些点击事件的信息包装成UITouch和UIEvent形式的实例,然后找到当前运行的程序,逐级寻找能够响应这个事件的对象,直到没有响应者响应。这一寻找的过程,被称作事件的响应链,如下图所示:

系统在收到点击事件的时候通过不断遍历当前视图上的子视图的这些方法,获取下一个响应的视图。

这里有两个方法比较重要:

- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event; // recursively calls -pointInside:withEvent:. point is in the receiver's coordinate system

- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event; // default returns YES if point is in bounds

系统先调用pointInSide: WithEvent:判断当前视图以及这些视图的子视图是否能接收这次点击事件,然后在调用hitTest: withEvent:依次获取处理这个事件的所有视图对象,在获取所有的可处理事件对象后,开始调用这些对象的touches回调方法。

所以简单点说整个响应链的顺序还是如上图所示。根据这个响应链查找出所有可以响应的对象。UIApplication对象维护着自己的一个响应者栈,当pointInSide: withEvent:返回YES
的时候,响应者入栈。
入栈部分如下图所示:

入栈完成后就会开始调用touches的回调方法。所以我们重写touchesBegan方法,逐级获取下一响应者,直到没有下一个响应者为止。

  1. - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
  2. {
  3. UIResponder * next = [self nextResponder];
  4. NSMutableString * prefix = @"".mutableCopy;
  5. while (next != nil) {
  6. NSLog(@"%@%@", prefix, [next class]);
  7. [prefix appendString: @"--"];
  8. next = [next nextResponder];
  9. }
  10. }

控制台输出的所有下级事件响应者如下:

AView
--UIView
----ViewController
------UIWindow
--------UIApplication
----------AppDelegate

这里还需要注意的是手势UIGestureRecognizer对象也可以附加在view上,来实现其他丰富的手势事件。在view添加单击手势之后,原来的touchesEnded方法就无效了。在测试demo中发现touchesBegan方法是有进行回调的,但是movedended就没有进行回调。因此,在系统的touches事件处理中,在touchesBegan之后,应该是存在着一个调度后续事件(nextHandler)处理的方法。猜测事件调度的处理大致如下图示:

参考

事件传递响应链

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注