iOS触摸事件深入
转载自:http://www.cnblogs.com/wengzilin/p/4720550.html
概述
本文主要解析从我们的手指触摸苹果设备到最终响应事件的整个处理机制。本质上讲,整个过程可以分为两个步骤:
步骤1:找目标。在iOS视图层次结构中找到触摸事件的最终接受者;
步骤2:事件响应。基于iOS响应者链(Responder Chain)处理触摸事件
找目标
在找目标阶段所使用到的两大利器是UIView的 hitTest:withEvent: 以及 pointInside:withEvent: 方法。找目标的过程也称为hit-Testing。先来看一张图(注: 图来自MJ)比较直观:

下面解释一下处理原理:
1、手指触摸屏幕,这个动作被包装成一个UIEvent对象发送给当前活跃的UIApplication (Active Application),Application将该Event对象插到任务队列的末尾等待处理(先进先出,先来的先处理);
2、UIApplication单例将事件发送给APP的主Window(所有显示的view都添加在Window上);
3、主Window调用视图层次结构上逐级使用hit-Testing确认最终的响应目标,这个目标也称为hitTesting view。
在没有做任何重载操作的前提下,系统默认的hit-Testing的处理机制如下:
当前view调用自身的pointInside: withEvent:方法判断触摸点是否在自己范围内:
- 若pointInside: withEvent:方法返回NO,则说明触摸点不在自己范围内,则 当前view的hitTest: withEvent:方法返回nil,当前view上的所有subview都不做判断。有点领导的意见一票否决的味道。
 - 若pointInside: withEvent:方法返回YES,则说明触摸点在自己的范围内。但无法判断是否在自己身上还是在subview的身上。此时,遍历所有的subviews,对每个subview调用hitTest方法。这里要注意,遍历的顺序是从当前view的subviews数组的尾部开始遍历。因此离用户最近的上层的subview会优先被调用hitTest方法。
 - 一旦hitTest方法返回非空的view,则被返回的view就是最终相应触摸事件的view,寻找hitTesting view的阶段到此结束,不再遍历。
 - 若当前view的所有subviews的hitTest方法都返回nil,则当前view的hitTest方法返回self作为最终的hitTesting view,处理结束。
 
以上就是第一阶段寻找响应view的机制。这里我们结合一个具体的例子再过一遍(图片引自技术哥的博客):

当用户点击ViewD所在的区域时会进行以下hit-Testing:
- ViewA的pointInside返回YES,因为触摸点在其bounds内。遍历ViewA的两个subview;
 - ViewB的pointInside返回NO,因为触摸点不在其bounds内,ViewB的hitTest方法返回nil。而且发生一票否决,在ViewB上的所有subviews受到牵连将不再进行hit-Testing处理。ViewC的pointInside返回YES,因为触摸点在其bounds范围内,ViewC的hitTest方法返回默认处理,也就是 return[super hitTest:point withEvent:event]; 遍历ViewC的两个subview;
 - ViewD的pointInside返回YES,因为触摸点在其bounds范围内,且ViewD没有subview,因此hitTest方法返回其自己。hitTesting view找到,结束处理。
 
这里有几点需要强调:
1、hitTest方法调用pointInside方法;
2、hit-Testing过程是从superView向subView逐级传递,也就是从层次树的根节点向叶子节点传递;
3、遇到以下设置时,view的pointInside将返回NO,hitTest方法返回nil:
- view.isHidden=YES;
 - view.alpah<=0.01;
 - view.userInterfaceEnable=NO;
 - control.enable=NO;(UIControl的属性)
 
hit-Testing过程用代码可以描述如下:
| 
 1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
 | 
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {    if (self.alpha <= 0.01 || !self.userInteractionEnabled || self.hidden) {        return nil;    }    BOOL inside = [self pointInside:point withEvent:event];    UIView *hitView = nil;    if (inside) {        NSEnumerator *enumerator = [self.subviews reverseObjectEnumerator];        for (UIView *subview in enumerator) {            hitView = [subview hitTest:point withEvent:event];            if (hitView) {                break;            }        }        if (!hitView) {            hitView = self;        }        return hitView;    } else {        return nil;    }} | 
事件响应
上一部分我们通过hit-Testing机制找到了hitTesting View,这个hitTesting View就是触摸事件的响应者Responder。在iOS系统中,能够响应并处理事件的对象称之为Responder Object,而UIResponder是所有responder的最顶层基类。当hitTesting view做完自己该做的动作后,可以根据需要将消息传给下一级响应者。那下一级响应者会是什么呢?这取决于iOS中的响应者链Responder Chain,如下图所示:

- UIView的nextResponder属性,如果有管理此view的UIViewController对象,则为此UIViewController对象;否则nextResponder即为其superview。
 - UIViewController的nextResponder属性为其管理view的superview.
 - UIWindow的nextResponder属性为UIApplication对象。
 - UIApplication的nextResponder属性为nil。
 
更具体的:
- 如果hit-test view或first responder不处理此事件,则将事件传递给其nextResponder处理,若有UIViewController对象则传递给UIViewController,传递给其superView。
 - 如果view的viewController也不处理事件,则viewController将事件传递给其管理view的superView。
 - 视图层级结构的顶级为UIWindow对象,如果window仍不处理此事件,传递给UIApplication.
 - 若UIApplication对象不处理此事件,则事件被丢弃。
 
了解响应者链有时候可以帮我解决一些实际问题。我举个例子,我们知道,当提供给你一个ViewController你可以很容易得到它的view,一句代码的事情:
| 
 1 
 | 
viewWanted = someViewController.view; | 
但如果反过来呢?当给你一个view,让你找到其所在的ViewController呢?这时候响应者链可以帮上忙了,代码如下:
| 
 1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
 | 
@implementation UIView (FindController)-(UIViewController*)parentController{    UIResponder *responder = [self nextResponder];    while (responder) {    if ([responder isKindOfClass:[UIViewController class]]) {        return (UIViewController*)responder;    }    responder = [responder nextResponder];    }    return nil;}@end | 
写在最后
这篇文章解析了iOS响应触摸事件的机制。或许你现在找不到这个知识的应用点,但是一旦你理解了,可以帮助你实现一些特别的需求,比如点击某个按钮,响应的却是另一个按钮;穿透某个view点击到view下面的view... 更有甚者,你可以用上面的知识解决不规则区域触摸问题(看我之前的文章)、不添加任何view就能扩大控件的可触摸区域等。天马行空,任我翱翔!
iOS触摸事件深入的更多相关文章
- IOS触摸事件和手势识别
		
IOS触摸事件和手势识别 目录 概述 触摸事件 手势识别 概述 为了实现一些新的需求,我们常常需要给IOS添加触摸事件和手势识别 触摸事件 触摸事件的四种方法 -(void)touchesBegan: ...
 - IOS——触摸事件 视图检测和事件传递
		
iPhone上有非常流畅的用户触摸交互体验,能检测各种手势:点击,滑动,放大缩小,旋转.大多数情况都是用UI*GestureRecognizer这样的手势对象来关联手势事件和手势处理函数.也有时候,会 ...
 - IOS 触摸事件分发机制详解
		
欢迎大家前往云+社区,获取更多腾讯海量技术实践干货哦~ 作者:MelonTeam 前言 很多时候大家都不关心IOS触摸事件的分发机制的实现原理,当遇到以下几种情形的时候你很可能抓破头皮都找不到解决方案 ...
 - 【原】iOS触摸事件深度解析
		
概述 本文主要解析从我们的手指触摸苹果设备到最终响应事件的整个处理机制.本质上讲,整个过程可以分为两个步骤: 步骤1:找目标.在iOS视图层次结构中找到触摸事件的最终接受者: 步骤2:事件响应.基于i ...
 - 浅谈iOS触摸事件理解
		
iOS的触摸事件个人总结,分为两步: 第一步:是找到哪个视图上触摸 第二步:分析由谁去响应(响应者连) 1.寻找被触摸的视图原理如下图 hitText:withEvent:的方法处理流程: 首先会在当 ...
 - iOS触摸事件深度解析-备用
		
概述 本文主要解析从我们的手指触摸苹果设备到最终响应事件的整个处理机制.本质上讲,整个过程可以分为两个步骤: 步骤1:找目标.在iOS视图层次结构中找到触摸事件的最终接受者: 步骤2:事件响应.基于i ...
 - iOS 触摸事件与手势识别器(Gesture Recognizers)
		
Gesture Recognizers与触摸事件分发 通过一个问题引出今天的知识: 1.大家应该都遇见过 当需要给tableView 添加一个tap 手势识别 但是tableView 的上的事件(滑动 ...
 - iOS触摸事件哦
		
主要是记录下iOS的界面触摸事件处理机制,然后用一个实例来说明下应用场景. 一.处理机制 界面响应消息机制分两块,(1)首先在视图的层次结构里找到能响应消息的那个视图.(2)然后在找到的视图里处理消息 ...
 - iOS 触摸事件与UIResponder(内容根据iOS编程编写)
		
触摸事件 因为 UIView 是 UIResponder 的子类,所以覆盖以下四个方法就可以处理四种不同的触摸事件: 1. 一根手指或多根手指触摸屏幕 - (void)touchesBegan:(N ...
 
随机推荐
- Chapter 2 Open Book——14
			
I backpedaled. "They seemed nice enough to me. I just noticed they keptto themselves. 我改口说道,他们看 ...
 - JavaScript在智能手机上的应用-使用手机GPS定位用户所在城市
			
---------------------------- <script type="text/javascript" language="javascript&q ...
 - CSS3秘笈:第三章
			
1.标签选择器:控制整体. 使用标签选择器,只需要输入标签的名称即可. 2.类选择器:精确控制. 假如你要指定一些相同的元素拥有不同的样式,可以给元素添加不同的类名,然后用类选择器来应用对应的样式. ...
 - Java静态语句块、语句块、构造方法执行顺序
			
package com.imooc.practice; class Parent{ public Parent(){ System.out.println("Parent构造方法执行!&qu ...
 - httpclient调用方法
			
/** * GET请求 * * @param url * 请求url,参数拼在请求串中 */ public static String get(String url) ...
 - Long-Polling, Websockets, SSE(Server-Sent Event), WebRTC 之间的区别
			
在下面的示例中,客户端指的是浏览器,服务器指的是网站服务器主机. 为了更好的理解这些知识点,你应该简单了解典型的http网站是如何工作的. 普通的http: 客户端从服务器端请求网页 服务器作出相应的 ...
 - 编译安装SVN不结合apache
			
前言:独立SVN服务器不结合APACHE安装使用,连接独立SVN服务器也不是用HTTP协议.这是比较快捷的SVN服务器配置方法. 一.下载工具 1.subversion-1.6.17.tar.gz 2 ...
 - wl18xx module crash with "wlcore: ERROR ELP wakeup timeout!"
			
[ 111.322967] wlcore: ERROR ELP wakeup timeout![ 111.327636] ------------[ cut here ]------------[ 1 ...
 - sql语句按月份统计查询
			
select year(createdate) 年,month(createdate) 月,count(1) from public_cms_arcwhere (userid in (select i ...
 - IIS express 7.5 设置默认文档
			
在C:\Users\[电脑用户名]\Documents\IISExpress\config 下面有个applicationhost.config文件,打开文件找到<system.webServe ...