⼀、事件
iOS 有三种事件类型:
触控事件(UIEventTypeTouches):单点、多点触控以及各种手势操作;
传感器事件(UIEventTypeMotion):重力、加速度传感器等;
远程控制事件(UIEventTypeRemoteControl):远程遥控iOS设备多媒体播放等;
⼆、触摸的基本概念
UIView⽀持触摸事件(因为继承于UIResponder),⽽且⽀持多点触摸。需要定义UIView⼦类,实现触摸相关的⽅法。
事件处理方法
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
从方法名字可以知道,他们分别对应了屏幕事件的开始、移动、结束和取消几个阶段,前三个阶段理解都没问题,最后一个取消事件的触发时机是在诸如突然来电话或是系统杀进程时调用。这些方法的第一个参数定义了UITouch对象的一个集合(NSSet),它的数量表示了这次事件是几个手指的操作。第二个参数是当前的UIEvent对象。下图展示了一个UIEvent对象与多个UITouch对象之间的关系。
//1.开始触摸
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// NSLog(@"++++touches:%@", touches);
NSLog(@"%s", __FUNCTION__);
self.backgroundColor = [UIColor randomColor];
_startPoint = [[touches anyObject] locationInView:self];
}
//2.移动触摸 拖动改变view的位置
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(@"%s", __FUNCTION__);
CGPoint currentPoint = [[touches anyObject] locationInView:self];
CGFloat tx = currentPoint.x - _startPoint.x;
CGFloat ty = currentPoint.y - _startPoint.y;
// //第一种平移方式:改变center
// CGPoint tempCenter = self.center;
// tempCenter.x += tx;
// tempCenter.y += ty;
// self.center = tempCenter;
//第二种平移方式:使用transform
self.transform = CGAffineTransformTranslate(self.transform, tx, ty);
#pragma mark -- 形变
// self.transform = CGAffineTransformMakeScale(2, 2);
}
#define fWidth self.window.frame.size.width
#define fHeight self.window.frame.size.height
//3.结束触摸
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
// NSLog(@"%.2f, %.2f", self.window.frame.size.height,self.window.frame.size.width);
//
// CGFloat x = arc4random()%(int)(fWidth-self.frame.size.width);
// CGFloat y = arc4random()%(int)(fHeight-self.frame.size.height);
//
// self.frame = CGRectMake(x, y, self.frame.size.width, self.frame.size.height);
//
//// self.center = CGPointMake(x+self.frame.size.width/2, y+self.frame.size.height/2);
// NSLog(@"%.1f, %.1f", x, y);
}
三、响应者链
响应者
iOS中所有能响应并处理事件(触摸、晃动、远程事件)的对象都是响应者。响应者是构成响应链和事件传递链的节点。
系统定义了⼀个抽象的⽗类UIResponder来表⽰响应者。其⼦类都是响应者。
举个例子,当手指去触摸屏幕上 UIView 的实例对象 aView。触摸的动作会产生一个触摸事件 UIEventTypeTouches,而接收触摸事件的对象 aView,就是一个响应者对象。
一个事件有可能被多个 responder 接收,第一个接收事件的 responder 就叫 first responder。
在 iOS 中,几乎所有类都是 responder,比如 UIWindow、UIView、UIControl、UIControllers 等,而这些类都有一个共同的父类 —— UIResponder。UIResponder 声明了用于处理事件的接口,并定义了默认的行为。
Responder 继承链如下:
响应链
当一个事件发生时,如果 first responder 不处理,事件就会继续往下传递,被下个 responder 接收,如果下个 responder 也不处理,又会被下下个 responder 接收…… 直到一个 responder 处理了事件或者没有 responder 了。这些 responder 按照传递次序连接起来的链条就构成了响应者链。
检测到响应者后,实现touchesBegan:withEvent:等⽅法,即处理事件。如果响应者没有处理事件,事件会向下传递。如果没有响应者处理, 则丢弃触摸事件。
响应链:由离用户最近的view向系统传递。
initial view –> super view –> …..–> view controller –> window –> AppDelegate –> Application
Hit-Testing 链(检测链,与响应链顺序相反):由系统向离用户最近的view传递。
UIKit –> active app's event queue –> window –> root view –>……–>lowest view
阻断响应者链
视图类的属性 : userInteractionEnabled。关闭后能阻断查询过程。
通过以前的使用发现,label并不能响应事件,其原因是label的userInteractionEnabled属性默认为NO。下面通过向label中添加button控件并设置label的userInteractionEnabled属性使其可以响应事件。
UILabel * label = [[UILabel alloc] initWithFrame:CGRectMake(100, 300, 200, 200)];
[self.view addSubview:label];
[label release];
UIButton * button = [UIButton buttonWithType:UIButtonTypeCustom];
button.backgroundColor = [UIColor blackColor];
button.frame = CGRectMake(0, 0, 20, 30);
[button addTarget:self action:@selector(haha) forControlEvents:UIControlEventTouchUpInside];
[label addSubview:button];
label.userInteractionEnabled = YES;
可以使用同样的方法设置button.userInteractionEnabled = NO阻断响应链。在此不再演示
http://ryantang.me/blog/2013/12/07/ios-event-dispatch-1/