屏幕旋转之后的触摸坐标_iOS 中触摸事件传递和响应原理

作者:雪山飞狐_91ae
链接:https://www.jianshu.com/p/4aeaf3aa0c7e

系统响应阶段

  • 1、手指触碰屏幕,屏幕感受到触摸后,将事件交由IOKit来处理。

  • 2、IOKIT将触摸事件封装成IOHIDEvent对象,并通过mach port传递给SpringBoard进程。

mach port是进程端口,各进程间通过它来通信。Springboard是一个系统进程,可以理解为桌面系统,可以统一管理和分发系统接收到的触摸事件。

  • 3、SpringBoard由于接收到触摸事件,因此触发了系统进程的主线程的runloop的source回调。

发生触摸事件的时候,你有可能正在桌面上翻页,也有可能正在头条上看新闻,如果是前者,则触发SpringBoard主线程的runloop的source0回调,将桌面系统交由系统进程去消耗。而如果是后者,则将触摸事件通过IPC传递给前台APP进程,后面的事便是APP内部对于触摸事件的响应了。

APP响应触摸事件

  • 1、APP进程的mach port接收来自SpringBoard的触摸事件,主线程的runloop被唤醒,触发source1回调。

  • 2、source1回调又触发了一个source0回调,将接收到的IOHIDEvent对象封装成UIEvent对象,此时APP将正式开始对于触摸事件的响应。

  • 3、source0回调将触摸事件添加到UIApplication的事件队列,当触摸事件出队后UIApplication为触摸事件寻找最佳响应者。

  • 4、寻找到最佳响应者之后,接下来的事情便是事件在响应链中传递和响应。

触摸 事件 响应者

触摸

触摸对象即UITouch对象。


一个手指触摸屏幕,就会生成一个UITouch对象,如果多个手指同时触摸,就会生成多个UITouch对象。


多个手指先后触摸,如果系统判断多个手指触摸的是同一个地方,那么不会生成多个UITouch对象,而是更新这个UITouch对象,改变其tap count。如果多个手指触摸的不是同一个地方,那就会生成多个UITouch对象。

触摸事件

触摸事件即UIEvent。


UIEvent即对UITouch的一次封装。由于一次触摸事件并不止有一个触摸对象,可能是多指同时触摸。触摸对象集合可以通过allTouches属性来获取。

响应者

响应者即UIResponser


下列实例都是UIResponser:

  • UIView

  • UIViewController

  • UIApplication

  • Appdelegate
    响应者响应触摸事件是通过下列四个方法来实现的:

//手指触碰屏幕,触摸开始

- (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-Testing)

当APP通过mach port得到这个触摸事件时,APP中有那么多UIView或者UIViewController,到底应该给谁去响应呢?寻找最佳响应者就是找出这个优先级最高的响应对象。

  • 寻找最佳响应者的具体流程如下:

  • 1、UIApplication首先将
    事件传递给窗口对象(UIWindow),如果有多个UIWindow对象,则先选择最后加上的UIWindow对象。

  • 2、若UIWindow对象能响应这个触摸事件,则继续向其子视图传递,向子视图传递时也是先传递给最后加上的子视图。

  • 3、若子视图无法响应该事件,则返回父视图,再传递给倒数第二个加入该父视图的子视图。
    [图片上传失败...(image-95c4b4-1523776714398)]
    例如上面这张图,C在B的后面加入,E在F的后面加入。那么寻找最佳响应者的顺序就是:

  • 1、UIWindow对象将事件传递给视图A,A判断自己能否响应触摸事件,如果能响应,则继续传递给其子视图。

  • 2、如果A能响应触摸事件,由于A有两个子视图B,C,而C又在B的后面加入的,所以A视图再把触摸事件传递给C,C再判断自己能否响应触摸事件,若能则继续传递给其子视图,若不能,则A视图再将触摸事件传递给B视图。

  • 3、如果C能响应触摸事件,C视图也有两个子视图,分别是E和F,但是由于E是在F之后加到C上面的,所以先传递到,由于E可以响应触摸事件,所以最终的最佳响应者就是E。

视图如何判断自己能否响应触摸事件?

下列情况下,视图不能响应触摸事件:

  • 1、触摸点不在试图范围内。

  • 2、不允许交互:视图的userInteractionEnabled = NO。

  • 3、隐藏:hidden = YES,如果视图隐藏了,则不能响应事件。

  • 4、透明度:当视图的透明度小于等于0.01时,不能响应事件。

寻找最佳响应者的原理

hitTest:withEvent:

每个UIView都有一个hitTest:withEvent:方法。这个方法是寻找最佳响应者的核心方法,同时又是传递事件的桥梁。它的作用是询问事件在当前视图中的响应者。hitTest:withEvent:返回一个UIView对象,作为当前视图层次中的响应者。其默认实现是:

  • 若当前视图无法响应事件,则返回nil。

  • 若当前视图能响应事件,但无子视图可响应事件,则返回当前视图。

  • 若当前视图能响应事件,同时有子视图能响应,则返回子视图层次中的事件响应者。
    开始时UIApplication调用UIWindow的hitTest:withEvent:方法将触摸事件传递给UIWindow,如果UIWindow能够响应触摸事件,则调用hitTest:withEvent:将事件传递给其子视图并询问子视图上的最佳响应者,这样一级一级传递下去,获取最终的最佳响应者。
    hitTest:withEvent:的代码实现大致如下:</

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值