iOS事件传递&响应者链条
原文:http://www.cnblogs.com/Quains/p/3369132.html
主要是记录下iOS的界面触摸事件处理机制,然后用一个实例来说明下应用场景.
一、处理机制
界面响应消息机制分两块,(1)首先在视图的层次结构里找到能响应消息的那个视图。(2)然后在找到的视图里处理消息。
【关键】(1)的过程是从父View到子View查找,而(2)是从找到的那个子View往父View回溯(不一定会往回传递消息)。
1.1、寻找响应消息视图的过程可以借用M了个J的一张图来说明。

处理原理如下:
• 当用户点击屏幕时,会产生一个触摸事件,系统会将该事件加入到一个由UIApplication管理的事件队列中
• UIApplication会从事件队列中取出最前面的事件进行分发以便处理,通常,先发送事件给应用程序的主窗口(UIWindow)
• 主窗口会调用hitTest:withEvent:方法在视图(UIView)层次结构中找到一个最合适的UIView来处理触摸事件
(hitTest:withEvent:其实是UIView的一个方法,UIWindow继承自UIView,因此主窗口UIWindow也是属于视图的一种)
• hitTest:withEvent:方法大致处理流程是这样的:
首先调用当前视图的pointInside:withEvent:方法判断触摸点是否在当前视图内:
▶ 若pointInside:withEvent:方法返回NO,说明触摸点不在当前视图内,则当前视图的hitTest:withEvent:返回nil
▶ 若pointInside:withEvent:方法返回YES,说明触摸点在当前视图内,则遍历当前视图的所有子视图(subviews),调用子视图的hitTest:withEvent:方法重复前面的步骤,子视图的遍历顺序是从top到bottom,即从subviews数组的末尾向前遍历,直到有子视图的hitTest:withEvent:方法返回非空对象或者全部子视图遍历完毕:
▷ 若第一次有子视图的hitTest:withEvent:方法返回非空对象,则当前视图的hitTest:withEvent:方法就返回此对象,处理结束
▷ 若所有子视图的hitTest:withEvent:方法都返回nil,则当前视图的hitTest:withEvent:方法返回当前视图自身(self)
• 最终,这个触摸事件交给主窗口的hitTest:withEvent:方法返回的视图对象去处理。
拿到这个UIView后,就调用该UIView的touches系列方法。
1.2、消息处理过程,在找到的那个视图里处理,处理完后根据需要,利用响应链nextResponder可将消息往下一个响应者传递。
UIAppliactionDelegate <- UIWindow <- UIViewController <- UIView <- UIView
【关键】:要理解的有三点:1、iOS判断哪个界面能接受消息是从View层级结构的父View向子View传递,即树状结构的根节点向叶子节点递归传递。2、hitTest和pointInside成对,且hitTest会调用pointInside。3、iOS的消息处理是,当消息被人处理后默认不再向父层传递。
二、应用实例
【需求】是:界面如下,
Window
-ViewA
-ButtonA
-ViewB
-ButtonB
层次结构:ViewB完全盖住了ButtonA,ButtonB在ViewB上,现在需要实现1)ButtonA和ButtonB都能响应消息 2)ViewA也能收到ViewB所收到的touches消息 3)不让ViewB(ButtonB)收到消息。
(首先解析下,默认情况下,点击了ButtonB的区域,iOS消息处理过程。
-ViewA
-ButtonA
-ViewB
-ButtonB
当点击ButtonB区域后,处理过程:从ViewA开始依次调用hitTest
pointInside的值依次为:
ViewA:YES;
ViewB:YES;
ButtonB:YES;
ButtonB的subViews:NO;
所以ButtonB的subViews的hitTest都返回nil,于是返回的处理对象是ButtonB自己。接下去开始处理touches系列方法,这里是调用ButtonB绑定的方法。处理完后消息就停止,整个过程结束。)
【分析】:
实现的方式多种,这里将两个需求拆解开来实现,因为实现2就可以满足1。
2.1、需求1的实现,ViewB盖住了ButtonA,所以默认情况下ButtonA收不到消息,但是在消息机制里寻找消息响应是从父View开始,所以我们可以在ViewA的hitTest方法里做判断,若touch point是在ButtonA上,则将ButtonA作为消息处理对象返回。
代码如下:
| 
 1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
 | 
#pragma mark - hitTest- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{    // 当touch point是在_btn上,则hitTest返回_btn    CGPoint btnPointInA = [_btn convertPoint:point fromView:self];    if ([_btn pointInside:btnPointInA withEvent:event]) {        return _btn;    }         // 否则,返回默认处理    return [super hitTest:point withEvent:event];     } | 
这样,当触碰点是在ButtonA上时,则touch消息就被拦截在ViewA上,ViewB就收不到了。然后ButtonA就收到touch消息,会触发onClick方法。
2.2、需求2的实现,上面说到响应链,ViewB只要override掉touches系列的方法,然后在自己处理完后,将消息传递给下一个响应者(即父View即ViewA)。
代码如下:在ViewB代码里
| 
 1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
 | 
#pragma mark - touches- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{    NSLog(@"B - touchesBeagan..");         // 把事件传递下去给父View或包含他的ViewController    [self.nextResponder touchesBegan:touches withEvent:event];}- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event{    NSLog(@"B - touchesCancelled..");    // 把事件传递下去给父View或包含他的ViewController    [self.nextResponder touchesBegan:touches withEvent:event];}- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{    NSLog(@"B - touchesEnded..");    // 把事件传递下去给父View或包含他的ViewController    [self.nextResponder touchesBegan:touches withEvent:event];}- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{    NSLog(@"B - touchesMoved..");    // 把事件传递下去给父View或包含他的ViewController    [self.nextResponder touchesBegan:touches withEvent:event];     } | 
然后,在ViewA上就可以接收到touches消息,在ViewA上写:
| 
 1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
 | 
#pragma mark - touches- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{    NSLog(@"A - touchesBeagan..");}- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event{    NSLog(@"A - touchesCancelled..");}- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{    NSLog(@"A - touchesEnded..");}- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{    NSLog(@"A - touchesMoved..");     } | 
这样就实现了向父View透传消息。
2.3 、不让ViewB收到消息,可以设置ViewB.UserInteractionEnable=NO;除了这样还可以override掉ViewB的ponitInside,原理参考上面。
在ViewB上写:
| 
 1 
2 
3 
4 
5 
6 
 | 
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{    // 本View不响应用户事件    return NO;  } | 
iOS事件传递&响应者链条的更多相关文章
- iOS事件传递->处理->响应
		
前言: 按照时间顺序,事件的生命周期是这样的: 事件的产生和传递(事件如何从父控件传递到子控件并寻找到最合适的view.寻找最合适的view的底层实现.拦截事件的处理)->找到最合适的view后 ...
 - iOS 事件传递(Touch事件)
		
先总说如下: 1.当手指触摸到屏幕时,会产生UITouch对象和UIEvent对象. 2.这两个对象产生后会被传递到UIApplication管理的一个事件队列中. 3.再有UIApplication ...
 - iOS 事件传递及响应过程
		
iOS 事件传递及响应过程 -->>事件到来-->>事件分发 -->>事件响应 事件( Events) 官方文档( Events(iOS)) 是这样描写叙述的: U ...
 - iOS事件传递和事件响应者链 20170810
		
一.事件响应者链 事件传递和事件响应链 区别 事件的传递和响应的区别: 事件的传递是从上到下(父控件到子控件),事件的响应是从下到上(顺着响应者链条向上传递:子控件到父控件. 引出 当我们手指触摸屏幕 ...
 - iOS 事件传递响应链
		
iOS中加载的时候会先执行main函数 int main(int argc, char * argv[]) { @autoreleasepool { return UIApplicationMain( ...
 - ios事件传递
		
http://blog.csdn.net/iefreer/article/details/4754482 本章描述了iPhone操作系统里的事件类型,并解释了如何处理它们.还讨论了怎么在一个应用程序里 ...
 - iOS中的事件传递和响应者链条
		
本文转自:http://www.linuxidc.com/Linux/2015-08/121270.htm 首先我们来看看ios中事件的产生和传递过程 1.发生触摸事件后,系统会将事件加入到一个由UI ...
 - iOS中事件的传递和响应者链条
		
在iOS 开发中,常见的事件有三种类型,分别是: (1)触摸事件:平常手指在屏幕上滑动,产生的事件都是触摸事件 (2)加速计事件:微信的摇一摇就是典型的加速计事件 (3)远程控制事件:耳机控制歌曲上一 ...
 - iOS开发——UI进阶篇(十二)事件处理,触摸事件,UITouch,UIEvent,响应者链条,手势识别
		
触摸事件 在用户使用app过程中,会产生各种各样的事件 一.iOS中的事件可以分为3大类型 触摸事件加速计事件远程控制事件 响应者对象在iOS中不是任何对象都能处理事件,只有继承了UIResponde ...
 
随机推荐
- 转: VMware 安装mac osx 10.11 安装步骤(一)(from伟东)
			
http://blog.csdn.net/soachenshui/article/details/49251513
 - windows下 git配置ssh
			
1. 打开 git bash 2. cd ~/.ssh 3. ssh-genkey (一致next), 会生成 id_rsa (密钥); id_rsa.pub(公钥) 4. 用记事本打开 id_rs ...
 - mui禁止横屏显示,仅支持竖屏显示
			
mui.plusReady(function () { plus.screen.lockOrientation("portrait-primary"); });
 - webstrom快捷键速查
			
编辑 Ctrl + Space 基本代码完成 (任何类. 方法或变量名称)Ctrl + Shift + Enter 完整的语句Ctrl + P (在方法调用参数) 内的参数信息Ctrl + Q 快速的 ...
 - Java核心技术点之内部类
			
1. 为什么要使用内部类 内部类就是定义在一个类内部的类,那么为什么要使用内部类呢?主要原因有以下几点:第一,内部类中定义的方法能访问到它所在外部类的私有属性及方法:第二,外部类无法实现对同一 ...
 - some basic graph theoretical measures
			
· mean characteristic path length calculated as the average length of the shortest path between two ...
 - 用sql查询当天,一周,一个月的数据
			
用sql查询当天,一周,一个月的数据 数据查询,不管在网站还是在系统,都很常见,下文是介绍最常见的以日期查询的语句 select * from ShopOrder where datediff(w ...
 - swift 随机生成背景颜色
			
swift是一门新语言,相关的文档资料现在基本上还不是很完整.在尝试开发过程中,走了不少弯路.在这里记录一下自己的”路“,希望以后能少走弯路. 生成随机背景颜色使用的语法和C#或者JAVA基本一致. ...
 - linux addr2line 定位so库崩溃位置
			
在Linux下写C/C++程序的程序员,时常与Core Dump相见.在内存越界访问,收到不能处理的信号,除零等错误出现时,我们精心或不精心写就的程序就直接一命呜呼了,Core Dump是Linux仁 ...
 - Windows数据类型
			
WORD:16位无符号整形数据 DWORD:32字节无符号整型数据(DWORD32) DWORD64:64字节无符号整型数据 INT:32位有符号整型数据类型 INT_PTR:指向INT数据类型的指针 ...