iOS触摸事件处理和响应链
iOS中的事件
用户与app交互的时候会产生各种各样的的事件,iOS中事件分为三大类型:1)触摸事件 ;2)加速计事件 ;3)远程控制事件
在iOS中不是任何对象都能处理事件,只有继承了UIResponder的对象才能接收并处理事件。我们称之为“响应者对象”。UIApplication、UIViewController、UIView都继承自UIResponder,因此它们都是响应者对象,都能够接收并处理事件
触摸事件
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;
加速计事件
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event;
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event;
- (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event;
远程控制事件
- (void)remoteControlReceivedWithEvent:(UIEvent *)event;
UITouch
用户用手指触摸屏幕时,会创建一个与手指相关联的UITouch对象。
UITouch的作用:保存着跟手指相关的信息,比如触摸的位置、时间、阶段
当手指移动时,系统会更新同一个UITouch对象,使之能够一直保存该手指在的触摸位置。
当手指离开屏幕时,系统会销毁相应的UITouch对象
提示:iPhone开发中,要避免使用双击事件!
处理原理如下:
1> 当用户点击屏幕时,会产生一个触摸事件,系统会将该事件加入到一个由UIApplication管理的事件队列中;
2>UIApplication会从事件队列中取出最前面的事件进行分发以便处理,通常,先发送事件给应用程序的主窗口(UIWindow);
3>主窗口会调用hitTest:withEvent:方法在视图(UIView)层次结构中找到一个最合适的UIView来处理触摸事件
(hitTest:withEvent:其实是UIView的一个方法,UIWindow继承自UIView,因此主窗口UIWindow也是属于视图的一种)
hitTest:withEvent:方法大致处理流程是这样的:首先调用当前视图的pointInside:withEvent:方法判断触摸点是否在当前视图内:
▶ 若pointInside:withEvent:方法返回NO,说明触摸点不在当前视图内,则当前视图的hitTest:withEvent:返回nil
▶ 若pointInside:withEvent:方法返回YES,说明触摸点在当前视图内,则遍历当前视图的所有子视图(subviews),调用子视图的
注意:hitTest:withEvent:方法忽略隐藏(hidden=YES)的视图,禁止用户操作(userInteractionEnabled=NO)的视图,以及alpha级别小于0.01(alpha<0.01)的视图。
这里ViewA包含ViewB和ViewC,ViewC中继续包含ViewD和ViewE。假设我们点击了viewE区域,则hit-test View判定过程如下:
1. 触摸在A内部,所以需要检查B和C
2. 触摸不在B内部,在C内部,所以需要检查D和E
3. 触摸不在D内部,但在E内部,由于E已经是叶子了,所以判定到此结束
消息处理过程
在找到的那个视图里处理,处理完后根据需要,利用响应链nextResponder可将消息往下一个响应者传递。
UIAppliaction <- UIWindow <- UIViewController <- UIView <- UIView
【关键】:要理解的有三点:
1、iOS判断哪个界面能接受消息是从View层级结构的父View向子View传递,即树状结构的根节点向叶子节点递归传递。
2、hitTest和pointInside成对,且hitTest会调用pointInside。
3、iOS的消息处理是,当消息被人处理后默认不再向父层传递。