觸摸發(fā)生時(shí),UIWindow會(huì)有一個(gè)隊(duì)列來(lái)存放所有的觸摸事件饲常,然后再把這些事件發(fā)送給對(duì)應(yīng)的hit-test view,hit-test view會(huì)通過(guò)touch的四個(gè)函數(shù)來(lái)接收這些事件。
四個(gè)函數(shù)分別為:(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];
}
手勢(shì)識(shí)別器同樣有touch的四個(gè)函數(shù),但是手勢(shì)識(shí)別器本身并不繼承自UIResponder鞋屈,本身并不在響應(yīng)鏈里范咨,只有手勢(shì)識(shí)別器對(duì)應(yīng)的view在響應(yīng)鏈中的時(shí)候手勢(shì)識(shí)別器才會(huì)監(jiān)聽(tīng)touch事件故觅,并根據(jù)自己的touch函數(shù)識(shí)別手勢(shì),然后觸發(fā)相應(yīng)的回調(diào)函數(shù)渠啊。本質(zhì)來(lái)說(shuō)输吏,hit-test view觸摸事件的回調(diào)跟手勢(shì)識(shí)別器是兩個(gè)獨(dú)立的過(guò)程,互不干涉替蛉,手勢(shì)識(shí)別器先開(kāi)始接收touch事件贯溅。但是手勢(shì)識(shí)別中定義了三個(gè)屬性,能夠影響hit-test view觸摸事件的調(diào)用過(guò)程躲查,這三個(gè)屬性如下所示:
gesture.cancelsTouchesInView
當(dāng)值為YES時(shí)(默認(rèn)值)它浅,表示手勢(shì)識(shí)別成功后觸摸事件取消掉,即識(shí)別成功后hitTest-View會(huì)調(diào)用touchesCancelled函數(shù)镣煮。
當(dāng)值為NO時(shí)姐霍,觸摸事件會(huì)正常起作用,會(huì)正常收到touchesEnded消息典唇。
gesture.delaysTouchesBegan ?=NO;
當(dāng)值為NO時(shí)(默認(rèn)值)镊折,觸摸事件和手勢(shì)識(shí)別的過(guò)程同時(shí)進(jìn)行,當(dāng)然先會(huì)發(fā)送觸摸事件介衔,然后當(dāng)手勢(shì)識(shí)別成功時(shí)恨胚,觸摸事件會(huì)被取消掉,即識(shí)別成功后hitTest-View會(huì)調(diào)用touchesCancelled函數(shù)炎咖。
當(dāng)值為YES時(shí)赃泡,手勢(shì)識(shí)別器先接收touch事件進(jìn)行手勢(shì)識(shí)別,識(shí)別過(guò)程中hit-test view的觸摸事件會(huì)先被UIWindow hold住乘盼,當(dāng)手勢(shì)識(shí)別成功時(shí)hit-test view的觸摸事件不會(huì)調(diào)用急迂,當(dāng)手勢(shì)識(shí)別失敗時(shí)才開(kāi)始調(diào)用touchesBegan函數(shù)。
gesture.delaysTouchesEnded = YES;
此屬性差別比較小蹦肴。
當(dāng)值為YES時(shí)(默認(rèn)值)僚碎,當(dāng)手勢(shì)識(shí)別失敗時(shí)會(huì)延遲(約0.15ms)調(diào)用touchesEnded函數(shù)。
當(dāng)值為NO時(shí)阴幌,當(dāng)手勢(shì)識(shí)別失敗時(shí)會(huì)立即調(diào)用touchesEnded函數(shù)勺阐。
delaysTouchesBegan卷中、delaysTouchesEnded這兩個(gè)屬性決定是否在手勢(shì)識(shí)別過(guò)程中向hit-test view發(fā)送觸摸事件。
總結(jié):觸摸事件與手勢(shì)識(shí)別是兩個(gè)相對(duì)獨(dú)立的過(guò)程渊抽,但是手勢(shì)識(shí)別可以通過(guò)一些屬性來(lái)影響觸摸事件的調(diào)用蟆豫,一般來(lái)說(shuō)手勢(shì)識(shí)別器的回調(diào)函數(shù)會(huì)比hit-test view的觸摸事件的晚一些,因?yàn)槭謩?shì)識(shí)別器只有在手勢(shì)識(shí)別出來(lái)之后才會(huì)觸發(fā)回調(diào)函數(shù)(默認(rèn)情況下只有一個(gè)手勢(shì)識(shí)別器能夠響應(yīng))懒闷,但是手勢(shì)識(shí)別器接收touch事件的時(shí)機(jī)比hit-test view早十减。
觸摸事件過(guò)程:
觸摸開(kāi)始,找到first responder同時(shí)找到響應(yīng)鏈愤估,當(dāng)響應(yīng)鏈上沒(méi)有手勢(shì)識(shí)別器時(shí)帮辟,觸摸事件通過(guò)first responder的響應(yīng)鏈開(kāi)始傳遞,如果響應(yīng)鏈上有手勢(shì)識(shí)別器玩焰,那么手勢(shì)識(shí)別器先接收事件由驹,然后再根據(jù)手勢(shì)識(shí)別器的三個(gè)屬性來(lái)決定是否同時(shí)將觸摸事件傳給first responder。
手勢(shì)識(shí)別器原理:
手勢(shì)識(shí)別器根據(jù)自身的四個(gè)touch函數(shù)來(lái)識(shí)別手勢(shì)昔园,例如長(zhǎng)按蔓榄、滑動(dòng)等,手勢(shì)識(shí)別器并不繼承自UIResponder默刚,因此它的四個(gè)touch函數(shù)不是UIResponder中的函數(shù)甥郑,而是UIResponder中這四個(gè)函數(shù)的鏡像(說(shuō)白了就是從UIResponder的頭文件中復(fù)制粘貼過(guò)來(lái)的)。
由此可以推測(cè)UIButton的“按下”事件等也是根據(jù)四個(gè)Touch函數(shù)來(lái)實(shí)現(xiàn)的荤西,因?yàn)閁IButton繼承自UIResponder澜搅,本身自帶四個(gè)Touch函數(shù)。(經(jīng)實(shí)驗(yàn)發(fā)現(xiàn)UIButton設(shè)置為enable時(shí)皂冰,在接收到觸摸事件之后不會(huì)繼續(xù)向上傳遞觸摸事件店展,設(shè)置為disable時(shí)會(huì)向上傳遞觸摸事件,但是加在UIButton上的tap手勢(shì)不會(huì)起作用)
以下來(lái)自蘋果代碼注釋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;