关于响应者链,有以下一段介绍:每个应用有一个响应者链,咱们的视图结构是一个N叉树(一个视图能够有多个子视图,一个子视图同一时刻只有一个父视图),而每个继承UIResponder的对象均可以在这个N叉树中扮演一个节点。当叶节点成为最高响应者的时候,从这个叶节点开始往其父节点开始追朔出一条链,那么对于这一个叶节点来说,这一条链就是当前的响应者链。响应者链将系统捕获到的UIEvent与UITouch从叶节点开始层层向下分发,期间能够选择中止分发,也能够选择继续向下分发。ios
那,我要是告诉大家,响应者链就是上面那段话介绍的,估计大家得拿板砖拍我了。这等于没说。别急,先来举个栗子:web
我用SingleView模板建立了一个新的工程,它的主Window上只有一个UIViewController,其View之上有一个Button。这个项目中全部UIResponder的子类所构成的N叉树为这样的结构:app
那么他看起来并不像N叉树,可是不表明者不是一颗N叉树,当咱们项目复杂以后,这个View可不能够有多个UIButton节点?因此他就是一棵树。函数
实际上咱们要把这棵树写完整,应该还要算上UIButton的UILabel和UIImageView,由于他们也是UIReponder的子类。这里先不考虑了。spa
因此咱们尝试在任意地方打印这个Button的nextReponder对象。nextResponder对象是UIReponder类的实例方法,它会返回任意对象在树中的上一个响应者实例:对象
控制台输出信息以下:继承
<UIView: 0x7f8973e92b60; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x7f8973e92ee0>>生命周期
我们能够一直打印下去,获取下一个Responder的下一个Responder,依次获取的响应者是:事件
<ViewController: 0x7f8973c2ccf0>ci
(null)
为何这里ViewController没有父亲呢?
注意这句代码我是写在ViewDidLoad中,而咱们知道这个方法的生命周期比较早,因此咱们换个地方写或者延迟一段时间再打印,两种方法均可以获得结果(由此能够推理出咱们响应者树的构造过程是在ViewDidLoad周期中来完成的,这个函数会将当前实例的构成的响应者子树合并到咱们整个根树中):
最后获取就是上边给出的那幅图的样子了。那说了这么多,这个Responder到底有什么用呢?
在AppDelegate里面重写touchesBegan方法:
在ViewController里面也重写:
用户手指触摸到了UIView上,因为咱们没有重写UIView的UITouchEvent,因此他里面和super执行的同样的,将该事件继续分发到UIViewController;
UIViewController的TouchBegan被咱们重写了,若是咱们不super,那么咱们在这里写响应代码。事件到这里就不继续分发了。可想而知,UIViewController祖先节点:UIWindow,UIApplication,AppDelegate都无权被分发此事件。
若是咱们super了TouchBegan,那么这次触摸事件由
ViewController分发给UIWindow,
UIWindow继而分发给UIApplication,
UIApplication再分发给AppDelegate,
因而咱们在ViewController和appDelegate的touchBegan方法中都捕获到了此次事件。
可是这只是处理点击事件顺序,也便是确认了第一响应者以后的处理流程。
那寻找第一响应者的流程是怎样的呢。
其实就是逆向走一遍,当沿着这条响应者链找到了第一响应者,那么就会返回事件给本身的nextResponder去处理,直到appDelegate。
下面回到我们的主题,既然点击按钮,没有触发点击事件,响应者链有很大嫌疑。
为何这么说呢??
我问你们,若是你点击完按钮以后,没有找到第一响应者,或者是第一响应者找错了,还会调用button的触发事件吗??显而易见。
下面列举常见的几大缘由,有兴趣的同窗能够去试试。
这个理解起来就简单一些,好比实现按钮点击事件所在的视图控制器被回收了。可是因为按钮被添加到当前可见视图上,按钮没有被回收,因此是可见的。可是点击这个按钮却没有任何做用。
单单是上面两个方面并不能涵盖全部的相似状况,并且这节我们只是从UIButton入手。在具体的开发中,有不少经验所不可以解释的现象,可是上面两点通常是优先考虑的。