iOS 事件体系知识及原理小记

基本知识点:

0 UITouch

当每一个指尖在屏幕上触发一次触摸事件时,系统就会生成对应一个UITouch对象,用于记录当前触摸的状态,主要包含运动相位、位置、大小、运动、力度(iOS9)等数据;当触摸事件发生变化时(指尖移动、压力变化),UITouch对象的相关信息也会跟着更新;每一次更新都是复用之前的UITouch对象,不会重新创建。

主要的一些属性与方法:

@property(nonatomic,readonly) UITouchPhase        phase;  // 运动相位(开始、移动、静止、结束、取消)
@property(nonatomic,readonly) NSUInteger          tapCount; // 在一小段时间内的连续计数
@property(nonatomic,readonly) CGFloat majorRadius NS_AVAILABLE_IOS(8_0); // 触摸半径
@property(nonatomic,readonly) CGFloat force NS_AVAILABLE_IOS(9_0); // 压力值
- (CGPoint)locationInView:(nullable UIView *)view; // 获取当前坐标
- (CGPoint)previousLocationInView:(nullable UIView *)view; // 获取上一次坐标

1 UIEvent

在iOS系统中一个UIEvent对象代表着一个事件在,有四种类型的事件:触摸事件( UIEventTypeTouches)、运动事件( UIEventTypeMotion)、远程控制事件( UIEventTypeRemoteControl)、物理按键事件( UIEventTypePresses);例如屏幕被点击了,系统会创建一个UIEvent,如果UIEvent对象已经存在,那直接复用已有的UIEvent,UIEvent在应用中一旦被创建,它的生命周期会一直伴随着应用,所以千万别retain一个UIEvent或者通过return来获取一个UIEvent,如果你希望保存UIEvent的相关信息,你可以直接copy某个属性。(TODO:到底是一种类型的事件复用一个还是整个应用只复用一个UIEvent对象)

主要的一些属性与方法:

@property(nonatomic,readonly) UIEventType    type NS_AVAILABLE_IOS(3_0); // 四种事件类型
@property(nonatomic,readonly) UIEventSubtype  subtype NS_AVAILABLE_IOS(3_0); // 在各个大类型中再细化区分
@property(nonatomic, readonly, nullable) NSSet <UITouch *> *allTouches; // 当前事件触发时的所有UITouch对象

2 UIResponder

事件响应者(UIResponder)的查找与事件的响应:

响应者(responder)的概念:在iOS系统中,响应者是指能响应并处理事件的对象,UIResponder是所有responder对象的基类。UIApplication / UIViewController / UIView 以及所有继承UIView的UIKit类(包含UIWindow)都直接或间接的继承了UIResponder,这就意味着所有的views以及大部分key controller都能响应并处理事件对象。

(1)查找阶段:

先介绍UIView的两个方法:

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

该方法用于检查当前坐标是否落在当前view

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

该方法的主要逻辑是:

  • 检查当前view是否能响应事件(userInteractionEnabled!=NO & hidden!=YES & alpha >0.01)
  • 不满足直接返回nil;
  • 通过pointInside:withEvent:方法,检查当前点击是否落在当前view中;
    • 如果点击落在当前view中,遍历subView执行hitTest:withEvent:;
        • 如果subView的hitTest:withEvent:有返回,则返回该返回;
          • 如果subView的hitTest:withEvent:没有返回,则返回当前view;
      • 如果点击没有落在当前view,则返回nil;

响应者查找阶段就要用到这两个方法,大致流程如下:

  • 当指尖触碰屏幕时,系统会创建一个UIEvent对象(如果已经存在,则复用),以及相应的UITouch;并将UIEvent对象放到当前活跃app的事件队列中;
  • UIApplication会从事件队列中取出最前面的事件进行分发以便处理,通常先发送事件给应用程序的主窗口(UIWindow);
  • 主窗口会调用hitTest:withEvent:方法在视图(UIView)层次结构中找到一个最合适的UIView来处理触摸事件,并将UITouch与UIEvent交给UIView处理(通过touchesBegan/touchesMoved/touchesEnded等方法传递)

看个例子:

view 2是view 1的子view,当一个点击落在view 2区域内,这个查找过程会从UIWindow开始,然后一层层子view查找下去,最终view 2会作为最合适的响应者被hitTest返回,因为view 2满足了两个条件:

  1. 通过hitTest找到了view 2
  2. view 2内部没有其他子view

再看看另一个特殊的场景:

view 2还是view 1的子view;但当一个点击落在view 2的区域内时,查找还是从UIWindow开始,但在view 1的hitTest中就返回了nil,因为点击区域不在view 1中,导致view 1的子view都不会再进行hitTest;这点跟web中的事件处理是截然不同的;这也导致一些超边界的点击必须由业务去重载hitText方法。

(2)事件响应阶段(响应链):

在上面提到的查找阶段,通过hitTest:withEvent:最终查找到的最后view自然就做为第一个可以响应该事件的view,当该view不能处理该事件,系统会通过nextResponder继续将事件传递给下一个响应者,如果一直没有能处理的响应者,这个事件会一直传递到UIApplication,最终废弃。所以,所谓的响应链就是一系列相连接的响应者,它由第一个响应者开始,通过nextResponder不断传递一直到UIApplication。

这里需要注意的是nextResponder的处理规则:

UIView的nextResponder属性,如果有管理此view的UIViewController对象,则为此UIViewController对象;否则nextResponder即为其superview。

UIViewController的nextResponder属性为其管理view的superview。

UIWindow的nextResponder属性为UIApplication对象。

UIApplication的nextResponder属性为nil

未完待续…

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏tkokof 的技术,小趣及杂念

Singleton模式小探

设计模式,这个多年前因为《设计模式》这本书而开始名声鹊起的软件名词,相信在当今年代早已为相关人士耳熟能详,你可以像大多数人那样对他顶礼膜拜,你也可以如极少数人...

883
来自专栏大内老A

如何解决分布式系统中的跨时区问题[实例篇]

关于如何解决分布式系统中的跨时区问题,上一篇详细介绍了解决方案的实现原理,在这一篇中我们通过一个完整的例子来对这个问题进行深入探讨。尽管《原理篇》中介绍了那么多...

3248
来自专栏一“技”之长

Objective-C中通过下标的方式访问自定义数据模型中属性

      在Objective-C中,可以通过下标来访问数组中的元素,如果数组是NSMutableArray类型的可变数组,则还可以通过下标来对数组中的元素进...

571
来自专栏Coding01

看 Lumen 源代码解析 Request 到 Response 过程

当我想分析 Laravel 是如何做到从 Request -> Response 的解析过程的,发现 Lumen 相对简单,所以今天从 Lumen 源代码入手,...

1732
来自专栏一“技”之长

Objective—C语言的新魅力——Nullability、泛型集合与类型延拓

        在Xcode7中,iOS9的SDK已经全面兼容了Objective-C的一些新特性和新功能。这些功能都只作用于编译期,对程序的运行并没有影响,因...

733
来自专栏冰霜之地

iOS如何优雅的处理“回调地狱Callback hell”(一)——使用PromiseKit

最近看了一些Swift关于封装异步操作过程的文章,比如RxSwift,RAC等等,因为回调地狱我自己也写过,很有感触,于是就翻出了Promise来研究学习一下。...

1503
来自专栏一“技”之长

iOS中通知中心(NSNotificationCenter)的使用总结

@property (readonly, copy) NSString *name;

513
来自专栏Java学习网

Android中Java和JavaScript交互

Android中Java和JavaScript交互 Android提供了一个很强大的WebView控件用来处理Web网页,而在网页中,JavaScript又是...

2676
来自专栏移动端开发

iOS RunTime你知道了总得用一下

说点题外话: 我刚来现在这家公司的时候,老板让我下载一个脉脉,上去找找自己的同行,多认识些同行。其实初衷的好的,但最近这两天我把它卸载了,不为别的,负能量太多...

2009
来自专栏web前端教室

javascript - 闭包

今天群里聊到JS的闭包,说是不理解。我看了下那个PDF的截图上的内容,。。。。我就看了一小会,反正也没看太看懂,写的太玄幻。。 我就觉得这个吧,看不懂闭包,其实...

1778

扫码关注云+社区