事件分发&响应链
iOS的三种事件:触摸事件/运动事件/远程控制事件
- typedef enum {
- UIEventTypeTouches,
- UIEventTypeMotion,
- UIEventTypeRemoteControl,
- } UIEventType;
只有继承UIResponder类的对象才能处理事件,如UIView、UIViewController、UIApplication都继承自UIResponder,都能接收并处理事件。UIResponder中定义了上面三类事件相关的处理方法:
下面主要讨论触摸事件。
- - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
- - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
- - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;
事件分发:Hit-Testing View
当用户触摸了设备的屏幕,iOS会识别这一系列触摸事件并且封装到UIEvent对象里面,并且放到application的事件队列,在处理事件时,UIApplication会从事件队列取出最前面的事件并分发处理,一般他会发送给主窗口,接下来主窗口将该事件传递到an initial object来处理,对于触摸事件来说该对象是Touch events
首先会将该事件发送给最合适的对象来处理。对于触摸事件该对象指的是hit-test view,对于其他事件,该对象指的是第一响应者。
UIKit first sends the event to the object that is best suited to handle the event. For touch events, that object is the hit-test view, and for other events, that object is the first responder.
Hit-Testing 指的是找到当前触摸事件在哪个View上发生的过程:如果触摸在一个View的边界里面,就会递归的检查它的子View是不是包含了触摸点。最外面的包含了该触摸点的View就称为hit-test view。iOS确定了hit-test view后就会将触摸事件传递给该View来处理。
hitTest:withEvent:具体的实现:
hitTest:withEvent:方法首先会调用pointInside:withEvent:方法,如果触摸点在View的内部,该方法返回YES。接下来会递归的调用所有pointInside:withEvent:方法返回YES的子view的hitTest:withEvent:方法。
如果触摸点不在View里面,调用pointInside:withEvent:方法会返回NO,hitTest:withEvent:会返回nil。如果子view在调用pointInside:withEvent:时返回NO,那么以这个子view为父视图的所有子view都不必再检查。也就是说如果触摸点没在一个子view上,那肯定也不可能在该子view的子view上面。也就是说在父view外面的任何子view的部分永远不可能接收到触摸事件。
当触摸最上面绿色控件的红色框框的那部分永远无法接收到触摸事件。这种情况在view的clipsToBounds属性设置为NO时会出现。
下面的例子展示了hitTest:withEvent:函数的过程
假设用户触摸了图中的view E。iOS通过如下顺序查找hit-test view
1.触摸点在A里面,因此检测子view B和C
2.触摸点不在B里面,但是在C里面。因此检测C的子View D和E。
3.触摸点不在D里面,但是在E里面,并且E是在最外层的包含触摸点的view,因此E就是要找的hit-test view
hitTest:withEvent:函数的实现代码:
1.能否自己处理?不能,return nil;
2.点在不在当前控件上?没在,return nil;
3.说明能处理触摸事件,并且在当前控件上,是合适的控件,但不一定是最合适的。从后往前遍历自己的子控件,是否是最合适的控件(包含该触摸点的View)。如果是,返回该View。
4.说明没找到比自己合适的View,返回自己。
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event的实现:
- - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
- {
- if (self.hidden || !self.userInteractionEnabled || self.alpha < 0.01)
- {
- return nil;
- }
- if (![self pointInside:point withEvent:event])
- {
- return nil;
- }
- __block UIView *hitView = self;
- [self.subViews enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id obj, NSUInteger idx, BOOLBOOL *stop) {
- CGPoint thePoint = [self convertPoint:point toView:obj];
- UIView *theSubHitView = [obj hitTest:thePoint withEvent:event];
- if (theSubHitView != nil)
- {
- hitView = theSubHitView;
- *stop = YES;
- }
- }];
- return hitView;
- }
响应链
响应链是一系列响应对象,响应链中,所有对象的基类都是UIResponder从firstResponse开始到application对象结束。firstResponse意味着首先接收到事件,一般第一响应者是一个view,对于触摸事件来说就是hit-test view。
前面说过,UIKit首先会将该事件发送给最合适的对象来处理。对于触摸事件该对象指的是hit-test view,对于其他事件,该对象指的是第一响应者。
但是实际上响应链也用在触摸事件/运动事件/远程控制事件/等
如果hit-test view无法处理一个事件,事件就通过响应链往上传递(hitTestView算是第一个Responder),直到找到一个可以处理的Responder为止。
下图给出了沿着响应链传递的顺序。两个图的区别是视图的层次关系不一样。响应链从firstResponse开始接下来是它的父视图,如果没有父视图直到它的控制器(如果有的话)再到window和application。
initial object可能是hit-test view或者是first responder,没有处理事件。UIkit就会将该事件传递给next responder下一个响应者,每个响应者通过调用
-nextResponder方法决定是处理该事件还是向响应链的上层传递,直到某个响应者处理了该事件或者没有响应者了为止。
不建议直接给nextResponder发消息即:
- [self.nextResponder touchesBegan:touches withEvent:event];
而建议调用父类的实现,让UIKit来帮我们做,因为默认的实现是将事件沿着响应链继续向上传递到下一个responder。
- [super touchesBegan:touches withEvent:event];
注:
刚开始看到hit-test view和响应链的概念有点弄混了,直到把文档好好看了一下才弄明白:当一个事件发生需要处理时,会让合适的对象去处理。如果是触摸事件的话,该对象就是hit-test view。如果是其他事件,该对象指的就是第一响应者(响应链中)。响应链是一个比较大的范畴,在触摸事件中,hit-test view就是响应链中的第一响应者。也就是说在触摸事件中通过hitTest:withEvent:方法找到的hit-test view就是第一响应者。
Android中的事件分发
回过头简单整理了一下android里面事件传递的过程: Android之View和ViewGroup事件分发
ViewGroup传递的时候经历了dispatchTouchEvent--->onInterceptTouchEvent
从后往前遍历子View,如果子View可以处理事件返回true。传递到此结束
如果子View不能处理该事件,自己处理....onTouchEvent
View处理事件
dispatchTouchEvent—> onTouch –-> onTouchEvent
设置监听onTouch返回了true 或者返回了false但是onTouchEvent返回true都可以认为子View成功处理了事件
若返回了false,事件会往上抛,那么该ViewGroup会调用自己的onTouchEvent处理事件。
在iOS里面事件会因为透明度为0而不能处理/传递事件,在android里面却可以。
iOS里面如果父控件设置不能交互那么上面的子空间也不能获得事件,而android里面即使服布局设置enable为false或者clickable设置为false,上面的子空间均可接收到事件。
iOS里面没有view和view容器之分,而android里面有View和ViewGroup之分,所以事件分发也稍麻烦,
等...
事件分发&响应链的更多相关文章
- iOS 中事件的响应链和传递链
iOS事件链有两条:事件的响应链:Hit-Testing事件的传递链 响应链:由离用户最近的view向系统传递.initial view –> super view –> ….. –> ...
- iOS开发 - 事件传递响应链
序言 当我们在使用微信等工具,点击扫一扫,就能打开二维码扫描视图.在我们点击屏幕的时候,iphone OS获取到了用户进行了“单击”这一行为,操作系统把包含这些点击事件的信息包装成UITouch和UI ...
- 消息点击事件的响应链---hitTest:withEvent:方法
*当用户点击屏幕时,会产生一个触摸事件,系统会将触摸事件加入到 UIApplication管理事件队里中 *UIApplication 会从事件队列中取出最前面的事件进行分发以便处理,通常,先发送事件 ...
- iOS 事件传递响应链
iOS中加载的时候会先执行main函数 int main(int argc, char * argv[]) { @autoreleasepool { return UIApplicationMain( ...
- iOS学习9_事件分发&响应链
iOS的三种事件:触摸事件/运动事件/远程控制事件 typedef enum { UIEventTypeTouches, UIEventTypeMotion, UIEventTypeRemoteCon ...
- iOS-UIResponse之事件响应链及其事件传递
UIResponse之事件响应链及其事件传递 我们的App与用户进行交互,基本上是依赖于各种各样的事件.一个视图是一个事件响应者,可以处理点击等事件,而这些事件就是在UIResponder类中定义的. ...
- iOS事件响应链(Responder Chain)
概述 在iOS中,视图的层级一般都是 父视图->添加各种子视图.这时候某个视图(子视图)上有个按钮,需要我们交互.但是有时候我们会发现无论如何都没有反应.这时候可能就是我们对iOS的事件传递响应 ...
- touch事件分发
touch事件分发 IOS事件分发 我们知道,如果要一个view(就是view,不是UIControl控件)能够响应事件操作,通常的做法是给该View加上相应的手势,或者重写和touch(当然也可以是 ...
- IOS 触摸事件分发机制详解
欢迎大家前往云+社区,获取更多腾讯海量技术实践干货哦~ 作者:MelonTeam 前言 很多时候大家都不关心IOS触摸事件的分发机制的实现原理,当遇到以下几种情形的时候你很可能抓破头皮都找不到解决方案 ...
随机推荐
- WebGoat视频教程下载
WebGoat视频教程下载:http://pan.baidu.com/s/1pJlsfQ7
- foo,bar,baz
https://en.wikipedia.org/wiki/Foobar 原文: The terms foobar, foo, bar, baz and qux are sometimes used ...
- Maven POM.xml (转)
简介 pom.xml文件是Maven进行工作的主要配置文件.在这个文件中我们可以配置Maven项目的groupId.artifactId和version等Maven项目必须的元素:可以配置Maven项 ...
- SharePoint 学习记事(二)
买了一本<sharepoint2010开发高级编程> 据说评价也不高. 搜到如下文章,留着看看:http://book.douban.com/review/5673741/ http:// ...
- smarty半小时快速上手入门教程
http://www.jb51.net/article/56754.htm http://www.yiibai.com/smarty/smarty_functions.html http://www. ...
- powerdesigner 使用的几点问题
一.powerdesigner 没有DataBase?: powerdesigner 只有在选择物理模型PDM的时候才会出现数据库菜单. 二.PowerDesigner版本控制功能? 1.首先介绍一下 ...
- 使用SQL Server 2005 新的语法ROW_NUMBER()进行分页的两种不同方式的性能比较
相比在SQL Server 2000 中使用的分页方式,在SQL Server 2005中使用新的语法ROW_NUMBER()来分页效率要高出很多,但是很多人在使用ROW_NUMBER()这种分页方式 ...
- resin的简单介绍和使用
1.resin是一款应用服务器(application server),它自身也包含一款支持Http1.1协议的WEB服务器(web server),它也可以和其他的web服务器一起工作如IIS和Ap ...
- 高并发编程陷阱之check and set
今天公司CTO跟隔壁部门开技术会,旁听了一下.所讲的内容感觉好高大上啊!简单记录一下 场景是这样的: if(check(id)===true) { }else{ set(id); } 什么意思呢? 就 ...
- MySQL生产库主从重新同步操作注意事项
因为一些原因,我们会遇到生产主从库重新同步的时候.重新同步MYSQL主从的时候有有一些注意的地方. 从库还原前一定要记得reset,因为重启mysql并不影响复制进程,如果忘记reset,会导致你一边 ...