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-SW1
After iOS determines the hit-test view, it passes the touch event to that view for handling.
手指觸摸操作時Touch,從iOS系統(打包成UIEvent對象)-目前活動的應用Application(将UIEvent對象存放到事件隊列中)-UIApplication(從隊列中拿到觸摸事件)-UIWindow(開始hit-test檢測)-subViews最終找到第一響應者。
如果第一響應者處理了該事件UIEvent,則事件分發結束。如果第一響應者沒做處理,則系統會講事件通過消息傳遞給響應者鍊中的下一個響應者,看它是否可以進行處理。如果響應者都沒做處理,最終傳遞到UIApplication,結束響應。
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiM4gDMwYDNxEjNxMDM3EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
http://www.cnblogs.com/tangbinblog/p/4066930.html
關于hitTest:withEvent:
字面意思是撞擊測試,當手指觸摸到目前螢幕上活躍的 app 界面。ios 系統會将 目前觸摸操作 打包,具體就是UIEvent
螢幕上的每一次動作都是一個UITouch,多個UITouch 組成一次UIEvent. UIEvent 表示一次事件。
傳遞給目前活躍的App keyWindow.正常情況下 hitTest 确定螢幕上衆多View 中哪一發生了事件。
http://www.cnblogs.com/iOS-mt/p/4197256.html
事件分發(Event Delivery)
第一響應者(First responder)指的是目前接受觸摸的響應者對象(通常是一個UIView對象),即表示目前該對象正在與使用者互動,它是響應者鍊的開端。整個響應者鍊和事件分發的使命都是找出第一響應者。
UIWindow對象以消息的形式将事件發送給第一響應者,使其有機會首先處理事件。如果第一響應者沒有進行處理,系統就将事件(通過消息)傳遞給響應者鍊中的下一個響應者,看看它是否可以進行處理。
單例的應用(UIApplication)是一個響應者鍊的終點,它的下一個響應者指向nil,以結束整個循環
http://blog.csdn.net/wzzvictory/article/details/9264335
iOS系統檢測到手指觸摸(Touch)操作時會将其打包成一個UIEvent對象,并放入目前活動Application的事件隊列,單例的UIApplication會從事件隊列中取出觸摸事件并傳遞給單例的UIWindow來處理,UIWindow對象首先會使用hitTest:withEvent:方法尋找此次Touch操作初始點所在的視圖(View),即需要将觸摸事件傳遞給其處理的視圖,這個過程稱之為hit-test view。
UIWindow執行個體對象會首先在它的内容視圖上調用hitTest:withEvent:,此方法會在其視圖層級結構中的每個視圖上調用pointInside:withEvent:(該方法用來判斷點選事件發生的位置是否處于目前視圖範圍内,以确定使用者是不是點選了目前視圖),如果pointInside:withEvent:傳回YES,則繼續逐級調用,直到找到touch操作發生的位置,這個視圖也就是要找的hit-test view。
hitTest:withEvent:方法的處理流程如下:
首先調用目前視圖的pointInside:withEvent:方法判斷觸摸點是否在目前視圖内;
若傳回NO,則hitTest:withEvent:傳回nil;
若傳回YES,則向目前視圖的所有子視圖(subviews)發送hitTest:withEvent:消息,所有子視圖的周遊順序是從最頂層視圖一直到到最底層視圖,即從subviews數組的末尾向前周遊,直到有子視圖傳回非空對象或者全部子視圖周遊完畢;
若第一次有子視圖傳回非空對象,則hitTest:withEvent:方法傳回此對象,處理結束;
如所有子視圖都傳回非,則hitTest:withEvent:方法傳回自身(self)。
http://ryantang.me/blog/2013/12/07/ios-event-dispatch-1/
在螢幕上的每一次動作事件都是一次Touch,在iOS中用UITouch對象表示每一次的觸控,多個Touch組成一次Event,用UIEvent來表示一次事件對象。
iOS中事件傳遞首先從App(UIApplication)開始,接着傳遞到Window(UIWindow),在接着往下傳遞到View之前,Window會将事件交給GestureRecognizer,如果在此期間,GestureRecognizer識别了傳遞過來的事件,則該事件将不會繼續傳遞到View去,而是像我們之前說的那樣交給Target(ViewController)進行處理。
http://www.cnblogs.com/klaus/archive/2013/04/22/3036692.html
想要實作點選穿透,點選下層視圖的功能,可以通過重寫hitTest:withEvent:方法
在此例子中button,scrollview同為topView的子視圖,但scrollview覆寫在button之上,這樣在在button上的觸摸操作傳回的hit-test view為scrollview,button無法響應,可以修改topView的hitTest:withEvent:方法如下:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
UIView *result = [super hitTest:point withEvent:event];
CGPoint buttonPoint = [underButton convertPoint:point fromView:self];
if ([underButton pointInside:buttonPoint withEvent:event]) {
return underButton;
}
return result;
}
http://www.cnblogs.com/vicstudio/archive/2013/06/12/3133359.html
-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
1.我們都知道,一個螢幕事件由響應鍊一步步傳下去。這個函數傳回的view就是可以讓你決定在這個point的事件,你用來接收事件的view。當然,如果這個point不在你的view的範圍,傳回nil。
2.這個函數忽略userInteractionEnabled,hidden,alpha<0.01,也就是你一個view隐藏或什麼了,還是可以作為接收者。
3.調用次序是從subview top到bottom,包括view本身。
補充一下UIResponder的事件順序,首先必須先調用hitTest判斷接收事件的view,如果hitTest傳回的view不為空,則會把hitTest傳回的view作為第一響應者,然後調用
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
如果有UIGestureRecognizer或者userInteractionEnabled這種優先級高的,就調用
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;
否則,如果中間手指移動什麼的,就調用
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;(移出目前view了,就不touchesEnded了,又touchesCancelled)
最後正常完成事件,就調用-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event。
參考: http://www.jianshu.com/p/d8512dff2b3e
示例連結 https://github.com/lookyoung/hitTest_Application.git