一. 事件
1. iOS中事件分为三种
- 触摸
- 加速计
- 远程
2. 什么是响应者对象?
继承了UIResponds的对象我们称它为响应者对象
UIApplication、UIViewController、UIView都继承自UIResponder
因此它们都是响应者对象,都能够接收并处理事件
继承UIResponder称响应者对象
3. 事件调用方法
1> 触摸事件
-(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;
2> 加速计事件调用
-(void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent*)event;
-(void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent*)event;
-(void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent*)event;
3> 远程控制事件
-(void)remoteControlReceivedWithEvent:(UIEvent*)event;
4. 完整触摸过程
触摸开始:
-(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
5. UIView拖拽
1> 自定义UIView,实现监听方法.
2> 确定在TouchMove方法当中进行操作,因为用户手指在视图上移动的时候才需要移动视图。
3> 获取当前手指的位置和上一个手指的位置.
4> 当前视图的位置 = 上一次视图的位置 + 手指的偏移量
关健代码实现:
- 拿到UITouch就能获取当前点
UITouch*touch =[touches anyObject];
- 获取当前点
CGPoint curP =[touch locationInView:self];
- 获取上一个点
CGPoint preP =[touch previousLocationInView:self];
- 获取手指x轴偏移量
CGFloat offsetX = curP.x - preP.x;
- 获取手指y轴偏移量
CGFloat offsetY = curP.y - preP.y;
- 移动当前view
self.transform =CGAffineTransformTranslate(self.transform, offsetX, offsetY);
二. 事件的产生和传递
1. 事件的产生和传递
当发生一个触摸事件后,系统会将该事件加入到一个由UIApplication管理的事件队列中.
UIApplication会从事件队列中取出最前面的事件,并将事件分发下去以便处理.
主窗口会在视图层次结构中找到一个最合适的视图来处理触摸事件
触摸事件的传递是从父控件传递到子控件的.
如果一个父控件不能接收事件,那么它里面的了子控件也不能够接收事件.
2. 一个控件什么情况下不能够接收事件
. 不接收用户交互时不能够处理事件
userInteractionEnabled = NO
. 当一个控件隐藏的时候不能够接收事件
Hidden = YES的时候
. 当一个控件为透明白时候也不能够接收事件
注意:UIImageView的userInteractionEnabled默认就是NO,因此UIImageView以及它的子控件默认是不能接收触摸事件的
3. 如何寻找合适的View
1> 先判断自己是否能够接收触摸事件,如果能再继续往下判断,
2> 再判断触摸的当前点在不在自己的身上.
3> 如果在自己身上,它会从后往前遍历子控件,遍历出每一个子控件后,重复前面的两个步骤.
4> 如果没有符合条件的子控件,那么它自己就是最适合的View.
4. hitTest方法与PointInside方法
1> hitTest方法寻找合适的View,只要一个事件传递给控件时就会调用
-(UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event
2> PointInside方法判断当前点是否在此控件上(执行hitTest方法底层会调用此方法)
-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)event{
return YES;
}
注:point必须得要跟当前View同一个坐标系.
5. hitText方法底层实现原理
//作用:寻找最适合的View
//什么时候调用:当事件传递给当前View时就会调用这个方法
-(UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event{
//1.判断自己能否接收事件
if(self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <=0.01){
return nil;
}
//2.触摸点在不在自己身上
if([self pointInside:point withEvent:event]== NO){
return nil;
}
//3.从后往前遍历自己的子控件(重复前面的两个步骤)
int count =(int)self.subviews.count;
//2,count i = 1 i = 0
for(int i = count -1; i >=0; i--){
//取出子控件
UIView*childV = self.subviews[i];
//point必须得要跟childV相同的坐标系.
//把point转换childV坐标系上面的点
CGPoint childP =[self convertPoint:point toView:childV];
UIView*fitView =[childV hitTest:childP withEvent:event];
if(fitView){
return fitView;
}
}
//4.如果没有符合条件的子控件,那么就自己最适合处理
return self;
}
三. 事件的响应和事件传递的完整过程
1. 事件响应
用户点击屏幕后产生的一个触摸事件,经过一系列的传递过程后,会找到最合适的视图控件来处理这个事件,
找到最合适的视图控件后,就会调用控件的touches方法来作具体的事件处理
那这些touches方法的默认做法是将事件顺着响应者链条向上传递,将事件交给上一个响应者进行处理
什么是响应者链条?
是由多个响应者对象连接起来的链条.
什么是响应者对象?
继承了UIResponder对象我们称之为响应者对象,也就是能处理事件的对象
2. 事件传递的完整过程?
在产生一个事件时,系统会将该事件加入到一个由UIApplication管理的事件队列中,
UIApplication会从事件队列中取出最前面的事件,将它传递给先发送事件给应用程序的主窗口.
主窗口会调用hitTest方法寻找最适合的视图控件,找到后就会调用视图控件的touches方法来做具体的事情.
当调用touches方法,它的默认做法, 就会将事件顺着响应者链条往上传递,
传递给上一个响应者,接着就会调用上一个响应者的touches方法
如何去寻找上一个响应者?
1.如果当前的View是控制器的View,那么控制器就是上一个响应者.
2.如果当前的View不是控制器的View,那么它的父控件就是上一个响应者.
3.在视图层次结构的最顶级视图,如果也不能处理收到的事件或消息,则其将事件或消息传递给window对象进行处理
4.如果window对象也不处理,则其将事件或消息传递给UIApplication对象
5.如果UIApplication也不能处理该事件或消息,则将其丢弃