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来维护,本质上只知道下一个响应者是谁。


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


iOS Touch以及事件响应链(二)The Responder Chain_第1张图片
enter image description here

原理

原理时序图

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

iOS Touch以及事件响应链(二)The Responder Chain_第2张图片
SequenceDiagram5.jpg

Apple Documents的图

iOS Touch以及事件响应链(二)The Responder Chain_第3张图片
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中视图结构

iOS Touch以及事件响应链(二)The Responder Chain_第4张图片
TSEventDemo_xcodeproj.png

Demo类图

iOS Touch以及事件响应链(二)The Responder Chain_第5张图片
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:
2016-12-09 16:36:09.613 TSEventDemo[51488:2121613] -[TSMainView nextResponder] After nextResponder:
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:>
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:>
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:; layer = >
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:
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:
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:
2016-12-09 16:36:09.686 TSEventDemo[51488:2121613] -[TSMainView nextResponder] After nextResponder:
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:>
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:>
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:; layer = >
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:
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:
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:
2016-12-09 16:36:09.693 TSEventDemo[51488:2121613] -[TSMainView nextResponder] After nextResponder:
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:>
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:>
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:; layer = >
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:
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:
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的事件没有做研究介绍。后续遇到问题研究了再写。

你可能感兴趣的:(iOS Touch以及事件响应链(二)The Responder Chain)