用戶交互-響應(yīng)事件的傳遞 -(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event

原文:理解點擊屏幕的事件響應(yīng)--->對UIView的hitTest: withEvent: 方法的理解

作者:??歲月崢嶸·我輩當先

當用戶點擊屏幕后,UIApplication 先響應(yīng)事件,然后傳遞給UIWindow。如果window可以響應(yīng)。就開始遍歷window的subviews。遍歷的過程中,如果第一個遍歷的view1可以響應(yīng),那就遍歷這個view1的subviews(依次這樣不停地查找,直至查找到合適的響應(yīng)事件view)。如果view1不可以響應(yīng),那就開始對view2進行判斷和子視圖的遍歷。依次類推view3,view4…… ?如果最后沒有找到合適的響應(yīng)view,這個消息就會被拋棄。(整個遍歷的過程就是樹的先序遍歷)。

交互

理解了上面的圖后,我們再來看看這兩個方法。

為了方便,我們將

- (nullable UIView*)hitTest:(CGPoint)point withEvent:(nullable UIEvent?*)event;稱為方法A

- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent?*)event;稱為方法B

對view進行重寫這兩個方法后,點擊屏幕后,首先響應(yīng)的是方法A

如果方法A中,我們沒有調(diào)用父類([super hitTest:point withEvent:event];)的這個方法,那就根據(jù)這個方法A的返回view,作為響應(yīng)事件的view。(當然返回nil,就是這個view不響應(yīng))


如果方法A中,我們調(diào)用了父類的方法([super hitTest:point withEvent:event];)那這個時候系統(tǒng)就要調(diào)用方法B;通過這個方法的返回值,來判斷當前這個view能不能響應(yīng)消息。

如果方法B返回的是no,那就不用再去遍歷它的子視圖。方法A返回的view就是可以響應(yīng)事件的view。

如果方法B返回的是YES,那就去遍歷它的子視圖。(就是上圖我們描述的那樣,找到合適的view返回,如果找不到,那就由方法A返回的view去響應(yīng)這個事件。)

總結(jié)下來:

返回一個view來響應(yīng)事件 (如果不想影響系統(tǒng)的事件傳遞鏈,在這個方法內(nèi),最好調(diào)用父類的這個方法)

- (nullable UIView*)hitTest:(CGPoint)point withEvent:(nullable UIEvent?*)event{
? ? return [super hitTest:point withEvent:event]; }

返回的值可以用來判斷是否繼續(xù)遍歷子視圖(返回的根據(jù)是觸摸的point是否在view的frame范圍內(nèi))

- (BOOL)pointInside:(CGPoint)point withEvent:(nullableUIEvent?*)event;? ? ??



出現(xiàn)視圖或者控件無法響應(yīng)的情況的可能原因

1.self.hidden = YES; (控件被隱藏了)

2.self.userInteractionEnabled = NO (視圖的交互性關(guān)閉了)

3.self.alpha <= 0.01 (視圖的透明度為0.01以下)

4.屏幕點擊點 不包含在你需要響應(yīng)的視圖里面(可能是父視圖沒有frame,或者點擊位置超過父視圖范圍)

? ? ? ? ·? 以上4種問題都可以實現(xiàn)下面這個方法來直接檢測出來是否響應(yīng)



- (UIView*)hitTest:(CGPoint)pointwithEvent:(UIEvent*)event{

?? if (self.hidden || !self.userInteractionEnabled || self.alpha <= 0.01) { ?

? ? ? return nil;//無響應(yīng)

? ? } ?? ? ?

? if([self pointInside:point withEvent:event]) { ??

? ? ? ? ? ? ? for(UIView* subView in[self.subviews reverseObjectEnumerator]) { ??

? ? ? ? ? ? ? ? ? ? ? CGPoint convertedPoint = [subView? convertPoint:point fromView:self]; ??

? ? ? ? ? ? ? ? ? ? ? UIView* hitTestView = [subView hitTest:convertedPoint withEvent:event];

?? ? ? ? ? ? ? ? ? ? ? ? if(hitTestView) { ?

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? return hitTestView;//找到響應(yīng)的視圖 可以響應(yīng) ?? ?

? ? ? ? ? ? ? ? ? ? ? ? } ?? ? ? ? ?

? ? ? ? ? } ?? ? ? ? ?

? } ?? ?

? ? return nil;//不響應(yīng)

? ?//return [super hitTest:point withEvent:event];?

}



屏幕穿透點擊

兩個重疊的視圖,響應(yīng)下面的視圖

- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event
{

? ? for (UIView *subview in [self.subviews reverseObjectEnumerator]) {

? ? ? ? CGPoint convertedPoint = [subview convertPoint:point fromView:self];

? ? ? ? UIView*hitTestView = [subview hitTest:convertedPoint withEvent:event];

//判斷視圖是否是你想要穿透的視圖類 如果是就不響應(yīng)該視圖

? ? ? ? if (hitTestView.tag == 2) { ? ? ? ? ? ? return nil; ? ? ? ? }

? ? ? ? if ([hitTestView isKindOfClass:[UICollectionReusableView class]]) {

? ? ? ? ? ? return nil;// 如果兩個重疊的視圖不在一個父視圖?

? ? ? ? ? ? return 你要響應(yīng)的視圖;? ? ?//如果兩個重疊的視圖在同一個父視圖上

? ? ? ? }

? ? ? ? if(hitTestView) {

? ? ? ? ? ? return hitTestView;

? ? ? ? }

? ? }

? ? return[super hitTest:point withEvent:event];

}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容