響應(yīng)者鏈
- 響應(yīng)者對象:繼承自UIResponder的對象稱之為響應(yīng)者對象暇矫。UIApplication、UIWindow择吊、UIViewController和所有繼承UIView的UIKit類都直接或間接的繼承自UIResponder李根。
UIResponder一般響應(yīng)以下幾種事件:觸摸事件(touch handling)、點按事件(press handling)几睛、加速事件和遠程控制事件:
觸摸事件(touch handling)
- (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);
點按事件(press handling) NS_AVAILABLE_IOS(9_0)
- (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);
加速事件
- (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);
-
響應(yīng)者鏈:由多個響應(yīng)者組合起來的鏈條房轿,就叫做響應(yīng)者鏈。它表示了每個響應(yīng)者之間的聯(lián)系所森,并且可以使得一個事件可選擇多個對象處理
假設(shè)觸摸了initial view囱持,
1.第一響應(yīng)者就是initial view即initial view首先響應(yīng)touchesBegan:withEvent:方法,接著傳遞給橘黃色的view
2.橘黃色的view開始響應(yīng)touchesBegan:withEvent:方法焕济,接著傳遞給藍綠色view
3.藍綠色view響應(yīng)touchesBegan:withEvent:方法纷妆,接著傳遞給控制器的view
4.控制器view響應(yīng)touchesBegan:withEvent:方法,控制器傳遞給了窗口
5.窗口再傳遞給application
如果上述響應(yīng)者都不處理該事件啥刻,那么事件被丟棄
事件的產(chǎn)生和傳遞
當(dāng)一個觸摸事件產(chǎn)生的時候进萄,我們的程序是如何找到第一響應(yīng)者的呢浮禾?
當(dāng)你點擊了屏幕會產(chǎn)生一個觸摸事件,消息循環(huán)(runloop)會接收到觸摸事件放到消息隊列里际邻,UIApplication會會從消息隊列里取事件分發(fā)下去,首先傳給UIWindow芍阎,UIWindow會使用hitTest:withEvent:方法找到此次觸摸事件初始點所在的視圖枯怖,找到這個視圖之后他就會調(diào)用視圖的touchesBegan:withEvent:方法來處理事件。
-
hitTest:withEvent:查找過程
圖片中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的范圍內(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對象還不處理乖篷,就丟棄該事件响驴。
注意:控件不能響應(yīng)的情況
1.userInteractionEnabled = NO
2.hidden = YES
3.透明度 alpha 小于等于0.01
4.子視圖超出了父視圖區(qū)域
子視圖超出父視圖,不響應(yīng)的原因:因為父視圖的pointInside:withEvent:方法返回了NO撕蔼,就不會遍歷子視圖了豁鲤。可以重寫pointInside:withEvent:方法解決此問題鲸沮。