美文网首页
iOS Touch以及事件响应链(二)The Responder

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

作者: ashura_ | 来源:发表于2017-05-01 21:05 被阅读0次

    上一篇找到对应的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的事件没有做研究介绍。后续遇到问题研究了再写。

    相关文章

      网友评论

          本文标题:iOS Touch以及事件响应链(二)The Responder

          本文链接:https://www.haomeiwen.com/subject/cmdqtxtx.html