iOS事件分类:
1. 触摸事件
- 手指点击,滑动,拖拽,捏合等都属于触摸事件
2. 加速计事件
- 手机摇晃等事件
3. 远程控制事件
- 耳机遥控音乐播放等事件
触摸事件的处理
在iOS开发中,我们要处理各种各样的事件,但是并不是所有的对象都能处理事件
只有继承自UIResponder的对象才能接受和处理事件。我们称这些对象为“响应者对象”。
当我们用手指触摸屏幕上的控件(UIView及其子类)时,系统会自动调用View的事件处理方法:
1. 当手指开始触摸时调用:
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
2. 当手指在控件上移动时调用:
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
3. 当手指离开控件时调用:
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
4. 触摸事件被强制结束,如电话呼入强制切换界面会打断触摸过程,此时系统会调用:
- (void) touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
以上方法中,系统都会传入两个参数:
1)(NSSet *)touches :这时一个UITouch对象的集合(你用几个手指触摸集合中就有几个UITouch对象)
2)(UIEvent *)event : 触摸产生的事件
UITouch对象:
1. UITouch对象中保存者跟手指触摸时相关的信息,比如触摸的位置,时间,阶段等
2. 手指在控件上滑动是,UITouch对象会更新自身信息,使之能一直保存手指触摸的位置信息等
3. 当手指离开屏幕时,系统会自动销毁这个UITouch对象
4. 利用UiTouch对象可以获得手指在屏幕上的触摸位置信息,调用一下俩个方法:
- (CGPoint)locationInView:(UIView *)view
这个方法返回当前手指触摸在view上的位置,如果传入的view为空,返回的触摸点为在UIWindow上的位置
- (CGPoint)previousLocationInView:(UIView *)view
这个方法返回的是前一个触摸点的位置
UIEvent对象:
每产生一个事件,都会产生一个UIEvent对象,这个对象记录了事件产生的事件和类型
每一个完整的触摸事件,都会经历上面3个状态:
触摸开始->触摸移动->触摸结束 (->触摸取消)对应:touchbegan ->touchMoved->touchEnded (->touchCancelled)
注意:
1.每一个完整的触摸过程中只会产生一个事件对象,4个触摸方法都是同一个事件对象
2.如果使用两根手指同时触摸屏幕,(NSSet *)touches中存放两根UITouch对象,并且系统只会调用一次touchBagan:withEvent:方法。
3.如果两根手指一前一后触摸屏幕,(NSSet *)touches中存放一个UITouch对象,并且系统会调用两次touchBagan:withEvent:方法。
事件的传递
事件的传递过程:
1. 发生触摸事件后,系统会将该事件丢到UIAppcation管理的事件队列中(按先入先出方式处理)
2. UIApplication会从事件队列中取出最前面的事件,将事件发送给应用程序主窗口(KeyWindow)
3. 程序主窗口会根据程序视图的层次结构,选出最适合处理该事件的视图来处理触摸事件
系统选择最适合处理事件的视图的方法:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
系统会调用这个方法来选择最适合处理事件的视图。 以下是内部实现原理:
1)判断自己能否处理事件(3个条件:1,能否和用户交互,UserInteractionEnable为YES 2,控件透明度>=0.01 3,控件hidden属性为YES)
2)如果自己能处理,则判断触摸点是否在自己身上
3)如果在自己身上,则遍历本控件的所有子控件,遍历顺序是,从后往前遍历(即后添加的先遍历),如果遍历有子控件,然后子控件重复这三个步骤。
4)如果没有符合条件的子控件,那么这个控件就是最适合处理事件的控件
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
// 1.判断下能否接收触摸事件
if (self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.0) return nil;
// 2.判断下点在不在控件上
if ([self pointInside:point withEvent:event] == NO) return nil;
// 3.从后往前遍历子控件
int count = (int)self.subviews.count;
for (int i = count - 1; i >= 0 ; i--) {
// 取出显示在最前面的子控件
UIView *childView = self.subviews[i];
// 转换成子控件坐标系上点
CGPoint childP = [self convertPoint:point toView:childView];
UIView *fitView = [childView hitTest:childP withEvent:event];
if (fitView) {
return fitView;
}
// 表示没有比自己更合适的view
}
return self;
}
我们一般可以是用这个方法来拦截事件传递。
4.找到视图控件后,系统就会调用控件的touch(4个touch方法)方法来处理事件
事件的处理过程:
当系统经过上面的步骤找到最适合处理事件的控件后,就会调用这个控件的touch方法来处理对象。而系统默认情况下,这些touch方法会顺这响应者链条向上传递,将事件移交给上一个响应者处理。
什么是响应者链条?
由多个响应者连接起来的链条。
注: 一个对象可以有多个响应者处理
实现方法:由于系统处理事件时会将事件顺着响应者链条向上传递,因此可以重写下一级的事件处理方法时调用回[super touch...]方法,在上一级再重写处理方法,这样两个响应者都能对这个事件进行处理了。
判断上一级响应者:
1.如果这个控件(view)是控制器的view,那么上一级响应者就是控制器
2.如果这个控件(view)不是控制器的view,那么上一级响应者就是它的父控件