iOS-完整的觸摸事件傳遞過(guò)程

知 識(shí) 點(diǎn) / 超 人


觸摸事件分為四個(gè)階段進(jìn)行傳遞:
1.硬件響應(yīng)階段
2.系統(tǒng)響應(yīng)階段
3.桌面響應(yīng)階段
4.應(yīng)用響應(yīng)階段
- 4.1事件傳遞
- 4.2尋找最佳響應(yīng)者
- 4.3事件傳遞的響應(yīng)鏈


硬件響應(yīng)階段

該階段是用戶觸摸到電容屏,電容屏響應(yīng)并產(chǎn)生觸摸信號(hào)的階段

知識(shí)點(diǎn)補(bǔ)充:

電容屏原理(百度百科)
電容技術(shù)觸摸面板CTP是利用人體的電流感應(yīng)進(jìn)行工作的银酗。電容屏是一塊四層復(fù)合玻璃屏,玻璃屏的內(nèi)表面和夾層各涂一層ITO(納米銦錫金屬氧化物)爆侣,最外層是只有0.0015mm厚的矽土玻璃保護(hù)層,夾層ITO涂層作工作面歌馍,四個(gè)角引出四個(gè)電極报慕,內(nèi)層ITO為屏層以保證工作環(huán)境豆巨。
當(dāng)用戶觸摸電容屏?xí)r,由于人體電場(chǎng)元扔,用戶手指和工作面形成一個(gè)耦合電容躯保,因?yàn)楣ぷ髅嫔辖佑懈哳l信號(hào),于是手指吸收走一個(gè)很小的電流澎语,這個(gè)電流分別從屏的四個(gè)角上的電極中流出途事,且理論上流經(jīng)四個(gè)電極的電流與手指頭到四角的距離成比例,控制器通過(guò)對(duì)四個(gè)電流比例的精密計(jì)算擅羞,得出位置尸变。可以達(dá)到99%的精確度减俏,具備小于3ms的響應(yīng)速度召烂。

電容屏觸摸點(diǎn)計(jì)算:
電容屏是在兩層ITO導(dǎo)電玻璃涂層上蝕刻出不同的ITO導(dǎo)電線路模塊。兩個(gè)模塊上蝕刻的圖形相互垂直垄懂,可以把它們看作是X和Y方向 連續(xù)變化的滑條骑晶。由于X痛垛、Y架構(gòu)在不同表面,其相交處形成一電容節(jié)點(diǎn)桶蛔。一個(gè)滑條可以當(dāng)成驅(qū)動(dòng)線匙头,另外一個(gè)滑條當(dāng)成是偵測(cè)線。當(dāng)電流經(jīng)過(guò)驅(qū)動(dòng)線中的一條導(dǎo)線時(shí)仔雷,如果外界有電容變化的信號(hào)蹂析,那么就會(huì)引起另一層導(dǎo)線上電容節(jié)點(diǎn)的變化。偵測(cè)電容值的變化可以通過(guò)與之相連的電子回路測(cè)量得到碟婆,再經(jīng)由A/D控制器轉(zhuǎn)為數(shù)字訊號(hào)讓計(jì)算機(jī)做運(yùn)算處理取得(X电抚,Y) 軸位置,進(jìn)而達(dá)到定位的目地竖共。
多點(diǎn)操作時(shí)蝙叛,控制器先后供電流給驅(qū)動(dòng)線,因而使各節(jié)點(diǎn)與導(dǎo)線間形成一特定電場(chǎng)公给。然后逐列掃描感測(cè)線測(cè)量其電極間的電容變化量借帘,從而達(dá)成多點(diǎn)定位。
響應(yīng)時(shí)間:響應(yīng)時(shí)間定義為淌铐,觸摸屏上手指觸碰事件與觸摸屏控制器產(chǎn)生中斷信號(hào)之間的時(shí)間肺然。

響應(yīng)步驟:
*第一步: ->用戶觸摸電容屏
*第二步: ->電容屏根據(jù)電流產(chǎn)生反應(yīng)
*第三步: ->電容屏控制芯片計(jì)算出觸控點(diǎn)產(chǎn)生電容信號(hào)
*第四步: ->CPU處理信號(hào)


系統(tǒng)響應(yīng)階段

該階段是iOS系統(tǒng)對(duì)CPU指令封裝的階段

知識(shí)點(diǎn)補(bǔ)充:

IOKit.framework
是與硬件或內(nèi)核服務(wù)通信的底層框架,主要是面向硬件驅(qū)動(dòng)開發(fā)者腿准。在 IOKit 里面际起,所有的通信都通過(guò) IOKit Master Port 來(lái)進(jìn)行。雖然這是一個(gè)公共框架吐葱,但蘋果不鼓勵(lì)開發(fā)者使用它街望,任何使用它的應(yīng)用程序都將被AppStore拒絕。這里有蘋果官方對(duì)IOKit.framework的說(shuō)明

IPC 通信:跨進(jìn)程通信唇撬。

Mach Port
是在系統(tǒng)內(nèi)核實(shí)現(xiàn)和維護(hù)的一種 IPC 消息隊(duì)列它匕,它持有用于 IPC 通信的 mach messages,可以看做是一個(gè)單向的數(shù)據(jù)發(fā)送渠道窖认,構(gòu)建一個(gè)消息結(jié)構(gòu)體后通過(guò)mach_msg() 方法發(fā)送消息出去。只有一個(gè)進(jìn)程可以從對(duì)應(yīng)的 port 里 dequeue 一條消息告希,這個(gè)進(jìn)程被稱為接收端(receive-right)扑浸。可以有多個(gè)進(jìn)程往某個(gè) port 里 enqueue 消息燕偶,這些進(jìn)程稱為發(fā)送端(send-rights)喝噪。因?yàn)橹荒軉蜗虬l(fā)送,所以當(dāng) B 進(jìn)程收到了 A 進(jìn)程發(fā)來(lái)的消息之后要自己創(chuàng)建一個(gè)新的 Port 然后又發(fā)回去 A 進(jìn)程指么。B要發(fā)消息給A酝惧,必須先知道 A進(jìn)程的 port 信息我們才能往里面發(fā)消息榴鼎。XNU 系統(tǒng)提供了 bootstrap port這個(gè)東西,由系統(tǒng)提供查詢服務(wù)晚唇,這樣所有的進(jìn)程都可以去廣播自己的 mach port 接收端的名字巫财,也可以查詢其他進(jìn)程的名字。

響應(yīng)步驟:
*第一步: ->CPU處理信號(hào)轉(zhuǎn)為指令
*第二步: ->iOS系統(tǒng)的輸入輸出驅(qū)動(dòng)IOKit.framework接收
*第三步: ->IOKit封裝觸摸事件為IOHIDEvent對(duì)象
*第四步: ->IOKit通過(guò)mach prot(IPC)將IOHIDEvent事件轉(zhuǎn)發(fā)給SpringBoard.app


桌面響應(yīng)階段

該階段是iOS桌面進(jìn)程響應(yīng)處理階段

知識(shí)點(diǎn)補(bǔ)充:

APP運(yùn)行的過(guò)程本質(zhì)上是處理各種事件的過(guò)程哩陕,把事件進(jìn)行分類平项,系統(tǒng)層事件、應(yīng)用層事件悍及、特殊事件闽瓢。source1基本就是系統(tǒng)事件,source0基本就是應(yīng)用層事件心赶。

SpringBoard.app
用來(lái)管理iOS的主屏幕的應(yīng)用程序扣讼,啟動(dòng)WindowSever(窗口服務(wù)器),bootstrapping(引導(dǎo)應(yīng)用程序),以及在啟動(dòng)時(shí)候系統(tǒng)的一些初始化設(shè)置都是由這個(gè)特定的應(yīng)用程序負(fù)責(zé)的。當(dāng)觸摸事件發(fā)生時(shí)缨叫,只有負(fù)責(zé)管理屏幕的SpringBoard.app才知道如何正確的響應(yīng)處理事件届谈。因?yàn)橹挥蠸pringBoard.app才知道屏幕處于什么狀態(tài),是有應(yīng)用在前臺(tái)活躍著弯汰,還是沒(méi)有應(yīng)用在前臺(tái)艰山,如果沒(méi)有應(yīng)用在前臺(tái)則說(shuō)明在桌面,則由SpringBoard.app進(jìn)入內(nèi)部響應(yīng)咏闪,觸發(fā)Source0回調(diào)曙搬。如果有應(yīng)用在前臺(tái)則通過(guò)mach port(IPC)轉(zhuǎn)發(fā)給對(duì)應(yīng)App。

Source1
基于 mach port的處理事件 鸽嫂,蘋果注冊(cè)了一個(gè) Source1 用來(lái)接收系統(tǒng)事件纵装,其回調(diào)函數(shù)為 __IOHIDEventSystemClientQueueCallback()。觸發(fā)回調(diào)后据某,會(huì)調(diào)用 _UIApplicationHandleEventQueue() 進(jìn)行應(yīng)用內(nèi)部的事件分發(fā)鹊杖。

Source0
非基于Port的 處理事件,什么叫非基于Port的呢匿又?就是說(shuō)這個(gè)消息不是其他進(jìn)程或者內(nèi)核直接發(fā)送的程拭。一般是APP內(nèi)部的事件, 比如hitTest:withEvent的處理, performSelectors的事件。

該階段分為兩種情況筷狼,無(wú)前臺(tái)App的響應(yīng)步驟瓶籽、有前臺(tái)App的響應(yīng)步驟。本質(zhì)上來(lái)說(shuō)是一樣的埂材。因?yàn)镾pringBoard.app本身也是App

無(wú)前臺(tái)App的響應(yīng)步驟:
*第一步:->SpringBoard.app的主線程的MainRunloop收到IOKit.framework轉(zhuǎn)發(fā)來(lái)的消息后蘇醒
*第二步:->SpringBoard.app通過(guò)注冊(cè)的source1的回調(diào)函數(shù)__IOHIDEventSystemClientQueueCallback()接收IOHIDEvent事件
*第三步:->SpringBoard.app判斷是否由誰(shuí)響應(yīng)IOHIDEvent事件
*第四步:->發(fā)現(xiàn)無(wú)前臺(tái)App
*第五步:->觸發(fā)SpringBoard.app內(nèi)部的source0回調(diào)函數(shù)__UIApplicationHandleEventQueue()
*第六步:->由SpringBoard.app內(nèi)部自行處理

有前臺(tái)App的響應(yīng)步驟:
*第一步:->SpringBoard.app的主線程的MainRunloop收到IOKit.framework轉(zhuǎn)發(fā)來(lái)的消息后蘇醒
*第二步:->SpringBoard.app通過(guò)注冊(cè)的source1的回調(diào)函數(shù)__IOHIDEventSystemClientQueueCallback()接收IOHIDEvent事件
*第三步:->SpringBoard.app判斷是否由誰(shuí)響應(yīng)IOHIDEvent事件
*第四步:->發(fā)現(xiàn)有前臺(tái)App
*第五步:->通過(guò)mach port將IOHIDEvent事件轉(zhuǎn)發(fā)給對(duì)應(yīng)前臺(tái)App


應(yīng)用響應(yīng)階段

該階段是App內(nèi)部的響應(yīng)處理階段塑顺,比較復(fù)雜。內(nèi)容比較多俏险,因此知識(shí)點(diǎn)補(bǔ)充放在了最后严拒,請(qǐng)耐心看扬绪。

我把該階段細(xì)分為三個(gè)小階段:
第一階段:->事件傳遞
第二階段:->尋找最佳響應(yīng)者
第三階段:->事件傳遞的響應(yīng)鏈

第一階段: 事件傳遞響應(yīng)步驟:
*第一步: ->App主線程的MainRunloop收到消息后蘇醒
*第二步: ->通過(guò)App的source1的回調(diào)函數(shù)__IOHIDEventSystemClientQueueCallback()接收IOHIDEvent事件
*第三步: ->source1內(nèi)部觸發(fā)source0的回調(diào)函數(shù)__UIApplicationHandleEventQueue()
*第四步: ->source0內(nèi)部對(duì)IOHIDEvent進(jìn)行封裝處理成UIEvent
*第五步: ->soucre0內(nèi)部調(diào)用UIApplication的sendEvent:方法,將UIEvent傳給UIWindow(不一定是KeyWindow裤唠,如果有多個(gè)window挤牛,則會(huì)傳遞給最上層的window)

第二階段: 尋找最佳響應(yīng)者:
*第一步: ->UIWindow通過(guò)hitTest:withEvent:與pointInside:withEvent:方法從UIView層級(jí)的最頂層往最底層遞歸查詢由那一層View是最佳的事件響應(yīng)者。
*第二步: -> 首先通過(guò)hitTest:withEvent:方法判斷該視圖是否能響應(yīng)事件巧骚,userInteractionEnabled赊颠、hidden、alpha三個(gè)屬性判斷該界面是否能響應(yīng)事件
*第三步: -> 然后通過(guò)pointInside:withEvent:方法判斷觸摸點(diǎn)是否在該View中
*第四步: -> 最后判斷該View是否有子視圖劈彪,如果有子視圖則遞歸判斷子視圖是否能響應(yīng)竣蹦,如果能響應(yīng)則在hitTest:withEvent:方法中返回子視圖作為最佳響應(yīng)者,如果子視圖不能響應(yīng)沧奴,則返回本身View作為最佳響應(yīng)者

第三階段: 事件傳遞的響應(yīng)鏈:
該階段有兩種情況痘括,有手勢(shì)識(shí)別器沒(méi)有手勢(shì)識(shí)別器滔吠。

有手勢(shì)識(shí)別器的傳遞:
*第一步: ->判斷是否有手勢(shì)識(shí)別器
*第二步: ->有手勢(shì)識(shí)別器
*第三步: ->首先把事件傳遞給手勢(shì)識(shí)別器纲菌,手勢(shì)識(shí)別器接收事件,調(diào)用touchesBegan方法,并進(jìn)入手勢(shì)識(shí)別器UIGestureRecognizer的響應(yīng)鏈傳遞
*第四步: ->然后把事件傳遞給最佳響應(yīng)者疮绷,調(diào)用最佳響應(yīng)者自身的touchesBegan方法翰舌,并進(jìn)入U(xiǎn)IResponder的響應(yīng)鏈傳遞。
*第五步: ->手勢(shì)識(shí)別器自底向上的進(jìn)行事件傳遞冬骚,直到上面某一層不響應(yīng)則斷層椅贱,會(huì)分別調(diào)用每層響應(yīng)者手勢(shì)識(shí)別器的touchesMoved、touchesEnded方法只冻。
*第六步: ->手勢(shì)識(shí)別器最終成功識(shí)別手勢(shì)并執(zhí)行任務(wù)
*第七步: ->手勢(shì)識(shí)別器調(diào)用響應(yīng)者的touchesCancelled方法來(lái)終止最佳響應(yīng)者對(duì)于事件的響應(yīng)庇麦。(因此最佳響應(yīng)者自身并不會(huì)調(diào)touchesMoved、touchesEnded方法)

沒(méi)有手勢(shì)識(shí)別器的傳遞:
*第一步: ->判斷是否有手勢(shì)識(shí)別器
*第二步: ->沒(méi)有手勢(shì)識(shí)別器
*第三步: ->把事件傳遞給最佳響應(yīng)者喜德,調(diào)用最佳響應(yīng)者自身的touchesBegan方法山橄,并進(jìn)入U(xiǎn)IResponder的響應(yīng)鏈傳遞。
*第四步: ->最佳響應(yīng)者自底向上的進(jìn)行事件傳遞舍悯,直到上面某一層不響應(yīng)則斷層航棱,會(huì)分別調(diào)用每層響應(yīng)者的touchesMoved、touchesEnded方法贱呐。
*第五步: ->最佳響應(yīng)者最終成功識(shí)別事件并執(zhí)行任務(wù)

知識(shí)點(diǎn)補(bǔ)充:

響應(yīng)鏈

響應(yīng)鏈傳遞流程

上圖是官網(wǎng)對(duì)于響應(yīng)鏈的示例展示丧诺,如果最佳響應(yīng)者對(duì)象是UITextField,則響應(yīng)鏈為:
UITextField->UIView->UIView->UIViewController->UIWindow->UIApplication->UIApplicationDelegate
如果一直沒(méi)有UIResponder做響應(yīng)處理奄薇,則這些UITouches到達(dá)最后的響應(yīng)者即UIApplication后,就被忽略而消失抗愁。

UIGestureRecognizer馁蒂、UIResponder呵晚、UIControl
這里只做簡(jiǎn)單的介紹,不做深入探討

UIGestureRecognizer
手勢(shì)識(shí)別器沫屡,繼承NSObject饵隙,UIGestureRecognizer是一個(gè)抽象類,因此我們只能使用它的子類:
UITapGestureRecognizer(點(diǎn)擊)
UIPinchGestureRecognizer(捏合)
UIRotationGestureRecognizer(旋轉(zhuǎn))
UISwipeGestureRecognizer(滑動(dòng)沮脖,快速移動(dòng)金矛,是用于監(jiān)測(cè)滑動(dòng)的方向的)
UIPanGestureRecognizer(拖移,慢速移動(dòng)勺届,是用于監(jiān)測(cè)偏移的量的)
UILongPressGestureRecognizer(長(zhǎng)按)

手勢(shì)識(shí)別是具有互斥原則的驶俊,例如 Tap 一次與Tap 兩次、Tap 與 LongPress免姿、Swipe與 Pan饼酿。比如單擊和雙擊,如果它識(shí)別出一種手勢(shì)胚膊,其后的手勢(shì)將不被識(shí)別故俐。所以對(duì)于關(guān)聯(lián)手勢(shì),要做特殊處理來(lái)幫助程序進(jìn)行甄別(requireGestureRecognizerToFail:方法)紊婉。應(yīng)該先把當(dāng)前手勢(shì)歸于一類手勢(shì)里面药版,然后做一個(gè)特殊處理邏輯,判斷手勢(shì)是否是雙擊喻犁,如果是雙擊則相應(yīng)雙擊事件槽片,如果雙擊失效,則作為單擊手勢(shì)處理株汉。
其核心就是設(shè)置delegate和在需要手勢(shì)監(jiān)測(cè)的view上使用addGestureRecognizer添加指定的手勢(shì)監(jiān)測(cè)筐乳。
當(dāng)然要記得在作為delegate的view的頭文件加上<UIGestureRecognizerDelegate>。

常用屬性介紹

//當(dāng)前手勢(shì)的狀態(tài)
@property(nonatomic,readonly) UIGestureRecognizerState state;
//設(shè)置代理
@property(nullable,nonatomic,weak) id <UIGestureRecognizerDelegate> delegate; 
//設(shè)置手勢(shì)是否有效
@property(nonatomic, getter=isEnabled) BOOL enabled;
//獲取手勢(shì)所在的view
@property(nullable, nonatomic,readonly) UIView *view; 
//獲取觸發(fā)觸摸的點(diǎn)
-(CGPoint)locationInView:(nullable UIView*)view; 
//設(shè)置觸摸點(diǎn)數(shù)
-(NSUInteger)numberOfTouches; 
//獲取某一個(gè)觸摸點(diǎn)的觸摸位置
-(CGPoint)locationOfTouch:(NSUInteger)touchIndex inView:(nullable UIView*)view;
//調(diào)用對(duì)象是需要失效的手勢(shì)乔妈,參數(shù)對(duì)象是生效的手勢(shì)蝙云。如果生效手勢(shì)失效,則調(diào)用對(duì)象手勢(shì)生效
-(void)requireGestureRecognizerToFail:(UIGestureRecognizer *)otherGestureRecognizer;
//獲取到的是手指移動(dòng)后路召,在相對(duì)坐標(biāo)中的偏移量
-(CGPoint)translationInView:(nullable UIView *)view;

/** 
當(dāng)這個(gè)屬性設(shè)置為YES時(shí)勃刨,默認(rèn)為YES
如果識(shí)別到了手勢(shì),系統(tǒng)將會(huì)發(fā)送touchesCancelled:withEvent:消息在其事件傳遞的響應(yīng)鏈上股淡,終止觸摸事件的傳遞身隐。

設(shè)置為NO時(shí)
則不會(huì)終止事件的傳遞
 */
@property(nonatomic) BOOL cancelsTouchesInView;

/**
當(dāng)這個(gè)屬性設(shè)置為NO時(shí),默認(rèn)為NO
手勢(shì)識(shí)別器識(shí)別手勢(shì)期間唯灵,觸摸對(duì)象就算狀態(tài)發(fā)生變化贾铝,也會(huì)發(fā)送事件給最佳響應(yīng)者

設(shè)置為YES時(shí)
識(shí)別手勢(shì)期間,觸摸狀態(tài)若發(fā)生變化,則不會(huì)發(fā)送事件給最佳響應(yīng)者垢揩。
*/
@property(nonatomic) BOOL delaysTouchesBegan;

/**
當(dāng)這個(gè)屬性設(shè)置為NO時(shí)玖绿,默認(rèn)為NO
在手勢(shì)識(shí)別成功后,發(fā)送touchesCancelled,手勢(shì)識(shí)別失敗時(shí)叁巨,發(fā)送touchesEnded

設(shè)置為YES時(shí)
在手勢(shì)識(shí)別成功后,發(fā)送touchesCancelled斑匪,手勢(shì)識(shí)別失敗時(shí),會(huì)延遲大概0.15ms,期間沒(méi)有接收到別的touch才會(huì)發(fā)送touchesEnded
*/
@property(nonatomic) BOOL delaysTouchesEnded

UIGestureRecognizerDelegate方法介紹

//手指觸摸屏幕后回調(diào)的方法锋勺,返回NO則不再進(jìn)行手勢(shì)識(shí)別蚀瘸,方法觸發(fā)等
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch;
//開始進(jìn)行手勢(shì)識(shí)別時(shí)調(diào)用的方法,返回NO則結(jié)束庶橱,不再觸發(fā)手勢(shì)
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer;
//是否支持多時(shí)候觸發(fā)贮勃,返回YES,則可以多個(gè)手勢(shì)一起觸發(fā)方法悬包,返回NO則為互斥
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer;
//下面這個(gè)兩個(gè)方法也是用來(lái)控制手勢(shì)的互斥執(zhí)行的
//這個(gè)方法返回YES衙猪,第一個(gè)手勢(shì)和第二個(gè)互斥時(shí),第一個(gè)會(huì)失效
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer NS_AVAILABLE_IOS(7_0);
//這個(gè)方法返回YES布近,第一個(gè)和第二個(gè)互斥時(shí)垫释,第二個(gè)會(huì)失效
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer NS_AVAILABLE_IOS(7_0);

手勢(shì)狀態(tài)介紹

typedef NS_ENUM(NSInteger, UIGestureRecognizerState) {
       UIGestureRecognizerStatePossible,    // 已經(jīng)觸發(fā)了觸摸事件,但尚未識(shí)別是何種手勢(shì)操作撑瞧,默認(rèn)狀態(tài)
       UIGestureRecognizerStateBegan,       // 手勢(shì)已經(jīng)開始棵譬,此時(shí)已經(jīng)被識(shí)別,但是這個(gè)過(guò)程中可能發(fā)生變化预伺,手勢(shì)操作尚未完成
       UIGestureRecognizerStateChanged, // 手勢(shì)狀態(tài)發(fā)生轉(zhuǎn)變  
       UIGestureRecognizerStateEnded,       // 手勢(shì)識(shí)別操作完成(此時(shí)已經(jīng)松開手指) 
       UIGestureRecognizerStateCancelled,  // 手勢(shì)被取消订咸,恢復(fù)到默認(rèn)狀態(tài)
       UIGestureRecognizerStateFailed,      // 手勢(shì)識(shí)別失敗,恢復(fù)到默認(rèn)狀態(tài)
       UIGestureRecognizerStateRecognized // 手勢(shì)識(shí)別完成酬诀,同UIGestureRecognizerStateEnded 
       UIGestureRecognizerStateEnded // 手勢(shì)識(shí)別完成
};

UIResponder
是專門用來(lái)響應(yīng)用戶的操作處理和各種事件的類脏嚷,包括觸摸事件(Touch Events)、運(yùn)動(dòng)事件(Motion Events)瞒御、遠(yuǎn)程控制事件(Remote Control Events)父叙,UIResponder是一個(gè)抽象類,因此我們主要是使用它的子類:
UIApplication
UIViewController
UIView
UIWindow(它能響應(yīng)事件是因?yàn)樗母割愂荱IView肴裙,而UIView是UIResponder的子類)

常用方法介紹

//對(duì)象是否能成為第一響應(yīng)者 YES表示能成為
- (BOOL)canBecomeFirstResponder
//設(shè)置對(duì)象成為第一響應(yīng)者
- (void)becomeFirstResponder
//對(duì)象是否能失去第一響應(yīng)者的資格 YES表示能
- (BOOL)canResignFirstResponder
//取消對(duì)象的第一響應(yīng)者資格
- (void)resignFirstResponder
//手指觸碰屏幕趾唱,觸摸開始
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
//手指在屏幕上移動(dòng)
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
//手指離開屏幕,觸摸結(jié)束
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
//觸摸結(jié)束前蜻懦,某個(gè)系統(tǒng)事件中斷了觸摸甜癞,例如電話呼入
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;

UIControl
是UIView的子類,因此也是UIResponder的子類宛乃,UIControl是一個(gè)抽象類悠咱,因此我們主要使用它的子類:
UIButton
UISwitch
UITextField

UIControl采用了一種不同于UIResponder的處理機(jī)制蒸辆,它將標(biāo)準(zhǔn)的觸摸事件轉(zhuǎn)換封裝成為易于使用的特殊的控件事件,例如通過(guò)UIControl對(duì)象處理后乔煞,按下按鈕的事件就被封裝成一個(gè)控件事件吁朦,而不用去判斷觸摸屏幕的整個(gè)操作過(guò)程柒室。
例如使用繼承之UIControl的UIButton時(shí)渡贾,為UIButton對(duì)象添加觸摸事件
例如
UIButton *button = [UIButton new];
[button addTarget:self action:@selector(buttonAction) forControlEvents:UIControlEvents];
button:是要響應(yīng)事件的控件對(duì)象。
self:是要把觸摸事件的消息發(fā)送到哪個(gè)對(duì)象里去雄右。
@selector(buttonAction):后面是一個(gè)方法選擇器空骚,表示該事件需要響應(yīng)的方法,方法里面是執(zhí)行的任務(wù)擂仍;
UIControlEvents:是事件類型囤屹,表示響應(yīng)什么樣的事件。

UIControlEvents事件類型介紹

typedef NS_OPTIONS(NSUInteger, UIControlEvents) {
    UIControlEventTouchDown                                         = 1 <<  0,      // 單點(diǎn)觸摸按下事件:用戶點(diǎn)觸屏幕或者又有新的手指落下的時(shí)候逢渔。
    UIControlEventTouchDownRepeat                                   = 1 <<  1,      //多點(diǎn)觸摸按下事件肋坚,點(diǎn)觸計(jì)數(shù)大于1:用戶按下第二、三肃廓、或第四根手指的時(shí)候智厌。
    UIControlEventTouchDragInside                                   = 1 <<  2,      //當(dāng)一次觸摸在控件窗口內(nèi)拖動(dòng)時(shí)。
    UIControlEventTouchDragOutside                                  = 1 <<  3,  //當(dāng)一次觸摸在控件窗口之外拖動(dòng)時(shí)盲赊。
    UIControlEventTouchDragEnter                                    = 1 <<  4,      //當(dāng)一次觸摸從控件窗口之外拖動(dòng)到內(nèi)部時(shí)铣鹏。
    UIControlEventTouchDragExit                                     = 1 <<  5,      //當(dāng)一次觸摸從控件窗口內(nèi)部拖動(dòng)到外部時(shí)。
    UIControlEventTouchUpInside                                     = 1 <<  6,      //所有在控件之內(nèi)觸摸抬起事件哀蘑。
    UIControlEventTouchUpOutside                                    = 1 <<  7,      //所有在控件之外觸摸抬起事件(點(diǎn)觸必須開始與控件內(nèi)部才會(huì)發(fā)送通知)诚卸。
    UIControlEventTouchCancel                                       = 1 <<  8,      //所有觸摸取消事件,即一次觸摸因?yàn)榉派狭颂嗍种付蝗∠媲ǎ蛘弑簧湘i或者電話呼叫打斷合溺。

    UIControlEventValueChanged                                      = 1 << 12,     // 當(dāng)控件的值發(fā)生改變時(shí),發(fā)送通知缀台。用于滑塊棠赛、分段控件、以及其他取值的控件将硝。你可以配置滑塊控件何時(shí)發(fā)送通知恭朗,在滑塊被放下時(shí)發(fā)送,或者在被拖動(dòng)時(shí)發(fā)送依疼。
    UIControlEventPrimaryActionTriggered API_AVAILABLE(ios(9.0)) = 1 << 13,     // 按鈕觸發(fā)的語(yǔ)義動(dòng)作

    UIControlEventEditingDidBegin                                   = 1 << 16,     // 當(dāng)文本控件中開始編輯時(shí)發(fā)送通知痰腮。
    UIControlEventEditingChanged                                    = 1 << 17,      //當(dāng)文本控件中的文本被改變時(shí)發(fā)送通知。
    UIControlEventEditingDidEnd                                     = 1 << 18,      //當(dāng)文本控件中編輯結(jié)束時(shí)發(fā)送通知律罢。
    UIControlEventEditingDidEndOnExit                               = 1 << 19,     // 當(dāng)文本控件內(nèi)通過(guò)按下回車鍵(或等價(jià)行為)結(jié)束編輯時(shí)膀值,發(fā)送通知棍丐。

    UIControlEventAllTouchEvents                                    = 0x00000FFF,  // 通知所有觸摸事件。
    UIControlEventAllEditingEvents                                  = 0x000F0000,  // 通知所有關(guān)于文本編輯的事件沧踏。
    UIControlEventApplicationReserved                               = 0x0F000000,  // 應(yīng)用保留事件
    UIControlEventSystemReserved                                    = 0xF0000000,  // 系統(tǒng)保留事件
    UIControlEventAllEvents                                         = 0xFFFFFFFF    //通知所有事件
};

UIGestureRecognizer歌逢、UIResponder、翘狱、UIControl的優(yōu)先級(jí)
UIGestureRecognizer優(yōu)先級(jí) 大于 UIResponder優(yōu)先級(jí) 秘案,UIResponder優(yōu)先級(jí) 與 UIControl優(yōu)先級(jí) 相同

最后補(bǔ)充一張圖片


觸摸事件流程圖

參考資料:
ios開發(fā) 之 UIResponder詳解
iOS觸摸事件的傳遞過(guò)程

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市潦匈,隨后出現(xiàn)的幾起案子阱高,更是在濱河造成了極大的恐慌,老刑警劉巖茬缩,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件赤惊,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡凰锡,警方通過(guò)查閱死者的電腦和手機(jī)未舟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)掂为,“玉大人裕膀,你說(shuō)我怎么就攤上這事∑刑停” “怎么了魂角?”我有些...
    開封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)智绸。 經(jīng)常有香客問(wèn)我野揪,道長(zhǎng),這世上最難降的妖魔是什么瞧栗? 我笑而不...
    開封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任斯稳,我火速辦了婚禮,結(jié)果婚禮上迹恐,老公的妹妹穿的比我還像新娘挣惰。我一直安慰自己,他們只是感情好殴边,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開白布憎茂。 她就那樣靜靜地躺著,像睡著了一般锤岸。 火紅的嫁衣襯著肌膚如雪竖幔。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天是偷,我揣著相機(jī)與錄音拳氢,去河邊找鬼募逞。 笑死,一個(gè)胖子當(dāng)著我的面吹牛馋评,可吹牛的內(nèi)容都是我干的放接。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼留特,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼纠脾!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起磕秤,我...
    開封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤乳乌,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后市咆,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡再来,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年蒙兰,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片芒篷。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡搜变,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出针炉,到底是詐尸還是另有隱情挠他,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布篡帕,位于F島的核電站殖侵,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏镰烧。R本人自食惡果不足惜拢军,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望怔鳖。 院中可真熱鬧茉唉,春花似錦、人聲如沸结执。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)献幔。三九已至懂傀,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間斜姥,已是汗流浹背鸿竖。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工沧竟, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人缚忧。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓悟泵,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親闪水。 傳聞我的和親對(duì)象是個(gè)殘疾皇子糕非,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 系統(tǒng)響應(yīng)階段 1.手指觸碰屏幕,屏幕感受到觸摸后球榆,將事件交由IOKit來(lái)處理朽肥。 2.IOKIT將觸摸事件封裝成IO...
    雪山飛狐_91ae閱讀 7,339評(píng)論 4 37
  • 在開發(fā)過(guò)程中,大家或多或少的都會(huì)碰到令人頭疼的手勢(shì)沖突問(wèn)題持钉,正好前兩天碰到一個(gè)類似的bug衡招,于是借著這個(gè)機(jī)會(huì)了解了...
    閆仕偉閱讀 5,298評(píng)論 2 23
  • 觸摸事件的生命周期 當(dāng)我們手指觸碰屏幕的那一刻,一個(gè)觸摸事件便產(chǎn)生了每强。經(jīng)過(guò)進(jìn)程間通信始腾,觸摸事件被傳遞到合適的應(yīng)用之...
    Gintok閱讀 1,333評(píng)論 0 3
  • 在iOS開發(fā)中經(jīng)常會(huì)涉及到觸摸事件。本想自己總結(jié)一下空执,但是遇到了這篇文章浪箭,感覺(jué)總結(jié)的已經(jīng)很到位,特此轉(zhuǎn)載辨绊。作者:L...
    WQ_UESTC閱讀 5,989評(píng)論 4 26
  • 系統(tǒng)響應(yīng)階段 1.手指觸碰屏幕奶栖,屏幕感受到觸摸后,將事件交由IOKit來(lái)處理门坷。 2.IOKIT將觸摸事件封裝成IO...
    二斤寂寞閱讀 675評(píng)論 0 1