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

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

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

十分感谢前辈的分享

iOS事件的响应和传递机制的更多相关文章

  1. 谈一谈iOS事件的产生和传递

    谈一谈iOS事件的产生和传递 1.事件的产生 发生触摸事件后,系统会将该事件加入到一个由UIApplication管理的事件队列中. UIApplication会从事件队列中取出最前面的事件,并将事件 ...

  2. iOS 事件的产生、传递、响应

    一.事件的产生和传递 1.1.事件的产生 发生触摸事件后,系统会将该事件加入到一个由UIApplication管理的事件队列中为什么是队列而不是栈?因为队列的特定是先进先出,先产生的事件先处理才符合常 ...

  3. iOS响应者链和事件传递机制

    原文来自:http://www.cnblogs.com/zhw511006/p/3517248.html 响应者链(Responder Chain) 通常,一个iOS应用中,在一块屏幕上通常有很多的U ...

  4. IOS和OSX事件传递机制

    本文ios部分转载自: http://zhoon.github.io/ios/2015/04/12/ios-event.html iOS的事件有好几种:Touch Events(触摸事件).Motio ...

  5. 深入浅出iOS事件机制

    原文地址: http://zhoon.github.io/ios/2015/04/12/ios-event.html 本文章将讲解有关iOS事件的传递机制,如有错误或者不同的见解,欢迎留言指出. iO ...

  6. iOS事件机制(二)

    从上一篇的内容我们知道,在iOS中一个事件用一个UIEvent对象表示,UITouch用来表示一次对屏幕的操作动作,由多个UITouch对象构成了一个UIEvent对象.另外,UIResponder是 ...

  7. iOS事件机制(一)

    运用的前提是掌握 掌握的本质是理解 本篇内容将围绕iOS中事件及其传递机制进行学习和分析.在iOS中,事件分为三类: 触控事件(单点.多点触控以及各种手势操作) 传感器事件(重力.加速度传感器等) 远 ...

  8. iOS响应链和传递机制

    iOS中加载的时候会先执行main函数 int main(int argc, charchar * argv[]) { @autoreleasepool { return UIApplicationM ...

  9. iOS事件传递->处理->响应

    前言: 按照时间顺序,事件的生命周期是这样的: 事件的产生和传递(事件如何从父控件传递到子控件并寻找到最合适的view.寻找最合适的view的底层实现.拦截事件的处理)->找到最合适的view后 ...

随机推荐

  1. [php] 猴子偷桃

    <?php /* 10:五只猴子采得一堆桃子,猴子彼此约定隔天早起后再分食. 不过,就在半夜里,一只猴子偷偷起来,把桃子均分成五堆后, 发现还多一个,它吃掉这桃子,并拿走了其中一堆.第二只猴子醒 ...

  2. 【题解】POJ3041 Asteroids - 图论 - 二分图匹配

    声明:本博客所有题解都参照了网络资料或其他博客,仅为博主想加深理解而写,如有疑问欢迎与博主讨论✧。٩(ˊᗜˋ)و✧*。 POJ3041 Asteroids 题目描述 假如你现在正处在一个 \(N*N\ ...

  3. Java讲解RPC的基本实现

    RPC远程过程调用可以说是分布式系统的基础,本文将通过Java演示一次普通的rpc调用到底发生了什么. 我曾经在网上看到有人提问,为什么RPC要叫作远程过程调用,而不叫作RMC远程方法调用.个人认为R ...

  4. 0day学习笔记(2)--函数调用

    函数调用过程 调用函数操作 函数参数入栈(在当前函数栈帧),从左至右或从右至左视情况而定 一般为从右至左 mov 地址,参数 的一个操作并不直接pop而是定位到地址将参数传递进去 call offse ...

  5. QT 执行windows cmd 命令并读取结果

    1,写好命令, 2,用QProcess执行,等待完成(有超时), 3,读取结果 注意形如“Program Files”的有 空格 的路径,要加上双引号. QProcess process; QStri ...

  6. VirtualBox 原始镜像转换成 vdi 镜像

    VBoxManage convertdd [-static] <filename> <outputfile> 将raw硬盘转换成vdi虚拟硬盘

  7. Cannot find or open the PDB file

    打开VS:工具 --> 选项 --> 调试-->符号接下来就是选择Microsoft,然后确认

  8. Zabbix CPU utilization监控参数

    工作中查看Zabbix linux 监控项的时候对linux 监控的cpu使用的各个参数没怎么明白,特意查看了下资料 Zabbix linux模板下的CPU utilization是自带的监控Linu ...

  9. java中for循环和while循环,哪个更快?--一道面试题

    for的 while的

  10. Random Number Generator

    rand()函数可以产生[0,RAND_MAX]之间的均匀的伪随机数,它定义在头文件stdlib.h中,函数原型: int rand(void); C标准库的实现是: unsigned ; /*ran ...