响应者链

$\color{red}{响应者链的原理 ?}$

原理 :

首先只有继承UIResponder的的类,才能处理事件。

当用户触摸(Touch)屏幕进行交互时,系统首先要找到响应者(Responder)。系统检测到手指触摸(Touch)操作时,将Touch 以UIEvent的方式加入UIApplication事件队列中。UIApplication从事件队列中取出最新的触摸事件进行分发传递到UIWindow进行处理。UIWindow 会通过hitTest:withEvent:方法寻找触碰点所在的视图,这个过程称之为hit-test view。

hitTest 的顺序如下:

点击屏幕--->UIApplication->UIWindow->Root View->···->subview

在顶级视图(Root View)上调用pointInside:withEvent:方法判断触摸点是否在当前视图内;

如果返回NO,那么hitTest:withEvent:返回nil;

如果返回YES,那么它会向当前视图的所有子视图发送hitTest:withEvent:消息,所有子视图的遍历顺序是从最顶层视图一直到到最底层视图(倒叙遍历),即从subviews数组的末尾向前遍历,直到有子视图返回非空对象或者全部子视图遍历完毕。

如果有subview的hitTest:withEvent:返回非空对象则A返回此对象,处理结束(注意这个过程,子视图也是根据pointInside:withEvent:的返回值来确定是返回空还是当前子视图对象的。并且这个过程中如果子视图的hidden=YES、userInteractionEnabled=NO或者alpha小于0.1都会并忽略);

如果所有subview遍历结束仍然没有返回非空对象,则hitTest:withEvent:返回self;

系统就是这样通过hit test找到触碰到的视图(Initial View)进行响应。

触摸事件会优先分发给附在view的手势,在这段延迟的期间,如果手势被识别,那么view的touches系列将被立刻取消,如果没有被识别,那么会继续我们所熟知的touches系列流程。

系统判别返回的 UIView是不是 UIControl的子类,如果是的话,直接将整个触摸事件交付 UIControl实例完成

一下是视图没有响应的几个情况:

1.userInteractionEnabled=NO;

2.hidden=YES;

3.alpha=0~0.01;

4.没有实现touchesBegan:withEvent:方法,直接执行touchesMove:withEvent:等方法;

5.目标视图点击区域不在父视图的Frame上 (superView背景色为clear Color的时候经常会忽略这个问题)。

触摸事件的方法:

// 一根或者多根手指开始触摸view,系统会自动调用下面方法

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;

// 一根或者多根手指在view上移动,系统会自动调用下面方法(随着手指的移动,会持续调用该方法)

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;

// 一根或者多根手指离开view,系统会自动调用下面方法

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;

// 触摸结束前,某个系统事件(例如电话呼入)会打断触摸过程,系统会自动调用下面方法

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;

第一响应者 (The First Responder):

简单的讲,第一响应者是一个UIWindow对象接收到一个事件后,第一个来响应的该事件的对象。

这个第一响应者与之前讨论的触摸检测到的第一个响应的UIView并不是一个概念。第一响应者一般情况下用于处理非触摸事件(手机摇晃、耳机线控的远程空间)或非本窗口的触摸事件(键盘触摸事件),通俗点讲其实就是管别人闲事的响应者

如果方法canBecomeFirstResponder返回YES,这个响应者对象才有资格称为第一响应者。有资格并不代表一定可以成为第一响应者,就好像符合要求并不一定能够应聘成功一样,所以还差一个聘用环节,那就是becomeFirstResponder正式成为第一响应者。

使用场景:

不规则图形的点击事件,或者扩大缩小点击范围,

响应者链的事件传递过程:

响应机制的顺序和事件传递是反着的,事件传递是从上层往下层传递,像剥洋葱一样,找到最终响应事件的view,响应机制则是从下层向上层,响应机制主要依赖的方法是:

touchesBegin , touchesMoved, touchesEnded等方法

响应链是通过nextResponder属性组成的一个链表。

点击的view有 superView,nextResponder就是superView;

如果view的控制器存在,就传递给控制器;如果控制器不存在,则将其传递给它的父视图

在视图层次结构的最顶级视图,如果也不能处理收到的事件或消息,则其将事件或消息传递给window对象进行处理

如果window对象也不处理,则其将事件或消息传递给UIApplication对象

如果UIApplication也不能处理该事件或消息,则将其丢弃

响应链:由离用户最近的view向系统传递。initial view –> super view –> ….. –> view controller –> window –> Application –> AppDelegate

基本的开发目标,不让父视图的手势识别干扰子视图UIView的点击事件响应或者说响应链的正常传递。

一般都会是重写UIGestureRecognizerDelegate中的- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch方法。

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch

    // 若为UITableViewCellContentView(即点击了tableViewCell),

    if ([NSStringFromClass([touch.view class]) isEqualToString:@"UITableViewCellContentView"]) { 

    // cell 不需要响应 父视图的手势,保证didselect 可以正常

        return NO; 

    } 

    //默认都需要响应

    return  YES; 

}


1. 简述一下响应者链?假如一个viewA 上有一个viewB,且viewB的面积大于viewA 点击viewB且不和viewA重叠的区域 是否会响应 为什么?

答案和其他问题待更新!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

2. 有哪几种手势通知方法、写清楚方法名?

答:

-(void)touchesBegan:(NSSet)touchedwithEvent:(UIEvent)event;

-(void)touchesMoved:(NSSet)touched withEvent:(UIEvent)event;

-(void)touchesEnded:(NSSet)touchedwithEvent:(UIEvent)event;

-(void)touchesCanceled:(NSSet)touchedwithEvent:(UIEvent)event;

你可能感兴趣的:(响应者链)