hit-test的用法總結:如何阻止touch事件傳遞到子view
阿新 • • 發佈:2019-02-08
今天,群裡有人問了這個問題:添加了touch事件之後怎麼阻止touch事件傳遞到子view。其實看了官方的文件Event Handling Guide for iOS的童鞋,應該是沒有問題的。但是自己還是總結一下。
觸控之後,主要的步驟如下:
(1), 事件分發:如何確定當前點選的點由哪個view來處理? hit-test來確定hit-view (2), 事件響應:確定hit-view之後,如何處理事件?當確定了hit-view之後,第一響應者就是當前的hit-view,然後就會根據響應者鏈來處理觸控事件。
有手勢的先處理手勢,手勢識別失敗後,執行touch系列回撥處理。
情景應用{ UIView *hitView = [super hitTest:point withEvent:event];
* 如果想要阻斷觸控事件傳遞給subView,下面的2種做法是不太合理的:
*/ /* 方法1: * 不管3721的直接返回self也是不對的,因為當沒有點選在self上(包括它的subView)的時候,self都成了hit-view
*/return self; /* 方法2: * 因為hitView可能返回的是它的subView的subView的subView...
* 所以不能這麼做. 如果能確定self的subView只有一級,這麼做也是可以的.
*/if (hitView.superview == self && hitView == self) {
return self; // 點選在它的subView上也由它自己來處理,subView永遠不是hit-view(永遠不會是第一響應者,不處理觸控)
} /* 正確的做法:也就是下面的方法二,在subView中過載hitTest * 1,可以在self的subView中過載hitTest方法,直接返回nil,那麼點選在self的subView上的時候,最後hit-view還是self
* 所以在過載此方法的時候一定要搞清楚具體的應用場景.
*/return nil; } 可以看到直接在viewA中過載還是不太好的實現,而且如果viewA是一個vc.view,那麼就沒辦法過載hitTest方法了。 方法二:推薦 在viewA的subView(YLViewSub1)的類中過載hitTest, 在YLViewSub1的.m檔案中, -(id)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
UIView *hitView = [super hitTest:point withEvent:event]; if (hitView == self) {
return nil;
} else {
return hitView; } } 有人可能有疑問了:上面說過直接在這裡返回nil不就可以了,為什麼還要分情況處理。 其實這要看具體的情況了,如果YLViewSub1上面還有subView,直接的返回nil,那麼就會忽略掉,所以如果你想全部忽略掉就直接返回nil,不然可以像上面這麼處理。 另外,還有一種更簡單的做法,直接讓viewA的subView的userInteraction為NO,那麼subView就不會受到觸控訊息了。 擴充套件: hit-test還有另外一個場景,比如viewA有B,C兩個subView,但是他們2個有重合的部分,點選重合部分,那如何指定讓B響應還是C來響應。 分析: 預設情況下,hit-test的時候,是從subViews的最頂上的subView開始執行hit-test,即假如先加B,再載入C,那麼hit-test就是先C先B後,這樣點選重合部分,那就是C就是hit-view,那麼由C來響應。 如何變成讓B先響應呢? -(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
UIView *hitView = [super hitTest:point withEvent:event]; if (hitView == viewC) { // 點選在C上的時候,讓hit-view為B,那麼B就是第一響應者 return viewB;
} else {
return hitView;
} } 這樣,當點選重合部分就由B響應,點選B,C非重合部分由他們各自響應. 問題4:如果一個view自己不願意處理touch事件,但是希望它的subViews處理,怎麼辦? 應用場景:這個問題有點sb,因為預設情況下就是subView來先處理,應用場景在哪? 可能是如果點選view自己,讓他的父檢視處理,點選view上面的subView由subView響應更合理? 分析: 設定view.userInteractionEnable = NO;之後,雖然自己不會響應touch事件,但是它的子view也不會響應了, 所以不能這麼做。這時候就需要使用hitTest來處理, -( UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { UIView *hitView = [superhitTest:point withEvent:event]; if (hitView == self) { return nil; // 是self的時候, 不做處理,由它的父檢視處理 } else {
return hitView; // 是subView的時候,由subView去處理
}
} 問題5:如果一個view自己不想處理,也不願意往它的響應者鏈傳遞讓別人處理,怎麼辦? 分析:首先,確定處理物件的時候必須是自己,然後,在自己這裡處理的時候丟棄, 也就是自己過載響應函式,然後響應函式裡面不做任何事,這樣就不會繼續向上傳遞了,也就是在自己這裡做一個空處理來截止響應的處理. 問題6:一個全屏的UIView上載入了tap手勢,在此view上載入一個UITableView,點選cell的時候沒有執行tableview的didSelected:方法,而是響應了_onTap手勢,如何處理? 解決方法: - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
// tianyi memo:// 點選在tableView上時,因為tableView自己不響應tap,所以會交給它的父檢視self來響應,也就是響應_onTap:,但這不是我們想要的// 我們需要點選tableView上面時,響應tableView的didSelectRowAtIndexPath方法.點選其他空白地方相應_onTap:// 返回NO表示,tap手勢不會根據響應者鏈傳遞了,當前的touch物件會被忽略,也就是丟棄這個手勢,// 丟棄手勢之後,相當於手勢識別失敗,然後就會走預設的touch系列回撥方法,我猜測在這個時候UITableView執行了自己預設的選擇cell的流程.if ([touch.viewisDescendantOfView:_tableView]) {
returnNO;
} returnYES; } 如果有不正確或者考慮不完善的地方,歡迎指正交流。