iOS 底层 day30 iOS触摸事件

一、本节只记录问题,答案和细节从大神的文章中寻找

二、描述触摸事件的生命周期(从用户触碰屏幕开始)

1. 系统响应阶段

1.1 手指触碰屏幕,屏幕感受到触碰后,将事件交由 IOKit 处理。
1.2 IOKit 将触摸事件封装成IOHIDEvent 对象,并通过 mach port 传递给 SpringBoard 进程。

mach port 是 macOS 的 IPC 进程通信方式,各个进程之间通过它通信。
SpringBoard.app 是一个系统进程,可以理解为桌面系统,可以统一管理和分发系统收到的触摸事件。

1.3 SpringBoard 进程因接收到触摸事件,触发了主线程的 RunLoopsource1 事件源回调,会把事件传递给当前屏幕运行的 APP。(暂不讨论当前屏幕进程就是 Springboard 的情况)

2. APP响应阶段

2.1 Springboard 进程将事件通过 mach port 传递给当前 APP,会唤醒当前 APP 的 RunLoop 并且触发 source1 的回调。
2.2 source1 的回调又会触发 source0 的回调,source0 将事件封装成 UIEvent 对象,并且将触摸事件添加到 UIApplication 对象的事件队列中。
2.3 事件出队列后,开始了寻找最佳响应者的过程,这个过程又称为 hitTest 过程。
2.4 寻找到最佳响应者后,接下来 UIApplication 会调用 sendEvent: 将事件分发给最佳响应者
2.5 最佳响应者拿到事件后,可以决定对事件进行独自消化,也可以选择让事件在响应者链条中继续传递
2.6 触摸事件历经坎坷后要么被某个响应对象捕获后释放,要么致死也没能找到能够相应的对象,最终释放。至此,这个触摸事件的使命就算终结了,RunLoop 若没有其他事件需要处理,也将重归于眠,等待新的事件到来后唤醒。

三、UITouch 、 UIEvent、UIResponder 分别是什么?

1. UITouch

触摸的起源

  • 一个手指一次触摸屏幕,就对应生成一个 UITouch 对象。多个手指同时触摸,生成多个 UITouch 对象。
  • 多个手指先后触摸,系统会根据触摸的位置判断是否更新同一个 UITouch 对象。
  • 每个 UITouch 对象记录了触摸的一些信息,包括触摸时间、位置、阶段、所处的视图、窗口等信息。
  • 手指离开屏幕一段时间后,确定该 UITouch 对象不会再被更新将被释放。
2. UIEvent

事件的真身

  • 触摸的目的是生成触摸事件响应者响应,一个触摸事件对于一个 UIEvent 对象,其中 type 属性标识了事件的类型。
  • UIEvent 对象中包含了触发该事件的触摸对象的集合,因为一个触摸事件可能是由多个手指同时触摸产生的。触摸对象集合通过 allTouches 属性获取。
3. UIResponder

一切都是为了满足它的野心

  • 每个响应者都是一个 UIResponder 对象,即所有派生自 UIResponder 的对象,本身都具备响应事件的能力。因此以下类的实例都是响应者:UIViewUIViewControllerUIWindowAppDelegate

  • 响应者之所以能响应事件,因为其提供了 4 个处理触摸事件的方法:

//手指触碰屏幕,触摸开始
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
//手指在屏幕上移动
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
//手指离开屏幕,触摸结束
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
//触摸结束前,某个系统事件中断了触摸,例如电话呼入
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;

四、寻找事件的最佳响应者(hit-tested View)

1. 如何寻找最佳响应者?
  • 自上而下:(父视图 → 子视图)
  • 从后往前 (同级视图,优先询问后添加的视图)
  • 若视图没有能响应的子视图了,则自身就是最合适的响应者。
2. 如何判断视图能否响应?

通过 hitTest:withEvent: 方法的返回值来判断:

  • 若当前视图无法响应事件,则返回 nil
  • 若当前视图可以响应事件,当无子视图可以响应事件,则返回自身作为当前视图层中的事件响应者
  • 若当前视图可以响应事件,同时有子视图可以响应,继续调用子视图的 hitTest:withEvent: ,直到找到 最佳响应者
3. hitTest:withEvent: 的内部实现?
  • 先判断视图 userInteractionEnabled hidden alpha 三个属性是否符合
  • 再判断点击事件的触发位置是否在视图之内
  • 再从后往前调用子视图的 hitTest:withEvent: 方法
  • 最后返回找到的最佳响应者

五、事件的响应以及在响应链中的传递

1. 找到最佳响应者之后,事件是如何传递给最佳响应者的?
  • 首先,UIApplication 将事件通过 sendEvent: 传递给事件所属的 window
  • 然后,window 同样通过 sendEvent: 再将事件传递给 最佳响应者
2. 什么是响应链?

最佳响应者首先接收到事件,然后便拥有了对事件的绝对控制权。它可以选择独吞这个事件,也可以将这个事件往下传递给其他响应者,这个由响应者构成的视图链就称之为响应链

六、总结

  • 触摸发生时,系统内核生成触摸事件,先由 IOKit 处理封装成 IOHIDEvent 对象,通过 IPC 传递给系统进程 SpringBoard,而后再传递给前台 APP 处理。
  • 事件传递到 APP 内部时被封装成开发者可见的 UIEvent 对象,经过 hit-test 寻找第一响应者,而后由 Window 对象将事件传递给 hit-tested view,并开始在响应链上的传递。
  • UIResponder、UIGestureRecognizer、UIControl,笼统地讲,事件响应优先级依次递增。

七、补充

1. 如何通过给定的 view,拿到它的控制器 Controller ?
- (UIViewController *)viewController
{
    //获取当前view的superView对应的控制器
    UIResponder *next = [self nextResponder];
    do {
        if ([next isKindOfClass:[UIViewController class]]) {
            return (UIViewController *)next;
        }
        next = [next nextResponder];
    } while (next != nil);
    return nil;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342