iOS事件响应

作者: 鲲鹏DP | 来源:发表于2019-08-10 14:46 被阅读1次
24CF98B1-83C5-4206-99B2-B9FFE552AF98.png

1.系统响应阶段

  • a).手指触碰屏幕,屏幕感应到触碰后,将事件交由IOKit处理。
  • b).IOKit将触摸事件封装成一个IOHIDEvent对象,并通过mach port传递给SpringBoad进程。
  • c).SpringBoard进程因接收到触摸事件,触发了主线程runloop的source1事件源的回调。此时SpringBoard会根据当前桌面的状态,判断应该由谁处理此次触摸事件

2.寻找最佳响应者

  • 1.从根视图往上寻找,每个视图干3件事;
    a).当前视图能否响应事件(hitTest:withEvent:),self.userInteractionEnabled == YES && self.hidden == NO && self.alpha > 0.01
    b).点击的坐标点是否在当前视图内(pointInside:withEvent:)
    c).遍历子视图,分别判断上面两个条件,有子视图满足条件则最佳在其子视图中,没有则发回nil,当前视图为最佳响应视图;
  • 2.寻找最佳响应视图完成后,事件响应者链也就生成了,事件响应者链由一些列继承至UIResponder的元素组成。

3.事件分发

找到最佳响应视图后,会调用最佳响应视图的touch相关方法,如果没有实现该方法,就会继承关系,逐一往上查找,如果某个祖先视图实现了,事件就交给它处理,一直没有找到,事件到了UIAapplication就会被抛弃;


代码分析

  • 创建三个视图


    9C1B6B33-50FE-41EC-8C59-0A60D98A26D0.png
  • 三个视图都继承自一个BaseView,在BaseView中交换hitTest:withEvent:,pointInside:withEvent:和touchesBegan:withEvent:三个方法的实现,输出提示信息


#import "BaseView.h"
#import <objc/message.h>
@implementation BaseView

+(void)load{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class class = [self class];
        
        Method   originMethod1 = class_getInstanceMethod(class, @selector(hitTest:withEvent:));
        Method   newMethod1 = class_getInstanceMethod(class, @selector(DKP_hitTest:withEvent:));
        BOOL didAddMethod1 = class_addMethod(class,  @selector(hitTest:withEvent:), method_getImplementation(newMethod1), method_getTypeEncoding(newMethod1));
        if (didAddMethod1) {
            class_replaceMethod(class, @selector(DKP_hitTest:withEvent:),method_getImplementation(originMethod1), method_getTypeEncoding(originMethod1));
        }else{
            method_exchangeImplementations(originMethod1, newMethod1);
        }
        
        Method   originMethod2 = class_getInstanceMethod(class, @selector(pointInside:withEvent:));
        Method   newMethod2 = class_getInstanceMethod(class, @selector(DKP_pointInside:withEvent:));
        BOOL didAddMethod2 = class_addMethod(class,  @selector(pointInside:withEvent:), method_getImplementation(newMethod2), method_getTypeEncoding(newMethod2));
        if (didAddMethod2) {
            class_replaceMethod(class, @selector(DKP_pointInside:withEvent:),method_getImplementation(originMethod2), method_getTypeEncoding(originMethod2));
        }else{
            method_exchangeImplementations(originMethod2, newMethod2);
        }
        
        
        Method   originMethod3 = class_getInstanceMethod(class, @selector(touchesBegan:withEvent:));
        Method   newMethod3 = class_getInstanceMethod(class, @selector(DKP_touchesBegan:withEvent:));
        BOOL didAddMethod3 = class_addMethod(class,  @selector(touchesBegan:withEvent:), method_getImplementation(newMethod3), method_getTypeEncoding(newMethod3));
        if (didAddMethod3) {
            class_replaceMethod(class, @selector(DKP_touchesBegan:withEvent:),method_getImplementation(originMethod3), method_getTypeEncoding(originMethod3));
        }else{
            method_exchangeImplementations(originMethod3, newMethod3);
        }
        
        
    });
}
-(UIView *)DKP_hitTest:(CGPoint)point withEvent:(UIEvent *)event{
    
    NSLog(@"%@------%s",NSStringFromClass([self class]),__func__);
    return [self DKP_hitTest:point withEvent:event];
}
-(BOOL)DKP_pointInside:(CGPoint)point withEvent:(UIEvent *)event{
    NSLog(@"%@------%s",NSStringFromClass([self class]),__func__);
    return  [self DKP_pointInside:point withEvent:event];
}
-(void)DKP_touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
     NSLog(@"%@------%s",NSStringFromClass([self class]),__func__);

}
  • 点击最顶上黄色视图 D3A6192A-BAB4-45BD-9B8F-77A68A3F18A6.png
    结果:找到了thirdView为最佳响应者,开始处理事件,调用thirdView的touche方法。由于其实现了改发放,事件就交由thirdvie处理了。
  • 点击蓝色视图:预计结果应该是secondView响应touche方法
B9021476-EE94-4904-994A-39757945C396.png
  • 点击红色视图
    773B202C-340C-4358-9E71-D74055607E08.png
    结果:黄色的thirdView没有调用hitTest和pointInside方法。因为,点击点在oneView中,不在secondView和thirdView中。寻找最佳响应者时,当遍历oneView的子视图,递归判断时,发现secondView不包含点击点,直接返回了nil,所以就不会再遍历secondView的子视图了,自然不会调用thirdView的相关方法。

应用

扩大视图响应事件的范围,或则让指定视图响应事件

将上面的hitTest方法稍作修改,寻找最佳视图到了secondView时,return self,指定secodView为最佳响应者。

-(UIView *)DKP_hitTest:(CGPoint)point withEvent:(UIEvent *)event{
    
    NSLog(@"%@------%s",NSStringFromClass([self class]),__func__);
    if ([NSStringFromClass([self class]) isEqualToString:@"secondView"]) {
        return self;
    }
    return [self DKP_hitTest:point withEvent:event];
}

再来点击黄色的thirdView

6B2E45CB-F6EF-4427-B957-0B049A9DF315.png

结果:事件响应者链在secondView就中断了,scondView响应事件。


最后

  • 寻找最佳响应者和事件传递顺序是相反的。前者从父到子,后者从子到父;
  • 寻找到的最佳响应者,并非就一定是处理事件的控件,最佳响应者只是处在事件处理的第一顺位,最先判断他是否有处理本次事件的能力而已。
  • 可以在hitTest方法中,改变最佳响应者,解决开发中一些事件响应相关的需求。

相关文章

  • iOS 响应链

    iOS开发 - 事件传递响应链iOS 响应者链,事件的传递事件传递之响应链Cocoa Touch事件处理流程--响...

  • 深入浅出iOS事件机制

    深入浅出iOS事件机制事件传递:响应链事件传递响应链

  • 响应链

    iOS事件响应链中Hit-Test View的应用从iOS的事件响应链看TableView为什么不响应touche...

  • 二、事件传递链和响应者链

    iOS触摸事件详解iOS开发-事件传递响应链 响应者链 UIResponser包括了各种Touch message...

  • iOS基础补完计划--透过堆栈看事件响应机制

    iOS基础补完计划--透过堆栈看事件响应机制 iOS基础补完计划--透过堆栈看事件响应机制

  • 初识iOS事情处理机制

    参考:史上最详细的iOS之事件的传递和响应机制-原理篇iOS触摸事件全家桶史上最详细的iOS之事件的传递和响应机制...

  • iOS响应者链

    参考好文 iOS开发-事件传递响应链,用运行时分析 iOS事件传递:响应者链[译] http://www.jian...

  • 响应者链浅谈

    响应者对象 响应者对象(Response object) 响应者对象就是可以响应事件并对事件作出处理。iOS中UI...

  • iOS事件的响应者链

    iOS 事件响应者链 1 iOS中的事件 触摸事件 加速计事件 远程控制事件 在iOS中不是任何对象都能处理事件,...

  • iOS 触摸事件与响应理解

    参考文章: iOS触摸事件的流动 iOS触摸事件的传递与响应 UIViewController UIAppli...

网友评论

    本文标题:iOS事件响应

    本文链接:https://www.haomeiwen.com/subject/qezljctx.html