iOS觸摸事件、手勢識別原理

觸摸發生時,UIWindow會有一個隊列來存放所有的觸摸事件,然后再把這些事件發送給對應的hit-test view,hit-test view會通過touch的四個函數來接收這些事件。

四個函數分別為:(began,move, end, cancel)

//touches method

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event

{

? ? NSLog(@"%s", __func__);

? ? [super touchesBegan:touches withEvent:event];

}


- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event

{

NSLog(@"%s", __func__);

? ? [super touchesMoved:touches withEvent:event];

}


- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event

{

NSLog(@"%s", __func__);

? ? [super touchesEnded:touches withEvent:event];

}


- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent

*)event {

NSLog(@"%s", __func__);

? ? [super touchesCancelled:touches withEvent:event];

}

手勢識別器同樣有touch的四個函數,但是手勢識別器本身并不繼承自UIResponder,本身并不在響應鏈里,只有手勢識別器對應的view在響應鏈中的時候手勢識別器才會監聽touch事件,并根據自己的touch函數識別手勢,然后觸發相應的回調函數。本質來說,hit-test view觸摸事件的回調跟手勢識別器是兩個獨立的過程,互不干涉,手勢識別器先開始接收touch事件。但是手勢識別中定義了三個屬性,能夠影響hit-test view觸摸事件的調用過程,這三個屬性如下所示:


gesture.cancelsTouchesInView

當值為YES時(默認值),表示手勢識別成功后觸摸事件取消掉,即識別成功后hitTest-View會調用touchesCancelled函數。

當值為NO時,觸摸事件會正常起作用,會正常收到touchesEnded消息。


gesture.delaysTouchesBegan ?=NO;

當值為NO時(默認值),觸摸事件和手勢識別的過程同時進行,當然先會發送觸摸事件,然后當手勢識別成功時,觸摸事件會被取消掉,即識別成功后hitTest-View會調用touchesCancelled函數。

當值為YES時,手勢識別器先接收touch事件進行手勢識別,識別過程中hit-test view的觸摸事件會先被UIWindow hold住,當手勢識別成功時hit-test view的觸摸事件不會調用,當手勢識別失敗時才開始調用touchesBegan函數。


gesture.delaysTouchesEnded = YES;

此屬性差別比較小。

當值為YES時(默認值),當手勢識別失敗時會延遲(約0.15ms)調用touchesEnded函數。

當值為NO時,當手勢識別失敗時會立即調用touchesEnded函數。


delaysTouchesBegan、delaysTouchesEnded這兩個屬性決定是否在手勢識別過程中向hit-test view發送觸摸事件。


總結:觸摸事件與手勢識別是兩個相對獨立的過程,但是手勢識別可以通過一些屬性來影響觸摸事件的調用,一般來說手勢識別器的回調函數會比hit-test view的觸摸事件的晚一些,因為手勢識別器只有在手勢識別出來之后才會觸發回調函數(默認情況下只有一個手勢識別器能夠響應),但是手勢識別器接收touch事件的時機比hit-test view早。


觸摸事件過程:

觸摸開始,找到first responder同時找到響應鏈,當響應鏈上沒有手勢識別器時,觸摸事件通過first responder的響應鏈開始傳遞,如果響應鏈上有手勢識別器,那么手勢識別器先接收事件,然后再根據手勢識別器的三個屬性來決定是否同時將觸摸事件傳給first responder。


手勢識別器原理:

手勢識別器根據自身的四個touch函數來識別手勢,例如長按、滑動等,手勢識別器并不繼承自UIResponder,因此它的四個touch函數不是UIResponder中的函數,而是UIResponder中這四個函數的鏡像(說白了就是從UIResponder的頭文件中復制粘貼過來的)。


由此可以推測UIButton的“按下”事件等也是根據四個Touch函數來實現的,因為UIButton繼承自UIResponder,本身自帶四個Touch函數。(經實驗發現UIButton設置為enable時,在接收到觸摸事件之后不會繼續向上傳遞觸摸事件,設置為disable時會向上傳遞觸摸事件,但是加在UIButton上的tap手勢不會起作用)


以下來自蘋果代碼注釋UIGestureRecognizerSubclass.h:

// mirror of the

touch-delivery methods on UIResponder

//

UIGestureRecognizers aren't in the responder chain, but observe touches

hit-tested to their view and their view's subviews

//

UIGestureRecognizers receive touches before the view to which the touch was

hit-tested

-? (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent

*)event;

- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent

*)event;

- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent

*)event;

- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent

*)event;

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容