iOS事件的响应和传递机制

跟二狗子哥哥交流的时候,他总说我,说的过程太业余。故 好好学习整理一下。努力不那么业余。

一、事件的产生、传递、响应:

1、事件从父控件依次传递到子控件,寻找最合适的子控件View。

2、寻找最合适的View的底层实现,拦截事件的处理。

3、找到最合适的view之后的事件处理,也就是事件响应,重写touch方法等。

 

传递过程中比较重要的两点:

1.如何寻找最合适的view
2.寻找最合适的view的底层实现(hitTest:withEvent:底层实现)

触摸事件 加速事件 远程控制事件 我们现在 讨论触摸事件

 

二、响应者对象(UIResponder)

只有继承了UIResponder的对象,才可以接受并处理事件,我们称之为响应者对象

响应者对象有:UIApplication UIViewController UIView

UIResponder提供了方法以下方法处理触摸事件:

- (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;

- (void)touchesEstimatedPropertiesUpdated:(NSSet<UITouch *> *)touches NS_AVAILABLE_IOS(9_1);

 

Presses事件

- (void)pressesBegan:(NSSet<UIPress *> *)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0);

- (void)pressesChanged:(NSSet<UIPress *> *)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0);

- (void)pressesEnded:(NSSet<UIPress *> *)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0);

- (void)pressesCancelled:(NSSet<UIPress *> *)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0);

 

UIResponder提供了方法以下方法处理加速事件:

- (void)motionBegan:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(3_0);

- (void)motionEnded:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(3_0);

- (void)motionCancelled:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(3_0);

 

UIResponder提供了方法以下方法处理远程事件:

- (void)remoteControlReceivedWithEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(4_0);

 

另外还有:

- (BOOL)canPerformAction:(SEL)action withSender:(nullable id)sender NS_AVAILABLE_IOS(3_0);

 

允许一个事件处理是可转发给其他对象, 默认情况是 调用方法 -canPerformAction:withSender: 来返回自己或者提交返回给响应链去判断处理。

// Allows an action to be forwarded to another target. By default checks -canPerformAction:withSender: to either return self, or go up the responder chain.

- (nullable id)targetForAction:(SEL)action withSender:(nullable id)sender NS_AVAILABLE_IOS(7_0)

 

三、事件传递:用户的触摸事件,首先被封装成一个UIEvent事件,然后UIEvent事件传递给UIResponder的事件,进行判断,寻找最合适的View。

UIEvent事件中会有UITouch对象集,保存着UITouch对象

@property(nonatomic, readonly, nullable) NSSet <UITouch *> *allTouches;

一根手指对应一个UITouch对象,

如果两根手指同事触摸,会调用一次- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;,其中的UIEvent事件中有两个UITouch对象

如果两根手指先后触摸,会调用两次- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;方法,每个方法的UIEvent事件中各有一个UITouch对象

UITouch中保存着手指相关的信息,触摸的位置,时间,阶段。

当手指移动时,系统会更新同一个UITouch对象,使之能够一直保存该手指在的触摸位置

当手指离开屏幕时,系统会销毁相应的UITouch对象

事件产生之后,系统会将事件添加到UIApplication管理的事件队列中,UIApplication会从事件队列中取出最前面的事件,并将事件分发下去处理,先将事件发送给应用程序的主窗口 keyWindow。

keyWindow会在视图层次结构中找到一个最合适的视图来处理触摸事件,这也是整个事件处理过程的第一步。

事件产生之后,就是事件的传递过程:

从父控件到子控件寻找合适的视图控件:

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

          recursively calls -pointInside:withEvent:. point is in the receiver's coordinate system

         递归调用-pointInside:withEvent:方法,判断point是否在其范围内

 

  • 1.首先判断主窗口(keyWindow)自己是否能接受触摸事件

 

 

  • 2.判断触摸点是否在自己身上(pointInside方法)

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

         // default returns YES if point is in bounds  如果点在bouns范围内,默认返回yes

  • 3.子控件数组中从后往前遍历子控件,重复前面的两个步骤(所谓从后往前遍历子控件,就是首先查找子控件数组中最后一个元素,然后执行1、2步骤)
  • 4.view,比如叫做fitView,那么会把这个事件交给这个fitView,再遍历这个fitView的子控件,直至没有更合适的view为止。
  • 5.如果没有符合条件的子控件,那么就认为自己最合适处理这个事件,也就是自己是最合适的view。

期间用到的重要的两个方法:

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

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

UIView不能接收触摸事件的三种情况:

  • 不允许交互:userInteractionEnabled = NO
  • 隐藏:如果把父控件隐藏,那么子控件也会隐藏,隐藏的控件不能接受事件
  • 透明度:如果设置一个控件的透明度<0.01,会直接影响子控件的透明度。alpha:0.0~0.01为透明。

主要方法解析:

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

调用时机:

只要事件一传给某个控件,控件就会调用自己的该方法,寻找合适的子控件返回。

作用:

寻找合适的子控件返回

注 意:不管这个控件能不能处理事件,也不管触摸点在不在这个控件上,事件都会先传递给这个控件,随后再调用hitTest:withEvent:方法

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

拦截事件的处理:

  • 正因为hitTest:withEvent:方法可以返回最合适的view,所以可以通过重写hitTest:withEvent:方法,返回指定的view作为最合适的view。
  • 不管点击哪里,最合适的view都是hitTest:withEvent:方法中返回的那个view。
  • 通过重写hitTest:withEvent:,就可以拦截事件的传递过程,想让谁处理事件谁就处理事件。

如果hitTest:withEvent:方法中返回nil,那么调用该方法的控件本身和其子控件都不是最合适的view,也就是在自己身上没有找到更合适的view。那么最合适的view就是该控件的父控件。

事件传递顺序:

产生触摸事件->UIApplication事件队列->[UIWindow hitTest:withEvent:]->返回更合适的view->[子控件 hitTest:withEvent:]->返回最合适的view

重写的技巧:在父控件的hitTest:withEvent:中返回子控件作为最合适的view!

 

hit:withEvent:方法底层会调用pointInside:withEvent:方法判断点在不在方法调用者的坐标系上

 

 

pointInside:withEvent:方法

判断点在不在当前view上(方法调用者的坐标系上)如果返回YES,代表点在方法调用者的坐标系上;返回NO代表点不在方法调用者的坐标系上,那么方法调用者也就不能处理事件。

 

四、事件的响应:

响应者链条

如何判断上一个响应者

  • 1> 如果当前这个view是控制器的view,那么控制器就是上一个响应者
  • 2> 如果当前这个view不是控制器的view,那么父控件就是上一个响应者

响应者链的事件传递过程:

  • 1>如果当前view是控制器的view,那么控制器就是上一个响应者,事件就传递给控制器;如果当前view不是控制器的view,那么父视图就是当前view的上一个响应者,事件就传递给它的父视图
  • 2>在视图层次结构的最顶级视图,如果也不能处理收到的事件或消息,则其将事件或消息传递给window对象进行处理
  • 3>如果window对象也不处理,则其将事件或消息传递给UIApplication对象
  • 4>如果UIApplication也不能处理该事件或消息,则将其丢弃

五、事件处理的整个流程总结:
  1.触摸屏幕产生触摸事件后,触摸事件会被添加到由UIApplication管理的事件队列中(即,首先接收到事件的是UIApplication)。
  2.UIApplication会从事件队列中取出最前面的事件,把事件传递给应用程序的主窗口(keyWindow)。
  3.主窗口会在视图层次结构中找到一个最合适的视图来处理触摸事件。(至此,第一步已完成)
  4.最合适的view会调用自己的touches方法处理事件
  5.touches默认做法是把事件顺着响应者链条向上抛。

参考:http://www.cnblogs.com/machao/p/5471094.html

十分感谢前辈的分享

转载于:https://www.cnblogs.com/Jordandan/p/6483835.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/488977.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

php 树状数组公式,PY个树状数组

树状数组比较简单&#xff0c;于是就挑它下手了...于是生活终于也对咱下手了...要讲的就两个东西&#xff0c;一个是开数组&#xff0c;全局变量写最前面&#xff0c;数组是这么开的&#xff1a; f[0 for i in range(500005)]另外一个就是 注意 split()还有一句就是咱发现 pyth…

三宝机器人怎么充电_巨人通力导致吉祥三宝+36号故障怪现象的又一因素

想必大家都有过这样的经历&#xff1a;在电梯故障频繁出现时&#xff0c;明明可以判断是哪里的问题&#xff0c;但事实却是让你感觉不可思议&#xff0c;并且排查过程颇为周折。今天和大家分享一下我经历的巨人通力GPS30K电梯出现的吉祥三宝故障的排查过程。下图为电梯控制柜铭…

《自然》展望2020年重大科学事件!中国有两个大计划安排上了

来源&#xff1a;科技日报图片来源&#xff1a;Nature官网&#xff08;不包括标注了来源的图片&#xff09;“千门万户曈曈日&#xff0c;总把新桃换旧符”&#xff01;在辞旧迎新之际&#xff0c;我们除了要埋头梳理过去一年的悲欢得失&#xff0c;也应抬头眺望新一年的星辰大…

discuz search.php修改,discuz程序如何将热门搜索修改为指向门户搜索的方法

站长朋友们使用discuz论坛程序建站&#xff0c;细心的站长朋友们发现首页右上角有个热门搜索功能。这个功能的主要作用就是可以让会员们第一时间搜索到本论坛内需要找到的帖子&#xff0c;这样做就给会员朋友们提供了很大的好处&#xff0c;不用再为了找到需要的帖子而大费周章…

vue aplayer 进度条无法拖动_Vue-rideo-player视频播放插件的使用

***** git项目地址&#xff1a; https://github.com/surmon-china/vue-video-player***** 参考文章&#xff1a; https://www.jianshu.com/p/532fc1d8c90c使用安装&#xff1a; npm install vue-video-player --save2. 在main.js入口文件中引入import VideoPlayer from vue-vid…

php流程控制作业题,php流程控制

一、get传值我们请求服务器下一步操作啊&#xff0c;都是用get进行传值&#xff0c;然后服务器用预定义变量数组$_GET&#xff0c;接受。二、流程控制1 三元运算符 ? :逻辑很简单&#xff0c;就是设置条件&#xff0c;条件成立就执行“&#xff1f;” 。条件不成立就执行”:…

JS中的call()和apply()方法(转)

转自&#xff1a;http://uule.iteye.com/blog/1158829 JS中的call()和apply()方法 博客分类&#xff1a; JS1、方法定义 call方法: 语法&#xff1a;call([thisObj[,arg1[, arg2[, [,.argN]]]]]) 定义&#xff1a;调用一个对象的一个方法&#xff0c;以另一个对象替换当前对象…

中国AI芯片产业发展白皮书:未来三年年均增长率超50%

来源&#xff1a;赛迪顾问《中国AI芯片产业发展白皮书》从AI芯片的定义及分类、发展过程与现状、应用机会、竞争格局、发展趋势等多角度全面剖析AI芯片的发展新态势、 技术演进及行业格局&#xff0c;旨在为业内相关企业把握行业发展动态、挖掘市场机遇&#xff0c;提供借鉴与参…

python队列怎么用_如何在Python中使用多处理队列? - python

我很难理解多处理队列如何在python上工作以及如何实现它。假设我有两个python模块&#xff0c;它们从共享文件中访问数据&#xff0c;我们将这两个模块称为writer和Reader。我的计划是让读取器和写入器都将请求放入两个单独的多处理队列中&#xff0c;然后让第三个进程将这些请…

php获取显示图书数据,php基于dom实现读取图书xml格式数据的方法

本文实例讲述了php基于dom实现读取图书xml格式数据的方法。分享给大家供大家参考&#xff0c;具体如下&#xff1a;<?php $doc new DOMDocument();$doc->load( books.xml );$books $doc->getElementsByTagName( "book" );foreach( $books as $book ){$au…

codeforces problem 768B

这是第一篇博客&#xff1f;日志也行。。。codeforces problem 768B这个题做了很长时间--好几天吧&#xff0c;有空的时候就想怎么做。问题描述看 http://codeforces.com/problemset/problem/768/B看起来是个三叉树的题目。最开始我想用递归完成&#xff0c;遍历每一个节点&…

人工智能与经济生产力:期待进化,而不是革命

Illustration: Edmon de Haro来源&#xff1a; IEEE电气电子工程师尽管大肆宣传&#xff0c;人工智能仍需数年时间才能显著提高经济生产力。2016年&#xff0c;总部位于伦敦的Alphabet&#xff08;也是谷歌的母公司&#xff09;的子公司DeepMind Technologies报告称&#xff0c…

oracle 表 上限,Oracle分区表(Partition Table)的数量限制

Oracle分区表(Partition Table)的数量限制有朋友在我的留言板上问到这样一个问题:oracle分区表是不是有最大分区个数限制,我有一张大约20G的表,有好多分区(按时间),结果根据这个时间条件查询就报ORA-03113 end-of-file on communication channel错误,然后断开链接,硬件与网络都…

wpf 控件生成图片_EyeshotCAD控件生成以及作用

EyeshotCAD控件生成以及作用devDept Software是一家软件开发公司&#xff0c;成立于2006年&#xff0c;总部位于意大利博洛尼亚。 devDept Software是Microsoft .NET Framework的全球最大的CAD软件组件提供商。devDept SoftwareEyeshot的旗舰产品Eyeshot旨在满足CAD开发人员不断…

张恭庆院士:数学的意义(最全最牛的解释)

来源&#xff1a;数学严老师北京大学数学科学学院教授、中国科学院院士、第三世界科学院院士数学既是一种文化、一种“思想的体操”&#xff0c;更是现代理性文化的核心。马克思说&#xff1a;“一门科学只有当它达到了能够成功地运用数学时&#xff0c;才算真正发展了。”在前…

动态通过网络获取json来tabbar图片和文字或其他信息

动态通过网络获取json来tabbar图片和文字或其他信息转载于:https://www.cnblogs.com/TheYouth/p/6488843.html

oracle 根据分隔符提取,oracle使用指定分隔符导出数据到文件

最近有需求要导出一个专业的详单&#xff0c;用于倒入sqlserver系统&#xff0c;要求用分隔符隔开各字段。一开始准备用toad查询出来在save as&#xff0c;但发现表太多&#xff0c;要一张一张表盯着&#xff0c;而且由于详单数据量大&#xff0c;查询导出占用了大量的客户端内…

vue mxgraph渲染xml页面_Vue的两个版本

Vue.js分为两个版本&#xff0c;简单来说就是一个是完整版vue.js&#xff0c;一个是非完整版vue.runtime.js&#xff1a;1. HTML的书写位置不同完整版&#xff1a;完整版的HTML书写的位置有两个&#xff0c;一个是直接在 *.html 文件中使用 Vue 语法&#xff0c;如下所示&#…

美智库发布报告:《美国在人工智能时代的行动蓝图》

来源&#xff1a;新美国安全中心12月17日&#xff0c;新美国安全中心发布报告《美国人工智能世纪&#xff1a;行动蓝图》&#xff0c;指出先进计算、量子科学、人工智能、合成生物学、5G、增材制造等技术的快速进步正在改变技术运行机制&#xff0c;其中人工智能将产生最广泛的…

四则运算01

编写随机生成30道小学数学题目&#xff1a; 源代码&#xff1a; package test; public class lianxi {String f(){int i (int)(Math.random()*10);int j (int)(Math.random()*10);if(i>j){int temp i;i j;j temp;}return ("("i"/"j")")…