史上最全最细最直接解析IOS事件传递机制

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]和它的另外三个小伙伴。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值