iOS響應(yīng)者鏈类垫、事件的傳遞

1寿桨、響應(yīng)鏈的傳遞

Responder一點(diǎn)也不神秘————iOS用戶響應(yīng)者鏈完全剖析(建議全看)
看完上面一篇應(yīng)該能完全熟悉了響應(yīng)鏈的傳遞此衅,自己可以打印一下響應(yīng)鏈看看,代碼如下:

- (IBAction)click:(id)sender {
    UIResponder *res = sender;
    
    while (res) {
        NSLog(@"*************************************\n%@",res);
        res = [res nextResponder];
    }
}

2、Hit-Test 機(jī)制

當(dāng)用戶觸摸(Touch)屏幕進(jìn)行交互時(shí)炕柔,系統(tǒng)首先要找到響應(yīng)者(Responder)酌泰。系統(tǒng)檢測到手指觸摸(Touch)操作時(shí),將Touch 以UIEvent的方式加入U(xiǎn)IApplication事件隊(duì)列中匕累。UIApplication從事件隊(duì)列中取出最新的觸摸事件進(jìn)行分發(fā)傳遞到UIWindow進(jìn)行處理陵刹。UIWindow 會(huì)通過hitTest:withEvent:方法尋找觸碰點(diǎn)所在的視圖,這個(gè)過程稱之為hit-test view欢嘿。
hitTest 的順序如下

UIApplication -> UIWindow -> Root View -> ··· -> subview

在頂級(jí)視圖(Root View)上調(diào)用pointInside:withEvent:方法判斷觸摸點(diǎn)是否在當(dāng)前視圖內(nèi)衰琐;

如果返回NO,那么hitTest:withEvent:返回nil炼蹦;

如果返回YES羡宙,那么它會(huì)向當(dāng)前視圖的所有子視圖發(fā)送hitTest:withEvent:消息,所有子視圖的遍歷順序是從最頂層視圖一直到到最底層視圖掐隐,即從subviews數(shù)組的末尾向前遍歷狗热,直到有子視圖返回非空對(duì)象或者全部子視圖遍歷完畢。

如果有subview的hitTest:withEvent:返回非空對(duì)象則A返回此對(duì)象虑省,處理結(jié)束(注意這個(gè)過程匿刮,子視圖也是根據(jù)pointInside:withEvent:的返回值來確定是返回空還是當(dāng)前子視圖對(duì)象的。并且這個(gè)過程中如果子視圖的hidden=YES探颈、userInteractionEnabled=NO或者alpha小于0.1都會(huì)并忽略)熟丸;

如果所有subview遍歷結(jié)束仍然沒有返回非空對(duì)象,則hitTest:withEvent:返回self伪节;

系統(tǒng)就是這樣通過hit test找到觸碰到的視圖(Initial View)進(jìn)行響應(yīng)光羞。

如果還不清楚Hit-Test 機(jī)制,看更加清晰的Hit-Test 機(jī)制(建議還不清楚的看)

Paste_Image.png

3怀大、手勢的原理及與touches系列的關(guān)系纱兑,具體的可以看iOS觸摸事件傳遞響應(yīng)之被忽視的手勢識(shí)別器工作原理(建議不看也沒關(guān)系,結(jié)論在下面了化借。)

簡而言之萍启,就是下面這幅圖了。觸摸事件會(huì)優(yōu)先分發(fā)給附在view的手勢屏鳍,在這段延遲的期間,如果手勢被識(shí)別局服,那么view的touches系列將被立刻取消钓瞭,如果沒有被識(shí)別,那么會(huì)繼續(xù)我們所熟知的touches系列流程淫奔。


Paste_Image.png

4山涡、實(shí)際開發(fā)中常見的相關(guān)問題

在實(shí)際開發(fā)中,經(jīng)常會(huì)遇到視圖沒有響應(yīng)的情況,特別是新手會(huì)經(jīng)常搞不清楚狀況鸭丛。

一下是視圖沒有響應(yīng)的幾個(gè)情況:

1.userInteractionEnabled=NO竞穷;

2.hidden=YES;

3.alpha=0~0.01鳞溉;

4.沒有實(shí)現(xiàn)touchesBegan:withEvent:方法瘾带,直接執(zhí)行touchesMove:withEvent:等方法;

5.目標(biāo)視圖點(diǎn)擊區(qū)域不在父視圖的Frame上 (superView背景色為clear Color的時(shí)候經(jīng)常會(huì)忽略這個(gè)問題)熟菲。

5看政、手勢代理

ios手勢識(shí)別代理,看這個(gè)基本上就夠了抄罕。引用文章中的一段話允蚣,如下:

  • 當(dāng)時(shí)做項(xiàng)目時(shí)這個(gè)主控制器就是RootViewController,雖然用的是ScrollView但也沒考慮到導(dǎo)航欄的手勢返回的問題 ,現(xiàn)在做小區(qū)寶3.0的閃購訂單呆贿,用之前的就有問題了嚷兔。導(dǎo)航欄的返回手勢用不了,根據(jù)響應(yīng)者鏈和響應(yīng)事件,手勢被ScrollView識(shí)別了做入,就到不了導(dǎo)航的手勢識(shí)別冒晰,所以導(dǎo)致無法手勢返回。

我也曾經(jīng)處理過這樣的問題母蛛,不過我那時(shí)候是帶有QQ的側(cè)滑功能翩剪,主控制器用的View是ScrollView,導(dǎo)致不能側(cè)滑彩郊。但是處理的方法都是一樣的前弯,自定義的ScrollView的代碼重寫gestureRecognizerShouldBegin方法如下,我是手勢方向向右并且x軸起點(diǎn)小于60px的秫逝,讓ScrollView的手勢失效恕出。這樣就不會(huì)截獲對(duì)應(yīng)的事件了。但是其實(shí)看完上面违帆,還有更簡單的方法浙巫,就是讓ScrollView的手勢共存,但是這樣可能會(huì)帶來一些其它的問題刷后。shouldRecognizeSimultaneouslyWithGestureRecognizer設(shè)置為true的畴,不過應(yīng)該要判斷手勢為UIScreenEdgePanGestureRecognizer時(shí)才return true,這樣就可以了尝胆。

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
    CGPoint velocity = [(UIPanGestureRecognizer *)gestureRecognizer velocityInView:self];
    CGPoint location = [gestureRecognizer locationInView:self];
    
    NSLog(@"velocity.x:%f----location.x:%d",velocity.x,(int)location.x%(int)[UIScreen mainScreen].bounds.size.width);
    if (velocity.x > 0.0f&&(int)location.x%(int)[UIScreen mainScreen].bounds.size.width<60) {
        return NO;
    }
    return YES;
} 

案例分析

案例一

下面這種做法丧裁,除非你很熟悉,否則不要這么干含衔。因?yàn)?[super touchesBegan:touches withEvent:event];會(huì)執(zhí)行原來默認(rèn)的操作煎娇,如果按鈕本來就沒有添加對(duì)應(yīng)的事件二庵。那么[[self nextResponder] touchesBegan:touches withEvent:event];和[super touchesBegan:touches withEvent:event];將會(huì)向下一響應(yīng)者發(fā)送兩次事件。

-(void)touchesBegan:(NSSet<uitouch *> *)touches withEvent:(UIEvent *)event{
    if (self.enableNextResponder) {
        [[self nextResponder] touchesBegan:touches withEvent:event];
        [super touchesBegan:touches withEvent:event];
    }
}

案例二

Window
  -ViewA(能響應(yīng))
    -ButtonA
    -ViewB(不響應(yīng))

假設(shè)ViewB完全覆蓋在ButtonA上缓呛,結(jié)果是:
ViewA能觸發(fā)
Button沒反應(yīng)
ViewB沒反應(yīng)

簡單來說催享,ViewB能阻隔ButtonA的響應(yīng),但是不能阻隔ViewA的響應(yīng)哟绊。假設(shè)ViewB是個(gè)遮罩因妙,那么并不能阻隔ViewA的事件觸發(fā)。假設(shè)需要阻隔ViewA的相應(yīng)匿情,那么可以在ViewB添加空事件的相應(yīng)兰迫,從而達(dá)到ViewB(遮罩)阻隔ViewA(如游戲?qū)樱┑氖录鄳?yīng)。

案例三

一個(gè)按鈕添加了點(diǎn)擊事件到底發(fā)生了什么事兒炬称。
我們有時(shí)候需要使用到一些特殊的情況汁果,比如:
1、A包含B玲躯,AB都響應(yīng)事件据德。
對(duì)于普通View,根據(jù)響應(yīng)鏈跷车,讓B作為第一個(gè)響應(yīng)者處理棘利,然后B根據(jù)nextResponder傳遞觸摸事件。
針對(duì)手勢做分析:
手勢不會(huì)走view的touches系列方法朽缴,但有自己的一系列touches方法善玫,不過沒有暴露出來。但是shouldRecognizeSimultaneouslyWithGestureRecognizer也可以做到密强。

針對(duì)UIButton的分析:
UIButton addTarget分析茅郎,addTarget是UIControl的方法,其實(shí)addTarget的方法原理是或渤,UIControl對(duì)touches的觸摸事件的封裝系冗。[super touchesBegan:touches withEvent:event];包括了對(duì)
事件的封裝處理,如果重新了[super touchesBegan:touches withEvent:event];薪鹦,并且里面什么都不實(shí)現(xiàn)掌敬,那么當(dāng)前UIButton添加的addTarget所綁定的所有事件都不會(huì)觸發(fā)。因?yàn)楦采w了父類UIControl的封裝方法池磁。
如果我想一個(gè)按鈕的事件觸發(fā)奔害,并且它的下一響應(yīng)者也能觸發(fā)相應(yīng)的事件。那么該怎么處理呢地熄?
我們?cè)诎粹o上處理舀武,重寫touchesBegan系列的方法,那么根據(jù)上面所說离斩,必須要調(diào)用super的方法银舱,并且主動(dòng)像下一響應(yīng)者[self nextResponder]發(fā)送touchesBegan系列的方法。

2跛梗、A包含B寻馏、C,C在B的上面核偿,但是想讓B接收事件诚欠,C不接收事件
這種可以這么處理,自定義C的View漾岳,重寫hitTest:withEvent方法轰绵,返回nil,這樣自定義C的View及其子類都不會(huì)攔截事件尼荆。這樣B就可以順利處理事件左腔。
還可以把C的userInteractionEnabled設(shè)置為NO

3、A是B捅儒、C的父視圖液样,C在B的上面,這時(shí)候巧还,CB都處理事件鞭莽。這樣到底行不行?根據(jù)響應(yīng)鏈麸祷,這樣應(yīng)該是不靠譜的了澎怒。在C的touches方法中調(diào)用C的touches方法,然后重寫B(tài)的touches方法阶牍,但是這樣怪怪的喷面。有什么高招也請(qǐng)多多指教。貌似也沒有這樣的必要荸恕。

最后還發(fā)現(xiàn)了一篇一步到位的iOS響應(yīng)者鏈的全過程:iOS觸摸事件的流動(dòng)(想有更清晰的了解的看)
直接引用里面的一張圖:

image.png

參考資料:
響應(yīng)者鏈及相關(guān)機(jī)制總結(jié)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末乖酬,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子融求,更是在濱河造成了極大的恐慌咬像,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件生宛,死亡現(xiàn)場離奇詭異县昂,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)陷舅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門倒彰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人莱睁,你說我怎么就攤上這事待讳∶⒗剑” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵创淡,是天一觀的道長痴晦。 經(jīng)常有香客問我,道長琳彩,這世上最難降的妖魔是什么誊酌? 我笑而不...
    開封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮露乏,結(jié)果婚禮上碧浊,老公的妹妹穿的比我還像新娘。我一直安慰自己瘟仿,他們只是感情好箱锐,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著猾骡,像睡著了一般瑞躺。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上兴想,一...
    開封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天幢哨,我揣著相機(jī)與錄音,去河邊找鬼嫂便。 笑死捞镰,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的毙替。 我是一名探鬼主播岸售,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼厂画!你這毒婦竟也來了凸丸?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤袱院,失蹤者是張志新(化名)和其女友劉穎屎慢,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體忽洛,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡腻惠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了欲虚。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片集灌。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖复哆,靈堂內(nèi)的尸體忽然破棺而出欣喧,到底是詐尸還是另有隱情腌零,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布续誉,位于F島的核電站莱没,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏酷鸦。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一牙咏、第九天 我趴在偏房一處隱蔽的房頂上張望臼隔。 院中可真熱鬧,春花似錦妄壶、人聲如沸摔握。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽氨淌。三九已至,卻和暖如春伊磺,著一層夾襖步出監(jiān)牢的瞬間盛正,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來泰國打工屑埋, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留豪筝,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓摘能,卻偏偏與公主長得像续崖,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子团搞,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

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