ios touchescancelledd究竟是个什么动作

13.1 事件概述
13.2 触摸事件
13.1 事件概述
事件是当用户手指触击屏幕及在屏幕上移动时,系统不断发送给应用程序的对象。
系统将事件按照特定的路径传递给可以对其进行处理的对象。
在iOS中,一个UITouch对象表示一个触摸,一个UIEvent对象表示一个事件。事件对象中包含与当前多点触摸序列相对应的所有触摸对象,还可以提供与特定视图或窗口相关联的触摸对象。
响应者对象
响应者对象是可以响应事件并对其进行处理的对象。
UIResponder是所有响应者对象的基类,它不仅为事件处理,而且也为常见的响应者行为定义编程接口。
UIApplication、UIView、和所有从UIView派生出来的UIKit类(包括UIWindow)都直接或间接地继承自UIResponder类。
第一响应者是应用程序中当前负责接收触摸事件的响应者对象(通常是一个UIView对象)。UIWindow对象以消息的形式将事件发送给第一响应者,使其有机会首先处理事件。如果第一响应者没有进行处理,系统就将事件(通过消息)传递给响应者链中的下一个响应者,看看它是否可以进行处理。
响应链是一个响应者对象的连接序列,事件或动作消息(或菜单编辑消息)依次传递。它允许响应者对象把事件处理的职责转交给其它更高层的对象。应用程序通过向上传递一个事件来查找合适的处理对象。因为点击检测视图也是一个响应者对象,应用程序在处理触摸事件时也可以利用响应链。响应链由一系列的下一个响应者组成。
响应者链处理原则
1. 点击检测视图或者第一响应者传递事件或动作消息给它的视图控制器(如果它有的话);如果没有一个视图控制器,就传递给它的父视图。
2. 如果一个视图或者它的视图控制器不能处理这个事件或动作消息,它将传递给该视图的父视图。
3. 在这个视图层次中的每个后续的父视图遵循上述的模式,如果它不能处理这个事件或动作消息的话。
4. 最顶层的视图如果不能处理这个事件或动作消息,就传递给UIWindow对象来处理。
5. 如果UIWindow 对象不能处理,就传给单件应用程序对象UIApplication。
如果应用程序对象也不能处理这个事件或动作消息,将抛弃它。
13.2 触摸事件
触摸信息有时间和空间两方面,时间方面的信息称为阶段(phrase),表示触摸是否刚刚开始、是否正在移动或处于静止状态,以及何时结束&也就是手指何时从屏幕抬起。触摸信息还包括当前在视图或窗口中的位置信息,以及之前的位置信息(如果有的话)。当一个手指接触屏幕时,触摸就和某个窗口或视图关联在一起,这个关联在事件的整个生命周期都会得到维护。
触摸事件的阶段
事件处理方法
在给定的触摸阶段中,如果发生新的触摸动作或已有的触摸动作发生变化,应用程序就会发送这些消息:
当一个或多个手指触碰屏幕时,发送touchesBegan:withEvent:消息。
当一个或多个手指在屏幕上移动时,发送touchesMoved:withEvent:消息。
当一个或多个手指离开屏幕时,发送touchesEnded:withEvent:消息。
当触摸序列被诸如电话呼入这样的系统事件所取消时,发送touchesCancelled:withEvent:消息。
触摸事件实例 EventInfo
#import &UIKit/UIKit.h&
@interface TouchView : UIView {
- (void)logTouchInfo:(UITouch *)
@implementation TouchView
- (void)logTouchInfo:(UITouch *)touch {
CGPoint locInSelf = [touch locationInView:self];
CGPoint locInWin = [touch locationInView:nil];
touch.locationInView = {%2.3f, %2.3f}", locInSelf.x, locInSelf.y);
touch.locationInWin = {%2.3f, %2.3f}", locInWin.x, locInWin.y);
touch.phase = %d", touch.phase);
touch.tapCount = %d", touch.tapCount);
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(@"touchesBegan - touch count = %d", [touches count]);
for(UITouch *touch in event.allTouches) {
[self logTouchInfo:touch];
touch.phase,触摸事件的阶段。
touch.tapCount,触摸事件的轻碰次数,可以判断双击事件。
UIEvent 的allTouches方法,可以获得触摸点的集合,可以判断多点触摸事件。
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(@"touchesMoved - touch count = %d", [touches count]);
for(UITouch *touch in event.allTouches) {
[self logTouchInfo:touch];
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(@"touchesEnded - touch count = %d", [touches count]);
for(UITouch *touch in event.allTouches) {
[self logTouchInfo:touch];
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(@"touchesCancelled - touch count = %d", [touches count]);
for(UITouch *touch in event.allTouches) {
[self logTouchInfo:touch];
手势在iPhone中很重要,手势就是手触摸屏幕的方式。
多点触摸(合拢和展开)
单碰击和双碰击实例:MultiTap
单碰击为红色,双碰击为蓝色
#import &UIKit/UIKit.h&
@interface MultiTapView : UIView {
#import "MultiTapView.h"
@implementation MultiTapView
- (void)turnBlue {
self.backgroundColor = [UIColor blueColor];
- (void)turnRed {
self.backgroundColor = [UIColor redColor];
//START:code.MultiTapView.touchesBegan:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
if(touch.tapCount == 2) {
[[self class] cancelPreviousPerformRequestsWithTarget:self
selector:@selector(turnRed)
object:nil];
//END:code.MultiTapView.touchesBegan:
//START:code.MultiTapView.touchesEnded:
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
if(touch.tapCount == 1) {
[self performSelector:@selector(turnRed) withObject:nil afterDelay:0.10f];
if(touch.tapCount == 2) {
[self turnBlue];
//END:code.MultiTapView.touchesEnded:
[self performSelector:@selector(turnRed) withObject:nil afterDelay:0.10f]; 是在0.1秒后调用turnRed方法。
[[self class]cancelPreviousPerformRequestsWithTarget:self selector:@selector(turnRed) object:nil]; 是取消调用方法turnRed。
多点触摸(合拢和展开)PinchZoom
PinchZoomView .h文件
#import &UIKit/UIKit.h&
#import &QuartzCore/QuartzCore.h&
@interface PinchZoomView : UIView {
CALayer *robotL
CGFloat previousD
CGFloat zoomF
BOOL pinchZ
@property(nonatomic, retain) CALayer *robotL
#import "PinchZoomView.h"
@implementation PinchZoomView
@synthesize robotL
- (void)awakeFromNib {
self.robotLayer = [CALayer layer];
UIImage *image = [UIImage imageNamed:@"Robot.png"];
self.robotLayer.contents = (id)[image CGImage];
self.robotLayer.bounds = CGRectMake(0.0f, 0.0f, image.size.width, image.size.height);
self.robotLayer.position = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
[self.layer addSublayer:self.robotLayer];
pinchZoom = NO;
previousDistance = 0.0f;
zoomFactor = 1.0f;
awakeFromNib当nib文件被加载的时候,加载器会发送一个awakeFromNib的消息到nib文件中的每个对象,每个对象都可以定义自己的 awakeFromNib方法来响应这个消息,执行一些必要的操作。也就是说通过nib文件创建view对象是执行awakeFromNib 。
robotLayer是 CALayer 对象,本例子中我们把图片对象添加到robotLayer对象中。使用 CALayer需要引入 &QuartzCore/QuartzCore.h&头文件和添加QuartzCore.framework框架。
//START:code.PinchZoomView.touchesBegan
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
if(event.allTouches.count == 2) {
pinchZoom = YES;
NSArray *touches = [event.allTouches allObjects];
CGPoint pointOne = [[touches objectAtIndex:0] locationInView:self];
CGPoint pointTwo = [[touches objectAtIndex:1] locationInView:self];
previousDistance = sqrt(pow(pointOne.x - pointTwo.x, 2.0f) +
pow(pointOne.y - pointTwo.y, 2.0f));
pinchZoom = NO;
//END:code.PinchZoomView.touchesBegan
previousDistance 是获得两个点的距离。
pow是平方函数。
sqrt是开平方根函数。
//START:code.PinchZoomView.touchesMoved
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
if(YES == pinchZoom && event.allTouches.count == 2) {
NSArray *touches = [event.allTouches allObjects];
CGPoint pointOne = [[touches objectAtIndex:0] locationInView:self];
CGPoint pointTwo = [[touches objectAtIndex:1] locationInView:self];
CGFloat distance = sqrt(pow(pointOne.x - pointTwo.x, 2.0f) +
pow(pointOne.y - pointTwo.y, 2.0f));
zoomFactor += (distance - previousDistance) / previousD
zoomFactor = fabs(zoomFactor);
previousDistance =
self.robotLayer.transform = CATransform3DMakeScale(zoomFactor, zoomFactor, 1.0f);
//END:code.PinchZoomView.touchesMoved
//START:code.PinchZoomView.touchesEnded
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if(event.allTouches.count != 2) {
pinchZoom = NO;
previousDistance = 0.0f;
if(event.allTouches.count == 1) {
NSArray *touches = [event.allTouches allObjects];
UITouch *touch = [touches objectAtIndex:0];
UITouch *touch = [touches anyObject];
NSInteger tapCount = [touch tapCount];
if (tapCount == 2) {
zoomFactor += 0.4;
self.robotLayer.transform = CATransform3DMakeScale(zoomFactor, zoomFactor, 1.0f);
} else if (tapCount == 3) {
zoomFactor += 0.6;
self.robotLayer.transform = CATransform3DMakeScale(zoomFactor, zoomFactor, 1.0f);
} else if (tapCount == 4) {
zoomFactor += 0.8;
self.robotLayer.transform = CATransform3DMakeScale(zoomFactor, zoomFactor, 1.0f);
//END:code.PinchZoomView.touchesEnded
- (void)dealloc {
self.robotLayer =
[robotLayer release];
[super dealloc];
注: 1 本教程是基于关东升老师的教程
2 基于黑苹果10.6.8和xcode4.2
3 本人初学,有什么不对的望指教
4 教程会随着本人学习,持续更新
5 教程是本人从word笔记中拷贝出来了,所以格式请见谅
阅读(...) 评论()UIScrollView有一个BOOL类型的tracking属性,用来返回用户是否已经触及内容并打算开始滚动,我们从这个属性开始探究UIScrollView的工作原理:
当手指触摸到UIScrollView内容的一瞬间,会产生下面的动作:
拦截触摸事件
tracking属性变为YES
一个内置的计时器开始生效,用来监控在极短的事件间隔内是否发生了手指移动
case1:当检测到时间间隔内手指发生了移动,UIScrollView自己触发滚动,tracking属性变为NO,手指触摸下即使有(可以响应触摸事件的)内部控件也不会再响应触摸事件。
case2:当检测到时间间隔内手指没有移动,tracking属性保持YES,手指触摸下如果有(可以响应触摸事件的)内部控件,则将触摸事件传递给控件进行处理。
有很多新闻类的App顶部都有一个滑动菜单栏,主要模型可能是由一个UIScrollView包含多个UIButton控件组成;当你操作的时候,手指如果是很迅速的在上面划过,会发现即使手指触摸的地方有UIButton,但是并没有触发该UIButton的任何触摸事件,这就是上面提到的case1;当你手指是缓慢划过或根本就没动,才会触发UIButton的触摸事件,这是case2的情况。
上面的工作原理其实有一个属性开关来控制:delaysContentTouches。默认值为YES;如果设置为NO,则无论手指移动的多么快,始终都会将触摸事件传递给内部控件;设置为NO可能会影响到UIScrollView的滚动功能。
再看另一个BOOL类型的属性canCencelContentTouches,从字面上理解是“可以取消内容触摸“,默认值为YES。文档里的解释是这样的:
A Boolean value that controls whether touches in the content view always lead to tracking.
If the value of this property is YES and a view in the content has begun tracking a finger touching it, and if the user drags the finger enough to initiate a scroll, the view receives a touchesCancelled:withEvent: message and the scroll view handles the touch
as a scroll. If the value of this property is NO, the scroll view does not scroll regardless of finger movement once the content view starts tracking.
翻译为中文大致如下:
这个BOOL类型的值控制content view里的触摸是否总能引发跟踪(tracking)
如果属性值为YES并且跟踪到手指正触摸到一个内容控件,这时如果用户拖动手指的距离足够产生滚动,那么内容控件将收到一个touchesCancelled:withEvent:消息,而scroll view将这次触摸作为滚动来处理。如果值为NO,一旦content view开始跟踪(tracking==YES),则无论手指是否移动,scrollView都不会滚动。
简单通俗点说,如果为YES,就会等待用户下一步动作,如果用户移动手指到一定距离,就会把这个操作作为滚动来处理并开始滚动,同时发送一个touchesCancelled:withEvent:消息给内容控件,由控件自行处理。如果为NO,就不会等待用户下一步动作,并始终不会触发scrollView的滚动了。
可以用一段代码来验证并观察一下,定义一个MyScrollView继承自UIScrollView,一个MyButton继承自UIButton,然后重写部分方法:
MyScrollView.m
- (BOOL)touchesShouldCancelInContentView:(UIView *)view
[super touchesShouldCancelInContentView:view];
NSLog(@&touchesShouldCancelInContentView&);
return YES;
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
[super touchesCancelled:touches withEvent:event];
NSLog(@&touchesCancelled&);
MyButton.m
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
[super touchesCancelled:touches withEvent:event];
NSLog(@&【Button's touch cancelled】&);
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
[super touchesBegan:touches withEvent:event];
NSLog(@&【Button's touch began】&);
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
[super touchesMoved:touches withEvent:event];
NSLog(@&【Button's touch moved】&);
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
[super touchesEnded:touches withEvent:event];
NSLog(@&【Button's touch ended】&);
其实就是在各个方法执行时打印出一个标记,当canCencelContentTouches值为YES时,用户触摸并移动手指再放开:
【Button's touch began】
【Button's touch moved】
【Button's touch moved】
touchesShouldCancelInContentView
【Button's touch cancelled】
当canCencelContentTouches值为NO时,用户触摸并移动手指再放开:
【Button's touch began】
【Button's touch moved】
【Button's touch moved】
【Button's touch ended】
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:3596次
排名:千里之外
原创:59篇
(2)(2)(2)(5)(5)(6)(5)(25)(11)(1)(2)objetive-c(65)
iOS(152)
UIScrollview(1)
UIScrollView有一个BOOL类型的tracking属性,用来返回用户是否已经触及内容并打算开始滚动,我们从这个属性开始探究UIScrollView的工作原理:
当手指触摸到UIScrollView内容的一瞬间,会产生下面的动作:
拦截触摸事件
tracking属性变为YES
一个内置的计时器开始生效,用来监控在极短的事件间隔内是否发生了手指移动
case1:当检测到时间间隔内手指发生了移动,UIScrollView自己触发滚动,tracking属性变为NO,手指触摸下即使有(可以响应触摸事件的)内部控件也不会再响应触摸事件。
case2:当检测到时间间隔内手指没有移动,tracking属性保持YES,手指触摸下如果有(可以响应触摸事件的)内部控件,则将触摸事件传递给控件进行处理。
有很多新闻类的App顶部都有一个滑动菜单栏,主要模型可能是由一个UIScrollView包含多个UIButton控件组成;当你操作的时候,手指如果是很迅速的在上面划过,会发现即使手指触摸的地方有UIButton,但是并没有触发该UIButton的任何触摸事件,这就是上面提到的case1;当你手指是缓慢划过或根本就没动,才会触发UIButton的触摸事件,这是case2的情况。
上面的工作原理其实有一个属性开关来控制:delaysContentTouches。默认值为YES;如果设置为NO,则无论手指移动的多么快,始终都会将触摸事件传递给内部控件;设置为NO可能会影响到UIScrollView的滚动功能。
再看另一个BOOL类型的属性canCencelContentTouches,从字面上理解是“可以取消内容触摸“,默认值为YES。文档里的解释是这样的:
A Boolean value that controls whether touches in the content view always lead to tracking.
If the value of this property is YES and a view in the content has begun tracking a finger touching it, and if the user drags the finger enough to initiate a scroll, the view receives a touchesCancelled:withEvent: message and the scroll view handles the touch
as a scroll. If the value of this property is NO, the scroll view does not scroll regardless of finger movement once the content view starts tracking.
翻译为中文大致如下:
这个BOOL类型的值控制content view里的触摸是否总能引发跟踪(tracking)
如果属性值为YES并且跟踪到手指正触摸到一个内容控件,这时如果用户拖动手指的距离足够产生滚动,那么内容控件将收到一个touchesCancelled:withEvent:消息,而scroll view将这次触摸作为滚动来处理。如果值为NO,一旦content view开始跟踪(tracking==YES),则无论手指是否移动,scrollView都不会滚动。
简单通俗点说,如果为YES,就会等待用户下一步动作,如果用户移动手指到一定距离,就会把这个操作作为滚动来处理并开始滚动,同时发送一个touchesCancelled:withEvent:消息给内容控件,由控件自行处理。如果为NO,就不会等待用户下一步动作,并始终不会触发scrollView的滚动了。
可以用一段代码来验证并观察一下,定义一个MyScrollView继承自UIScrollView,一个MyButton继承自UIButton,然后重写部分方法:
MyScrollView.m
- (BOOL)touchesShouldCancelInContentView:(UIView *)view
[super touchesShouldCancelInContentView:view];
NSLog(@&touchesShouldCancelInContentView&);
return YES;
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
[super touchesCancelled:touches withEvent:event];
NSLog(@&touchesCancelled&);
MyButton.m
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
[super touchesCancelled:touches withEvent:event];
NSLog(@&【Button's touch cancelled】&);
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
[super touchesBegan:touches withEvent:event];
NSLog(@&【Button's touch began】&);
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
[super touchesMoved:touches withEvent:event];
NSLog(@&【Button's touch moved】&);
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
[super touchesEnded:touches withEvent:event];
NSLog(@&【Button's touch ended】&);
其实就是在各个方法执行时打印出一个标记,当canCencelContentTouches值为YES时,用户触摸并移动手指再放开:
【Button's touch began】
【Button's touch moved】
【Button's touch moved】
touchesShouldCancelInContentView
【Button's touch cancelled】
当canCencelContentTouches值为NO时,用户触摸并移动手指再放开:
【Button's touch began】
【Button's touch moved】
【Button's touch moved】
【Button's touch ended】
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:83923次
积分:1818
积分:1818
排名:第15688名
原创:76篇
转载:159篇
(4)(1)(14)(5)(10)(4)(1)(6)(7)(7)(13)(20)(51)(39)(31)(13)(12)iOS事件机制(一) - 城之内 - 博客园
iOS事件机制(一)
DEC 7TH, 2013
运用的前提是掌握掌握的本质是理解
本篇内容将围绕iOS中事件及其传递机制进行学习和分析。在iOS中,事件分为三类:
触控事件(单点、多点触控以及各种手势操作)
传感器事件(重力、加速度传感器等)
远程控制事件(远程遥控iOS设备多媒体播放等)
这三类事件共同构成了iOS设备丰富的操作方式和使用体验,本次就首先来针对第一类事件:触控事件,进行学习和分析。
Gesture Recognizers
Gesture Recognizers是一类手势识别器对象,它可以附属在你指定的View上,并且为其设定指定的手势操作,例如是点击、滑动或者是拖拽。当触控事件 发生时,设置了Gesture Recognizers的View会先通过识别器去拦截触控事件,如果该触控事件是事先为View设定的触控监听事件,那么Gesture Recognizers将会发送动作消息给目标处理对象,目标处理对象则对这次触控事件进行处理,先看看如下流程图。
在iOS中,View就是我们在屏幕上看到的各种UI控件,当一个触控事件发生时,Gesture Recognizers会先获取到指定的事件,然后发送动作消息(action message)给目标对象(target),目标对象就是ViewController,在ViewController中通过事件方法完成对该事件的处理。Gesture Recognizers能设置诸如单击、滑动、拖拽等事件,通过Action-Target这种设计模式,好处是能动态为View添加各种事件监听,而不用去实现一个View的子类去完成这些功能。
以上过程就是我们在开发中在方法中常见的设置action和设置target,例如为UIButton设置监听事件等。
常用手势识别类
在UIKit框架中,系统为我们事先定义好了一些常用的手势识别器,包括点击、双指缩放、拖拽、滑动、旋转以及长按。通过这些手势识别器我们可以构造丰富的操作方式。
在上表中可以看到,UIKit框架中已经提供了诸如UITapGestureRecognizer在内的六种手势识别器,如果你需要实现自定义的手势识别器,也可以通过继承UIGestureRecognizer类并重写其中的方法来完成,这里我们就不详细讨论了。
每一个Gesture Recognizer关联一个View,但是一个View可以关联多个Gesture Recognizer,因为一个View可能还能响应多种触控操作方式。当一个触控事件发生时,Gesture Recognizer接收一个动作消息要先于View本身,结果就是Gesture Recognizer作为View处理触控事件的代表,或者叫代理。当Gesture Recognizer接收到指定的事件时,它就会发送一条动作消息(action message)给ViewController并处理。
连续和不连续动作
触控动作同时分为连续动作(continuous)和不连续动作(discrete),连续动作例如滑动和拖拽,它会持续一小段时间,而不连续动作例如单击,它瞬间就会完成,在这两类事件的处理上又稍有不同。对于不连续动作,Gesture Recognizer只会给ViewContoller发送一个单一的动作消息(action message),而对于连续动作,Gesture Recognizer会发送多条动作消息给ViewController,直到所有的事件都结束。
为一个View添加GestureRecognizer有两种方式,一种是通过InterfaceBuilder实现,另一种就是通过代码实现,我们看看通过代码来如何实现。
MyViewContoller.m
- (void)viewDidLoad {
[super viewDidLoad];
// 创建并初始化手势对象
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc]
initWithTarget:self action:@selector(respondToTapGesture:)];
// 指定操作为单击一次
tapRecognizer.numberOfTapsRequired = 1;
// 为当前View添加GestureRecognizer
[self.view addGestureRecognizer:tapRecognizer];
通过上述代码,我们实现了为当前MyViewController的View添加一个单击事件,首先构造了UITapGestureRecognizer对象,指定了target为当前ViewController本身,action就是后面自己实现的处理方法,这里就呼应了前文提到的Action-Target模式。
在事件处理过程中,这两种方式所处的状态又各有不同,首先,所有的触控事件最开始都是处于可用状态(Possible),对应UIKit里面的UIGestureRecognizerStatePossible类,如果是不连续动作事件,则状态只会从Possible转变为已识别状态(Recognized,UIGestureRecognizerStateRecognized)或者是失败状态(Failed,UIGestureRecognizerStateFailed)。例如一次成功的单击动作,就对应了Possible-Recognized这个过程。
如果是连续动作事件,如果事件没有失败并且连续动作的第一个动作被成功识别(Recognized),则从Possible状态转移到Began(UIGestureRecognizerStateBegan)状态,这里表示连续动作的开始,接着会转变为Changed(UIGestureRecognizerStateChanged)状态,在这个状态下会不断循环的处理连续动作,直到动作执行完成变转变为Recognized已识别状态,最终该动作会处于完成状态(UIGestureRecognizerStateEnded),另外,连续动作事件的处理状态会从Changed状态转变为Canceled(UIGestureRecognizerStateCancelled)状态,原因是识别器认为当前的动作已经不匹配当初对事件的设定了。每个动作状态的变化,Gesture Recognizer都会发送消息(action message)给Target,也就是ViewController,它可以根据这些动作消息进行相应的处理。例如一次成功的滑动手势动作就包括按下、移动、抬起的过程,分别对应了Possible-Began-Changed-Recognized这个过程。
UITouch & UIEvent
在屏幕上的每一次动作事件都是一次Touch,在iOS中用UITouch对象表示每一次的触控,多个Touch组成一次Event,用UIEvent来表示一次事件对象。
在上述过程中,完成了一次双指缩放的事件动作,每一次手指状态的变化都对应事件动作处理过程中得一个阶段。通过Began-Moved-Ended这几个阶段的动作(Touch)共同构成了一次事件(Event)。在事件响应对象UIResponder中有对应的方法来分别处理这几个阶段的事件。
touchesBegan:withEvent:
touchesMoved:withEvent:
touchesEnded:withEvent:
touchesCancelled:withEvent:
后面的参数分别对应UITouchPhaseBegan、UITouchPhaseMoved、UITouchPhaseEnded、UITouchPhaseCancelled这几个类。用来表示不同阶段的状态。
如上图,iOS中事件传递首先从App(UIApplication)开始,接着传递到Window(UIWindow),在接着往下传递到View之前,Window会将事件交给GestureRecognizer,如果在此期间,GestureRecognizer识别了传递过来的事件,则该事件将不会继续传递到View去,而是像我们之前说的那样交给Target(ViewController)进行处理。
响应者链(Responder Chain)
通常,一个iOS应用中,在一块屏幕上通常有很多的UI控件,也就是有很多的View,那么当一个事件发生时,如何来确定是哪个View响应了这个事件呢,接下来我们就一起来看看。
寻找hit-test view
什么是hit-test view呢?简单来说就是你触发事件所在的那个View,寻找hit-test view的过程就叫做Hit-Testing。那么,系统是如何来执行Hit-Testing呢,首先假设现在有如下这么一个UI布局,一种有ABCDE五个View。
假设一个单击事件发生在了View D里面,系统首先会从最顶层的View A开始寻找,发现事件是在View A或者其子类里面,那么接着从B和C找,发现事件是在C或者其子类里面,那么接着到C里面找,这时发现事件是在D里面,并且D已经没有子类了,那么hit-test view就是View D啦。
响应者对象(Responsder Object)
响应者对象是能够响应并且处理事件的对象,UIResponder是所有响应者对象的父类,包括UIApplication、UIView和UIViewController都是UIResponder的子类。也就意味着所有的View和ViewController都是响应者对象。
第一响应者(First Responder)
第一响应者是第一个接收事件的View对象,我们在Xcode的Interface Builder画视图时,可以看到视图结构中就有First Responder。
这里的First Responder就是UIApplication了。另外,我们可以控制一个View让其成为First Responder,通过实现 canBecomeFirstResponder方法并返回YES可以使当前View成为第一响应者,或者调用View的becomeFirstResponder方法也可以,例如当UITextField调用该方法时会弹出键盘进行输入,此时输入框控件就是第一响应者。
事件传递机制
如上所说,,如果hit-test view不能处理当前事件,那么事件将会沿着响应者链(Responder Chain)进行传递,知道遇到能处理该事件的响应者(Responsder Object)。通过下图,我们来看看两种不同情况下得事件传递机制。
左边的情况,接收事件的initial view如果不能处理该事件并且她不是顶层的View,则事件会往它的父View进行传递。initial view的父View获取事件后如果仍不能处理,则继续往上传递,循环这个过程。如果顶层的View还是不能处理这个事件的话,则会将事件传递给它们的ViewController,如果ViewController也不能处理,则传递给Window(UIWindow),此时Window不能处理的话就将事件传递给Application(UIApplication),最后如果连Application也不能处理,则废弃该事件。
右边图的流程唯一不同就在于,如果当前的ViewController是由层级关系的,那么当子ViewController不能处理事件时,它会将事件继续往上传递,直到传递到其Root ViewController,后面的流程就跟之前分析的一样了。
这就是事件响应者链的传递机制,通过这些内容,我们可以更深入的了解事件在iOS中得传递机制,对我们在实际开发中更好的理解事件操作的原理有很大的帮助,也对我们实现复杂布局进行事件处理时增添了多一份的理解。
通过前面的内容分析,我们已经学习并了解了如下内容:
Gesture Recognizers,是用来控制手势识别的过程和方法,并且其通过Action-Target模式与ViewController的通信的方式。连续和不连续手势动作情况下GestureRecognizer的状态转变。
UITouch和UIEvent对象,他们都是UIKit中来进行事件处理的对象,多个UITouch对象构成一个UIEvent对象,重写相应的方法可以控制和处理事件各个阶段的操作。
系寻找hit-test view的方式、事件传递机、制响应者链
后记:本篇是iOS事件传递机制的上篇,下篇将继续讨论多点触控事件和手势操作的内容!

我要回帖

更多关于 究竟什么是太岁 的文章

 

随机推荐