iOS开发 事件响应链和事件的传递

前言

在UIKit框架中,接收和处理事件的是UIResponder对象。UIResponder对象不仅能处理触摸事件,它还能处理按压、加速计和远程控制事件。

本文只以最常见的触摸事件来说明事件响应链。首先熟悉一下以下几个概念:
响应者:所有继承自UIResponder类的对象都可以是响应者,比如UIApplication、UIWindow、UIViewController、UIView等。
第一响应者:满足既能够接受事件,坐标点又在自身内部的最顶层响应者,被称作第一响应者。
响应链:默认情况下,由从第一响应者到UIApplication之间的所有响应者对象组成的链状层级结构,被称作响应链。
最终响应者:从第一响应者开始,顺着响应链,第一个实现touchesBegin等方法的响应者被称作最终响应者。第一响应者只能说明他有响应事件的能力,并不表示他一定就是事件的最终响应者,就像一个人考了教师资格证,具备了当老师的能力和资格,但是没有去当老师。
事件的传递:找到第一响应者之后,事件顺着响应链传递,直到找到最终响应者的过程,被称作事件传递。

下面就本文内容展开说明:

确定第一响应者

在触摸手机屏幕之后,一个UIEvent事件或者由多个UIEvent事件对象组成的数组会随着事件传递链由UIApplication传递给最上层试图。系统只有触摸点的坐标,仅凭坐标怎么才能知道哪个试图才是第一响应者呢?其实UIKit框架已经为我们提供了解决策略:hit-testing(命中测试,一种专门用来确定第一响应者的的策略)。

在这里插入图片描述
关于上图的说明:

检查自身可否接受事件的判断条件总共有以下三个,缺一不可:

  1. view.isUserInteractionEnabled = YES
  2. view.alpha > 0.01
  3. view.isHidden = NO

确定响应链

在通过hit-testing策略找到第一响应者之后,整个响应链也就确定了。由第一响应者为头,反向沿着hit-testing走通的路径上的各个响应者组成的链表就是响应链(未走通的路径无效)。响应链表的头就是第一响应者,其中任何一个结点的下一个结点都是该结点的next属性。

事件沿着响应链传递

触摸事件首先会由第一响应者进行响应,通过touchesBegin等方法处理该事件。但是如果在这个方法中没有处理该事件甚至压根就没有实现该方法,那么该事件就会沿着响应链传递给下一个响应者,直到有响应者处理。如果到最后依然没有被处理,那么就会被当作一次误触而丢弃。

隔断传递

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event返回nil

或者

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event返回NO

指定第一响应者

  - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event返回self

指定事件的处理者

 - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event

一个事件多个响应

[super touchesBegan:touches withEvent:event];

特殊情况:有UIGestureRecognizer手势参与

问题1:自定义UIControl,把它加在有手势的父试图上,生效的是手势还是target-action 方法?如果给他们本身添加手势,生效的是手势还是target-action 方法?

答案:手势;手势。

问题2:系统UIButton, UISwitch, UIStepper, UISegmentedControl, or UIPageControl等,把他们添加在有手势的父试图上,生效的是手势还是target-action 方法?如果给他们本身添加手势,生效的是手势还是target-action 方法?

答案:target-action 方法;手势。

为什么呢?
因为 UIControl 接收 target-action 方法的方式是在其 touches 方法中识别、接收、处理,而手势一定比其所在视图的 touches 方法早触发。不论是相对于UIResponder的触摸事件来说,还是相对于自定义的UIControl的target-action模式来说,只要有手势参与的情况,识别手势的优先级都要比他们高。而系统的UIControl,比如 UIButton, UISwitch, UIStepper, UISegmentedControl, or UIPageControl等,因为🍎对他们进行了额外处理,能让这些UIKit 控件有机会跳过父视图的手势识别,去获得事件的控制权,只响应 target-action。

所以,我们在开发的过程中,一方面要注意别让手势覆盖控件本身的方法实现;另一方面,我们也要理解默认情况下,手势识别在一开始实际上并不会阻止控件自身的 touches 系列方法,而是在之后的某个时机去取消,这个时机就是系统识别手势成功。

总结

  • 在不使用 UIGestureRecognizer的前提下,触摸屏幕后事件的传递可以分为以下几个步骤:
  1. 通过「hit-testing」来找到「第一响应者」
  2. 由「第一响应者」来确定「响应链」
  3. 将事件沿「响应链」传递
  4. 事件被某个响应者接收,或没有响应者接收从而被丢弃
  • 在使用UIGestureRecognizer的前提下,确定最终响应者要根据手势是添加在父试图上还是试图本身上,根据UIControl对象是系统类型还是自定义类型这四种组合情况具体分析。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值