iOS事件的响应和传递机制
跟二狗子哥哥交流的時(shí)候,他總說(shuō)我,說(shuō)的過(guò)程太業(yè)余。故 好好學(xué)習(xí)整理一下。努力不那么業(yè)余。
一、事件的產(chǎn)生、傳遞、響應(yīng):
1、事件從父控件依次傳遞到子控件,尋找最合適的子控件View。
2、尋找最合適的View的底層實(shí)現(xiàn),攔截事件的處理。
3、找到最合適的view之后的事件處理,也就是事件響應(yīng),重寫touch方法等。
?
傳遞過(guò)程中比較重要的兩點(diǎn):
1.如何尋找最合適的view
2.尋找最合適的view的底層實(shí)現(xiàn)(hitTest:withEvent:底層實(shí)現(xiàn))
觸摸事件 加速事件 遠(yuǎn)程控制事件 我們現(xiàn)在 討論觸摸事件
?
二、響應(yīng)者對(duì)象(UIResponder)
只有繼承了UIResponder的對(duì)象,才可以接受并處理事件,我們稱之為響應(yīng)者對(duì)象
響應(yīng)者對(duì)象有:UIApplication UIViewController UIView
UIResponder提供了方法以下方法處理觸摸事件:
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesEstimatedPropertiesUpdated:(NSSet<UITouch *> *)touches NS_AVAILABLE_IOS(9_1);
?
Presses事件
- (void)pressesBegan:(NSSet<UIPress *> *)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0);
- (void)pressesChanged:(NSSet<UIPress *> *)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0);
- (void)pressesEnded:(NSSet<UIPress *> *)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0);
- (void)pressesCancelled:(NSSet<UIPress *> *)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0);
?
UIResponder提供了方法以下方法處理加速事件:
- (void)motionBegan:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(3_0);
- (void)motionEnded:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(3_0);
- (void)motionCancelled:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(3_0);
?
UIResponder提供了方法以下方法處理遠(yuǎn)程事件:
- (void)remoteControlReceivedWithEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(4_0);
?
另外還有:
- (BOOL)canPerformAction:(SEL)action withSender:(nullable id)sender NS_AVAILABLE_IOS(3_0);
?
允許一個(gè)事件處理是可轉(zhuǎn)發(fā)給其他對(duì)象, 默認(rèn)情況是 調(diào)用方法?-canPerformAction:withSender: 來(lái)返回自己或者提交返回給響應(yīng)鏈去判斷處理。
// Allows an action to be forwarded to another target. By default checks -canPerformAction:withSender: to either return self, or go up the responder chain.
- (nullable id)targetForAction:(SEL)action withSender:(nullable id)sender NS_AVAILABLE_IOS(7_0)
?
三、事件傳遞:用戶的觸摸事件,首先被封裝成一個(gè)UIEvent事件,然后UIEvent事件傳遞給UIResponder的事件,進(jìn)行判斷,尋找最合適的View。
UIEvent事件中會(huì)有UITouch對(duì)象集,保存著UITouch對(duì)象
@property(nonatomic, readonly, nullable) NSSet <UITouch *> *allTouches;
一根手指對(duì)應(yīng)一個(gè)UITouch對(duì)象,
如果兩根手指同事觸摸,會(huì)調(diào)用一次- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;,其中的UIEvent事件中有兩個(gè)UITouch對(duì)象
如果兩根手指先后觸摸,會(huì)調(diào)用兩次- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;方法,每個(gè)方法的UIEvent事件中各有一個(gè)UITouch對(duì)象
UITouch中保存著手指相關(guān)的信息,觸摸的位置,時(shí)間,階段。
當(dāng)手指移動(dòng)時(shí),系統(tǒng)會(huì)更新同一個(gè)UITouch對(duì)象,使之能夠一直保存該手指在的觸摸位置
當(dāng)手指離開(kāi)屏幕時(shí),系統(tǒng)會(huì)銷毀相應(yīng)的UITouch對(duì)象
事件產(chǎn)生之后,系統(tǒng)會(huì)將事件添加到UIApplication管理的事件隊(duì)列中,UIApplication會(huì)從事件隊(duì)列中取出最前面的事件,并將事件分發(fā)下去處理,先將事件發(fā)送給應(yīng)用程序的主窗口 keyWindow。
keyWindow會(huì)在視圖層次結(jié)構(gòu)中找到一個(gè)最合適的視圖來(lái)處理觸摸事件,這也是整個(gè)事件處理過(guò)程的第一步。
事件產(chǎn)生之后,就是事件的傳遞過(guò)程:
從父控件到子控件尋找合適的視圖控件:
? ? ? ? ?- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event;
? ? ? ? ? recursively calls -pointInside:withEvent:. point is in the receiver's coordinate system
? ? ? ? ?遞歸調(diào)用-pointInside:withEvent:方法,判斷point是否在其范圍內(nèi)
?
- 1.首先判斷主窗口(keyWindow)自己是否能接受觸摸事件
?
?
- 2.判斷觸摸點(diǎn)是否在自己身上(pointInside方法)
? ? ? ?- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event;
? ? ? ? ?// default returns YES if point is in bounds ?如果點(diǎn)在bouns范圍內(nèi),默認(rèn)返回yes
- 3.子控件數(shù)組中從后往前遍歷子控件,重復(fù)前面的兩個(gè)步驟(所謂從后往前遍歷子控件,就是首先查找子控件數(shù)組中最后一個(gè)元素,然后執(zhí)行1、2步驟)
- 4.view,比如叫做fitView,那么會(huì)把這個(gè)事件交給這個(gè)fitView,再遍歷這個(gè)fitView的子控件,直至沒(méi)有更合適的view為止。
- 5.如果沒(méi)有符合條件的子控件,那么就認(rèn)為自己最合適處理這個(gè)事件,也就是自己是最合適的view。
期間用到的重要的兩個(gè)方法:
- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event;
?- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event
UIView不能接收觸摸事件的三種情況:
- 不允許交互:userInteractionEnabled = NO
- 隱藏:如果把父控件隱藏,那么子控件也會(huì)隱藏,隱藏的控件不能接受事件
- 透明度:如果設(shè)置一個(gè)控件的透明度<0.01,會(huì)直接影響子控件的透明度。alpha:0.0~0.01為透明。
主要方法解析:
- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event;
調(diào)用時(shí)機(jī):
只要事件一傳給某個(gè)控件,控件就會(huì)調(diào)用自己的該方法,尋找合適的子控件返回。
作用:
尋找合適的子控件返回
注 意:不管這個(gè)控件能不能處理事件,也不管觸摸點(diǎn)在不在這個(gè)控件上,事件都會(huì)先傳遞給這個(gè)控件,隨后再調(diào)用hitTest:withEvent:方法
?- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event
攔截事件的處理:
- 正因?yàn)閔itTest:withEvent:方法可以返回最合適的view,所以可以通過(guò)重寫hitTest:withEvent:方法,返回指定的view作為最合適的view。
- 不管點(diǎn)擊哪里,最合適的view都是hitTest:withEvent:方法中返回的那個(gè)view。
- 通過(guò)重寫hitTest:withEvent:,就可以攔截事件的傳遞過(guò)程,想讓誰(shuí)處理事件誰(shuí)就處理事件。
如果hitTest:withEvent:方法中返回nil,那么調(diào)用該方法的控件本身和其子控件都不是最合適的view,也就是在自己身上沒(méi)有找到更合適的view。那么最合適的view就是該控件的父控件。
事件傳遞順序:
產(chǎn)生觸摸事件->UIApplication事件隊(duì)列->[UIWindow hitTest:withEvent:]->返回更合適的view->[子控件 hitTest:withEvent:]->返回最合適的view
重寫的技巧:在父控件的hitTest:withEvent:中返回子控件作為最合適的view!
?
hit:withEvent:方法底層會(huì)調(diào)用pointInside:withEvent:方法判斷點(diǎn)在不在方法調(diào)用者的坐標(biāo)系上
?
?
pointInside:withEvent:方法
判斷點(diǎn)在不在當(dāng)前view上(方法調(diào)用者的坐標(biāo)系上)如果返回YES,代表點(diǎn)在方法調(diào)用者的坐標(biāo)系上;返回NO代表點(diǎn)不在方法調(diào)用者的坐標(biāo)系上,那么方法調(diào)用者也就不能處理事件。
?
四、事件的響應(yīng):
響應(yīng)者鏈條
如何判斷上一個(gè)響應(yīng)者
- 1> 如果當(dāng)前這個(gè)view是控制器的view,那么控制器就是上一個(gè)響應(yīng)者
- 2> 如果當(dāng)前這個(gè)view不是控制器的view,那么父控件就是上一個(gè)響應(yīng)者
響應(yīng)者鏈的事件傳遞過(guò)程:
- 1>如果當(dāng)前view是控制器的view,那么控制器就是上一個(gè)響應(yīng)者,事件就傳遞給控制器;如果當(dāng)前view不是控制器的view,那么父視圖就是當(dāng)前view的上一個(gè)響應(yīng)者,事件就傳遞給它的父視圖
- 2>在視圖層次結(jié)構(gòu)的最頂級(jí)視圖,如果也不能處理收到的事件或消息,則其將事件或消息傳遞給window對(duì)象進(jìn)行處理
- 3>如果window對(duì)象也不處理,則其將事件或消息傳遞給UIApplication對(duì)象
- 4>如果UIApplication也不能處理該事件或消息,則將其丟棄
五、事件處理的整個(gè)流程總結(jié):
1.觸摸屏幕產(chǎn)生觸摸事件后,觸摸事件會(huì)被添加到由UIApplication管理的事件隊(duì)列中(即,首先接收到事件的是UIApplication)。
2.UIApplication會(huì)從事件隊(duì)列中取出最前面的事件,把事件傳遞給應(yīng)用程序的主窗口(keyWindow)。
3.主窗口會(huì)在視圖層次結(jié)構(gòu)中找到一個(gè)最合適的視圖來(lái)處理觸摸事件。(至此,第一步已完成)
4.最合適的view會(huì)調(diào)用自己的touches方法處理事件
5.touches默認(rèn)做法是把事件順著響應(yīng)者鏈條向上拋。
參考:http://www.cnblogs.com/machao/p/5471094.html
十分感謝前輩的分享
轉(zhuǎn)載于:https://www.cnblogs.com/Jordandan/p/6483835.html
總結(jié)
以上是生活随笔為你收集整理的iOS事件的响应和传递机制的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 预编译对象解决SQL注入问题
- 下一篇: codevs 1283 等差子序列