UIResponder-(学习整理)

2017-12-22  本文已影响64人  lionsom_lin

参考文件

1.事件类别

1.1.响应者对象(UIResponder)

学习触摸事件首先要了解一个比较重要的概念-响应者对象(UIResponder)。

在iOS中不是任何对象都能处理事件,只有继承了UIResponder的对象才能接受并处理事件,我们称之为“响应者对象”。以下都是继承自UIResponder的,所以都能接收并处理事件。

因为UIResponder中提供了以下4个对象方法来处理触摸事件。

UIResponder内部提供了以下方法来处理事件触摸事件
- (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;
加速计事件
- (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;

2.响应链工作原理

从你手指触到到屏幕中某一控件到其响应相关事件其实是分为两步:事件的传递与事件的响应

事件的传递涉及到了UIView中的两个方法

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
//判断当前点击事件是否存在最优响应者(First Responder)
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event;
//判断当前点击是否在控件的Bounds之内

事件的传递其实就是在事件产生与分发之后如何寻找最优响应视图的一个过程

2.1事件的传递流程

1.触碰屏幕产生事件UIEvent并存入UIApplication中的事件队列中, 并且在整个视图结构中自上而下的进行分发
2.UIWindow接受到事件开始进行最优响应视图查询的过程(逆序遍历subviews)
3.当到UIViewController这一层时同样对其根视图(self.view及其上subviews)开始最优响应视图查询。该查询会调用上述提及到两个于UIView的方法,之所以采用逆序查询也是为了优化查找速度,毕竟后addSubview的视图在上易于命中

事件分发与传递流程
UIView不能接收触摸事件的三种情况:
/*
  - 什么时候调用:只要事件一传递给一个控件,那么这个控件就会调用自己的这个方法
  - 作用:寻找并返回最合适的view
  - UIApplication -> [UIWindow hitTest:withEvent:]寻找最合适的view告诉系统
  - point:当前手指触摸的点
  - point:是方法调用者坐标系上的点
 */
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
    // 1.判断下窗口能否接收事件
    if (self.userInteractionEnabled == NO || self.hidden == YES ||  self.alpha <= 0.01){
        NSLog(@"%s--1.窗口 <不能> 接收事件",__func__);
        return nil;
    }
    // 2.判断下点在不在窗口上
    // 不在窗口上
    if ([self pointInside:point withEvent:event] == NO) {
        NSLog(@"%s--2.点 <不在> 窗口上",__func__);
        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) {
            NSLog(@"%s--3.从后往前遍历子控件数组 -- 遍历到了合适View",__func__);
            // 如果能找到最合适的view
            return fitView;
        }
    }
    NSLog(@"%s--4.没有找到更合适的view,也就是没有比自己更合适的view",__func__);
    // 4.没有找到更合适的view,也就是没有比自己更合适的view
    return self;
}

/*
作者:VV木公子
鏈接:http://www.jianshu.com/p/2e074db792ba
來源:簡書
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。
*/

测试事件传递过程 Demo

例图.png

2.2事件的响应流程

官方图
响应链 其实是由一个个UIResponder的子类构成的,UIResponder是系统一个负责接受和处理事件的类。
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;

而以上这几个响应触碰的方法其实也是出自于UIResponder类,UIView作为UIResponder的子类能够处理点击事件也就无可厚非了。

具体事件的响应流程:

Note:
也并非所有的nextResponder即是superview,比如UIViewController的根视图self.viewnextResponder是其所在UIViewController。而如果UIViewController如果是UIWindow的根控制器,那么它的nextResponder就是UIWindow,但如果UIViewController是另外一个 UIViewController present出来的话,那么它的nextResponder就是之前所执行present操作的那个UIViewController

测试事件响应流程 Demo

如图 传递流程

整理到此,详情请前往参考文档!!!

上一篇 下一篇

猜你喜欢

热点阅读