ios 连续点击button_调试iOS用户交互事件响应流程

本文详细探讨了iOS用户交互事件响应链,包括响应链的结构、Target-Action机制、手势识别和响应链的修改。在Touch事件传递部分,讲解了碰撞检测、事件传递流程,并通过调试分析了事件从产生到被处理的完整过程。通过实例展示了点击Button和Label时,事件如何在视图层级和响应链中传递。
摘要由CSDN通过智能技术生成

调试iOS用户交互事件响应流程一、响应链1.1 Next Responder1.1.1 调试nextResponder1.2 Target-Action和响应链1.2.1 注册UIControlEvents1.2.2 调试UIControlEvents的传递结论一:Action不会在同级视图层级中传递结论二:Target为空时Action仍可以被响应结论三:Target为空时Action沿响应链传递1.3 手势识别和响应链1.4 修改响应链二、Touch事件传递2.1 碰撞检测2.2 调试Touch事件传递步骤零:准备工作步骤一:下断点步骤二:简单分析 touch 事件在 Window 层的分发步骤三:分析 Touch 事件的产生步骤四:分析 touch 事件开始后的传递情况一:点击 Button 控件时情况二:点击 Label 视图步骤五:分析 touch 事件结束后的传递三、RunLoop与事件(TODO)四、总结

调试iOS用户交互事件响应流程

2020-03-19

通常 iOS 界面开发中处理各种用户交互事件。其中,UIControlEvent以注册的 Target-Action 的方式绑定到控件;UIGestureRecognizer通过addGestureRecognizer:添加到UIViewgestureRecognizers属性中;UIResponder提供了touchesBegin/Moved/Ended/Canceled/:withEvent:motionsXXX:withEvent:pressXX:withEvent:系列接口,将用户设备的触摸、运动、按压事件通知到UIResponder对象等等。以上都是常用开发者处理用户交互事件的方式,那么隐藏在这些接口之下,从驱动层封装交互事件对象到 UI 控件接收到用户事件的流程是怎样的呢?本文主要探讨的就是这个问题。

一、响应链

Apple Documentation 官方文档Using Responders and the Responder Chain to Handle Events介绍了利用UIResponder的响应链来处理用户事件。UIResponder实现了touchesXXXpressXXXmotionXXX分别用于响应用户的触摸、按压、运动(例如UIEventSubtypeMotionShake)交互事件。UIResponder包含nextResponder属性。UIViewUIWindowUIControllerUIApplication都是UIResponder的派生类,所以都能响应以上事件。

1.1 Next Responder

响应链结构如下图所示,基本上是通过UIRespondernextResponder成员串联而成,基本上是按照 view 的层级,从前向后由子视图向父视图传递,且另外附加其他规则。总的响应链的规则如下:

  • View 的nextResponder是其父视图;

  • 当 View 为 Controller 的根视图时,nextResponder是 Controller;

  • Controller 的nextResponder是 present Controller 的控制器;

  • 当 Controller 为根控制器时,nextResponder是 Window;

  • Window 的nextResponder是 Application;

  • Application 的nextResponder是 App Delegate(仅当 App Delegate 为UIResponder类型);

9b38759570e91afd3973a3efd9fa2125.png
响应链

UIResponder响应touchesXXXpressXXXmotionXXX事件不需要指定userInteractionEnabledYES。但是对于UIView则需要指定userInteractionEnabled,原因是UIView重新实现了这些方法。响应UIGesture则需要指定userInteractionEnabledaddGestureRecognizer:UIView类的接口。

注意:新版本中,分离了 Window 和 View 的响应链。当 Controller 为根控制器时,nextResponder实际上是nil;Windows 的nextResponder是 Window Scene;Window Scene 的nextResponder是 Application。在后面的调试过程会有体现。

1.1.1 调试nextResponder

使用一个简单的 Demo 调试nextResponder。界面如下图所示,包含三个 Label,从颜色可以判断其层次从后往前的顺序是:A >> B >> C。下面两个按钮另做他用,先忽略。

769bce440387b0c761c040340f88017a.png

运行 Demo,查看各个元素的nextResponder,确实如前面所述。

5d6ebfa577e999a74543392aec1d549d.png

1.2 Target-Action和响应链

UIControl控件与关联的 target 对象通信,直接通过向 target 对象发送 action 消息。虽然 Action 消息虽然不是事件,但是 Action 消息的传递是要经过响应链的。当接收到用户交互事件的控件的 target 为nil时,会沿着控件的响应链向下搜索,直到找到实现该 action 方法的对象为止。UIKit 的编辑菜单就是通过这个机制实现的,UIKit 会沿着控件的响应链搜索实现了cut:copy:paste:等方法的对象。

1.2.1 注册UIControlEvents

UIControl控件调用addTarget:action:forControlEvents:方法注册事件时,会将构建UIControlTargetAction对象并将其添加到UIControl控件的(NSMutableArray*)_targetActions私有成员中,addTarget:action:forControlEvents:方法的 Apple Documentation 注释中有声明调用该方法时UIControl并不会持有 target 对象,因此无需考虑循环引用的问题。UIControl Events 注册过程的简单调试过程如下:

2c73737124761db3290e29eb7060a7ff.png
UIControl Target Action

附注:The control does not retain the object in the target parameter. It is your responsibility to maintain a strong reference to the target object while it is attached to a control.

1.2.2 调试UIControlEvents的传递

前面内容提到,控件的 action 是沿着响应链传递的,那么,当两个控件在界面上存在重合的区域,那么在重合区域触发用户事件时,action 消息会在哪个控件上产生呢?在 1.1.1 中的两个重合的按钮就是为了验证这个问题。

稍微改造一下 1.1.1 的 Demo 程序,将 Label A、B、C 指定为自定义的继承自UILabel的类型TestEventsLabel,将两个 Button 指定为继承自UIButtonTestEventsButton类型。然后在TestEventsLabelTestEventsButtonViewController中,为touchesXXX:系列方法、nextResponder方法、hitTest:withEvent:方法添加打印日志的代码,以TestEventsButton的实现为例(当然也可以用 AOP 实现):

@implementation TestEventsButton

-(UIResponder *)nextResponder{
    UIResponder* responder = [super nextResponder];
    NSLog(@"Next Responder Button %@ - return responder: %@", [self titleForState:UIControlStateNormal], responder);
    return responder;
}

-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
    UIView* view = [super hitTest:point withEvent:event];
    NSLog(@"Hit Test Button %@ - return view: %@", [self titleForState:UIControlStateNormal], view);
    return view;
}

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [super touchesBegan:touches withEvent:event];
    NSLog(@"Button %@ - %s", [self titleForState:UIControlStateNormal], __func__);
}
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [super touchesEnded:touches withEvent:event];
    NSLog(@"Button %@ - %s", [self titleForState:UIControlStateNormal], __
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值