每一個(gè)應(yīng)用有一個(gè)響應(yīng)者鏈,我們的視圖結(jié)構(gòu)是一個(gè)N叉樹(shù)(一個(gè)視圖可以有多個(gè)子視圖,一個(gè)子視圖同一時(shí)刻只有一個(gè)父視圖),而每一個(gè)繼承UIResponder的對(duì)象都可以在這個(gè)N叉樹(shù)中扮演一個(gè)節(jié)點(diǎn)。當(dāng)葉節(jié)點(diǎn)成為最高響應(yīng)者的時(shí)候,從這個(gè)葉節(jié)點(diǎn)開(kāi)始往其父節(jié)點(diǎn)開(kāi)始追朔出一條鏈,那么對(duì)于這一個(gè)葉節(jié)點(diǎn)來(lái)講,這一條鏈就是當(dāng)前的響應(yīng)者鏈。響應(yīng)者鏈將系統(tǒng)捕獲到的UIEvent與UITouch從葉節(jié)點(diǎn)開(kāi)始層層向下分發(fā),期間可以選擇停止分發(fā),也可以選擇繼續(xù)向下分發(fā)。
例子:
我用SingleView模板創(chuàng)建了一個(gè)新的工程,它的主Window上只有一個(gè)UIViewController,其View之上有一個(gè)Button。這個(gè)項(xiàng)目中所有UIResponder的子類所構(gòu)成的N叉樹(shù)為這樣的結(jié)構(gòu):

那么他看起來(lái)并不像N叉樹(shù),但是不代表者不是一顆N叉樹(shù),當(dāng)我們項(xiàng)目復(fù)雜之后,這個(gè)View可不可以有多個(gè)UIButton節(jié)點(diǎn)?所以他就是一棵樹(shù)。
實(shí)際上我們要把這棵樹(shù)寫完整,應(yīng)該還要算上UIButton的UILabel和UIImageView,因?yàn)樗麄円彩荱IReponder的子類。這里先不考慮了。
我們對(duì)UIButton來(lái)講,他此時(shí)若是葉節(jié)點(diǎn),那么這時(shí)我們針對(duì)他所在的響應(yīng)鏈來(lái)說(shuō),他在他之前的響應(yīng)者就應(yīng)該是我們controller的view(樹(shù)中的葉節(jié)點(diǎn)比父節(jié)點(diǎn)永遠(yuǎn)更優(yōu)先被分發(fā)事件,但是并不是說(shuō)他就能在時(shí)間上先響應(yīng),我們下面講為什么)。所以我們嘗試在任意地方打印這個(gè)Button的nextReponder對(duì)象。nextResponder對(duì)象是UIReponder類的實(shí)例方法,它會(huì)返回任意對(duì)象在樹(shù)中的上一個(gè)響應(yīng)者實(shí)例:
控制臺(tái)輸出消息:**2013-09-21 03:40:25.989** **響應(yīng)鏈** **[614:60b] <UIView: 0x16555e10; frame = (0 0; 320 568); autoresize = RM+BM; layer = <CALayer: 0x16555e70>>** ```
我們可以根據(jù)這個(gè)UIView的尺寸來(lái)得知,他就是我們唯一的控制器中的那個(gè)UIView。
接下來(lái)我們?cè)俅蛴∠逻@個(gè)UIView的下一個(gè)響應(yīng)者是誰(shuí):
```NSLog(@"%@",_testButton.nextResponder.nextResponder);
輸出: **2013-09-21 03:45:03.914** **響應(yīng)鏈** **[621:60b] <RSViewController: 0x15da0e30>** ```
依次看,接著加一個(gè)nextResponder:
```**2013-09-21 03:50:49.428** **響應(yīng)鏈** **[669:60b] (null)** ```
為什么這里ViewController沒(méi)有父親呢?注意這句代碼我是寫在ViewDidLoad中,而我們知道這個(gè)方法的生命周期比較早,所以我們換個(gè)地方寫或者延遲一段時(shí)間再打印,兩種方法都可以得到結(jié)果(由此可以推理出我們響應(yīng)者樹(shù)的構(gòu)造過(guò)程是在ViewDidLoad周期中來(lái)完成的,這個(gè)函數(shù)會(huì)將當(dāng)前實(shí)例的構(gòu)成的響應(yīng)者子樹(shù)合并到我們整個(gè)根樹(shù)中):
```**2013-09-21 03:53:47.304** **響應(yīng)鏈** **[681:60b] <UIWindow: 0x14e24200; frame = (0 0; 320 568); gestureRecognizers = <NSArray: 0x14e242e0>; layer = <UIWindowLayer: 0x14e244a0>>** ```
再繼續(xù)往上追朔:double delayInSeconds = 2.0; dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ NSLog(@"%@",_testButton.nextResponder.nextResponder.nextResponder.nextResponder); });
```**2013-09-21 03:56:22.043** **響應(yīng)鏈** **[690:60b] <UIApplication: 0x15659c00>** ```
再加一個(gè)```:**2013-09-21 03:56:51.186** **響應(yīng)鏈** **[696:60b] <RSAppDelegate: 0x16663520>** ```
那么我們的appDelegate還有沒(méi)有父節(jié)點(diǎn)?```**2013-09-21 03:57:22.588** **響應(yīng)鏈** **[706:60b] (null)** ```
沒(méi)有了,注意,一個(gè)從葉節(jié)點(diǎn)開(kāi)始分發(fā)的事件,最多也就只能分發(fā)到我們的AppDelegate了!
這個(gè)樹(shù)形結(jié)構(gòu)在我們的項(xiàng)目中尤為重要,舉個(gè)栗子,如果我們想在一個(gè)view中重寫UITouchEvent的4個(gè)方法,并且不影響他的父視圖也響應(yīng)這些事件,就要注意你重寫的方式了,比如我們?cè)赩iewController中重寫touchBegan如下:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ NSLog(@"ViewController接收到觸摸事件");}
在appDelegate的中同樣也寫上這一段:-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ NSLog(@"appDelegate接收到觸摸事件");}
那么究竟是誰(shuí)被觸發(fā)呢?
```**2013-09-21 04:02:49.405** **響應(yīng)鏈** **[743:60b] ```ViewController** **接收到觸摸事件**
這個(gè)很好理解,我剛剛也說(shuō)了,viewController明顯是appDelegate的子節(jié)點(diǎn),他有事件分發(fā)的優(yōu)先權(quán)。如果我們想兩個(gè)地方都觸發(fā)呢?這里super一下就可以了:-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ [super touchesBegan:touches withEvent:event]; NSLog(@"ViewController接收到觸摸事件");}
```輸出:**2013-09-21 04:07:26.206** **響應(yīng)鏈** **[749:60b] appDelegate** **接收到觸摸事件** ```
**2013-09-21 04:07:26.208** **響應(yīng)鏈** **[749:60b] ViewController** **接收到觸摸事件** ```
注意看時(shí)間戳,appDelegate雖然優(yōu)先級(jí)別不如ViewController,但是他響應(yīng)的時(shí)間上面足足比ViewController早了0.002秒,我這里試了幾次,都是相差0.002秒。那么我們分析一下這里的響應(yīng)者鏈?zhǔn)窃鯓庸ぷ鞯?
用戶手指觸摸到了UIView上,由于我們沒(méi)有重寫UIView的UITouchEvent,所以他里面和super執(zhí)行的一樣的,將該事件繼續(xù)分發(fā)到UIViewController;
UIViewController的TouchBegan被我們重寫了,如果我們不super,那么我們?cè)谶@里寫響應(yīng)代碼。事件到這里就不繼續(xù)分發(fā)了??上攵?,UIViewController祖先節(jié)點(diǎn):UIWindow,UIApplication,AppDelegate都無(wú)權(quán)被分發(fā)此事件。
如果我們super了TouchBegan,那么此次觸摸事件由
ViewController分發(fā)給UIWindow,
UIWindow繼而分發(fā)給UIApplication,
UIApplication再分發(fā)給AppDelegate,
于是我們?cè)赩iewController和appDelegate的touchBegan方法中都捕獲到了這次事件。
到這里大家應(yīng)該對(duì)這個(gè)響應(yīng)者樹(shù)有一個(gè)很好的理解了吧?
接下來(lái)我們?cè)僬務(wù)劦谝豁憫?yīng)者,和UIButton上的事件分發(fā)。