简述:
(一)iOS中的事件
iOS中的事件可以分为3大类型:
触摸事件
加速计事件
远程控制事件
这里我们只讨论iOS中的触摸事件。
(二)事件的处理
// UIView是UIResponder的子类,可以覆盖下列4个方法处理不同的触摸事件
// 一根或者多根手指开始触摸view,系统会自动调用view的下面方法
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
// 一根或者多根手指在view上移动,系统会自动调用view的下面方法(随着手指的移动,会持续调用该方法)
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
// 一根或者多根手指离开view,系统会自动调用view的下面方法- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
// 触摸结束前,某个系统事件(例如电话呼入)会打断触摸过程,系统会自动调用view的下面方法
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
// 提示:touches中存放的都是UITouch对象
(三)iOS中的事件的产生和传递
3.1.事件的产生
(1) 发生触摸事件后,系统会将该事件加入到一个由UIApplication管理的事件队列中。
为什么是队列而不是栈?
因为队列的特定是先进先出,先产生的事件先处理才符合常理,所以把事件添加到队列。
(2) UIApplication会从事件队列中取出最前面的事件,并将事件分发下去以便处理,通常,先发送事件给应用程序的主窗口(keyWindow)。
(3) 主窗口会在视图层次结构中找到一个最合适的视图来处理触摸事件,这也是整个事件处理过程的第一步。
(4) 找到合适的视图控件后,就会调用视图控件的touches方法来作具体的事件处理。
3.2.事件的传递
触摸事件的传递是从父控件传递到子控件
也就是
UIApplication -> window -> 寻找处理事件最合适的view
应用如何找到最合适的控件来处理事件?
- 首先判断主窗口(keyWindow)自己是否能接受触摸事件
- 判断触摸点是否在自己身上
- 子控件数组中从后往前遍历子控件,重复前面的两个步骤(所谓从后往前遍历子控件,就是首先查找子控件数组中最后一个元素,然后执行1、2步骤)
- view,比如叫做fitView,那么会把这个事件交给这个fitView,再遍历这个fitView的子控件,直至没有更合适的view为止。
- 如果没有符合条件的子控件,那么就认为自己最合适处理这个事件,也就是自己是最合适的view。
3.3 寻找最合适的view底层剖析
两个重要的方法:
-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
(1) hitTest:withEvent:方法
什么时候调用?
只要事件一传递给一个控件,这个控件就会调用他自己的hitTest:withEvent:方法
作用
寻找并返回最合适的view(能够响应事件的那个最合适的view)
注 意:不管这个控件能不能处理事件,也不管触摸点在不在这个控件上,事
件都会先传递给这个控件,随后再调用hitTest:withEvent:方法
拦截事件的处理
- 正因为hitTest:withEvent:方法可以返回最合适的view,所以可以通过重写hitTest:withEvent:方法,返回指定的view作为最合适的view。
- 不管点击哪里,最合适的view都是hitTest:withEvent:方法中返回的那个view。
- 通过重写hitTest:withEvent:,就可以拦截事件的传递过程,想让谁处理事件谁就处理事件。
- 事件传递给谁,就会调用谁的hitTest:withEvent:方法。
注 意:如果hitTest:withEvent:方法中返回nil,那么调用该方法的控件本身和
其子控件都不是最合适的view,也就是在自己身上没有找到更合适的view。
那么最合适的view就是该控件的父控件。
所以事件的传递顺序是这样的:
产生触摸事件 -> UIApplication事件队列 -> [UIWindow hitTest:withEvent:] -> 返回更合适的view -> [子控件 hitTest:withEvent:] -> 返回最合适的view
(2) pointInside:withEvent:方法
pointInside:withEvent:方法判断点在不在当前view上(方法调用者的坐标系上)
- 如果返回YES,代表点在方法调用者的坐标系上;
- 返回NO代表点不在方法调用者的坐标系上,那么方法调用者也就不能处理事件。
UIView不能接收触摸事件的三种情况:
- 不允许交互:userInteractionEnabled = NO
- 隐藏:如果把父控件隐藏,那么子控件也会隐藏,隐藏的控件不能接受事件
- 透明度:如果设置一个控件的透明度<0.01,会直接影响子控件的透明度。alpha:0.0~0.01为透明。