不要怂一起看看响应者链和事件传递

在iOS中,只有继承了UIResponder的对象才可以接受并处理时间,这些对象被称为响应者对象

>   UIApplication

>  UIViewController

> UIView

这些都继承自UIResponder,因此他们都是响应者对象,都能够接收并且处理事件,UIResponder提供了以下方法来处理事件

触摸事件

// 开始点击
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;

// 当手指在view上移动的时候 
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;

// 当手指离开这个view的时候
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;

// 当触摸事件被打断的时候调用(电话打入)
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;

加速计事件
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event;
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event;
- (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event;
远程控制事件
- (void)remoteControlReceivedWithEvent:(UIEvent *)event;

-------              当要支持多根手指点击设置属性 : mutableTouch            --------------

事件对象:UIEvent

UIEvent表示用户交互的事件对象,有以下属性

@property(nonatomic,readonly) UIEventType     type;
@property(nonatomic,readonly) UIEventSubtype  subtype;

其中type表示当前响应事件的类型,分别有多点触控、摇一摇、远程操控、3DTouch,在一个用户点击事件处理的过程中,UIEvent对象时唯一的

点击对象:UITouch

UITouch表示单个点击,其类文件中存在枚举类型UITouchPhase的属性,用来表示当前点击的状态。这些状态包括点击开始、移动、停止不动、结束、取消五个状态,每次点击发生的时候,点击对象都放在一个集合中传入UIResponder的回调方法中,我们通过集合中对象获取用户点击的位置

============属性===============

触摸产生时所处的窗口
@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;

============方法===============

// 返回值表示触摸在view上的位置
// 这里返回的位置是针对view的坐标系的(以view的左上角为原点(0, 0))
// 调用时传入的view参数为nil的话,返回的是触摸点在UIWindow的位置
-(CGPoint)locationInView:(UIView *)view;

// 该方法记录了前一个触摸点的位置
-(CGPoint)previousLocationInView:(UIView *)view;

-(CGPoint)locationInView:(UIView  *)view   获取当前点击坐标点

-(CGPoint)  previousLocationInView: (UIview *) view 获取上个点击位置的坐标点

除了touch回调的几个点击事件,手势UIGestureRecognizer对象也附加在view上,来实现其他手势事件,在view添加单击手势之后,原来的touchesEnded就无效了

UITouch的作用,保存着跟手指相关的信息,比如位置、时间、阶段,当手指离开时,系统会销毁响应的UITouch对象。

事件的传递和原理

1  >   发生触摸事件后,系统会将该事件加入到一个由UIApplication管理的事件队列中(为什么是队列而不是栈呢?因为队列是FIFO,也就是先进先出,先产生的事件先处理才对,栈是先进后出的。),UIApplication会从事件中取出最前面的事件,发送给程序的主窗口--->keyWindow,主窗口会在视图层次结构中找到一个最适合的试图来处理事件。

2  >  找到最合适的试图空间后,会调用试图控件的触摸事件的方法

示例:

点击了绿色:UIApplication  --> UIWindow   -->  白色   ---> 绿色

点击了蓝色:UIApplication  --> UIWindow  -->  白色   --->  橙色  ---> 蓝色

如果父控件不能接受触摸事件,那么子控件就不可能接受到触摸事件

一个试图无法接收触摸事件可能是以下情况

1、不接收用户交互

      userInteractionEnabled = NO

2、隐藏

       hidden = yes;

3、透明度

    alpha  = 0;

其中:UIImageView的userInteractionEnable默认为NO,因此UIImageVIew以及它的子控件默认不能接受触摸事件,如果想让某个view不能处理事件,那么就设置这三个属性

    那么怎么找到最合适的控件来处理事件呢?

    1️⃣、首先判断主窗口是否能接受事件

    2️⃣、判断触摸点是否在自己身上

    3️⃣、子控件数组中从后往前遍历子控件,重复1️⃣、2️⃣两个步骤

    4️⃣、比如有个LHView,那么把这个事件交给这个view,再遍历LHView的子控件,直至没有更合适的为止

    5️⃣、如果没有符合条件的子控件,那么就认为自己最适合处理这个事件

当一个试图接收到事件传递时,就会调用  - hitText方法,这个方法用于判断找出最合适的view,找到最合适的view就会调用响应的处理方法

代码如下

-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    //1、判断当前的view能否接收事件
    if (self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01) return nil;
    //2、判断点在不在当前控件
    NSInteger pointCount = self.subviews.count;
    //3、从后往前遍历子控件
    for (NSInteger i = pointCount - 1; i >= 0; i--) {
        
        UIView * childView = self.subviews[i];
        //把当前控件的坐标转换成子控件上的坐标
        CGPoint childPoint = [self convertPoint:point toView:childView];
        UIView * fitView = [childView hitTest:childPoint withEvent:event];
        if (fitView) {
            return fitView;
        }
    }
    //循环结束,没有找到比自己更合适的view
    return self;
}

总结: 

事件的传递与响应(响应者链条)

1、当一个事件发生后,事件会从父控件传给子控件,也就是说有UIApplication -> UIWindow -> UIView -> initial view  的传递,寻找最合适的view的过程,当一个试图接收到事件之后就会调用   -(UIView *)hitTest:(CGpoint) point withEvent:(UIEvent *)event,经过5个步骤来寻找的过程。

2、事件的响应:首先看当前的initial view是否处理这个事件,如果不则会将事件传递给上级试图,如果上级试图仍然无法处理则会继续往上传递,一直传递到控制器 viewController。会判断试图控制器的根试图能否处理此事件,此时还是不能处理则交给UIWindow,如果还不能处理则交给最后的UIApplication,它如果还不能处理的话就丢弃

3、在事件的响应中,如果某个空间实现了touches....的方法,则这个事件将有此控件来接收,如果调用了【super touches。。。】;就会将事件顺着响应者链条网上往上传递,传递给上一个响应者,接着就会调用上一个响应者的touches方法。

====马上结束====

试问:能否做到一个事件多个对象处理

系统默认做法是把事件上抛给父控件,所以可以通过重写自己的touches方法和父控件的touches方法来达到这个目的

试问:事件的传递和响应的区别

事件的传递时从上到下,从父控件到子控件,事件的响应是从下往上,顺着响应者链条向上传递,子控件到父控件

====== 这回真的是马上结束 ======

-(UIResponder *)nextResponder;

它返回接收者下一个响应,没有就返回nil

结束

猜你喜欢

转载自blog.csdn.net/ningn111/article/details/82050246
今日推荐