iOS基础之响应者链

目录 

前言

可用来:
  1. 扩大按钮的点击区域
  2. 
用户操作设备的方式:触摸屏幕、晃动设备、通过遥控设施控制设备。对应的事件类型有以下三种:
    触屏事件(Touch Event)
    运动事件(Motion Event)
    远端控制事件(Remote-Control Event)
  1. 响应者链(即响应链)

由当前响应者向父响应者以上所延伸出的一条链(view->superView…->VC->Window->Appliction)
1、响应者(有响应和处理事件能力的对象)直接或间接继承自UIResponder
2、第一响应者:最先响应事件的响应者
3、当前响应者:当前负责响应事件的响应者

  1. 触摸操作的响应处理过程

1、寻找第一响应者

当iOS系统检测到触摸操作时,会将触摸事件打包成UIEvent对象并存入当前UIApplication应用的事件队列中。UIApplication将触摸事件从事件队列取出,并传递给UIWindow。

UIWindow会调用根视图的hitTest:withEvent:方法,该方法内部会调用pointInside:withEvent:判断触摸点是否在根视图内;
    若不在,则hitTest:withEvent:返回nil; 
    若在,则向当前视图的所有子视图(subviews,index越大越先被访问,所有子视图的遍历顺序是从最顶层视图一直到到最底层视图,即从subviews数组的末尾向前遍历)发送hitTest:withEvent:消息。若子视图还有子辈视图且触摸点在该子视图(当前视图)内则重复上步操作。直到有出现以下2种临界情况,则找到第一响应者,处理结束。
/*
直到2种临界情况
        1、子视图(且该子视图没有子辈视图)的pointInside:withEvent:方法返回true,hitTest:withEvent:方法返回此对象,处理结束。
        2、子视图(且该子视图所有子辈视图pointInside:withEvent:方法返回false即hitTest:withEvent:返回nil)的pointInside:withEvent:方法返回true,则hitTest:withEvent:方法返回此对象,处理结束。
*/

2、向上分发

当前响应者可以选择处理或者忽略响应。选择忽略则交给父节点去响应,选择处理则可选择是否向父节点传递事件(默认:停止分发) 。如果最后交给UIApplication也没处理该事件,则忽略该事件。

例子:如果一个View是UIScrollView的子View,触摸view上下滑动会使UIScrollView滚动。如果一个View只是在UIScrollView的上方,并不是UIScrollView的子View,则触摸view上下并不会使UIScrollView滚动。

返回下一个响应者(即父响应者)

[button nextResponder]

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
    UIView *touchView = self;
    if ([self pointInside:point withEvent:event] &&
       (!self.hidden) &&
       self.userInteractionEnabled &&
       (self.alpha >= 0.01f)) {

        for (UIView *subView in self.subviews) {
            [subview convertPoint:point fromView:self];
            UIView *subTouchView = [subView hitTest:subPoint withEvent:event];
            if (subTouchView) {
                touchView = subTouchView;
                break;
            }
        }
    } else {
        touchView = nil;
    }

    return touchView;
}
  1. hitTest:withEvent:会忽略哪些视图

1、隐藏(hidden=YES)的视图
2、禁止用户操作(userInteractionEnabled=false、或 enable=false)的视图
3、以及alpha级别小于0.01(alpha<0.01)的视图
4、子视图超过父视图的区域

  1. hitTest:withEvent:的应用

1. 扩大按钮的点击范围
2. 重叠时希望下方View响应事件

示例1: 扩大按钮的点击范围

继承:ZYDMatchPraiseButton即可

@interface ZYDMatchPraiseButton : UIButton
@end
@implementation ZYDMatchPraiseButton
-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{
    CGRect rect=self.bounds;
    CGFloat width=rect.size.width;
    CGFloat height=rect.size.height;
    rect=CGRectInset(rect, -width*0.5, -height*0.5);  // 宽高各扩大了一半
    return CGRectContainsPoint(rect, point);
}
/*
// 另一种,按固定大小
- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event {
    return CGRectContainsPoint(HitTestingBounds(self.bounds, 100, 100), point);
}
CGRect HitTestingBounds(CGRect bounds, CGFloat minimumHitTestWidth, CGFloat minimumHitTestHeight) {
    
    //
    CGRect hitTestingBounds = bounds;
    if (minimumHitTestWidth > bounds.size.width) {
        hitTestingBounds.size.width = minimumHitTestWidth;
        hitTestingBounds.origin.x -= (minimumHitTestWidth - bounds.size.width)/2;
    }
    if (minimumHitTestHeight > bounds.size.height) {
        hitTestingBounds.size.height = minimumHitTestHeight;
        hitTestingBounds.origin.y -= (minimumHitTestHeight - bounds.size.height)/2;
    }
    return hitTestingBounds;
}
*/
@end

你可能感兴趣的:(iOS基础之响应者链)