iOS 响应链
一、iOS 响应链过程
1、响应对象
在iOS中只有继承UIResponder的对象才能够接收并处理事件,UIResponder 是所有响应对象的基类;
2、响应过程
iOS 系统检测到手指触摸操作会将其打包成一个 UIEvent 事件,并放入当前活跃的UIApplication所管理的事件队列;
UIApplication从所管理的事件队列取出最前端的事件交给 UIWindow 处理;
UIWindow 会调用 hitTest:withEvent:方法寻找此次事件的最佳处理控件;
3、hit-test view 过程如下:
a.自己能响应触摸事件
b.触摸点在自己身上
c.倒序递归遍历子控件, 重复上两步
d.如果没有符合条件的子控件, 那么就自己最合适处理
4、hit-test view 过程的代码解释
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
// 1.如果控件不允许与用用户交互,那么返回nil
if (self.userInteractionEnabled == NO || self.alpha <= 0.01 || self.hidden == YES) {
return nil;
}
// 2. 如果点击的点在不在当前控件中,返回nil
if (![self pointInside:point withEvent:event]) {
return nil;
}
// 3.从后往前遍历每一个子控件
for(int i = (int)self.subviews.count - 1 ; i >= 0 ;i--) {
// 3.1获取一个子控件
UIView *childView = self.subviews[i];
// 3.2当前触摸点的坐标转换为相对于子控件触摸点的坐标
CGPoint childP = [self convertPoint:point toView:childView];
// 3.3判断是否在在子控件中找到了更合适的子控件(递归循环)
UIView *fitView = [childView hitTest:childP withEvent:event];
// 3.4如果找到了就返回
if (fitView) {
return fitView;
}
}
// 4.没找到,表示没有比自己更合适的view,返回自己
return self;
}
5、两个重要方法
// 此方法返回的View是本次事件需要的最佳View
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
// 判断一个点是否落在范围内
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
二、应用
1、扩大按钮响应范围。
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
// CGRect rect = 修改后的范围;
// return CGRectContainsPoint(rect, point);
CGRect bounds = self.bounds;
bounds = CGRectInset(bounds, -10, -10);
return CGRectContainsPoint(bounds, point);
}
2、子视图超出父范围时,需要子视图响应事件。
重写父视图方法
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
// 1.判断当前控件能否接收事件
if (self.userInteractionEnabled == NO || self.alpha <= 0.01 || self.hidden == YES) {
return nil;
}
// 2.从后往前遍历自己的子控件
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;
}