關(guān)于事件傳遞和響應(yīng)者鏈的定義和使用,建議直接詳細(xì)閱讀蘋果官方文檔(避免不恰當(dāng)?shù)亩种R(shí)):
https://developer.apple.com/library/content/documentation/EventHandling/Conceptual/EventHandlingiPhoneOS/event_delivery_responder_chain/event_delivery_responder_chain.html#//apple_ref/doc/uid/TP40009541-CH4-SW2
當(dāng)用戶點(diǎn)擊觸摸時(shí),iOS系統(tǒng)使用hit-testing來(lái)尋找接觸點(diǎn)下面的視圖。hit-testing 檢查接觸點(diǎn)是否在相關(guān)的視圖對(duì)象內(nèi)部,如果在,它會(huì)遞歸檢查該視圖的每個(gè)子視圖,視圖層級(jí)樹(shù)最下面的包含接觸點(diǎn)的視圖會(huì)成為所謂的hit-test視圖。當(dāng)iOS系統(tǒng)獲得了hit-test視圖,系統(tǒng)將觸摸事件傳遞給該視圖由該視圖響應(yīng)。
在這個(gè)過(guò)程中,主要用到以下兩個(gè)方法:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event; // recursively calls -pointInside:withEvent:. point is in the receiver's coordinate system
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event; // default returns YES if point is in bounds
hitTest:withEvent:會(huì)被視圖對(duì)象的
pointInside:withEvent:方法調(diào)用。如果
hitTest:withEvent:的參數(shù)point在這個(gè)視圖內(nèi)部,說(shuō)明觸摸點(diǎn)在視圖里面,
pointInside:withEvent:方法會(huì)返回YES;然后該視圖的所有子視圖只要pointInside:withEvent:返回YES的,都會(huì)調(diào)用hitTest:withEvent:方法,否則不會(huì)調(diào)用。如果hit-test視圖對(duì)象不能處理點(diǎn)擊事件,點(diǎn)擊事件會(huì)順著響應(yīng)者鏈一直傳播,直到系統(tǒng)找到某個(gè)可以處理它的對(duì)象。
講原理很簡(jiǎn)單,容易懂。但是原理應(yīng)用實(shí)際,就非??疾鞂?shí)用水平了。思考響應(yīng)者鏈可能的應(yīng)用場(chǎng)景,包括但不限于點(diǎn)擊蒙板下面的按鈕,蒙板不響應(yīng)觸摸事件,tableView中的滑動(dòng)控件等等。
在項(xiàng)目中經(jīng)常碰到響應(yīng)者鏈問(wèn)題,終于能將理論與實(shí)踐統(tǒng)一起來(lái)了。
就比如如下場(chǎng)景:
UITableViewCell中有UIDatePicker和UIPickerView子視圖用于選取時(shí)間和事件。
會(huì)出現(xiàn)如下問(wèn)題:觸摸Picker view下部往上滑動(dòng)時(shí),Picker view不滑動(dòng),反而是tableView隨著手指滑動(dòng)。只有當(dāng)手指剛好觸摸在Picker view的文字上并且滑動(dòng)時(shí),才會(huì)讓Picker view滑動(dòng)。即Picker view響應(yīng)區(qū)域并非它的frame,而是文字所在區(qū)域,造成用戶難以滑動(dòng)Picker view進(jìn)行選取操作。
期望的效果:只要滑動(dòng)觸摸在Picker view視圖內(nèi)部范圍,都是滑動(dòng)Picker view,而非tableView。
這是一個(gè)響應(yīng)者鏈問(wèn)題,觸摸事件從UIApplication單例一路傳遞,先傳遞到tableView,后傳遞到Picker view。從表現(xiàn)來(lái)看,應(yīng)該是觸摸在Picker view的文字區(qū)域,由Picker view負(fù)責(zé)響應(yīng);觸摸在Picker view的空白區(qū)域,由tableView負(fù)責(zé)響應(yīng)。這會(huì)造成用戶非常難操作。
解決方案:
步驟1.
以下代碼解決的問(wèn)題是觸摸在MMTimeSelectorCell時(shí)MMTimelineMaskView不響應(yīng)事件。即手指在MMTimeSelectorCell上滑動(dòng)時(shí),不讓tableView滑動(dòng)。

步驟2.
在MMTimeSlectorCell中終止響應(yīng)鏈,如下圖。
因?yàn)槿绻械淖右晥D都沒(méi)有響應(yīng)的話,在父視圖的hitTest:withEvent方法中會(huì)返回自身,想要響應(yīng)事件的子視圖將無(wú)法獲取事件。

這樣就能讓用戶正?;瑒?dòng)Picker view了,只要滑動(dòng)觸摸點(diǎn)在Picker view范圍以內(nèi),都可以滑動(dòng)Picker view,而不是tableView。