iOS 响应者

作者: Harely | 来源:发表于2017-12-09 10:57 被阅读42次

响应者

响应者对象:继承自UIResponder的对象称之为响应者对象。UIApplication、UIView、UIViewController和所有继承UIView的UIKit类都直接或间接的继承自UIResponder。如下图的继承关系图:

UI继承关系图

UIResponder一般响应以下几种事件:触摸事件(touch handling)、点按事件(press handling)、加速事件和远程控制事件。


响应者链

由多个响应者组合起来的链条,就叫做响应者链。它表示了每个响应者之间的联系,并且可以使得一个事件可选择多个对象处理

响应链

假设触摸了initial view,

1.第一响应者就是initial view即initial view首先响应touchesBegan:withEvent:方法,接着传递给橘黄色的view

2.橘黄色的view开始响应touchesBegan:withEvent:方法,接着传递给蓝绿色view

3.蓝绿色view响应touchesBegan:withEvent:方法,接着传递给控制器的view

4.控制器view响应touchesBegan:withEvent:方法,控制器传递给了窗口

5.窗口再传递给application

如果上述响应者都不处理该事件,那么事件被丢弃


事件的产生和传递

当一个触摸事件产生的时候,我们的程序是如何找到第一响应者的呢?

事件的产生与传递

当你点击了屏幕会产生一个触摸事件,消息循环(runloop)会接收到触摸事件放到消息队列里,UIApplication会会从消息队列里取事件分发下去,首先传给UIWindow,UIWindow会使用hitTest:withEvent:方法找到此次触摸事件初始点所在的视图,找到这个视图之后他就会调用视图的touchesBegan:withEvent:方法来处理事件。

在这里我们先要了解两个方法:

- (nullableUIView*)hitTest:(CGPoint)point withEvent:(nullableUIEvent*)event;称为方法A

- (BOOL)pointInside:(CGPoint)point withEvent:(nullableUIEvent*)event;称为方法B


对view进行重写这两个方法后,就会发现,点击屏幕后,首先响应的是方法A;

如果方法A中,我们没有调用父类的这个方法,那就根据这个方法A的返回view,作为响应事件的view。(当然返回nil,就是这个view不响应)

如果方法A中,我们调用了父类的这个方法,也就是

[superhitTest:pointwithEvent:event];那这个时候系统就要调用方法B;通过这个方法的返回值,来判断当前这个view能不能响应消息。

如果方法B返回的是no,那就不用再去遍历它的子视图。方法A返回的view就是可以响应事件的view。

如果方法B返回的是YES,那就去遍历它的子视图。(就是上图我们描述的那样,找到合适的view返回,如果找不到,那就由方法A返回的view去响应这个事件。)

因此总结下来:

//返回一个view来响应事件 (我们如果不想影响系统的事件传递链,在这个方法内,最好调用父类的这个方法)

- (nullableUIView*)hitTest:(CGPoint)point withEvent:(nullableUIEvent*)event;

//返回的值可以用来判断是否继续遍历子视图(返回的根据是触摸的point是否在view的frame范围内)

- (BOOL)pointInside:(CGPoint)point withEvent:(nullableUIEvent*)event;


扔个简单🌰:

hitTest:withEvent:查找过程

视图的添加我就不说了!

点击viewE:

1.A 是UIWindow的根视图,首先对A进行hitTest:withEvent:

2.判断A的userInteractionEnabled,如果为NO,A的hitTest:withEvent返回nil;

3.pointInside:withEvent:方法判断用户点击是否在A的范围内,显然返回YES

4.遍历A的子视图B和C,由于从后向前遍历,

因此先查看C,调用C的hitTest:withEvent方法:pointInside:withEvent:方法判断用户点击是否在C的范围内,不在返回NO,C对应的hitTest:withEvent: 方法return nil;

再查看B,调用B的hitTest:withEvent方法:pointInside:withEvent:判断用户点击是否在B的返回内,在返回YES

遍历B的子视图D和E,从后向前遍历,

先查看E,调用E的hitTest:withEvent方法:pointInside:withEvent:方法 判断用户点击是否在E的范围内,在返回YES,E没有子视图,因此E对应的hitTest:withEvent方法返回E,再往前回溯,就是B的hitTest:withEvent方法返回E,因此A的hitTest:withEvent方法返回E。

至此,点击事件的第一响应者就找到了。


如果hitTest:withEvent: 找到的第一响应者view没有处理该事件,那么事件会沿着响应者链向上传递->父视图->视图控制器,如果传递到最顶级视图还没处理事件,那么就传递给UIWindow处理,若window对象也不处理->交给UIApplication处理,如果UIApplication对象还不处理,就丢弃该事件。

事件流程

控件不能相应的情况:

1.userInteractionEnabled = NO

2.hidden = YES

3.透明度 alpha 小于等于0.01

4.子视图超出了父视图区域

子视图超出父视图,不响应的原因:因为父视图的pointInside:withEvent:方法返回了NO,就不会遍历子视图了。可以重写pointInside:withEvent:方法解决此问题。

例如: UIButton 放在 UIImageView 上,点击 UIButton ,UIButton不会相应事件,这是因为UIImageView 的userInteractionEnabled默认是NO的,即使UIButton是相应控件。

相关文章

  • 事件的分发和传递

    响应者: 在iOS中,响应者为能响应事件的UIResponder子类对象,如UIButton、UIView等。 响...

  • iOS 响应链

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

  • iOS响应者链

    iOS响应者链

  • iOS UI事件传递与响应者链

    iOS UI事件传递与响应者链 响应者链 响应者对象:继承自UIResponder的对象称之为响应者对象。UIAp...

  • iOS的事件传递和响应

    1.响应者对象 在iOS中不是所有对象都可以响应事件的,只有继承了UIResponder的对象才能响应事件。称为响...

  • ios响应者链

    iOS 响应者链 字数418 阅读41 评论0 喜欢3 响应者链 响应者链是一个响应者的连接序列,事件或者动作消息...

  • 响应者链浅谈

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

  • 事件传递和响应者链

    响应者:在iOS中,响应者为能响应事件的UIResponder子类对象,如UIButton、UIView等。 响应...

  • iOS开发:浅谈响应链与事件传递

    响应链: 第一响应者到application对象的一系列响应者形成的链条 如何寻找第一响应者: 1、当iOS程序发...

  • 转载:响应者链工作原理

    响应者链 响应者链是由一个一个响应者组成的长链;响应者链定义了iOS中触摸事件的交互规则;如果hit-test检测...

网友评论

    本文标题:iOS 响应者

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