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;
}
posted @ 2021-01-16 12:15  ebamboo  阅读(180)  评论(0编辑  收藏  举报