事件响应和传递

iOS设备会产生各种事件:触摸、晃动设备、远程控制等,事件发生了就需要有响应者去接收并处理事件,这就形成了一套事件响应机制。

事件类型

在iOS开发中,UIEventType定义了四种事件类型。

typedef NS_ENUM(NSInteger, UIEventType) {
    UIEventTypeTouches, // 最常接触的事件
    UIEventTypeMotion,  // 加速计,手机摇一摇
    UIEventTypeRemoteControl,       // 远程控制,例如一些音乐APP的耳机控制
    UIEventTypePresses API_AVAILABLE(ios(9.0)), // 一般碰不到,指的是物理按键被按下,例如电视的遥控器。
};

事件响应者和响应链

能响应事件的一定是UIResponder或者其子类,常见的有UIView、UIViewController、UIApplication、UIApplicationDelegate。

image.png

一个UIResponder通过它的nextResponder方法指向下一个UIResponder,可以重写nextResponder。UIView类就是重写了该方法,如果它是UIViewController的根视图,它的nextResponder会指向viewController,否则就会指向它的父视图。

如果顺着响应者链最终没有找到事件的响应者,这个事件就会被忽略

UIResponder中对应每种事件都有一系列对应的方法,对于touch来说:

- (void)touchesBegan:(NSSet *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesMoved:(NSSet *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesEnded:(NSSet *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesCancelled:(NSSet *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesEstimatedPropertiesUpdated:(NSSet *)touches API_AVAILABLE(ios(9.1));

如果UIResponder的子类重写了上述方法,那么事件传递到这个类的时候,就会被捕获,不会再往后面传递。举个例子我们在一个添加点击事件的视图上添加一个UIButton子视图,但是不给button添加事件,这时候如果点击button,虽然没有button事件,但是button的父视图的事件也是不会被触发的。如果我们把button换成它的父类UIControl,不添加target,事件就会被传递到父视图上。所以这里可以猜测UIButton是实现了上述响应事件的。

事件传递机制

当触摸屏幕,系统会将其包装成一个触摸事件(touch event),首先会把该事件传递给UIApplication,UIApplication传递给UIWindow,UIWindow会调用

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    // 
}

得到一个能响应触摸事件的UIView。

UIView的hitTest方法会遍历View的层级结构,找到包含特定touch的最深层的子视图,成为触摸事件的first responder。如果first responder不能响应事件,就会开始沿响应者链向上传递,直到被响应或被忽略。

image.png

pointInside:withEvent:

如果子视图的区域超过了父视图,默认超出的部分是不会响应事件的,这时候要重写父视图的pointInside:withEvent:方法,让超出的部分返回YES。

来自 https://leejnull.github.io/2020/03/05/2020-03-05-02/

你可能感兴趣的:(事件响应和传递)