iOS开发之事件传递响应链

2020-01-14 18:34:01王旭

为了确认UIView确实是通过UIResponder的点击方法响应点击事件的,我创建了UIView的类别,并重写+ (void)load方法,使用method_swizzling的方式交换点击事件的实现


+ (void)load
  Method origin = class_getInstanceMethod([UIView class], @selector(touchesBegan:withEvent:));
  Method custom = class_getInstanceMethod([UIView class], @selector(lxd_touchesBegan:withEvent:));
  method_exchangeImplementations(origin, custom);
 
  origin = class_getInstanceMethod([UIView class], @selector(touchesMoved:withEvent:));
  custom = class_getInstanceMethod([UIView class], @selector(lxd_touchesMoved:withEvent:));
  method_exchangeImplementations(origin, custom);
 
  origin = class_getInstanceMethod([UIView class], @selector(touchesEnded:withEvent:));
  custom = class_getInstanceMethod([UIView class], @selector(lxd_touchesEnded:withEvent:));
  method_exchangeImplementations(origin, custom);
}
 
- (void)lxd_touchesBegan: (NSSet *)touches withEvent: (UIEvent *)event
{
  NSLog(@"%@ --- begin", self.class);
  [self lxd_touchesBegan: touches withEvent: event];
}
 
- (void)lxd_touchesMoved: (NSSet *)touches withEvent: (UIEvent *)event
{
  NSLog(@"%@ --- move", self.class);
  [self lxd_touchesMoved: touches withEvent: event];
}
 
- (void)lxd_touchesEnded: (NSSet *)touches withEvent: (UIEvent *)event
{
  NSLog(@"%@ --- end", self.class);
  [self lxd_touchesEnded: touches withEvent: event];
}

在新建的项目中,我分别创建了AView、BView、CView和DView四个UIView的子类,然后点击任意一个位置:

iOS开发之事件传递响应链

项目结构图

在我点击上图绿色视图的时候,控制台输出了下面的日志(日期部分已经去除):


CView --- begin
CView --- end

由此可见在我们点击UIView的时候,是通过touches相关的点击事件进行回调处理的。

除了touches回调的几个点击事件,手势UIGestureRecognizer对象也可以附加在view上,来实现其他丰富的手势事件。在view添加单击手势之后,原来的touchesEnded方法就无效了。最开始我一直认为view添加手势之后,原有的touches系列方法全部无效。但是在测试demo中,发现view添加手势之后,touchesBegan方法是有进行回调的,但是moved跟ended就没有进行回调。因此,在系统的touches事件处理中,在touchesBegan之后,应该是存在着一个调度后续事件(nextHandler)处理的方法,个人猜测事件调度的处理大致如下图示: