每一個應用有一個響應者鏈,我們的視圖結(jié)構(gòu)是一個N叉樹(一個視圖可以有多個子視圖,一個子視圖同一時刻只有一個父視圖),而每一個繼承UIResponder的對象都可以在這個N叉樹中扮演一個節(jié)點。當葉節(jié)點成為最高響應者的時候荠呐,從這個葉節(jié)點開始往其父節(jié)點開始追朔出一條鏈,那么對于這一個葉節(jié)點來講,這一條鏈就是當前的響應者鏈昔园。響應者鏈將系統(tǒng)捕獲到的UIEvent與UITouch從葉節(jié)點開始層層向下分發(fā)蔓榄,期間可以選擇停止分發(fā),也可以選擇繼續(xù)向下分發(fā)默刚。
例子:
我用SingleView模板創(chuàng)建了一個新的工程甥郑,它的主Window上只有一個UIViewController,其View之上有一個Button荤西。這個項目中所有UIResponder的子類所構(gòu)成的N叉樹為這樣的結(jié)構(gòu):
那么他看起來并不像N叉樹澜搅,但是不代表者不是一顆N叉樹,當我們項目復雜之后邪锌,這個View可不可以有多個UIButton節(jié)點勉躺?所以他就是一棵樹。
實際上我們要把這棵樹寫完整觅丰,應該還要算上UIButton的UILabel和UIImageView饵溅,因為他們也是UIReponder的子類。這里先不考慮了妇萄。
我們對UIButton來講蜕企,他此時若是葉節(jié)點,那么這時我們針對他所在的響應鏈來說冠句,他在他之前的響應者就應該是我們controller的view(樹中的葉節(jié)點比父節(jié)點永遠更優(yōu)先被分發(fā)事件,但是并不是說他就能在時間上先響應轻掩,我們下面講為什么)。所以我們嘗試在任意地方打印這個Button的nextReponder對象懦底。nextResponder對象是UIReponder類的實例方法唇牧,它會返回任意對象在樹中的上一個響應者實例:
控制臺輸出消息:**2013-09-21 03:40:25.989** **響應鏈** **[614:60b] <UIView: 0x16555e10; frame = (0 0; 320 568); autoresize = RM+BM; layer = <CALayer: 0x16555e70>>** ```
我們可以根據(jù)這個UIView的尺寸來得知,他就是我們唯一的控制器中的那個UIView聚唐。
接下來我們再打印下這個UIView的下一個響應者是誰:
```NSLog(@"%@",_testButton.nextResponder.nextResponder);
輸出: **2013-09-21 03:45:03.914** **響應鏈** **[621:60b] <RSViewController: 0x15da0e30>** ```
依次看奋构,接著加一個nextResponder:
```**2013-09-21 03:50:49.428** **響應鏈** **[669:60b] (null)** ```
為什么這里ViewController沒有父親呢?注意這句代碼我是寫在ViewDidLoad中拱层,而我們知道這個方法的生命周期比較早弥臼,所以我們換個地方寫或者延遲一段時間再打印,兩種方法都可以得到結(jié)果(由此可以推理出我們響應者樹的構(gòu)造過程是在ViewDidLoad周期中來完成的根灯,這個函數(shù)會將當前實例的構(gòu)成的響應者子樹合并到我們整個根樹中):
```**2013-09-21 03:53:47.304** **響應鏈** **[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** **響應鏈** **[690:60b] <UIApplication: 0x15659c00>** ```
再加一個```:**2013-09-21 03:56:51.186** **響應鏈** **[696:60b] <RSAppDelegate: 0x16663520>** ```
那么我們的appDelegate還有沒有父節(jié)點?```**2013-09-21 03:57:22.588** **響應鏈** **[706:60b] (null)** ```
沒有了径缅,注意,一個從葉節(jié)點開始分發(fā)的事件烙肺,最多也就只能分發(fā)到我們的AppDelegate了纳猪!
這個樹形結(jié)構(gòu)在我們的項目中尤為重要,舉個栗子桃笙,如果我們想在一個view中重寫UITouchEvent的4個方法氏堤,并且不影響他的父視圖也響應這些事件,就要注意你重寫的方式了搏明,比如我們在ViewController中重寫touchBegan如下:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ NSLog(@"ViewController接收到觸摸事件");}
在appDelegate的中同樣也寫上這一段:-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ NSLog(@"appDelegate接收到觸摸事件");}
那么究竟是誰被觸發(fā)呢鼠锈?
```**2013-09-21 04:02:49.405** **響應鏈** **[743:60b] ```ViewController** **接收到觸摸事件**
這個很好理解闪檬,我剛剛也說了,viewController明顯是appDelegate的子節(jié)點购笆,他有事件分發(fā)的優(yōu)先權(quán)粗悯。如果我們想兩個地方都觸發(fā)呢?這里super一下就可以了:-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ [super touchesBegan:touches withEvent:event]; NSLog(@"ViewController接收到觸摸事件");}
```輸出:**2013-09-21 04:07:26.206** **響應鏈** **[749:60b] appDelegate** **接收到觸摸事件** ```
**2013-09-21 04:07:26.208** **響應鏈** **[749:60b] ViewController** **接收到觸摸事件** ```
注意看時間戳同欠,appDelegate雖然優(yōu)先級別不如ViewController样傍,但是他響應的時間上面足足比ViewController早了0.002秒,我這里試了幾次铺遂,都是相差0.002秒衫哥。那么我們分析一下這里的響應者鏈是怎樣工作的:
用戶手指觸摸到了UIView上,由于我們沒有重寫UIView的UITouchEvent,所以他里面和super執(zhí)行的一樣的,將該事件繼續(xù)分發(fā)到UIViewController襟锐;
UIViewController的TouchBegan被我們重寫了撤逢,如果我們不super,那么我們在這里寫響應代碼捌斧。事件到這里就不繼續(xù)分發(fā)了∪矗可想而知捞蚂,UIViewController祖先節(jié)點:UIWindow,UIApplication跷究,AppDelegate都無權(quán)被分發(fā)此事件姓迅。
如果我們super了TouchBegan,那么此次觸摸事件由
ViewController分發(fā)給UIWindow,
UIWindow繼而分發(fā)給UIApplication俊马,
UIApplication再分發(fā)給AppDelegate丁存,
于是我們在ViewController和appDelegate的touchBegan方法中都捕獲到了這次事件。
到這里大家應該對這個響應者樹有一個很好的理解了吧?
接下來我們再談談第一響應者柴我,和UIButton上的事件分發(fā)解寝。