文章目录
  1. 1. 文档更新说明
  2. 2. 前言
  3. 3. 触摸事件的传递
  4. 4. 响应者链
  5. 5. userInteractionEnabled属性的作用
  6. 6. 总结
  7. 7. 温馨提示

文档更新说明

  • 最后更新 2016年08月29日
  • 首次更新 2016年08月29日

前言

  先介绍一篇文章,讲解了触摸事件和响应者链的基本知识,省得我再说一遍,本文主要是结合实际例子对触摸事件和响应者链做一个示范(本例下载),方便理解iOS中的触摸事件和手势处理.

触摸事件的传递

  当用户触摸屏幕的时候,硬件就可以捕获到触摸的位置以及接下来触摸的详细动作,这里为了简化问题,我们只对用户点击屏幕时产生的touchesBegan事件做讨论.新建一个工程,在默认的ViewController上放蓝红两个视图(BlueView为RedView的父视图,ControllerView为控制器的View),如下图:

  接着,分别在ViewController,ControllerView,BlueView,RedView中重写touchesBegean方法.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//ViewController.m
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
NSLog(@"ViewController");
}

//ControllerView
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
NSLog(@"ControllerView");
[super touchesBegan:touches withEvent:event];
}

//BlueView.m
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
NSLog(@"Blue View");
[super touchesBegan:touches withEvent:event];
}

//RedView.m
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
NSLog(@"Red View");
[super touchesBegan:touches withEvent:event];
}

  运行程序,点击红色视图,控制台输出

2016-08-29 10:12:52.514 TestTouchEvent[3702:311422] Red View
2016-08-29 10:12:52.514 TestTouchEvent[3702:311422] Blue View
2016-08-29 10:12:52.514 TestTouchEvent[3702:311422] ControllerView
2016-08-29 10:12:52.514 TestTouchEvent[3702:311422] ViewController

恩,这样我们心里有个数了.硬件检测到用户触摸屏幕,就会把事件传给UIApplication,接着就在APP中寻找一个合适的视图,最后调用该视图的touchesBegan方法来处理事件.

响应者链

  修改一下上一小节的代码,将RedView中的部分代码注释掉,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//ViewController.m
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
NSLog(@"ViewController");
}

//ControllerView
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
NSLog(@"ControllerView");
[super touchesBegan:touches withEvent:event];
}

//BlueView.m
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
NSLog(@"Blue View");
[super touchesBegan:touches withEvent:event];
}

//RedView.m
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
NSLog(@"Red View");
//[super touchesBegan:touches withEvent:event];
}

  我们在RedView中实现了touchesBegan,但是不调用了父类方法.运行,再次点击RedView,输出如下:

2016-08-29 10:53:14.152 TestTouchEvent[6839:487156] Red View

  可以看到这次只有RedView的touchesBegan被触发.这说明了默认的touchesBegan方法实现了将触摸事件传给上一个响应者去处理的功能.   

userInteractionEnabled属性的作用

  将BlueView的userInteractionEnabled设置为NO,如图:

运行程序,点击红色视图和蓝色视图,输出都如下:

2016-08-29 10:56:55.104 TestTouchEvent[6877:501115] ControllerView
2016-08-29 10:56:55.104 TestTouchEvent[6877:501115] ViewController

我们可以看到,当BlueView不再接受用户的互交时,系统只会将触摸事件传递到BlueView的上一个响应者(这里是ControllerView)就结束事件传递了,然后再由ControllerView处理事件,处理完交给上一个响应者.

总结

  1. 当触摸事件产生后,系统会从UIApplication开始查找合适的视图来处理事件.
    UIApplication->UIWindow->ViewController->ControllerView->BlueView->RedView.
  2. 最合适的视图处理完事件之后,默认会将事件传递给上一个响应者.
    RedView=>BlueView=>ControllerView=>ViewController=>UIWindow=>UIApplication.
  3. 如果中途某一个视图的userInteractionEnabled被设置成NO,那么事件就不会传递到它那里.
    UIApplication->UIWindow->ViewController->ControllerView- - ->BlueView(userInteractionEnabled=NO)- - ->RedView.

温馨提示

  如果想让一个视图不接受触摸事件,希望触摸事件”穿透”它跑到它下方的视图去,那么只需要设置视图的userInteractionEnabled为NO即可.
  
  UIImageView的userInteractionEnabled属性默认为NO,所以它和它的子视图都不响应任何事件.
  
  touchsBegan默认实现虽然是将事件交给上一个响应者处理,但是不能在重写touchsBegan时使用nextResponder方法代替.Apple是这样说的

Tells the responder when one or more fingers touch down in a view or window.The default implementation of this method does nothing. However immediate UIKit subclasses of UIResponder, particularly UIView, forward the message up the responder chain.To forward the message to the next responder, send the message to super (the superclass implementation); do not send the message directly to the next responder.

文章目录
  1. 1. 文档更新说明
  2. 2. 前言
  3. 3. 触摸事件的传递
  4. 4. 响应者链
  5. 5. userInteractionEnabled属性的作用
  6. 6. 总结
  7. 7. 温馨提示