史上最詳細(xì)的iOS之事件的傳遞和響應(yīng)機制-實踐篇

前言

之前我已經(jīng)通過《史上最詳細(xì)的iOS之事件的傳遞和響應(yīng)機制-原理篇》比較詳細(xì)的介紹過了事件的響應(yīng)和傳遞的一些原理坡贺。如果說上篇是原理性文章渣玲,那么本篇文章更偏重于實踐器贩。本篇文章主要介紹如何利用事件處理的這些機制來處理公司開發(fā)中一些比較棘手的需求。例如蔬胯,點擊的是A視圖对供,卻要讓B視圖處理事件;點擊子視圖氛濒,卻要讓父視圖處理事件等等产场。今天,我整理了下之前的雜記泼橘,羅列出了一些開發(fā)中可能遇到的情景和應(yīng)對措施涝动!當(dāng)然,這要求我們對事件的傳遞和響應(yīng)機制非常了解炬灭。如果對此不太了解醋粟,請閱讀筆者的《史上最詳細(xì)的iOS之事件的傳遞和響應(yīng)機制-原理篇》

視圖層次

如上圖重归,視圖層次結(jié)構(gòu):白色->紅色->綠色米愿。紅色的view是綠色view的父視圖,白色的view又是紅色view的父視圖鼻吮。如下要求:

需求情景一

當(dāng)子控件和父控件重疊時育苟,點擊子控件,子控件響應(yīng)事件椎木。也就是說违柏,點擊綠色的view和紅色的view的重疊部分,只有綠色的view響應(yīng)事件香椎。
分析:其實這算不上一個需求漱竖,因為系統(tǒng)默認(rèn)就是這樣處理的。重寫綠色view的 - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event方法畜伐,默認(rèn)就是綠色的view響應(yīng)事件馍惹,但是僅限于重疊部分,點擊綠色view上的非重疊部分,綠色和紅色view都不會響應(yīng)万矾。
原因在于悼吱,系統(tǒng)從window向下尋找最合適的view時候,遍歷到紅色的view時候良狈,發(fā)現(xiàn)點不在紅色的view上后添,那么默認(rèn)控制器的view就是最合適的view。即控制器的view響應(yīng)了事件们颜。

#import "GreenView.h"

@implementation GreenView

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    NSLog(@"greenView %s",__func__);
}
@end

打印結(jié)果

2016-02-27 20:49:46.234 事件處理和響應(yīng)[905:44466] greenView -[GreenView touchesBegan:withEvent:]
2016-02-27 20:49:48.051 事件處理和響應(yīng)[905:44466] greenView -[GreenView touchesBegan:withEvent:]

需求情景二

當(dāng)子控件和父控件部分重疊時吕朵,點擊父子控件重疊部分,只有父控件響應(yīng)事件窥突。也就是說努溃,點擊綠色的view和紅色的view的重疊部分,只有紅色的view響應(yīng)事件阻问。
分析:點擊子控件梧税,卻要讓父控件響應(yīng)事件敞恋,說明子控件本身不是最合適的view甸鸟,父控件才是最合適的view,因為hitTest:withEvent:方法的作用就是控件接收到事件后纷闺,判斷自己是否能處理事件刨秆,判斷點在不在自己的坐標(biāo)系上凳谦,然后返回最合適的view。所以衡未,我們可以在hitTest:withEvent:方法里面強制返回父控件為最合適的view尸执,也就是返回紅色的view。
注意缓醋,不能夠重寫父控件的hitTest:withEvent:方法如失,也就是不能夠重寫紅色的view的hitTest:withEvent:方法。原因在于送粱,如果重寫父控件的hitTest:withEvent:方法褪贵,并在該方法中返回父控件本身,會導(dǎo)致點擊父控件的父控件時抗俄,也是父控件為最合適的view脆丁。反應(yīng)在上面的例子上就是,點擊白色的地方动雹,也是紅色的view響應(yīng)事件偎快。

#import "GreenView.h"

@implementation GreenView

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    return [self superview]; // return nil; // 此處返回nil也可以。返回nil就相當(dāng)于當(dāng)前的view不是最合適的view
}

@end

打印結(jié)果:

2016-02-27 20:52:09.083 事件處理和響應(yīng)[921:46215] redView -[RedView touchesBegan:withEvent:]
2016-02-27 20:52:10.154 事件處理和響應(yīng)[921:46215] redView -[RedView touchesBegan:withEvent:]

需求情景三

無論點擊什么地方洽胶,父控件響應(yīng)事件。也就是說,點擊白色的view姊氓、紅色的view或者綠色的view上的任一點(屏幕上任一點)丐怯,都只是紅色的view響應(yīng)事件。
分析:點擊屏幕上任意點翔横,都是紅色的view響應(yīng)事件读跷,根據(jù)視圖層次結(jié)構(gòu),我們只需要重寫紅色View的hit:test:方法禾唁,并在此方法中返回紅色的view即可效览。

@implementation RedView
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    NSLog(@"RedView %s",__func__);
}

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    return self;
}
@end

打印結(jié)果

2016-02-27 20:58:29.883 CharactersRange[4843:853272] RedView -[RedView touchesBegan:withEvent:]
2016-02-27 20:58:30.653 CharactersRange[4843:853272] RedView -[RedView touchesBegan:withEvent:]
2016-02-27 20:58:31.479 CharactersRange[4843:853272] RedView -[RedView touchesBegan:withEvent:]
2016-02-27 20:58:32.199 CharactersRange[4843:853272] RedView -[RedView touchesBegan:withEvent:]

需求情景四

當(dāng)子控件和父控件部分重疊時,點擊非重疊部分荡短,父控件響應(yīng)事件丐枉。也就是說,點擊綠色的view的上半部分(即不重疊的部分)掘托,紅色的view響應(yīng)事件瘦锹。
分析:我們知道,系統(tǒng)尋找能夠處理事件的最合適的view有兩個標(biāo)準(zhǔn)闪盔。第一弯院,這個view能接收事件,第二泪掀,這個點在自己身上听绳。這兩個條件缺一不可。此處我們點擊的點是綠色的view的上部分异赫,這個點沒有在紅色的view的坐標(biāo)系上椅挣。 也就是當(dāng)事件傳遞給紅色的view時,紅色的view雖然能夠接收這個事件祝辣,但是點不在紅色的view的坐標(biāo)系上贴妻,所以紅色的view不是最合適的view,這個事件就不會交給紅色的view處理蝙斜。紅色的view作為父控件都沒有接收到這個事件名惩,當(dāng)然他的子控件綠色的view也肯定接收不到這個觸摸事件。
我起初嘗試重寫紅色的view的hitTest:withEvent:方法孕荠,返回紅色的view為最合適的view娩鹉。雖然可以解決問題,但是點擊其他任何地方稚伍,也是紅色的view響應(yīng)事件弯予。這個結(jié)果不是我想要的,我只希望點擊紅色和綠色的部分个曙,紅色能夠響應(yīng)事件锈嫩,至于點擊其他部分,我不需要紅色響應(yīng)事件,那么該怎么做呢呼寸?
隨后艳汽,我又重寫了綠色的view的hitTest:withEvent:方法,返回紅色的view对雪。但是河狐,點擊非重疊區(qū),沒響應(yīng)瑟捣。
最后的解決方法是這樣的:

#import "RedView.h"

@implementation RedView

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    NSLog(@"redView %s",__func__);
}

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    // 判斷被點擊的點在不在自己身上
    // 把被點擊的點轉(zhuǎn)換成子控件坐標(biāo)系的點
    CGPoint greenViewPoint = [self convertPoint:point toView:self.subviews[0]];
 
    if ([self pointInside:point withEvent:event] || [self.subviews[0] pointInside:greenViewPoint withEvent:event]) { // 觸摸點在自己身上或者在子控件身上都返回自己作為最合適的view
        return self;
    }
    return  [super hitTest:point withEvent:event];
}
@end

需求情景五

當(dāng)子控件和父控件部分重疊時馋艺,點擊子控件,父控件和子控件都響應(yīng)事件迈套。也就是說捐祠,點擊綠色的view,不但綠色的view本身響應(yīng)事件交汤,紅色的view也響應(yīng)事件雏赦。

分析:事件的響應(yīng)是順著響應(yīng)者鏈條向上傳遞的,即從子控件傳遞給父控件芙扎,touch方法默認(rèn)不處理事件星岗,而是把事件順著響應(yīng)者鏈條傳遞給上一個響應(yīng)者。這樣我們就可以依托這個原理戒洼,讓一個事件多個控件響應(yīng)俏橘。

#import "GreenView.h"

@implementation GreenView

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    NSLog(@"greenView %s",__func__); // 重寫touch方法,自己可以響應(yīng)事件
    [super touchesBegan:touches withEvent:event]; // 在調(diào)用系統(tǒng)默認(rèn)的方法圈浇,又把事件順著響應(yīng)者鏈條拋給上一個響應(yīng)者寥掐。這就做到了一個事件,多個控件響應(yīng)磷蜀。
}
@end

打印結(jié)果:

這是一次點擊:
2016-02-27 21:23:37.503 事件處理和響應(yīng)[1020:60725] greenView -[GreenView touchesBegan:withEvent:]
2016-02-27 21:23:37.504 事件處理和響應(yīng)[1020:60725] redView -[RedView touchesBegan:withEvent:]

文/VV木公子(簡書作者)PS:如非特別說明召耘,所有文章均為原創(chuàng)作品,著作權(quán)歸作者所有褐隆,轉(zhuǎn)載轉(zhuǎn)載請聯(lián)系作者獲得授權(quán)污它,并注明出處,所有打賞均歸本人所有庶弃!
如果您是iOS開發(fā)者衫贬,或者對本篇文章感興趣,請關(guān)注本人歇攻,后續(xù)會更新更多相關(guān)文章固惯!敬請期待!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末缴守,一起剝皮案震驚了整個濱河市葬毫,隨后出現(xiàn)的幾起案子镇辉,更是在濱河造成了極大的恐慌,老刑警劉巖供常,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件摊聋,死亡現(xiàn)場離奇詭異,居然都是意外死亡栈暇,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進店門箍镜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來源祈,“玉大人,你說我怎么就攤上這事色迂∠闳保” “怎么了?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵歇僧,是天一觀的道長图张。 經(jīng)常有香客問我,道長诈悍,這世上最難降的妖魔是什么祸轮? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮侥钳,結(jié)果婚禮上适袜,老公的妹妹穿的比我還像新娘。我一直安慰自己舷夺,他們只是感情好苦酱,可當(dāng)我...
    茶點故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著给猾,像睡著了一般疫萤。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上敢伸,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天扯饶,我揣著相機與錄音,去河邊找鬼详拙。 笑死帝际,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的饶辙。 我是一名探鬼主播蹲诀,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼弃揽!你這毒婦竟也來了脯爪?” 一聲冷哼從身側(cè)響起则北,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎痕慢,沒想到半個月后尚揣,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡掖举,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年快骗,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片塔次。...
    茶點故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡方篮,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出励负,到底是詐尸還是另有隱情藕溅,我是刑警寧澤,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布继榆,位于F島的核電站巾表,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏略吨。R本人自食惡果不足惜集币,卻給世界環(huán)境...
    茶點故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望晋南。 院中可真熱鬧惠猿,春花似錦、人聲如沸负间。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽政溃。三九已至趾访,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間董虱,已是汗流浹背扼鞋。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留愤诱,地道東北人云头。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像淫半,于是被迫代替她去往敵國和親溃槐。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,933評論 2 355

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