IOS系统中只有继承了UIResponder类,也就是说只有UIResponder的子类才能处理IOS用户交互的各种事件。
这是因为UIResponder有一下几个方法:
//一根或者多根手指开始触摸view,系统会自动调用view的下面方法
1. - (void)touchesBegan:(NSSet )touches withEvent:(UIEvent )event
// 一根或者多根手指在view上移动,系统会自动调用view的下面方法(随着手指的移动,会持续调用该方法)
2. - (void)touchesMoved:(NSSet )touches withEvent:(UIEvent )event
// 一根或者多根手指离开view,系统会自动调用view的下面方法- -
3. -(void)touchesEnded:(NSSet )touches withEvent:(UIEvent )event
// 触摸结束前,某个系统事件(例如电话呼入)会打断触摸过程,系统会自动调用view的下面方法
- (void)touchesCancelled:(NSSet )touches withEvent:(UIEvent )event
// 提示:touches中存放的都是UITouch对象
系统中继承UIResponder的类有:
1.UIApplication
2.UIController
3.UIView
4.UIWindow
5.AppDelegate
¥¥事件的传递机制:
当我们点击页面的时候:事件首先传到UIApplication,UIApplication收集所有的事件源放入到事件队列中,并且是FIFO,然后把事件逐一传递给UIViewController [hitTest: withEvent:]把事件传递给它的RootView[hitTest: withEvent:]迭代传递给它所有子布局子子孙孙SonLayView[hitTest: withEvent:]直到有接收这个事件的View,如果找到底都没有找到这个事件就会被遗弃。
¥¥事件的相应
在事件传递的过程中一旦有要接收事件的View或者Controller出现,就开始下一步,就是事件的响应,所谓相应就是系统开始调用上面列举的UIResponder的那几个方法,从接收事件的View或者Controller开始,如果愿意可以逐级向上相响,也就是说我们我在touchesBegain:响应完成后只要最后调用[super touchesBegain:]就可以了。
实例代码如下:
细心的小伙伴,都会发现刚才说事件传递的时候一直都提到的一个很重要的方法[ hitTest: withEvent:],它到底是干什么的呢?它是事件传递过程重要的不能再重要的一环了,就是通过它给我们返回愿意接收事件的对象的,然后我们才能开始进行下一步:事件的响应。
-(UIView*)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
BOOL b=[self pointInside:point withEvent:event];
if(b==NO){
//如果点击的点不在本View的范围内 本View和它的所有布局View都不会接收到事件
return nil;
}
//如果本View是隐藏的或者是不接收用户交互的或者透明度小于0.01几乎就是完全透明了
//本View和它的所有子View都不会接收到事件
if(self.hidden==YES||self.userInteractionEnabled==NO||self.alpha<0.01)return nil;
//遍历所有的子布局View把事件传递给他们 看看他们有没有愿意接受本事件的
int sonLayCount=(int)self.subviews.count;
//从后向前迭代
for(int i=sonLayCount-1;i>=0;i--){
UIView *sonLayView=self.subviews[i];
//坐标系转换 转换成自布局相对于父布局的坐标点
CGPoint sonLayPoint=[self convertPoint:point toView:sonLayView];
if([sonLayView hitTest:sonLayPoint withEvent:event]){
return sonLayView;
}
}
//如果所有的子布局都不接收这个事件 可以返回自己接受,也可以抛给自己父布局直接返回nil
return self;或者return nil;
//上面是系统默认的处理方式 我们也可以重写成如下:无论点击那里都把这个事件传递给这个View的第一个子View,上面代码也可以自己自定义
return [self subviews][0];
}
其中还有一个很重要的中间类就是UITouch:
1.UITouch对象
当用户用一根手指触摸屏幕时,会创建一个与手指相关的UITouch对象
一根手指对应一个UITouch对象
如果两根手指同时触摸一个view,那么view只会调用一次touchesBegan:withEvent:方法,touches参数中装着2个UITouch对象
如果这两根手指一前一后分开触摸同一个view,那么view会分别调用2次touchesBegan:withEvent:方法,并且每次调用时的touches参数中只包含一个UITouch对象
2.UITouch的作用
保存着跟手指相关的信息,比如触摸的位置、时间、阶段
当手指移动时,系统会更新同一个UITouch对象,使之能够一直保存该手指在的触摸位置
当手指离开屏幕时,系统会销毁相应的UITouch对象
3.UITouch的属性
触摸产生时所处的窗口
@property(nonatomic,readonly,retain) UIWindow *window;
触摸产生时所处的视图
@property(nonatomic,readonly,retain) UIView *view
;
短时间内点按屏幕的次数,可以根据tapCount判断单击、双击或更多的点击
@property(nonatomic,readonly) NSUInteger tapCount;
记录了触摸事件产生或变化时的时间,单位是秒@property(nonatomic,readonly) NSTimeInterval timestamp;
当前触摸事件所处的状态
@property(nonatomic,readonly) UITouchPhase phase;
4.UITouch的方法
(CGPoint)locationInView:(UIView *)view;
// 返回值表示触摸在view上的位置
// 这里返回的位置是针对view的坐标系的(以view的左上角为原点(0, 0))
// 调用时传入的view参数为nil的话,返回的是触摸点在UIWindow的位置
(CGPoint)previousLocationInView:(UIView *)view;
// 该方法记录了前一个触摸点的位置
用到的UIView中两个重要的方法:
1.[UIView pointInSide: evetWith: ]判断某个点是否在本View范围内
2.[UIView convertPoint: toView: ]转换坐标系,一点在一个坐标系的转换成在另一个坐标系的点
总结:
IOS的事件的处理其实就分两块,一块是处理事件的传递,一块是处理事件的响应。事件传递是最关键的是找有谁来开始接收这个事件,核心功臣就是这个方法[hitTest: eventWith:],事件的响应就是我们接到事件后来做些什么来响应,核心工程方法是[touchesBegain:withEvent]和它的另外三个小伙伴。