事件傳遞與視圖響應(yīng)鏈

一腹尖、UIView與CALayer區(qū)別

圖1 UIView與CALayer關(guān)系圖

備注:這里的backing store指的是位圖。位圖最終是給計(jì)算機(jī)硬件操作的叙淌。

  • CALayer為UIView提供顯示的內(nèi)容慢宗,只負(fù)責(zé)內(nèi)容顯示,不參與事件處理余素。
  • UIView作為CALayer的代理豹休,提供交互操作;負(fù)責(zé)處理觸摸事件桨吊,參與響應(yīng)鏈威根。

二、為什么UIView只負(fù)責(zé)事件傳遞视乐、CALayer負(fù)責(zé)視圖顯示

這個(gè)問題等同于為什么iOS中提供UIView和CALayer兩個(gè)平行的層級結(jié)構(gòu)洛搀。

答:主要是為了做到單一職責(zé)原則,做到職責(zé)分離佑淀,避免過多重復(fù)代碼留美。

三、為什么CALayer不能響應(yīng)觸摸事件

從繼承關(guān)系圖來回答伸刃,響應(yīng)事件必須繼承自UIResponder谎砾。


圖2 UIView和CALayer繼承關(guān)系

四、事件傳遞

4.1奕枝、相關(guān)事件方法

//返回當(dāng)前響應(yīng)事件的視圖View
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event

//判斷當(dāng)前點(diǎn)擊的位置point是否在視圖范圍內(nèi)
//在hitTest: withEvent:內(nèi)部使用棺榔,用來判斷點(diǎn)擊了哪個(gè)View
- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event;

4.2瓶堕、簡述事件傳遞流程

圖3 事件傳遞流程

1隘道、點(diǎn)擊屏幕某一位置,這個(gè)事件會(huì)傳遞給UIApplication郎笆。
2谭梗、UIApplication傳遞給當(dāng)前的UIWindow。
3宛蚓、在UIWindow里面就會(huì)使用hitTest:withEvent:方法激捏,返回最終響應(yīng)的視圖。
4凄吏、在hitTest:withEvent:方法里远舅,會(huì)調(diào)用pointInside:withEvent:方法闰蛔,來判斷當(dāng)前點(diǎn)擊的位置是否在UIWindow內(nèi)。
5图柏、如果當(dāng)前的點(diǎn)在UIWindow內(nèi)序六,則遍歷UIWindow子視圖,查找最終響應(yīng)事件的視圖例诀;需要注意的是,這里的遍歷是倒序遍歷,即后添加的最優(yōu)先被點(diǎn)擊。
6坏瘩、在Subviews中的子視圖中,采用倒序遍歷views哪自。在每個(gè)view中都會(huì)調(diào)用hitTest:withEvent:,在view的子視圖中同樣會(huì)調(diào)用hitTest:withEvent:,也就是一直遞歸調(diào)用。
如果當(dāng)前view的hitTest:withEvent返回的不為nil悲没,則這個(gè)視圖就作為事件響應(yīng)的視圖,結(jié)束了事件傳遞的流程渗鬼;否則,繼續(xù)遍歷其它view。
如果整個(gè)Subviews都沒有找到,則當(dāng)前UIWindow就作為事件響應(yīng)的視圖

備注:這里的事件響應(yīng)視圖崇猫,不如叫做命中視圖。因?yàn)辄c(diǎn)擊了這個(gè)視圖,但是這個(gè)視圖不一定能為當(dāng)前事件綁定了一個(gè)觸發(fā)函數(shù)议纯,也就是不能響應(yīng)了。
這個(gè)時(shí)候,就會(huì)沿著響應(yīng)鏈向上尋找包晰,看看父節(jié)點(diǎn)是否能夠響應(yīng)赫模,這就是下面的響應(yīng)鏈

4.3蒸矛、hitTest:withEvent:內(nèi)部實(shí)現(xiàn)

圖4 hitTest內(nèi)部實(shí)現(xiàn)

1瀑罗、判斷hidden=YES、userInteractionEnabled=YES雏掠、alpha<0.01斩祭。
如果不滿足上述條件,則會(huì)返回nil乡话,父類繼續(xù)遍歷其它子視圖摧玫。
2、使用pointInside:withEvent:绑青,判斷點(diǎn)擊的point是否在視圖范圍內(nèi)诬像。
如果不滿足上述條件,則會(huì)返回nil闸婴,父類繼續(xù)遍歷其它子視圖坏挠。
3、上述條件滿足邪乍,則會(huì)采用倒序方法遍歷當(dāng)前子視圖
4癞揉、遍歷過程中,子視圖調(diào)用hitTest:withEvent:方法溺欧,如果返回不為nil喊熟,則將當(dāng)前子視圖作為事件響應(yīng)視圖,返回給調(diào)用方姐刁。否則芥牌,繼續(xù)遍歷其它子視圖
5、如果沒有找到子視圖聂使,由于點(diǎn)擊位置在當(dāng)前視圖范圍內(nèi)壁拉,則會(huì)把當(dāng)前視圖作為事件響應(yīng)視圖返回給調(diào)用方。

4.4柏靶、擴(kuò)大按鈕的點(diǎn)擊區(qū)域

核心點(diǎn)重寫pointInside:withEvent:方法弃理、在里面寫明point在什么區(qū)域返回YES,什么區(qū)域返回NO即可屎蜓。

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
    CGFloat x1 = point.x;
    CGFloat y1 = point.y;
    
    CGFloat x2 = self.frame.size.width / 2;
    CGFloat y2 = self.frame.size.height / 2;
    
    double dis = sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
    if (dis <= self.frame.size.width / 2) {
        return NO;
    }
    else{
        return YES;
    }
}

備注:上面例題產(chǎn)生的效果痘昌,只有點(diǎn)擊區(qū)域大于某個(gè)圓形區(qū)域,才會(huì)有效,否則無效辆苔。

上面講的是事件的傳遞流程算灸,這里講的是事件的響應(yīng)流程

五驻啤、響應(yīng)鏈

5.1菲驴、響應(yīng)事件流程

圖5 響應(yīng)流程

1、點(diǎn)擊UILabel骑冗、UITextField赊瞬、UIButton后,它們的下一個(gè)響應(yīng)者是UIView(容器)贼涩。
2巧涧、容器View繼續(xù)傳遞給UIView(可能是UIViewController的View)。如果有UIViewController磁携,則下一個(gè)響應(yīng)者是UIViewController褒侧。
3、如果上面都沒有響應(yīng)者谊迄,則會(huì)傳遞兒UIWindow闷供。
4、UIWindow傳遞給UIApplication统诺。
5歪脏、UIApplication傳遞給UIApplicationDelegate。

簡單總結(jié)如下:First Responser -> The Window ->The
Applicationn->AppDelegate

備注:事件傳遞給UIApplication粮呢,代表這個(gè)事件沒有實(shí)際的響應(yīng)動(dòng)作婿失,響應(yīng)循環(huán)也就結(jié)束了。

5.2啄寡、視圖響應(yīng)事件

//一根或者多根手指開始觸摸view(手指按下)
-(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event

//一根或者多根手指在view上移動(dòng)(隨著手指的移動(dòng)豪硅,會(huì)持續(xù)調(diào)用該方法)
-(void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event

//一根或者多根手指離開view(手指抬起)
-(void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event

//某個(gè)系統(tǒng)事件(例如電話呼入)打斷觸摸過程
-(void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event

總結(jié)

如果問相關(guān)響應(yīng)鏈的問題,可以從下面兩個(gè)方面回答:
1挺物、hitTest尋找命中視圖懒浮。
2、從命中視圖開始识藤,沿著響應(yīng)鏈向上尋找真正的響應(yīng)者砚著。
3、如果最終沒有找到響應(yīng)者痴昧,就會(huì)忽略到這個(gè)事件稽穆,也就是不會(huì)產(chǎn)生實(shí)質(zhì)性的動(dòng)作,不會(huì)引起崩潰赶撰。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末舌镶,一起剝皮案震驚了整個(gè)濱河市柱彻,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌乎折,老刑警劉巖绒疗,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件侵歇,死亡現(xiàn)場離奇詭異骂澄,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)惕虑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進(jìn)店門坟冲,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人溃蔫,你說我怎么就攤上這事健提。” “怎么了伟叛?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵私痹,是天一觀的道長。 經(jīng)常有香客問我统刮,道長紊遵,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任侥蒙,我火速辦了婚禮暗膜,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘鞭衩。我一直安慰自己学搜,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布论衍。 她就那樣靜靜地躺著瑞佩,像睡著了一般。 火紅的嫁衣襯著肌膚如雪坯台。 梳的紋絲不亂的頭發(fā)上炬丸,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天,我揣著相機(jī)與錄音捂人,去河邊找鬼御雕。 笑死,一個(gè)胖子當(dāng)著我的面吹牛滥搭,可吹牛的內(nèi)容都是我干的酸纲。 我是一名探鬼主播,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼瑟匆,長吁一口氣:“原來是場噩夢啊……” “哼闽坡!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤疾嗅,失蹤者是張志新(化名)和其女友劉穎外厂,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體代承,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡汁蝶,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了论悴。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片掖棉。...
    茶點(diǎn)故事閱讀 39,841評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖膀估,靈堂內(nèi)的尸體忽然破棺而出幔亥,到底是詐尸還是另有隱情,我是刑警寧澤察纯,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布帕棉,位于F島的核電站,受9級特大地震影響饼记,放射性物質(zhì)發(fā)生泄漏香伴。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一握恳、第九天 我趴在偏房一處隱蔽的房頂上張望瞒窒。 院中可真熱鬧,春花似錦乡洼、人聲如沸崇裁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽拔稳。三九已至,卻和暖如春锹雏,著一層夾襖步出監(jiān)牢的瞬間巴比,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工礁遵, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留轻绞,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓佣耐,卻偏偏與公主長得像政勃,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子兼砖,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評論 2 354

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