iOS用戶響應(yīng)者鏈完全剖析

歡迎使用好劇場.

如在使用 app 中遇到任何問題,請聯(lián)系:

Email: haoJCTV@163.com

QQ:512119154




Responder一點也不神秘————iOS用戶響應(yīng)者鏈完全剖析

這篇文章想跟大家分享的主旨是iOS捕獲用戶事件的各種情況跃惫,以及內(nèi)部封裝的一些特殊事件瘩缆。

我們先從UIButton談起,UIButton大家使用的太多了名段,他特殊的地方就在于其內(nèi)置的普通Default/高亮Highlighted/選擇Selected/可用Enable的幾個狀態(tài)(UIControlState)。其次就是SDK內(nèi)部已經(jīng)為我們封裝了以下用戶事件:



最常用的莫過于Touch Up Inside這個事件了,他代表: ?用戶在按鈕區(qū)域內(nèi)按下豪娜,并且也在按鈕區(qū)域內(nèi)松開。


關(guān)鍵點:按下并且松開 才能觸發(fā)此方法哟楷,也就是正確的操作 按下一次瘤载,松開一次只會觸發(fā)一次此事件。與之不同的Touch Drag Inside等方法不需要松開這個過程卖擅,Up變?yōu)榱薉rag,其實大家都能理解鸣奔,SDK在封裝的時候原理跟UITouchEvent是一個道理,第一個單詞Touch 代表按下(Began)第二個單詞Up代表松開(Ended),Drag代表拖動(Moved)惩阶。TouchMoved方法在一次完整的觸摸中會被觸發(fā)很多次挎狸,所以Touch Drag Inside方法會在用戶手松開之前一直被觸發(fā)。


這些就是UIButton已封裝的事件断楷,而UIButton繼承自UIControl锨匆。UIControl又繼承自UIView。我們平時能用這些已封裝的事件的控件都是UIControl的子類冬筒。那么父類UIView是沒有內(nèi)部事件的恐锣。


我們常常利用UIView來寫自己的UITouchEvent。例如在一個View/ViewController中直接實現(xiàn)以下3個方法:


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

{

}

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

{

}


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

{

}

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

{

}

我們用的非常多账千,但是大家知道這4個方法是誰的實例方法嗎侥蒙?如果你一下就說出是UIView的,那么為什么我們在UIViewController中也可以用呢匀奏,他們不是繼承關(guān)系。


注意這4個實例方法來自UIView與UIViewController的共同父類:UIResponder学搜。它是我們今天的主角娃善。


基本上我們所能看到的所有圖形界面都是繼承自UIResponder的,So瑞佩,它究竟為何方神圣?


UIResponder所謂很多視圖的父類聚磺,他掌管著用戶的操作事件分發(fā)大權(quán)。如果沒有他炬丸,我們的電容屏如何將用戶的操作傳遞給我們的視圖令其做出反應(yīng)呢瘫寝?


我們先看看iOS中的響應(yīng)者鏈的概念:


每一個應(yīng)用有一個響應(yīng)者鏈,我們的視圖結(jié)構(gòu)是一個N叉樹(一個視圖可以有多個子視圖稠炬,一個子視圖同一時刻只有一個父視圖),而每一個繼承UIResponder的對象都可以在這個N叉樹中扮演一個節(jié)點焕阿。當(dāng)葉節(jié)點成為最高響應(yīng)者的時候,從這個葉節(jié)點開始往其父節(jié)點開始追朔出一條鏈首启,那么對于這一個葉節(jié)點來講暮屡,這一條鏈就是當(dāng)前的響應(yīng)者鏈。響應(yīng)者鏈將系統(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叉樹彻秆,當(dāng)我們項目復(fù)雜之后渐扮,這個View可不可以有多個UIButton節(jié)點?所以他就是一棵樹掖棉。


實際上我們要把這棵樹寫完整墓律,應(yīng)該還要算上UIButton的UILabel和UIImageView,因為他們也是UIReponder的子類幔亥。這里先不考慮了耻讽。


我們對UIButton來講,他此時若是葉節(jié)點帕棉,那么這時我們針對他所在的響應(yīng)鏈來說针肥,他在他之前的響應(yīng)者就應(yīng)該是我們controller的view(樹中的葉節(jié)點比父節(jié)點永遠更優(yōu)先被分發(fā)事件,但是并不是說他就能在時間上先響應(yīng),我們下面講為什么)香伴。所以我們嘗試在任意地方打印這個Button的nextReponder對象慰枕。nextResponder對象是UIReponder類的實例方法,它會返回任意對象在樹中的上一個響應(yīng)者實例:


NSLog(@"%@",_testButton.nextResponder);


控制臺輸出消息:

2013-09-21 03:40:25.989 響應(yīng)鏈 [614:60b] >



我們可以根據(jù)這個UIView的尺寸來得知即纲,他就是我們唯一的控制器中的那個UIView具帮。


接下來我們再打印下這個UIView的下一個響應(yīng)者是誰:


NSLog(@"%@",_testButton.nextResponder.nextResponder);


輸出:

2013-09-21 03:45:03.914 響應(yīng)鏈 [621:60b]


依次看,接著加一個nextResponder:


2013-09-21 03:50:49.428 響應(yīng)鏈 [669:60b] (null)


為什么這里ViewController沒有父親呢低斋?

注意這句代碼我是寫在ViewDidLoad中蜂厅,而我們知道這個方法的生命周期比較早,所以我們換個地方寫或者延遲一段時間再打印膊畴,兩種方法都可以得到結(jié)果(由此可以推理出我們響應(yīng)者樹的構(gòu)造過程是在ViewDidLoad周期中來完成的掘猿,這個函數(shù)會將當(dāng)前實例的構(gòu)成的響應(yīng)者子樹合并到我們整個根樹中):


2013-09-21 03:53:47.304 響應(yīng)鏈 [681:60b] ; layer = >


再繼續(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]


再加一個:

2013-09-21 03:56:51.186 響應(yīng)鏈 [696:60b]


那么我們的appDelegate還有沒有父節(jié)點?

2013-09-21 03:57:22.588 響應(yīng)鏈 [706:60b] (null)


沒有了,注意唇跨,一個從葉節(jié)點開始分發(fā)的事件稠通,最多也就只能分發(fā)到我們的AppDelegate了!


這個樹形結(jié)構(gòu)在我們的項目中尤為重要买猖,舉個栗子改橘,如果我們想在一個view中重寫UITouchEvent的4個方法,并且不影響他的父視圖也響應(yīng)這些事件政勃,就要注意你重寫的方式了唧龄,比如我們在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 響應(yīng)鏈 [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 響應(yīng)鏈 [749:60b] appDelegate 接收到觸摸事件


2013-09-21 04:07:26.208 響應(yīng)鏈 [749:60b] ViewController 接收到觸摸事件


注意看時間戳耽梅,appDelegate雖然優(yōu)先級別不如ViewController,但是他響應(yīng)的時間上面足足比ViewController早了0.002秒胖烛,我這里試了幾次眼姐,都是相差0.002秒。

那么我們分析一下這里的響應(yīng)者鏈是怎樣工作的:


用戶手指觸摸到了UIView上,由于我們沒有重寫UIView的UITouchEvent,所以他里面和super執(zhí)行的一樣的佩番,將該事件繼續(xù)分發(fā)到UIViewController众旗;


UIViewController的TouchBegan被我們重寫了,如果我們不super趟畏,那么我們在這里寫響應(yīng)代碼贡歧。事件到這里就不繼續(xù)分發(fā)了「承悖可想而知利朵,UIViewController祖先節(jié)點:UIWindow,UIApplication猎莲,AppDelegate都無權(quán)被分發(fā)此事件绍弟。


如果我們super了TouchBegan,那么此次觸摸事件由


ViewController分發(fā)給UIWindow,


UIWindow繼而分發(fā)給UIApplication著洼,


UIApplication再分發(fā)給AppDelegate樟遣,


于是我們在ViewController和appDelegate的touchBegan方法中都捕獲到了這次事件。


到這里大家應(yīng)該對這個響應(yīng)者樹有一個很好的理解了吧?


接下來我們再談?wù)劦谝豁憫?yīng)者郭脂,和UIButton上的事件分發(fā)年碘。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市展鸡,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌埃难,老刑警劉巖莹弊,帶你破解...
    沈念sama閱讀 217,084評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異涡尘,居然都是意外死亡忍弛,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評論 3 392
  • 文/潘曉璐 我一進店門考抄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來细疚,“玉大人,你說我怎么就攤上這事川梅》杓妫” “怎么了然遏?”我有些...
    開封第一講書人閱讀 163,450評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長吧彪。 經(jīng)常有香客問我待侵,道長,這世上最難降的妖魔是什么姨裸? 我笑而不...
    開封第一講書人閱讀 58,322評論 1 293
  • 正文 為了忘掉前任秧倾,我火速辦了婚禮,結(jié)果婚禮上傀缩,老公的妹妹穿的比我還像新娘那先。我一直安慰自己,他們只是感情好赡艰,可當(dāng)我...
    茶點故事閱讀 67,370評論 6 390
  • 文/花漫 我一把揭開白布售淡。 她就那樣靜靜地躺著,像睡著了一般瞄摊。 火紅的嫁衣襯著肌膚如雪勋又。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,274評論 1 300
  • 那天换帜,我揣著相機與錄音楔壤,去河邊找鬼。 笑死惯驼,一個胖子當(dāng)著我的面吹牛蹲嚣,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播祟牲,決...
    沈念sama閱讀 40,126評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼言沐,長吁一口氣:“原來是場噩夢啊……” “哼坚俗!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,980評論 0 275
  • 序言:老撾萬榮一對情侶失蹤妙痹,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后套菜,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體尔觉,經(jīng)...
    沈念sama閱讀 45,414評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,599評論 3 334
  • 正文 我和宋清朗相戀三年傲宜,在試婚紗的時候發(fā)現(xiàn)自己被綠了运杭。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,773評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡函卒,死狀恐怖辆憔,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤虱咧,帶...
    沈念sama閱讀 35,470評論 5 344
  • 正文 年R本政府宣布熊榛,位于F島的核電站,受9級特大地震影響彤钟,放射性物質(zhì)發(fā)生泄漏来候。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,080評論 3 327
  • 文/蒙蒙 一逸雹、第九天 我趴在偏房一處隱蔽的房頂上張望营搅。 院中可真熱鬧,春花似錦梆砸、人聲如沸转质。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,713評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽休蟹。三九已至,卻和暖如春日矫,著一層夾襖步出監(jiān)牢的瞬間赂弓,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,852評論 1 269
  • 我被黑心中介騙來泰國打工哪轿, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留盈魁,地道東北人。 一個月前我還...
    沈念sama閱讀 47,865評論 2 370
  • 正文 我出身青樓窃诉,卻偏偏與公主長得像杨耙,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子飘痛,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,689評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 好奇觸摸事件是如何從屏幕轉(zhuǎn)移到APP內(nèi)的珊膜?困惑于Cell怎么突然不能點擊了?糾結(jié)于如何實現(xiàn)這個奇葩響應(yīng)需求宣脉?亦或是...
    Lotheve閱讀 57,069評論 51 599
  • 在iOS開發(fā)中經(jīng)常會涉及到觸摸事件车柠。本想自己總結(jié)一下,但是遇到了這篇文章塑猖,感覺總結(jié)的已經(jīng)很到位堪遂,特此轉(zhuǎn)載。作者:L...
    WQ_UESTC閱讀 6,009評論 4 26
  • 序言 當(dāng)我們在使用微信等工具萌庆,點擊掃一掃,就能打開二維碼掃描視圖币旧。在我們點擊屏幕的時候践险,iphone OS獲取到了...
    九洲仙人閱讀 521評論 1 0
  • 當(dāng)我們在使用微信等工具,點擊掃一掃,就能打開二維碼掃描視圖巍虫。在我們點擊屏幕的時候彭则,iphone OS獲取到了用戶進...
    lbfly_boy閱讀 657評論 0 0
  • 我看了一上午書,并且總結(jié)寫了下來占遥。但下午不想學(xué)俯抖,1我不知道做不做產(chǎn)品,2我回宿舍也很無聊 所以去自習(xí)室待著瓦胎,沒勁打...
    teksab閱讀 198評論 0 0