iOS Touch以及事件响应链(二)The Responder Chain

上一篇找到对应的touch View,找到touch View,TSWindow的SendEvent会解析成touchesBegan:withEvent:,touchesMoved:withEvent:,touchesEnded:withEvent:,touchesCancelled:withEvent:等事件。

事件解析部分对应表

| UIWindow(SendEvent:) | UITouchPhaseBegan | UITouchPhaseMoved |UITouchPhaseEnded|UITouchPhaseCancelled|
| :-------- | --------:| :------: |
| UIView | touchesBegan| touchesMoved |touchesEnded/touchesCancelled|touchesCancelled|

TSWindowSendEvent:调用TouchBegin系统并不是一一对应关系,UITouchPhaseEnded可能会调用touchesEnded或者touchesCancelled。具体怎么对应有个前置知识.

前置知识

iOS中主要用到的触摸事件分为3种

  1. 使用Target-Action,此方法只适用于UIControl的子类,例如UIButton,UISlider
  2. 使用手势,例如单击事件UITapGestureRecognizer.
  3. 直接在TouchesBegin:withEvents:系列里边实现,例如自定义手势。

理解精要

  • Gesture相对于Action优先级较高
  • GestureAction从逻辑上说,与TouchesBegin系列是两个- 层级的。无论有无GestureAction,TouchesBegin系列都会调用。但是有Gesture会影响TouchesBegin系列。
  • 所有的TouchesBegin系列GestureAction触发根本点是UIWIndowSendEvent方法。
  • 识别响应链获取需要响应的Target是在TouchBegins阶段进行的
  • 触发最终的响应实在SendEventUITouchPhaseEnded阶段。
  • 手势识别是有一个延时的,靠UITouchPhaseBeganUITouchPhaseEnded一个短暂的时间,在此中间能够正确识别就是一个手势。
  • 只有ViewControllerMainView才会调用ViewControllerTouchesBegin系列事件。
  • 响应链跟View相关,所以在要自动调出键盘的ViewController里边,要在ViewDidAppare:里边调用becomeFirstResponder,而不能在ViewWillAppare:.
  • hit-testing获取的UIView,直接调用TouchesBegin:withEvents:在此方法里,直接调用响应链.

第二步:响应链(The Responder Chain)


什么是响应链?

响应链是一个类似于链表的结构,通过UIResponder类的nextResponder来维护,本质上只知道下一个响应者是谁。


响应链的顺序(别人的图)


enter image description here

原理

原理时序图

结合Demo以及日志堆栈信息学习比较容易理解。

SequenceDiagram5.jpg

Apple Documents的图

1481262855185.png

下边只是对Apple Documents的翻译

左边视图的执行顺序是:

  1. initial view试图处理事件或者消息,因为initial view不是它的ViewController的层级结构中最顶层的视图,如果它不能处理这个事件,则传递给它的SuperView(不好翻译)。
  2. 它的SuperView试图处理事件,如果SuperView不能处理这个事件,因为SuperView还不是视图结构的最顶层,继续传递给SuperViewSuperView
  3. ViewController层级中最顶级的View试图去处理事件,如果topmost视图也不能处理,则传递事件给Controller.
  4. 如果ViewController也管不了,交给Window.
  5. Window也不行,交给App.
  6. App也不行,扔了。

右边的略有不同,但套路一致

  1. view向上传递事件,直到ViewControllertopmostView.
  2. topmostView传给ViewController.
  3. ViewController又传递给它的topmostView的superView,1-3步循环,直到找到rootController.
  4. rootController传给Window.
  5. Window传给App.
  6. App也不行,扔了。

其实右图说的是ViewControllerViewController.有一个ViewController是容器类,容器类就是UIPAgeViewController,UINAvigatorController,UITabbarController以及自定义的使用AddChildController的各种。


举个例子

打断点跟用日志,表现方式不一致。原因在于触摸事件是时间相关的,所以在测试本工程请看日志,打断点会调用TouchEnd,日志则是TouchCancel.或者在touchesbegin打断点走Target-Action不走Gesture事件。


这是Demo中视图结构

TSEventDemo_xcodeproj.png

Demo类图

ClassDiagram1.jpg

以单击事件为例

Target-Action以及Gesture

Target-Action是适用于UIControl以及所有子类的,响应事件的一种方式

用法
TSTapView *tapView = [[TSTapView alloc] initWithFrame:CGRectMake(20, 120, 200, 300)];
tapView.backgroundColor = [UIColor orangeColor];
[self.view addSubview:tapView];
[tapView addTarget:self action:@selector(_actionTap:) forControlEvents:UIControlEventTouchUpInside];
日志
  • 点击MainView区域,MainViewTarget-Action以及Gesture事件。
2016-12-09 16:36:09.611 TSEventDemo[51488:2121613] -[TSAplication sendEvent:] Before 0
2016-12-09 16:36:09.612 TSEventDemo[51488:2121613] -[TSWindow sendEvent:] Before 0
2016-12-09 16:36:09.612 TSEventDemo[51488:2121613] -[TSMainView touchesBegan:withEvent:] Before
2016-12-09 16:36:09.613 TSEventDemo[51488:2121613] -[TSView touchesBegan:withEvent:] [name:(null)] Before
2016-12-09 16:36:09.613 TSEventDemo[51488:2121613] -[TSMainView nextResponder] Before
2016-12-09 16:36:09.613 TSEventDemo[51488:2121613] -[TSView nextResponder] [name:(null)] Before
2016-12-09 16:36:09.613 TSEventDemo[51488:2121613] -[TSView nextResponder] [name:(null)] After nextResponder:<TSTapViewController: 0x7f93cbf59500>
2016-12-09 16:36:09.613 TSEventDemo[51488:2121613] -[TSMainView nextResponder] After nextResponder:<TSTapViewController: 0x7f93cbf59500>
2016-12-09 16:36:09.613 TSEventDemo[51488:2121613] -[TSTapViewController touchesBegan:withEvent:] Before
2016-12-09 16:36:09.614 TSEventDemo[51488:2121613] -[TSTapViewController nextResponder] Before
2016-12-09 16:36:09.614 TSEventDemo[51488:2121613] -[TSTapViewController nextResponder]  After nextResponder:<UIViewControllerWrapperView: 0x7f93cbeba600; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x7f93cbe97000>>
2016-12-09 16:36:09.614 TSEventDemo[51488:2121613] -[TSNavigationController touchesBegan:withEvent:] Before
2016-12-09 16:36:09.614 TSEventDemo[51488:2121613] -[TSNavigationController nextResponder] Before
2016-12-09 16:36:09.614 TSEventDemo[51488:2121613] -[TSNavigationController nextResponder]  After nextResponder:<UIViewControllerWrapperView: 0x7f93cbd04a00; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x7f93cbd04d70>>
2016-12-09 16:36:09.615 TSEventDemo[51488:2121613] -[TSTabBarController touchesBegan:withEvent:] Before
2016-12-09 16:36:09.615 TSEventDemo[51488:2121613] -[TSTabBarController nextResponder] Before
2016-12-09 16:36:09.615 TSEventDemo[51488:2121613] -[TSTabBarController nextResponder]  After nextResponder:<TSWindow: 0x7f93cbf3bc30; baseClass = UIWindow; frame = (0 0; 375 667); gestureRecognizers = <NSArray: 0x7f93cbf3e5e0>; layer = <UIWindowLayer: 0x7f93cbf21ca0>>
2016-12-09 16:36:09.615 TSEventDemo[51488:2121613] -[TSWindow touchesBegan:withEvent:] Before
2016-12-09 16:36:09.616 TSEventDemo[51488:2121613] -[TSWindow nextResponder] Before
2016-12-09 16:36:09.616 TSEventDemo[51488:2121613] -[TSWindow nextResponder] After nextResponder:<TSAplication: 0x7f93cbe06930>
2016-12-09 16:36:09.616 TSEventDemo[51488:2121613] -[TSAplication touchesBegan:withEvent:] Before
2016-12-09 16:36:09.616 TSEventDemo[51488:2121613] -[TSAplication nextResponder] Before
2016-12-09 16:36:09.616 TSEventDemo[51488:2121613] -[TSAplication nextResponder] After nextResponder:<AppDelegate: 0x7f93cd101290>
2016-12-09 16:36:09.617 TSEventDemo[51488:2121613] -[TSAplication touchesBegan:withEvent:] After
2016-12-09 16:36:09.617 TSEventDemo[51488:2121613] -[TSWindow touchesBegan:withEvent:] After
2016-12-09 16:36:09.617 TSEventDemo[51488:2121613] -[TSTabBarController touchesBegan:withEvent:] After
2016-12-09 16:36:09.617 TSEventDemo[51488:2121613] -[TSNavigationController touchesBegan:withEvent:] After
2016-12-09 16:36:09.617 TSEventDemo[51488:2121613] -[TSTapViewController touchesBegan:withEvent:] After
2016-12-09 16:36:09.617 TSEventDemo[51488:2121613] -[TSView touchesBegan:withEvent:] [name:(null)] After
2016-12-09 16:36:09.618 TSEventDemo[51488:2121613] -[TSMainView touchesBegan:withEvent:] After
2016-12-09 16:36:09.618 TSEventDemo[51488:2121613] -[TSWindow sendEvent:] After 0
2016-12-09 16:36:09.618 TSEventDemo[51488:2121613] -[TSAplication sendEvent:] After 0
2016-12-09 16:36:09.684 TSEventDemo[51488:2121613] -[TSAplication sendEvent:] Before 3
2016-12-09 16:36:09.684 TSEventDemo[51488:2121613] -[TSWindow sendEvent:] Before 3
2016-12-09 16:36:09.685 TSEventDemo[51488:2121613] -[TSMainView touchesEnded:withEvent:] Before
2016-12-09 16:36:09.685 TSEventDemo[51488:2121613] -[TSView touchesEnded:withEvent:] [name:(null)] Before
2016-12-09 16:36:09.685 TSEventDemo[51488:2121613] -[TSMainView nextResponder] Before
2016-12-09 16:36:09.685 TSEventDemo[51488:2121613] -[TSView nextResponder] [name:(null)] Before
2016-12-09 16:36:09.685 TSEventDemo[51488:2121613] -[TSView nextResponder] [name:(null)] After nextResponder:<TSTapViewController: 0x7f93cbf59500>
2016-12-09 16:36:09.686 TSEventDemo[51488:2121613] -[TSMainView nextResponder] After nextResponder:<TSTapViewController: 0x7f93cbf59500>
2016-12-09 16:36:09.686 TSEventDemo[51488:2121613] -[TSTapViewController touchesEnded:withEvent:] Before
2016-12-09 16:36:09.686 TSEventDemo[51488:2121613] -[TSTapViewController nextResponder] Before
2016-12-09 16:36:09.687 TSEventDemo[51488:2121613] -[TSTapViewController nextResponder]  After nextResponder:<UIViewControllerWrapperView: 0x7f93cbeba600; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x7f93cbe97000>>
2016-12-09 16:36:09.687 TSEventDemo[51488:2121613] -[TSNavigationController touchesEnded:withEvent:] Before
2016-12-09 16:36:09.687 TSEventDemo[51488:2121613] -[TSNavigationController nextResponder] Before
2016-12-09 16:36:09.688 TSEventDemo[51488:2121613] -[TSNavigationController nextResponder]  After nextResponder:<UIViewControllerWrapperView: 0x7f93cbd04a00; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x7f93cbd04d70>>
2016-12-09 16:36:09.688 TSEventDemo[51488:2121613] -[TSTabBarController touchesEnded:withEvent:] Before
2016-12-09 16:36:09.688 TSEventDemo[51488:2121613] -[TSTabBarController nextResponder] Before
2016-12-09 16:36:09.689 TSEventDemo[51488:2121613] -[TSTabBarController nextResponder]  After nextResponder:<TSWindow: 0x7f93cbf3bc30; baseClass = UIWindow; frame = (0 0; 375 667); gestureRecognizers = <NSArray: 0x7f93cbf3e5e0>; layer = <UIWindowLayer: 0x7f93cbf21ca0>>
2016-12-09 16:36:09.689 TSEventDemo[51488:2121613] -[TSWindow touchesEnded:withEvent:] Before
2016-12-09 16:36:09.689 TSEventDemo[51488:2121613] -[TSWindow nextResponder] Before
2016-12-09 16:36:09.690 TSEventDemo[51488:2121613] -[TSWindow nextResponder] After nextResponder:<TSAplication: 0x7f93cbe06930>
2016-12-09 16:36:09.690 TSEventDemo[51488:2121613] -[TSAplication touchesEnded:withEvent:] Before
2016-12-09 16:36:09.690 TSEventDemo[51488:2121613] -[TSAplication nextResponder] Before
2016-12-09 16:36:09.690 TSEventDemo[51488:2121613] -[TSAplication nextResponder] After nextResponder:<AppDelegate: 0x7f93cd101290>
2016-12-09 16:36:09.691 TSEventDemo[51488:2121613] -[TSAplication touchesEnded:withEvent:] After
2016-12-09 16:36:09.691 TSEventDemo[51488:2121613] -[TSWindow touchesEnded:withEvent:] Before
2016-12-09 16:36:09.691 TSEventDemo[51488:2121613] -[TSTabBarController touchesEnded:withEvent:] After
2016-12-09 16:36:09.691 TSEventDemo[51488:2121613] -[TSNavigationController touchesEnded:withEvent:] After
2016-12-09 16:36:09.691 TSEventDemo[51488:2121613] -[TSTapViewController touchesEnded:withEvent:] After
2016-12-09 16:36:09.692 TSEventDemo[51488:2121613] -[TSView touchesEnded:withEvent:] [name:(null)] After
2016-12-09 16:36:09.692 TSEventDemo[51488:2121613] -[TSMainView touchesEnded:withEvent:] After
2016-12-09 16:36:09.692 TSEventDemo[51488:2121613] -[TSMainView nextResponder] Before
2016-12-09 16:36:09.692 TSEventDemo[51488:2121613] -[TSView nextResponder] [name:(null)] Before
2016-12-09 16:36:09.692 TSEventDemo[51488:2121613] -[TSView nextResponder] [name:(null)] After nextResponder:<TSTapViewController: 0x7f93cbf59500>
2016-12-09 16:36:09.693 TSEventDemo[51488:2121613] -[TSMainView nextResponder] After nextResponder:<TSTapViewController: 0x7f93cbf59500>
2016-12-09 16:36:09.693 TSEventDemo[51488:2121613] -[TSTapViewController nextResponder] Before
2016-12-09 16:36:09.693 TSEventDemo[51488:2121613] -[TSTapViewController nextResponder]  After nextResponder:<UIViewControllerWrapperView: 0x7f93cbeba600; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x7f93cbe97000>>
2016-12-09 16:36:09.693 TSEventDemo[51488:2121613] -[TSNavigationController nextResponder] Before
2016-12-09 16:36:09.693 TSEventDemo[51488:2121613] -[TSNavigationController nextResponder]  After nextResponder:<UIViewControllerWrapperView: 0x7f93cbd04a00; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x7f93cbd04d70>>
2016-12-09 16:36:09.694 TSEventDemo[51488:2121613] -[TSTabBarController nextResponder] Before
2016-12-09 16:36:09.694 TSEventDemo[51488:2121613] -[TSTabBarController nextResponder]  After nextResponder:<TSWindow: 0x7f93cbf3bc30; baseClass = UIWindow; frame = (0 0; 375 667); gestureRecognizers = <NSArray: 0x7f93cbf3e5e0>; layer = <UIWindowLayer: 0x7f93cbf21ca0>>
2016-12-09 16:36:09.694 TSEventDemo[51488:2121613] -[TSWindow nextResponder] Before
2016-12-09 16:36:09.694 TSEventDemo[51488:2121613] -[TSWindow nextResponder] After nextResponder:<TSAplication: 0x7f93cbe06930>
2016-12-09 16:36:09.694 TSEventDemo[51488:2121613] -[TSAplication nextResponder] Before
2016-12-09 16:36:09.695 TSEventDemo[51488:2121613] -[TSAplication nextResponder] After nextResponder:<AppDelegate: 0x7f93cd101290>
2016-12-09 16:36:09.695 TSEventDemo[51488:2121613] -[TSWindow sendEvent:] After 3
2016-12-09 16:36:09.695 TSEventDemo[51488:2121613] -[TSAplication sendEvent:] After 3

上边日志,严格按照原理里提到的规则。这里不作分析

调试堆栈

这里TSTapView同时有Target-Action以及Gesture事件,分别给出堆栈信息。

同时注册Target-Action以及Gesture

TSTapView *tapView = [[TSTapView alloc] initWithFrame:CGRectMake(20, 120, 200, 300)];
tapView.backgroundColor = [UIColor orangeColor];
[self.view addSubview:tapView];

UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(_gestureTap:)];
[tapView addGestureRecognizer:tap];

[tapView addTarget:self action:@selector(_actionTap:) forControlEvents:UIControlEventTouchUpInside];

结果:
只有_gestureTap:响应,_actionTap:无响应。代表Gesture优先Target-Action响应,在_gestureTap:响应之后,向hit-ttestting获取的View发送了touchesCancelled:withEvent:事件。

堆栈

 thread #1: tid = 0x223729, 0x00000001000aa925 TSEventDemo`-[TSTapViewController _gestureTap:](self=0x00007faff9911810, _cmd="_gestureTap:", aTap=0x00007faff6c8aca0) + 53 at TSTapViewController.m:99, queue = 'com.apple.main-thread', stop reason = breakpoint 7.1
  * frame #0: 0x00000001000aa925 TSEventDemo`-[TSTapViewController _gestureTap:](self=0x00007faff9911810, _cmd="_gestureTap:", aTap=0x00007faff6c8aca0) + 53 at TSTapViewController.m:99
    frame #1: 0x0000000101ab8b28 UIKit`_UIGestureRecognizerSendTargetActions + 153
    frame #2: 0x0000000101ab519a UIKit`_UIGestureRecognizerSendActions + 162
    frame #3: 0x0000000101ab3197 UIKit`-[UIGestureRecognizer _updateGestureWithEvent:buttonEvent:] + 843
    frame #4: 0x0000000101abb655 UIKit`___UIGestureRecognizerUpdate_block_invoke898 + 79
    frame #5: 0x0000000101abb4f3 UIKit`_UIGestureRecognizerRemoveObjectsFromArrayAndApplyBlocks + 342
    frame #6: 0x0000000101aa8e75 UIKit`_UIGestureRecognizerUpdate + 2634
    frame #7: 0x000000010163548e UIKit`-[UIWindow _sendGesturesForEvent:] + 1137
    frame #8: 0x00000001016366c4 UIKit`-[UIWindow sendEvent:] + 849
    frame #9: 0x00000001000a949d TSEventDemo`-[TSWindow sendEvent:](self=0x00007faff9a32ec0, _cmd="sendEvent:", event=0x00007faff6f0d2a0) + 861 at TSWindow.m:51
    frame #10: 0x00000001015e1dc6 UIKit`-[UIApplication sendEvent:] + 263
    frame #11: 0x00000001000a624d TSEventDemo`-[TSAplication sendEvent:](self=0x00007faff6f0c7c0, _cmd="sendEvent:", event=0x00007faff6f0d2a0) + 861 at TSAplication.m:42
    frame #12: 0x00000001015bb553 UIKit`_UIApplicationHandleEventQueue + 6660
    frame #13: 0x0000000100a68301 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
    frame #14: 0x0000000100a5e22c CoreFoundation`__CFRunLoopDoSources0 + 556
    frame #15: 0x0000000100a5d6e3 CoreFoundation`__CFRunLoopRun + 867
    frame #16: 0x0000000100a5d0f8 CoreFoundation`CFRunLoopRunSpecific + 488
    frame #17: 0x00000001057d8ad2 GraphicsServices`GSEventRunModal + 161
    frame #18: 0x00000001015c0f09 UIKit`UIApplicationMain + 171
    frame #19: 0x00000001000aaaf3 TSEventDemo`main(argc=1, argv=0x00007fff5fb5a580) + 115 at main.m:14
    frame #20: 0x00000001039e492d libdyld.dylib`start + 1

注释掉Gesture事件,只有Target-Action

* thread #1: tid = 0x205f8d, 0x000000010d509985 TSEventDemo`-[TSTapViewController _actionTap:](self=0x00007f93cbf59500, _cmd="_actionTap:", aTapView=0x00007f93cbee8f70) + 53 at TSTapViewController.m:105, queue = 'com.apple.main-thread', stop reason = breakpoint 5.1
  * frame #0: 0x000000010d509985 TSEventDemo`-[TSTapViewController _actionTap:](self=0x00007f93cbf59500, _cmd="_actionTap:", aTapView=0x00007f93cbee8f70) + 53 at TSTapViewController.m:105
    frame #1: 0x000000010ea21a8d UIKit`-[UIApplication sendAction:to:from:forEvent:] + 92
    frame #2: 0x000000010eb94e67 UIKit`-[UIControl sendAction:to:forEvent:] + 67
    frame #3: 0x000000010eb95143 UIKit`-[UIControl _sendActionsForEvents:withEvent:] + 327
    frame #4: 0x000000010eb94263 UIKit`-[UIControl touchesEnded:withEvent:] + 601
    frame #5: 0x000000010d50a714 TSEventDemo`-[TSTapView touchesEnded:withEvent:](self=0x00007f93cbee8f70, _cmd="touchesEnded:withEvent:", touches=1 element, event=0x00007f93cbe07440) + 148 at TSTapView.m:67
    frame #6: 0x000000010ea9499f UIKit`-[UIWindow _sendTouchesForEvent:] + 835
    frame #7: 0x000000010ea956d4 UIKit`-[UIWindow sendEvent:] + 865
    frame #8: 0x000000010d50849d TSEventDemo`-[TSWindow sendEvent:](self=0x00007f93cbf3bc30, _cmd="sendEvent:", event=0x00007f93cbe07440) + 861 at TSWindow.m:51
    frame #9: 0x000000010ea40dc6 UIKit`-[UIApplication sendEvent:] + 263
    frame #10: 0x000000010d50524d TSEventDemo`-[TSAplication sendEvent:](self=0x00007f93cbe06930, _cmd="sendEvent:", event=0x00007f93cbe07440) + 861 at TSAplication.m:42
    frame #11: 0x000000010ea1a553 UIKit`_UIApplicationHandleEventQueue + 6660
    frame #12: 0x000000010dec7301 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
    frame #13: 0x000000010debd22c CoreFoundation`__CFRunLoopDoSources0 + 556
    frame #14: 0x000000010debc6e3 CoreFoundation`__CFRunLoopRun + 867
    frame #15: 0x000000010debc0f8 CoreFoundation`CFRunLoopRunSpecific + 488
    frame #16: 0x0000000112551ad2 GraphicsServices`GSEventRunModal + 161
    frame #17: 0x000000010ea1ff09 UIKit`UIApplicationMain + 171
    frame #18: 0x000000010d509af3 TSEventDemo`main(argc=1, argv=0x00007fff526fb580) + 115 at main.m:14
    frame #19: 0x0000000110e4392d libdyld.dylib`start + 1

区别
Gesture核心调用是_sendGesturesForEvent,Target-Action_sendTouchesForEvent:,而且明显_sendGesturesForEvent会先于_sendTouchesForEvent:调用。

模拟代码

UIWindowsendEvent模拟代码

模拟的真实代码肯定还有很多处理,例如ScrollView等上的处理。

- (void)sendEvent:(UIEvent *)event
{
    //如果是touch事件
    if (event.type == UIEventTypeTouches) {
        NSSet *touches = [event touchesForWindow:self];
        NSMutableSet *gestureRecognizers = [NSMutableSet setWithCapacity:0];
        
        for (UITouch *touch in touches) {
            [gestureRecognizers addObjectsFromArray:touch.gestureRecognizers];
        }
        //先处理Gesture事件,如果正确识别则发送touchesCancelled:withEvent:
        for (UIGestureRecognizer *recognizer in gestureRecognizers) {
            [recognizer _recognizeTouches:touches withEvent:event];
        }
        
        for (UITouch *touch in touches) {

            UIView *view = touch.view;
            
            const UITouchPhase phase = touch.phase;
            const _UITouchGesture gesture = [touch _gesture];
            
            if (phase == UITouchPhaseBegan) {
                [view touchesBegan:touches withEvent:event];
            } else if (phase == UITouchPhaseMoved) {
                [view touchesMoved:touches withEvent:event];
            } else if (phase == UITouchPhaseEnded) {
                [view touchesEnded:touches withEvent:event];
            } else if (phase == UITouchPhaseCancelled) {
                [view touchesCancelled:touches withEvent:event];

            }
            

        }
    }
}

Target-Action具体是什么做的

首先从Target-ActionUIControl及其子类才有的功能,从Target-Action堆栈以及日志能看出,其实Target-Action事重写了TouchesBegin系列事件实现的。

模拟代码

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [touches anyObject];
    _touchInside = YES;
    _tracking = [self beginTrackingWithTouch:touch withEvent:event];

    self.highlighted = YES;

    if (_tracking) {
        UIControlEvents currentEvents = UIControlEventTouchDown;

        if (touch.tapCount > 1) {
            currentEvents |= UIControlEventTouchDownRepeat;
        }

        [self _sendActionsForControlEvents:currentEvents withEvent:event];
    }
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [touches anyObject];
    const BOOL wasTouchInside = _touchInside;
    _touchInside = [self pointInside:[touch locationInView:self] withEvent:event];

    self.highlighted = _touchInside;

    if (_tracking) {
        _tracking = [self continueTrackingWithTouch:touch withEvent:event];
        if (_tracking) {
            UIControlEvents currentEvents = ((_touchInside)? UIControlEventTouchDragInside : UIControlEventTouchDragOutside);

            if (!wasTouchInside && _touchInside) {
                currentEvents |= UIControlEventTouchDragEnter;
            } else if (wasTouchInside && !_touchInside) {
                currentEvents |= UIControlEventTouchDragExit;
            }

            [self _sendActionsForControlEvents:currentEvents withEvent:event];
        }
    }
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [touches anyObject];
    _touchInside = [self pointInside:[touch locationInView:self] withEvent:event];

    self.highlighted = NO;

    if (_tracking) {
        [self endTrackingWithTouch:touch withEvent:event];
        [self _sendActionsForControlEvents:((_touchInside)? UIControlEventTouchUpInside : UIControlEventTouchUpOutside) withEvent:event];
    }

    _tracking = NO;
    _touchInside = NO;
}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
    self.highlighted = NO;

    if (_tracking) {
        [self cancelTrackingWithEvent:event];
        [self _sendActionsForControlEvents:UIControlEventTouchCancel withEvent:event];
    }

    _touchInside = NO;
    _tracking = NO;
}

实际上addTarget:action:forControlEvents是往数组里里添加一个键值对。在TouchesBegin系列里进行处理的。

Demo

git地址TSEventDemo

结束语

一些复杂手势,以及在ScrollView的事件没有做研究介绍。后续遇到问题研究了再写。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,390评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,821评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,632评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,170评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,033评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,098评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,511评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,204评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,479评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,572评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,341评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,893评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,171评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,486评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,676评论 2 335

推荐阅读更多精彩内容