iOS Touch以及事件响应链(一)hit-testing

2017-04-30  本文已影响0人  ashura_

简介

本文的目的是让读者彻底了解,iOS系统是如何识别Touch事件,以及如何传递对应的事件到指定的View

iOS事件主要分为四种

  1. touch事件
  2. 运动事件
  3. 远程事件
  4. 按压事件
    参见UIEventtype属性
typedef NS_ENUM(NSInteger, UIEventType) {
    UIEventTypeTouches,
    UIEventTypeMotion,
    UIEventTypeRemoteControl,
    UIEventTypePresses NS_ENUM_AVAILABLE_IOS(9_0),
};
1480993244519.png

这里只介绍第一种Touch事件

为了便于区分把TouchEvent
touchesBegan:withEvent:叫做Touch,手势类型的叫Gesture.

TouchEvent.png

事件处理流程

  1. App(DemoTSAplication类,UIApplication子类,代表当前应用)先通过hit-testing找到touch View.
  2. 而后APP收到sendEvent:事件,传递给Window(TSWindow)的sendEvent:,由TSWindow分解成touchesBegan:withEvent:touchesMoved:withEvent,touchesEnd:withEvent,touchesCancel:withEvent等事件。
  3. 具体的手势识别以及Target-ActionTSWindowsendEvent:收到Touch Event状态的UITouchPhaseBeganUITouchPhaseEnded|UITouchPhaseCancelled之间进行。
    截屏2016_12_8_上午9_51.png

    当前controller叫hit-testing,controller对应的view叫AView,AView第一个子View叫BView,BView有一个唯一子ViewCView,AView第二个子View叫DView,DView没有子View。以此界面,来分析hit-testing

    点击AView

    时序图

    SequenceDiagram2.jpg

    时序图的步骤在一次touch调用了2遍,看日志,原因未知,没有查到资料。

    日志

    2016-12-08 11:00:07.546 TSEventDemo[35550:1352018] -[TSWindow hitTest:withEvent:] Before
    2016-12-08 11:00:07.547 TSEventDemo[35550:1352018] -[TSWindow pointInside:withEvent:] Before
    2016-12-08 11:00:07.547 TSEventDemo[35550:1352018] -[TSWindow pointInside:withEvent:] After
    2016-12-08 11:00:07.547 TSEventDemo[35550:1352018] -[TSView hitTest:withEvent:] [name:AView(MainView)] Before
    2016-12-08 11:00:07.548 TSEventDemo[35550:1352018] -[TSView pointInside:withEvent:] [name:AView(MainView)] Before
    2016-12-08 11:00:07.548 TSEventDemo[35550:1352018] -[TSView pointInside:withEvent:] [name:AView(MainView)] After
    2016-12-08 11:00:07.548 TSEventDemo[35550:1352018] -[TSView hitTest:withEvent:] [name:DView] Before
    2016-12-08 11:00:07.548 TSEventDemo[35550:1352018] -[TSView pointInside:withEvent:] [name:DView] Before
    2016-12-08 11:00:07.549 TSEventDemo[35550:1352018] -[TSView pointInside:withEvent:] [name:DView] After
    2016-12-08 11:00:07.549 TSEventDemo[35550:1352018] -[TSView hitTest:withEvent:] [name:DView] After
    2016-12-08 11:00:07.549 TSEventDemo[35550:1352018] -[TSView hitTest:withEvent:] [name:BView] Before
    2016-12-08 11:00:07.549 TSEventDemo[35550:1352018] -[TSView pointInside:withEvent:] [name:BView] Before
    2016-12-08 11:00:07.550 TSEventDemo[35550:1352018] -[TSView pointInside:withEvent:] [name:BView] After
    2016-12-08 11:00:07.550 TSEventDemo[35550:1352018] -[TSView hitTest:withEvent:] [name:BView] After
    2016-12-08 11:00:07.550 TSEventDemo[35550:1352018] -[TSView hitTest:withEvent:] [name:AView(MainView)] After
    2016-12-08 11:00:07.551 TSEventDemo[35550:1352018] -[TSWindow hitTest:withEvent:] After
    2016-12-08 11:00:07.551 TSEventDemo[35550:1352018] -[TSWindow hitTest:withEvent:] Before
    2016-12-08 11:00:07.551 TSEventDemo[35550:1352018] -[TSWindow pointInside:withEvent:] Before
    2016-12-08 11:00:07.551 TSEventDemo[35550:1352018] -[TSWindow pointInside:withEvent:] After
    2016-12-08 11:00:07.552 TSEventDemo[35550:1352018] -[TSView hitTest:withEvent:] [name:AView(MainView)] Before
    2016-12-08 11:00:07.552 TSEventDemo[35550:1352018] -[TSView pointInside:withEvent:] [name:AView(MainView)] Before
    2016-12-08 11:00:07.552 TSEventDemo[35550:1352018] -[TSView pointInside:withEvent:] [name:AView(MainView)] After
    2016-12-08 11:00:07.552 TSEventDemo[35550:1352018] -[TSView hitTest:withEvent:] [name:DView] Before
    2016-12-08 11:00:07.553 TSEventDemo[35550:1352018] -[TSView pointInside:withEvent:] [name:DView] Before
    2016-12-08 11:00:07.553 TSEventDemo[35550:1352018] -[TSView pointInside:withEvent:] [name:DView] After
    2016-12-08 11:00:07.553 TSEventDemo[35550:1352018] -[TSView hitTest:withEvent:] [name:DView] After
    2016-12-08 11:00:07.553 TSEventDemo[35550:1352018] -[TSView hitTest:withEvent:] [name:BView] Before
    2016-12-08 11:00:07.553 TSEventDemo[35550:1352018] -[TSView pointInside:withEvent:] [name:BView] Before
    2016-12-08 11:00:07.554 TSEventDemo[35550:1352018] -[TSView pointInside:withEvent:] [name:BView] After
    2016-12-08 11:00:07.554 TSEventDemo[35550:1352018] -[TSView hitTest:withEvent:] [name:BView] After
    2016-12-08 11:00:07.554 TSEventDemo[35550:1352018] -[TSView hitTest:withEvent:] [name:AView(MainView)] After
    2016-12-08 11:00:07.554 TSEventDemo[35550:1352018] -[TSWindow hitTest:withEvent:] After
    

    第一遍调试堆栈

    ![TSWindow_m1.png](https://img.haomeiwen.com/i3134371/057baa6e9a3c9e17.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

    第二遍调试堆栈

    解析:

    1. 第一个收到hitTest:withEvent:肯定是TSWindow,TSWindow调用pointInside:withEvent:返回YES,代表点击在TSWindows上,倒序递归遍历TSWindowsubviews。
    2. 只有一个子ViewAView,调用AViewpointInside:withEvent:返回YES,倒序递归遍历AViewsubviews.
    3. 先遍历DView,调用DViewpointInside:withEvent:返回NO,DViewhitTest:withEvent:返回nil。
    4. 再遍历BView,,调用BViewpointInside:withEvent:返回NO,BViewhitTest:withEvent:返回nil。
    5. AView所有子View都返回nil了,就返回self.
    点击DView

    时序图

    SequenceDiagram3.jpg

    日志

    2016-12-08 14:30:34.886 TSEventDemo[35550:1352018] -[TSWindow hitTest:withEvent:] Before
    2016-12-08 14:30:34.886 TSEventDemo[35550:1352018] -[TSWindow pointInside:withEvent:] Before
    2016-12-08 14:30:34.886 TSEventDemo[35550:1352018] -[TSWindow pointInside:withEvent:] After
    2016-12-08 14:30:34.887 TSEventDemo[35550:1352018] -[TSView hitTest:withEvent:] [name:AView(MainView)] Before
    2016-12-08 14:30:34.887 TSEventDemo[35550:1352018] -[TSView pointInside:withEvent:] [name:AView(MainView)] Before
    2016-12-08 14:30:34.888 TSEventDemo[35550:1352018] -[TSView pointInside:withEvent:] [name:AView(MainView)] After
    2016-12-08 14:30:34.888 TSEventDemo[35550:1352018] -[TSView hitTest:withEvent:] [name:DView] Before
    2016-12-08 14:30:34.888 TSEventDemo[35550:1352018] -[TSView pointInside:withEvent:] [name:DView] Before
    2016-12-08 14:30:34.888 TSEventDemo[35550:1352018] -[TSView pointInside:withEvent:] [name:DView] After
    2016-12-08 14:30:34.889 TSEventDemo[35550:1352018] -[TSView hitTest:withEvent:] [name:DView] After
    2016-12-08 14:30:34.889 TSEventDemo[35550:1352018] -[TSView hitTest:withEvent:] [name:AView(MainView)] After
    2016-12-08 14:30:34.889 TSEventDemo[35550:1352018] -[TSWindow hitTest:withEvent:] After
    2016-12-08 14:30:34.890 TSEventDemo[35550:1352018] -[TSWindow hitTest:withEvent:] Before
    2016-12-08 14:30:34.890 TSEventDemo[35550:1352018] -[TSWindow pointInside:withEvent:] Before
    2016-12-08 14:30:34.890 TSEventDemo[35550:1352018] -[TSWindow pointInside:withEvent:] After
    2016-12-08 14:30:34.890 TSEventDemo[35550:1352018] -[TSView hitTest:withEvent:] [name:AView(MainView)] Before
    2016-12-08 14:30:34.891 TSEventDemo[35550:1352018] -[TSView pointInside:withEvent:] [name:AView(MainView)] Before
    2016-12-08 14:30:34.891 TSEventDemo[35550:1352018] -[TSView pointInside:withEvent:] [name:AView(MainView)] After
    2016-12-08 14:30:34.891 TSEventDemo[35550:1352018] -[TSView hitTest:withEvent:] [name:DView] Before
    2016-12-08 14:30:34.891 TSEventDemo[35550:1352018] -[TSView pointInside:withEvent:] [name:DView] Before
    2016-12-08 14:30:34.892 TSEventDemo[35550:1352018] -[TSView pointInside:withEvent:] [name:DView] After
    2016-12-08 14:30:34.892 TSEventDemo[35550:1352018] -[TSView hitTest:withEvent:] [name:DView] After
    2016-12-08 14:30:34.892 TSEventDemo[35550:1352018] -[TSView hitTest:withEvent:] [name:AView(MainView)] After
    2016-12-08 14:30:34.892 TSEventDemo[35550:1352018] -[TSWindow hitTest:withEvent:] After
    
    点击BView

    时序图

    SequenceDiagram4.jpg
    日志
    2016-12-08 15:19:16.611 TSEventDemo[35550:1352018] -[TSWindow hitTest:withEvent:] Before
    2016-12-08 15:19:16.612 TSEventDemo[35550:1352018] -[TSWindow pointInside:withEvent:] Before
    2016-12-08 15:19:16.612 TSEventDemo[35550:1352018] -[TSWindow pointInside:withEvent:] After
    2016-12-08 15:19:16.613 TSEventDemo[35550:1352018] -[TSView hitTest:withEvent:] [name:AView(MainView)] Before
    2016-12-08 15:19:16.614 TSEventDemo[35550:1352018] -[TSView pointInside:withEvent:] [name:AView(MainView)] Before
    2016-12-08 15:19:16.614 TSEventDemo[35550:1352018] -[TSView pointInside:withEvent:] [name:AView(MainView)] After
    2016-12-08 15:19:16.615 TSEventDemo[35550:1352018] -[TSView hitTest:withEvent:] [name:DView] Before
    2016-12-08 15:19:16.615 TSEventDemo[35550:1352018] -[TSView pointInside:withEvent:] [name:DView] Before
    2016-12-08 15:19:16.616 TSEventDemo[35550:1352018] -[TSView pointInside:withEvent:] [name:DView] After
    2016-12-08 15:19:16.616 TSEventDemo[35550:1352018] -[TSView hitTest:withEvent:] [name:DView] After
    2016-12-08 15:19:16.617 TSEventDemo[35550:1352018] -[TSView hitTest:withEvent:] [name:BView] Before
    2016-12-08 15:19:16.617 TSEventDemo[35550:1352018] -[TSView pointInside:withEvent:] [name:BView] Before
    2016-12-08 15:19:16.617 TSEventDemo[35550:1352018] -[TSView pointInside:withEvent:] [name:BView] After
    2016-12-08 15:19:16.618 TSEventDemo[35550:1352018] -[TSView hitTest:withEvent:] [name:CView] Before
    2016-12-08 15:19:16.618 TSEventDemo[35550:1352018] -[TSView pointInside:withEvent:] [name:CView] Before
    2016-12-08 15:19:16.619 TSEventDemo[35550:1352018] -[TSView pointInside:withEvent:] [name:CView] After
    2016-12-08 15:19:16.619 TSEventDemo[35550:1352018] -[TSView hitTest:withEvent:] [name:CView] After
    2016-12-08 15:19:16.620 TSEventDemo[35550:1352018] -[TSView hitTest:withEvent:] [name:BView] After
    2016-12-08 15:19:16.620 TSEventDemo[35550:1352018] -[TSView hitTest:withEvent:] [name:AView(MainView)] After
    2016-12-08 15:19:16.621 TSEventDemo[35550:1352018] -[TSWindow hitTest:withEvent:] After
    2016-12-08 15:19:16.621 TSEventDemo[35550:1352018] -[TSWindow hitTest:withEvent:] Before
    2016-12-08 15:19:16.622 TSEventDemo[35550:1352018] -[TSWindow pointInside:withEvent:] Before
    2016-12-08 15:19:16.622 TSEventDemo[35550:1352018] -[TSWindow pointInside:withEvent:] After
    2016-12-08 15:19:16.623 TSEventDemo[35550:1352018] -[TSView hitTest:withEvent:] [name:AView(MainView)] Before
    2016-12-08 15:19:16.623 TSEventDemo[35550:1352018] -[TSView pointInside:withEvent:] [name:AView(MainView)] Before
    2016-12-08 15:19:16.623 TSEventDemo[35550:1352018] -[TSView pointInside:withEvent:] [name:AView(MainView)] After
    2016-12-08 15:19:16.624 TSEventDemo[35550:1352018] -[TSView hitTest:withEvent:] [name:DView] Before
    2016-12-08 15:19:16.624 TSEventDemo[35550:1352018] -[TSView pointInside:withEvent:] [name:DView] Before
    2016-12-08 15:19:16.625 TSEventDemo[35550:1352018] -[TSView pointInside:withEvent:] [name:DView] After
    2016-12-08 15:19:16.625 TSEventDemo[35550:1352018] -[TSView hitTest:withEvent:] [name:DView] After
    2016-12-08 15:19:16.625 TSEventDemo[35550:1352018] -[TSView hitTest:withEvent:] [name:BView] Before
    2016-12-08 15:19:16.625 TSEventDemo[35550:1352018] -[TSView pointInside:withEvent:] [name:BView] Before
    2016-12-08 15:19:16.626 TSEventDemo[35550:1352018] -[TSView pointInside:withEvent:] [name:BView] After
    2016-12-08 15:19:16.626 TSEventDemo[35550:1352018] -[TSView hitTest:withEvent:] [name:CView] Before
    2016-12-08 15:19:16.626 TSEventDemo[35550:1352018] -[TSView pointInside:withEvent:] [name:CView] Before
    2016-12-08 15:19:16.627 TSEventDemo[35550:1352018] -[TSView pointInside:withEvent:] [name:CView] After
    2016-12-08 15:19:16.627 TSEventDemo[35550:1352018] -[TSView hitTest:withEvent:] [name:CView] After
    2016-12-08 15:19:16.628 TSEventDemo[35550:1352018] -[TSView hitTest:withEvent:] [name:BView] After
    2016-12-08 15:19:16.628 TSEventDemo[35550:1352018] -[TSView hitTest:withEvent:] [name:AView(MainView)] After
    2016-12-08 15:19:16.628 TSEventDemo[35550:1352018] -[TSWindow hitTest:withEvent:] After
    

    解析
    不再解析,总结成一句话:

    倒序递归subview,直到某个子ViewpointInside:withEvent:返回YES,没有subview或者subviews的hitTest:withEvent:返回nil,则返回self.

    点击CView

    用提供的Demo,自验看日志,即可。

    模拟代码

    发现用文字流程图对程序员来说,还没代码更直观。故附上模拟代码

    伪代码

    - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
    {
        if (self.hidden || !self.userInteractionEnabled || self.alpha < 0.01 || ![self pointInside:point withEvent:event] || ![self _isAnimatedUserInteractionEnabled]) {
            return nil;
        } else {
            for (UIView *subview in [self.subviews reverseObjectEnumerator]) {
                UIView *hitView = [subview hitTest:[subview convertPoint:point fromView:self] withEvent:event];
                if (hitView) {
                    return hitView;
                }
            }
            return self;
        }
    }
    

    Demo代码

    放在第二篇

上一篇下一篇

猜你喜欢

热点阅读