iOS UI事件傳遞與響應(yīng)者鏈

iOS UI事件傳遞與響應(yīng)者鏈

響應(yīng)者鏈

  • 響應(yīng)者對象:繼承自UIResponder的對象稱之為響應(yīng)者對象。UIApplication、UIWindow、UIViewController和所有繼承UIView的UIKit類都直接或間接的繼承自UIResponder。

UIResponder一般響應(yīng)以下幾種事件:觸摸事件(touch handling)、點按事件(press handling)、加速事件和遠(yuǎn)程控制事件:

觸摸事件(touch handling)

- (``void``)touchesBegan:(NSSet *)touches withEvent:(nullable UIEvent *)event;

- (``void``)touchesMoved:(NSSet *)touches withEvent:(nullable UIEvent *)event;

- (``void``)touchesEnded:(NSSet *)touches withEvent:(nullable UIEvent *)event;

- (``void``)touchesCancelled:(NSSet *)touches withEvent:(nullable UIEvent *)event;

- (``void``)touchesEstimatedPropertiesUpdated:(NSSet *)touches NS_AVAILABLE_IOS(9_1);

點按事件(press handling) NS_AVAILABLE_IOS(9_0)

- (``void``)pressesBegan:(NSSet *)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0);

- (``void``)pressesChanged:(NSSet *)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0);

- (``void``)pressesEnded:(NSSet *)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0);

- (``void``)pressesCancelled:(NSSet *)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0);

加速事件

- (``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);

遠(yuǎn)程控制事件

- (``void``)remoteControlReceivedWithEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(4_0);

|

響應(yīng)者鏈:由多個響應(yīng)者組合起來的鏈條,就叫做響應(yīng)者鏈。它表示了每個響應(yīng)者之間的聯(lián)系,并且可以使得一個事件可選擇多個對象處理

image

響應(yīng)者鏈.png

假設(shè)觸摸了initial view,

1.第一響應(yīng)者就是initial view即initial view首先響應(yīng)touchesBegan:withEvent:方法,接著傳遞給橘黃色的view

2.橘黃色的view開始響應(yīng)touchesBegan:withEvent:方法,接著傳遞給藍(lán)綠色view

3.藍(lán)綠色view響應(yīng)touchesBegan:withEvent:方法,接著傳遞給控制器的view

4.控制器view響應(yīng)touchesBegan:withEvent:方法,控制器傳遞給了窗口

5.窗口再傳遞給application

如果上述響應(yīng)者都不處理該事件,那么事件被丟棄

事件的產(chǎn)生和傳遞

當(dāng)一個觸摸事件產(chǎn)生的時候,我們的程序是如何找到第一響應(yīng)者的呢?

image

事件的產(chǎn)生傳遞.png

當(dāng)你點擊了屏幕會產(chǎn)生一個觸摸事件,消息循環(huán)(runloop)會接收到觸摸事件放到消息隊列里,UIApplication會會從消息隊列里取事件分發(fā)下去,首先傳給UIWindow,UIWindow會使用hitTest:withEvent:方法找到此次觸摸事件初始點所在的視圖,找到這個視圖之后他就會調(diào)用視圖的touchesBegan:withEvent:方法來處理事件。

  • hitTest:withEvent:查找過程
image

hitTestview過程.png

圖片中view等級

[ViewA addSubview:ViewB];

[ViewA addSubview:ViewC];

[ViewB addSubview:ViewD];

[ViewB addSubview:ViewE];

點擊viewE:

1.A 是UIWindow的根視圖,首先對A進(jìn)行hitTest:withEvent:

2.判斷A的userInteractionEnabled,如果為NO,A的hitTest:withEvent返回nil;

3.pointInside:withEvent:方法判斷用戶點擊是否在A的范圍內(nèi),顯然返回YES

4.遍歷A的子視圖B和C,由于從后向前遍歷,

  • 因此先查看C,調(diào)用C的hitTest:withEvent方法:pointInside:withEvent:方法判斷用戶點擊是否在C的范圍內(nèi),不在返回NO,C對應(yīng)的hitTest:withEvent: 方法return nil;

  • 再查看B,調(diào)用B的hitTest:withEvent方法:pointInside:withEvent:判斷用戶點擊是否在B的返回內(nèi),在返回YES

遍歷B的子視圖D和E,從后向前遍歷,

先查看E,調(diào)用E的hitTest:withEvent方法:pointInside:withEvent:方法 判斷用戶點擊是否在E的范圍內(nèi),在返回YES,E沒有子視圖,因此E對應(yīng)的hitTest:withEvent方法返回E,再往前回溯,就是B的hitTest:withEvent方法返回E,因此A的hitTest:withEvent方法返回E。

至此,點擊事件的第一響應(yīng)者就找到了。

如果hitTest:withEvent: 找到的第一響應(yīng)者view沒有處理該事件,那么事件會沿著響應(yīng)者鏈向上傳遞->父視圖->視圖控制器,如果傳遞到最頂級視圖還沒處理事件,那么就傳遞給UIWindow處理,若window對象也不處理->交給UIApplication處理,如果UIApplication對象還不處理,就丟棄該事件。

image

事件流程.png

注意:控件不能響應(yīng)的情況

1.userInteractionEnabled = NO

2.hidden = YES

3.透明度 alpha 小于等于0.01

4.子視圖超出了父視圖區(qū)域

子視圖超出父視圖,不響應(yīng)的原因:因為父視圖的pointInside:withEvent:方法返回了NO,就不會遍歷子視圖了??梢灾貙憄ointInside:withEvent:方法解決此問題。

轉(zhuǎn)自:http://www.cocoachina.com/ios/20171012/20771.html

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

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

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