iOS UI事件傳遞與響應者鏈

轉載:http://www.cocoachina.com/ios/20171012/20771.htmlhtml

響應者鏈ios

  • 響應者對象:繼承自UIResponder的對象稱之爲響應者對象。UIApplication、UIWindow、UIViewController和全部繼承UIView的UIKit類都直接或間接的繼承自UIResponder。app

UIResponder通常響應如下幾種事件:觸摸事件(touch handling)、點按事件(press handling)、加速事件和遠程控制事件:ide

1oop

2spa

3code

4htm

5對象

6繼承

7

8

9

10

11

12

13

14

15

16

17

觸摸事件(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);

遠程控制事件

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

響應者鏈:由多個響應者組合起來的鏈條,就叫作響應者鏈。它表示了每一個響應者之間的聯繫,而且能夠使得一個事件可選擇多個對象處理

響應者鏈.png

假設觸摸了initial view,

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

2.橘黃色的view開始響應touchesBegan:withEvent:方法,接着傳遞給藍綠色view

3.藍綠色view響應touchesBegan:withEvent:方法,接着傳遞給控制器的view

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

5.窗口再傳遞給application

若是上述響應者都不處理該事件,那麼事件被丟棄

事件的產生和傳遞

當一個觸摸事件產生的時候,咱們的程序是如何找到第一響應者的呢?

事件的產生傳遞.png

 

當你點擊了屏幕會產生一個觸摸事件,消息循環(runloop)會接收到觸摸事件放到消息隊列裏,UIApplication會會從消息隊列裏取事件分發下去,首先傳給UIWindow,UIWindow會使用hitTest:withEvent:方法找到這次觸摸事件初始點所在的視圖,找到這個視圖以後他就會調用視圖的touchesBegan:withEvent:方法來處理事件。

  • hitTest:withEvent:查找過程

hitTestview過程.png

1

2

3

4

5

圖片中view等級

    [ViewA addSubview:ViewB];

    [ViewA addSubview:ViewC];

    [ViewB addSubview:ViewD];

    [ViewB addSubview:ViewE];

點擊viewE:

1.A 是UIWindow的根視圖,首先對A進行hitTest:withEvent:

2.判斷A的userInteractionEnabled,若是爲NO,A的hitTest:withEvent返回nil;

3.pointInside:withEvent:方法判斷用戶點擊是否在A的範圍內,顯然返回YES

4.遍歷A的子視圖B和C,因爲從後向前遍歷,

  • 所以先查看C,調用C的hitTest:withEvent方法:pointInside:withEvent:方法判斷用戶點擊是否在C的範圍內,不在返回NO,C對應的hitTest:withEvent: 方法return nil;

  • 再查看B,調用B的hitTest:withEvent方法:pointInside:withEvent:判斷用戶點擊是否在B的返回內,在返回YES

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

先查看E,調用E的hitTest:withEvent方法:pointInside:withEvent:方法 判斷用戶點擊是否在E的範圍內,在返回YES,E沒有子視圖,所以E對應的hitTest:withEvent方法返回E,再往前回溯,就是B的hitTest:withEvent方法返回E,所以A的hitTest:withEvent方法返回E。

至此,點擊事件的第一響應者就找到了。

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

事件流程.png

注意:控件不能響應的狀況

1.userInteractionEnabled = NO

2.hidden = YES

3.透明度 alpha 小於等於0.01

4.子視圖超出了父視圖區域

子視圖超出父視圖,不響應的緣由:由於父視圖的pointInside:withEvent:方法返回了NO,就不會遍歷子視圖了。能夠重寫pointInside:withEvent:方法解決此問題。

相關文章
相關標籤/搜索