天天看點

關于Cocoa Touch事件處理流程-響應者鍊以及hitTest方法流程

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,結束響應。

關于Cocoa Touch事件處理流程-響應者鍊以及hitTest方法流程

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來表示一次事件對象。

關于Cocoa Touch事件處理流程-響應者鍊以及hitTest方法流程

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