iOS触摸事件
搬运
原文链接
P1:什么是事件?
当我们在手机上点击屏幕或者滑动手机的时候,手机做出的一些响应,对应的应用程序里实现了某些代码的某些功能,完成这个过程就叫做一个事件。
iOS事件基本分为三大类:
(1)触摸事件:点击放大,按钮响应;
(2)加速计事件:微信摇一摇,通过对手机的运动操作,实现某些功能;
(3)远程控制事件:watch控制手机拍照。
在iOS中并不是所有的对象都能处理事件,只有继承于UIResponder的“响应者对象”才具有接收并响应事件的功能。
UIResponder提供了以下的处理事件的方法:
(1)触摸事件(touches存放的是UITouch对象)
- (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;
//当触摸事件被电话呼入等事件取消时
四个触摸事件处理方法中,都有NSSet* touches和UIEvent*event两个参数
1>一次完整的触摸过程中,只会产生一个事件对象,4个触摸方法都是同一个event参数;
2>如果两根手指同时触摸一个View,那么view只会调用一次touchesBegan: withEvent:方法,touches参数中装着两个UITouch对象;
3>如果这两根手指一前一后分开触摸同一view,那么view会分别调用2次touchesBegan:withEvent:方法,且每次调用了touches参数中仅装着一个UITouch对象;
4>根据touches中UITouch的个数,通过switch方法可以判断是单点触摸还是多点触摸.
5>上面的四个事件方法,在开发过程中并不要求全部实现,可以根据需要重写特定的方法。因为UIEvent包含了整个触摸过程中所有的触摸对象,因此可以调用allTouches方法获取该事件内所有的触摸对象,也可以调用touchesForVIew:或者touchesForWindows:取出特定视图或者窗口上的触摸对象。在这几个事件中,都可以拿到触摸对象,然后根据其位置,状态,时间属性做逻辑处理。
(2)加速计事件
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
- (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event
(3)远程控制事件
-(void)remoteControlReceivedWithEvent:(UIEvent*)event;
P2:触摸事件之UITouch
(1)UITouch触摸对象
当用户用一根触摸屏幕时,会创建一个与手指相关联的UITouch对象 一个手指对应着一个UITouch对象。
UITouch存着手指相关的信息,比如触摸的位置,时间,阶段。
当手指移动时,系统会更新同一个UItouch对象,使之能够一直保存该手指所在的触摸位置。
当手指离开屏幕时,系统会销毁相应的UITouch对象。
iPhone开发中,要避免双击事件!
(2)UITouch常用属性和方法
1>常用属性
@property(nonatomic,readonly) NSTimeInterval timestamp;
//记录触摸事件产生或变化的事件,单位秒
@property(nonatomic,readonly) UITouchPhase phase;
//当前触摸事件所处的状态
@property(nonatomic,readonly) NSUInteger tapCount;
//短时间内按屏幕的次数,可以根据次数判断触摸是单击还是双击或者更多
@property(nullable,nonatomic,readonly,strong) UIWindow *window;
//触摸产生所处的窗口
@property(nullable,nonatomic,readonly,strong) UIView *view;
//触摸产生所处的视图
@property(nullable,nonatomic,readonly,copy) NSArray <UIGestureRecognizer *> *gestureRecognizers
phase是UITouchPhase类型的,它是一个枚举:
· UITouchPhaseBegan
· UITouchPhaseMoved
· UITouchPhaseStationary
· UITouchPhaseEnded
window、view、tapCount、phase都是UITouch类中常用属性,也就是说建立一个UITouch对象就可以用这些属性来看触摸事件包含的信息
2>常用方法
- (CGPoint)locationInView:(nullable UIView *)view;
参数: 返回值
1.表示触摸在view上的位置(位置是以View本身的坐标系为参考,就是以view的左上角为原点)
2.加入view传入的为nil,那么返回的是以UIWindow为坐标系的触摸位置.
- (CGPoint)previousLocationInView:(nullable UIView *)view;
参数: 返回值
前一个触摸点的位置
- (CGPoint)preciseLocationInView:(nullable UIView *)view NS_AVAILABLE_IOS(9_1);
- (CGPoint)precisePreviousLocationInView:(nullable UIView *)view NS_AVAILABLE_IOS(9_1);
参数:在ios9.1之后引入的,因为ios9.1之后引入了苹果笔.(不常用)
3>示例:
// eg1:触摸时,图片移动
- (void)viewDidLoad
{ [super viewDidLoad];
UIImageView *image = [[UIImageView alloc] initWithFrame:CGRectMake(20.0, 50.0, 45.0, 45.0)];
image.image = [UIImage imageNamed:@"baby.png"];
image.tag = 100;
[self.view addSubview:image];
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject]; //取出一个触摸点
//UITouch类中有已经建立了很多UITouch对象,只需要从中取出来即可,不用重复创建,所以此处不用 alloc,用一个UITouch的对象touch接收就行了。anyObject是从类里面任意取出来一个。
UIImageView *view1 = (UIImageView*)[self.view viewWithTag:100];
CGPoint point = [touch locationInView:self.view];
CGRect frame = view1.frame;
frame.origin = point;
view1.frame = frame;
}
// eg2:判断是双击还是单击
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
if(touch.tapCount == 2) {
self.view.backgroundColor = [UIColor redColor];
}
}
// 检测tapCount可以放在touchesBegan也可以touchesEnded,不过一般后者跟准确,因为touchesEnded可以保证所有的手指都已经离开屏幕,这样就不会把轻击动作和按下拖动等动作混淆。
// 轻击操作很容易引起歧义,比如当用户点了一次之后,并不知道用户是想单击还是只是双击的一部分,或者点了两次之后并不知道用户是想双击还是继续点击。为了解决这个问题,一般可以使用“延迟调用”函数。
// eg3: 在第一次轻击之后,没有直接更改视图的背景属性,而是通过performSelector:withObject:afterDelay:方法设置2秒中后更改。
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
if(touch.tapCount == 1) {
[self performSelector:@selector(setBackground:) withObject:[UIColor blueColor] afterDelay:2];
self.view.backgroundColor = [UIColor redColor];
}
} <span style="font-family: 'Helvetica Neue', Helvetica, Tahoma, Arial, STXihei, 'Microsoft YaHei', 微软雅黑, sans-serif; background-color: rgb(255, 255, 255);"> </span>
// eg4: 上面代码表示在第一次轻击之后,没有直接更改视图的背景属性,而是通过performSelector:withObject:afterDelay:方法设置2秒中后更改。
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
if(touch.tapCount == 2) {
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(setBackground:) object:[UIColor redColor]];
self.view.backgroundColor = [UIColor redColor];
}
}
双击就是两次单击的组合,因此在第一次点击的时候,设置背景色的方法已经启动,在检测到双击的时候先要把先前对应的方法取消掉,可以通过调用 NSObject类的cancelPreviousPerformRequestWithTarget:selector:object方法取消指定对象的方法调用,然后调用双击对应的方法设置背景色为红色。
P3:触摸事件之UIEvent
(1)触摸事件对象
UIEvent:事件对象,记录事件的时刻和类型. 没产生一个事件,就会产生一个UIEvent对象
(2)常见属性
1>事件类型
@property(nonatomic,readonly) UIEventType type NS_AVAILABLE_IOS(3_0);
@property(nonatomic,readonly) UIEventSubtype subtype NS_AVAILABLE_IOS(3_0);
2>事件产生的事时间
@property(nonatomic,readonly) NSTimeInterval timestamp;
P4:响应者链
(1)响应者对象(Response object)
响应者对象就是可以响应事件并对事件作出处理。在iOS中,存在UIResponder类,它定义了响应者对象的所有方法。UIApplication、UIView等类都继承了 UIResponder类,UIWindow和UIKit中的控件因为继承了UIView,所以也间接继承了UIResponder类,这些类的实例都可以当作响应者。
(2)第一响应者(First responder)
当前接受触摸的响应者对象被称为第一响应者,即表示当前该对象正在与用户交互,它是响应者链的开端。
(3)响应者链(Responder chain)
响应者链表示一系列的响应者对象。事件被交由第一响应者对象处理,如果第一响应者不处理,事件被沿着响应者链向上传递,交给下一个响应者(next responder)。一般来说,第一响应者是个视图对象或者其子类对象,当其被触摸后事件被交由它处理,如果它不处理,事件就会被传递给它的视图控制器对象(如果存在),然后是它的父视图(superview)对象(如果存在),以此类推,直到顶层视图。接下来会沿着顶层视图(top view)到窗口(UIWindow对象)再到程序(UIApplication对象)。如果整个过程都没有响应这个事件,该事件就被丢弃。一般情况下,在响应者链中只要由对象处理事件,事件就停止传递。但有时候可以在视图的响应方法中根据一些条件判断来决定是否需要继续传递事件。
(4) 管理事件分发
视图对触摸事件是否需要作处回应的方法:
1>userInteractionEnabled属性。默认状态为YES,如果设置为NO,可以阻止视图接收和分发触摸事件.
2>设置视图被隐藏Hidden. Hidden属性为YES;
3>设置透明度(alpha值为(0~0.1之间)也不会收事件。
不过以上对视图有效,如果想要整个程序都不响应事件,可以调用UIApplication的beginIngnoringInteractionEvents方法来完全停止事件接收和分发。通过endIngnoringInteractionEvents方法来恢复让程序接收和分发事件。
如果要让视图接收多点触摸,需要设置它的multipleTouchEnabled属性为YES,默认状态下这个属性值为NO,即视图默认不接收多点触摸。
UIImageView的userInteractionEnabled默认是NO.即UIImageView以及它的子控件在默认状态下是不能接受触摸事件的
P5: 关于手势与触摸事件的区别
手势继承自UIGestureRecognizer–>NSObject。 而UITouch直接继承自NSObject。
事实上,手势和UITouch本身关系不大,在IOS3.2之前根本没有手势,以前的所有操作都依赖于UITouch,这也是为什么UITouch定义在UIRsponder中的原因之一。
此外UIResponder及其子类都自带监听UITouch的方法。