$\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;