iOS开发-事件响应链 2020-05-02 前进路上 暂无评论 1936 次阅读 本文发布于2020-05-02, 文章内容或资源可能已经失效,仅供参考,谢谢。 研究了一下iOS开发的响应链,这里做一下笔记。笔记的内容基于个人的理解,若有谬误之处有待后续不断学习更新改正。做一个尽量完美的记录是对所学知识的总结,也是一种升华。 响应链是iOS应用处理与用户交互事件的机制。所谓的交互事件包括触摸屏幕、晃动手机等。开发iOS所用的Objective-C编程语言是面向对象的语言,对于事件的响应是由对象完成的,但并不是所有的对象都能对事件做出响应,只有继承 UIResponder的类才能对事件做出响应。 在`UIResponder`类中以下几个方法是与处理触摸事件有关的方法。 ```objective-c - (void)touchesBegan:(NSSet *)touches withEvent:(nullable UIEvent *)event; - (void)touchesMoved:(NSSet *)touches withEvent:(nullable UIEvent *)event; - (void)touchesEnded:(NSSet *)touches withEvent:(nullable UIEvent *)event; - (void)touchesCancelled:(NSSet *)touches withEvent:(nullable UIEvent *)event; - (void)touchesEstimatedPropertiesUpdated:(NSSet *)touches NS_AVAILABLE_IOS(9_1); ``` > 在上面几个方法的参数中都有个`(NSSet *)touches`,其中包含的是触摸的信息,包括触点位置与触点个数等,默认只包含一个触点,如果想要处理多点触摸事件,需要把`UIView`的`multipleTouchEnabled`属性设置为YES,否则此视图只接受多点触摸时第一个触点的信息。 其中的`(UIEvent *)event`参数表示一次与用户的交互事件,包含事件发生时间、事件类型、此事件所关联的触点等信息。 如果想要一个视图对触摸事件做出响应,那么需要它重写上述方法。 iOS开发中有三种继承`UIResponder`的类,他们都能对交互事件做出响应。 - UIApplication - UIViewController - UIView # 事件传递链 既然继承`UIResponder`的类都能对触摸事件做出响应,那么当触摸事件发生时究竟要谁来处理呢?直觉告诉我点谁就让谁处理呗!但是这只是我们人类的直观感觉,计算机并不能像人类一样直观地看到用户点击的是哪个视图。在应用中如何找到用户点击的视图是有一套流程的。这套流程被形象地称为事件的传递链。 在iOS中,每个应用都会有一个`UIApplication`类,应用启动时,系统会调用此类的主函数,之后应用会创建一个`UIWindow`来作为应用的主窗口(keyWindow),`UIWindow`会将某个视图控制器`UIViewController`作为其根视图控制器,然后视图控制器有自己所管理的视图`UIView`,通常我们会在`UIView`上添加各种视图控件,比如`UIButton`。整体的结构如下图。 [![iOS应用结构](https://api.wuwz.net/distribute/web/zkkme/img/0030/0030-1.png "iOS应用结构")](https://api.wuwz.net/distribute/web/zkkme/img/0030/0030-1.png "iOS应用结构") 所以,当一个触摸事件发生时,系统会将这一事件加入到一个由`UIApplication`管理的事件队列的尾部,等待得到处理。当其前面的事件处理完成,此事件会被`UIApplication`分发给`UIWindow`,之后`UIWindow`会调用hitTest:thePoint方法,此方法会首先判断该视图能否响应触摸事件,如果不能响应那就直接返回nil;如果可以响应触摸事件,那就调用pointInside: withEvent:方法判断触点是否在该视图范围内,如果不在则返回nil,如果触点在视图范围内则向此视图的所有子视图发送`hitTest: thePoint:`消息。 所有子视图的遍历顺序是从栈顶到栈底,直到有子视图返回非空或者所有子视图全部返回为nil。如果一个视图没有子视图或者所有子视图`hitTest: thePoint:`都返回nil,则此视图的`hitTest: withEvent:`返回自己。 就这样一层层调用下去,直到有一个视图的`hitTest: withEvent:`返回自身,那么也就说UIKit找到了用户点击的是哪个视图。 > 当UIView中的 isUserInteractionEnabled = NO、isHidden = YES、alpha <= 0.01时,`hitTest: withEvent:`方法返回nil # 事件响应链 通过上面的事件传递链已经找到了用户点击的是哪个视图,那么具体怎么对触摸事件做出响应呢? 首先会判断用户点击的视图能否处理此触摸事件,如果能处理则处理触摸事件;如果不能处理则UIKit将触摸事件传递给父视图,重复此过程一直传递到所在的视图控制器的根视图,如果根视图不能处理,则传递给所在的视图控制器,如果视图控制器不能处理,则再传递给根视图的父视图。。。 直到传递给窗口(window),如果窗口还不能处理,则传递给`UIApplication`,如果`UIApplication`也不能处理则还可能传递给App Delegate(如果App Delegate是`UIResponder`类)。 整个过程如下图 [![响应链结构](https://api.wuwz.net/distribute/web/zkkme/img/0030/0030-2.png "响应链结构")](https://api.wuwz.net/distribute/web/zkkme/img/0030/0030-2.png "响应链结构") # 更改响应链 我们也可以通过重写nextResponder属性来更改响应链。如果我们不去更改此属性值,UIKit会按照默认的规则给此属性赋值。赋值规则是: - UIView:如果视图是某个视图控制器的根视图,那么它的`nextResponder`属性就是这个视图控制器,否则就是视图的父视图。 - UIViewController: - 如果一个视图控制器的视图是窗口的根视图,那么它的下一个响应者就是窗口 - 如果这个视图控制器是由另一个视图控制器`present`出来的,那么它的下一个响应者就是另一个视图控制器。 - UIWindow:下一个响应者是`UIApplication`对象 - UIApplication:如果App Delegate是`UIResponder`类且不是`UIView`、`UIViewController`或者应用本身,则其下一个响应者是App Delegate > REFERENCE - iOS开发 - 事件传递响应链 - UIView 中的hitTest方法 - iOS Touches事件处理知识总结 - iOS开发-事件响应及传递 - Using Responders and the Responder Chain to Handle Events 标签: iOS开发 如果您对此页面有任何问题或建议,请在本站留言,或联系邮箱me[at]zkk.me本网站部分内容转载自其他网站,如有侵权,请联系博主