- 响应者
响应者为响应事件的UIResponder子类对象,如UIButton、UIView等 - 响应链
响应链是由链接在一起的响应者(UIResponse子类)组成的。 - 事件传递
获得响应链后,将事件由第一响应者往application传递的过程
事件的传递过程
- 触摸事件的传递是从父控件传递到子控件
- 如果父控件不能接受触摸事件,那么子控件就不可能接收到触摸事件
1、当iOS程序发生触摸事件后,系统会利用Runloop将事件加入到UIApplication的任务队列中,具体过程可以参考深入理解RunLoop
2、UIApplication分发触摸事件到UIWindow,然后UIWindow依次向下分发给UIView
3、UIView调用hitTest:withEvent:
方法看看自己能否处理事件,以及触摸点是否在自己上面。
4、如果满足条件,就遍历UIView上的子控件。重复上面的动作。
5、直到找到最顶层的一个满足条件(既能处理触摸事件,触摸点又在上面)的子控件,此子控件就是我们需要找到的第一响应者。
// 此方法返回的View是本次点击事件需要的最佳View
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
// 判断一个点是否落在范围内
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
//返回最适合处理事件的视图,最好在父视图中指定子视图的响应
// 因为所有的视图类都是继承BaseView
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
// 1.判断当前控件能否接收事件
if (self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01) return nil;
// 2. 判断点在不在当前控件
if ([self pointInside:point withEvent:event] == NO) return nil;
// 3.从后往前遍历自己的子控件(先加的控件在下面,后加的在上面,为了让最上层的响应者最先响应事件)
NSInteger count = self.subviews.count;
for (NSInteger i = count - 1; i >= 0; i--) {
UIView *childView = self.subviews[I];
// 把当前控件上的坐标系转换成子控件上的坐标系
CGPoint childP = [self convertPoint:point toView:childView];
UIView *fitView = [childView hitTest:childP withEvent:event];
if (fitView) { // 寻找到最合适的view
return fitView;
}
}
// 循环结束,表示没有比自己更合适的view
return self;
}
事件的传递和响应的区别:
事件的传递是从上到下(父控件到子控件)
事件的响应是从下到上(顺着响应者链条向上传递:子控件到父控件)
事件的响应
- 事件响应会先从底层最合适的view开始,然后随着上一步找到的链一层一层响应touch事件。默认touch事件会传递给上一层。
- 如果到了viewcontroller的view,就会传递给viewcontroller。如果viewcontroller不能处理,就会传递给UIWindow
- 如果UIWindow无法处理,就会传递给UIApplication
- 如果UIApplication无法处理,就会传递给UIApplicationDelegate。
- 如果UIApplicationDelegate不能处理,则会丢弃该事件
网友评论