iOS Touch以及事件響應(yīng)鏈(一)hit-testing

簡(jiǎn)介

本文的目的是讓讀者徹底了解,iOS系統(tǒng)是如何識(shí)別Touch事件,以及如何傳遞對(duì)應(yīng)的事件到指定的View

iOS事件主要分為四種

  1. touch事件
  2. 運(yùn)動(dòng)事件
  3. 遠(yuǎn)程事件
  4. 按壓事件
    參見(jiàn)UIEventtype屬性
typedef NS_ENUM(NSInteger, UIEventType) {
    UIEventTypeTouches,
    UIEventTypeMotion,
    UIEventTypeRemoteControl,
    UIEventTypePresses NS_ENUM_AVAILABLE_IOS(9_0),
};
1480993244519.png

這里只介紹第一種Touch事件

為了便于區(qū)分把TouchEvent
touchesBegan:withEvent:叫做Touch,手勢(shì)類(lèi)型的叫Gesture.

TouchEvent.png

事件處理流程

  1. App(DemoTSAplication類(lèi),UIApplication子類(lèi),代表當(dāng)前應(yīng)用)先通過(guò)hit-testing找到touch View.
  2. 而后APP收到sendEvent:事件,傳遞給Window(TSWindow)的sendEvent:,由TSWindow分解成touchesBegan:withEvent:touchesMoved:withEvent,touchesEnd:withEvent,touchesCancel:withEvent等事件。
  3. 具體的手勢(shì)識(shí)別以及Target-ActionTSWindowsendEvent:收到Touch Event狀態(tài)的UITouchPhaseBeganUITouchPhaseEnded|UITouchPhaseCancelled之間進(jìn)行。

解析UITapGestureRecognizer

  • UIGestureRecognizer不入響應(yīng)鏈

UIGestureRecognizer objects are not in the responder chain, yet observe touches hit-tested to their view and their view's subviews.

原理

觸摸事件(Touch events)主要用兩步實(shí)現(xiàn)的

第一步:碰撞測(cè)試(hit-testing)

要響應(yīng)一個(gè)觸摸事件,先要找到觸摸的是哪個(gè)View,再根據(jù)Next Response來(lái)找到合適的處理方法

碰撞測(cè)試原理

  1. hit-testing通過(guò)UIView的hitTest:withEvent:找到對(duì)應(yīng)的觸摸的View。
  2. hitTest:withEvent:內(nèi)部會(huì)調(diào)用本類(lèi)pointInside:withEvent:,pointInside:withEvent:返回bool值,來(lái)確定是否點(diǎn)擊范圍在本View范圍內(nèi)。
  3. 如果在,倒序遞歸調(diào)用所有subviews的同一過(guò)程,只要找到最后一層 調(diào)用pointInside:withEvent:返回YES,沒(méi)有subview或者subview的pointInside:withEvent:返回為nil,則返回self,并逐級(jí)返回到頂層。 找到之后并不繼續(xù)進(jìn)行遞歸。

至于為什么hitTest:withEvent:在一個(gè)點(diǎn)擊事件里調(diào)用兩遍,還不知道為什么

舉個(gè)例子

Demo截屏

截屏2016_12_8_上午9_51.png

當(dāng)前controller叫hit-testing,controller對(duì)應(yīng)的view叫AView,AView第一個(gè)子View叫BView,BView有一個(gè)唯一子ViewCView,AView第二個(gè)子View叫DView,DView沒(méi)有子View。以此界面,來(lái)分析hit-testing

點(diǎn)擊AView

時(shí)序圖

SequenceDiagram2.jpg

時(shí)序圖的步驟在一次touch調(diào)用了2遍,看日志,原因未知,沒(méi)有查到資料。

日志

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

第一遍調(diào)試堆棧

![TSWindow_m1.png](http://upload-images.jianshu.io/upload_images/3134371-057baa6e9a3c9e17.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

第二遍調(diào)試堆棧

解析:

  1. 第一個(gè)收到hitTest:withEvent:肯定是TSWindow,TSWindow調(diào)用pointInside:withEvent:返回YES,代表點(diǎn)擊在TSWindows上,倒序遞歸遍歷TSWindowsubviews。
  2. 只有一個(gè)子ViewAView,調(diào)用AViewpointInside:withEvent:返回YES,倒序遞歸遍歷AViewsubviews.
  3. 先遍歷DView,調(diào)用DViewpointInside:withEvent:返回NO,DViewhitTest:withEvent:返回nil。
  4. 再遍歷BView,,調(diào)用BViewpointInside:withEvent:返回NO,BViewhitTest:withEvent:返回nil。
  5. AView所有子View都返回nil了,就返回self.
點(diǎn)擊DView

時(shí)序圖

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
點(diǎn)擊BView

時(shí)序圖

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

解析
不再解析,總結(jié)成一句話(huà):

倒序遞歸subview,直到某個(gè)子ViewpointInside:withEvent:返回YES,沒(méi)有subview或者subviews的hitTest:withEvent:返回nil,則返回self.

點(diǎn)擊CView

用提供的Demo,自驗(yàn)看日志,即可。

模擬代碼

發(fā)現(xiàn)用文字流程圖對(duì)程序員來(lái)說(shuō),還沒(méi)代碼更直觀。故附上模擬代碼

偽代碼

- (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代碼

放在第二篇

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容