觸摸事件
iOS中的事件
- 在用戶(hù)使用app過(guò)程中,會(huì)產(chǎn)生各種各樣的事件
- iOS中的事件可以分為3大類(lèi)型:觸摸事件,加速計(jì)事件,遠(yuǎn)程控制事件
響應(yīng)者對(duì)象
在iOS中不是任何對(duì)象都能處理事件榴芳,只有繼承了UIResponder的對(duì)象才能接收并處理事件。我們稱(chēng)之為“響應(yīng)者對(duì)象”
UIApplication跺撼、UIViewController窟感、UIView都繼承自UIResponder,因此它們都是響應(yīng)者對(duì)象财边,都能夠接收并處理事件
UIResponder
- UIResponder內(nèi)部提供了以下方法來(lái)處理事件
- 觸摸事件
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;
- 加速計(jì)事件
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event;
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event;
- (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event;
- 遠(yuǎn)程控制事件
- (void)remoteControlReceivedWithEvent:(UIEvent *)event
UIView的觸摸事件處理
- UIView是UIResponder的子類(lèi),可以覆蓋下列4個(gè)方法處理不同的觸摸事件
- 一根或者多根手指開(kāi)始觸摸view点骑,系統(tǒng)會(huì)自動(dòng)調(diào)用view的下面方法
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
- 一根或者多根手指在view上移動(dòng)酣难,系統(tǒng)會(huì)自動(dòng)調(diào)用view的下面方法(隨著手指的移動(dòng),會(huì)持續(xù)調(diào)用該方法)
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
- 一根或者多根手指離開(kāi)view黑滴,系統(tǒng)會(huì)自動(dòng)調(diào)用view的下面方法
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
- 觸摸結(jié)束前憨募,某個(gè)系統(tǒng)事件(例如電話呼入)會(huì)打斷觸摸過(guò)程,系統(tǒng)會(huì)自動(dòng)調(diào)用view的下面方法
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
提示:touches中存放的都是UITouch對(duì)象
UITouch
當(dāng)用戶(hù)用一根手指觸摸屏幕時(shí)袁辈,會(huì)創(chuàng)建一個(gè)與手指相關(guān)聯(lián)的UITouch對(duì)象
一根手指對(duì)應(yīng)一個(gè)UITouch對(duì)象
-
UITouch的作用
- 保存著跟手指相關(guān)的信息菜谣,比如觸摸的位置、時(shí)間晚缩、階段
當(dāng)手指移動(dòng)時(shí)尾膊,系統(tǒng)會(huì)更新同一個(gè)UITouch對(duì)象,使之能夠一直保存該手指在的觸摸位置
當(dāng)手指離開(kāi)屏幕時(shí)荞彼,系統(tǒng)會(huì)銷(xiāo)毀相應(yīng)的UITouch對(duì)象
提示:iPhone開(kāi)發(fā)中冈敛,要避免使用雙擊事件!
UITouch的屬性
- 觸摸產(chǎn)生時(shí)所處的窗口
@property(nonatomic,readonly,retain) UIWindow *window;
- 觸摸產(chǎn)生時(shí)所處的視圖
@property(nonatomic,readonly,retain) UIView *view;
- 短時(shí)間內(nèi)點(diǎn)按屏幕的次數(shù)鸣皂,可以根據(jù)tapCount判斷單擊抓谴、雙擊或更多的點(diǎn)擊
@property(nonatomic,readonly) NSUInteger tapCount;
- 記錄了觸摸事件產(chǎn)生或變化時(shí)的時(shí)間暮蹂,單位是秒
@property(nonatomic,readonly) NSTimeInterval timestamp;
- 當(dāng)前觸摸事件所處的狀態(tài)
@property(nonatomic,readonly) UITouchPhase phase;
UITouchPhase是一個(gè)枚舉類(lèi)型,包含:
UITouchPhaseBegan(觸摸開(kāi)始)
UITouchPhaseMoved(接觸點(diǎn)移動(dòng))
UITouchPhaseStationary(接觸點(diǎn)無(wú)移動(dòng))
UITouchPhaseEnded(觸摸結(jié)束)
UITouchPhaseCancelled(觸摸取消)
UITouch的方法
-
(CGPoint)locationInView:(UIView *)view;
- 返回值表示觸摸在view上的位置
- 這里返回的位置是針對(duì)view的坐標(biāo)系的(以view的左上角為原點(diǎn)(0, 0))
- 調(diào)用時(shí)傳入的view參數(shù)為nil的話癌压,返回的是觸摸點(diǎn)在UIWindow的位置
-
(CGPoint)previousLocationInView:(UIView *)view;
- 該方法記錄了前一個(gè)觸摸點(diǎn)的位置
UIEvent
每產(chǎn)生一個(gè)事件仰泻,就會(huì)產(chǎn)生一個(gè)UIEvent對(duì)象
UIEvent:稱(chēng)為事件對(duì)象,記錄事件產(chǎn)生的時(shí)刻和類(lèi)型
-
常見(jiàn)屬性
- 事件類(lèi)型
@property(nonatomic,readonly) UIEventType type;
@property(nonatomic,readonly) UIEventSubtype subtype;
- 事件產(chǎn)生的時(shí)間
@property(nonatomic,readonly) NSTimeInterval timestamp;
- UIEvent還提供了相應(yīng)的方法可以獲得在某個(gè)view上面的觸摸對(duì)象(UITouch)
touches和event參數(shù)
-
一次完整的觸摸過(guò)程滩届,會(huì)經(jīng)歷3個(gè)狀態(tài):
- 觸摸開(kāi)始:- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
- 觸摸移動(dòng):- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
- 觸摸結(jié)束:- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
- 觸摸取消(可能會(huì)經(jīng)歷):- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
-
4個(gè)觸摸事件處理方法中集侯,都有NSSet *touches和UIEvent *event兩個(gè)參數(shù)
- 一次完整的觸摸過(guò)程中,只會(huì)產(chǎn)生一個(gè)事件對(duì)象丐吓,4個(gè)觸摸方法都是同一個(gè)event參數(shù)
- 如果兩根手指同時(shí)觸摸一個(gè)view浅悉,那么view只會(huì)調(diào)用一次touchesBegan:withEvent:方法,touches參數(shù)中裝著2個(gè)UITouch對(duì)象
- 如果這兩根手指一前一后分開(kāi)觸摸同一個(gè)view券犁,那么view會(huì)分別調(diào)用2次touchesBegan:withEvent:方法术健,并且每次調(diào)用時(shí)的touches參數(shù)中只包含一個(gè)UITouch對(duì)象
- 根據(jù)touches中UITouch的個(gè)數(shù)可以判斷出是單點(diǎn)觸摸還是多點(diǎn)觸摸
事件的產(chǎn)生和傳遞
發(fā)生觸摸事件后,系統(tǒng)會(huì)將該事件加入到一個(gè)由UIApplication管理的事件隊(duì)列中
UIApplication會(huì)從事件隊(duì)列中取出最前面的事件粘衬,并將事件分發(fā)下去以便處理荞估,通常,先發(fā)送事件給應(yīng)用程序的主窗口(keyWindow)
主窗口會(huì)在視圖層次結(jié)構(gòu)中找到一個(gè)最合適的視圖來(lái)處理觸摸事件稚新,這也是整個(gè)事件處理過(guò)程的第一步
-
找到合適的視圖控件后勘伺,就會(huì)調(diào)用視圖控件的touches方法來(lái)作具體的事件處理
- touchesBegan…
- touchesMoved…
- touchedEnded…
UIView不接收觸摸事件的三種情況
- 不接收用戶(hù)交互
userInteractionEnabled = NO
- 隱藏
hidden = YES
- 透明
alpha = 0.0 ~ 0.01
- 提示:UIImageView的userInteractionEnabled默認(rèn)就是NO,因此UIImageView以及它的子控件默認(rèn)是不能接收觸摸事件的
觸摸事件處理的詳細(xì)過(guò)程
用戶(hù)點(diǎn)擊屏幕后產(chǎn)生的一個(gè)觸摸事件褂删,經(jīng)過(guò)一系列的傳遞過(guò)程后飞醉,會(huì)找到最合適的視圖控件來(lái)處理這個(gè)事件
-
找到最合適的視圖控件后,就會(huì)調(diào)用控件的touches方法來(lái)作具體的事件處理
- touchesBegan…
- touchesMoved…
- touchedEnded…
這些touches方法的默認(rèn)做法是將事件順著響應(yīng)者鏈條向上傳遞屯阀,將事件交給上一個(gè)響應(yīng)者進(jìn)行處理
響應(yīng)者鏈條
- 響應(yīng)者鏈條:是由多個(gè)響應(yīng)者對(duì)象連接起來(lái)的鏈條
- 作用:能很清楚的看見(jiàn)每個(gè)響應(yīng)者之間的聯(lián)系缅帘,并且可以讓一個(gè)事件多個(gè)對(duì)象處理。
- 響應(yīng)者對(duì)象:能處理事件的對(duì)象
事件傳遞的完整過(guò)程
1> 先將事件對(duì)象由上往下傳遞(由父控件傳遞給子控件)难衰,找到最合適的控件來(lái)處理這個(gè)事件钦无。
2> 調(diào)用最合適控件的touches….方法
3> 如果調(diào)用了[super touches….];就會(huì)將事件順著響應(yīng)者鏈條往上傳遞,傳遞給上一個(gè)響應(yīng)者
4> 接著就會(huì)調(diào)用上一個(gè)響應(yīng)者的touches….方法
- *如何判斷上一個(gè)響應(yīng)者
- 1> 如果當(dāng)前這個(gè)view是控制器的view,那么控制器就是上一個(gè)響應(yīng)者
- 2> 如果當(dāng)前這個(gè)view不是控制器的view,那么父控件就是上一個(gè)響應(yīng)者
響應(yīng)者鏈的事件傳遞過(guò)程
- 如果view的控制器存在盖袭,就傳遞給控制器失暂;如果控制器不存在斯嚎,則將其傳遞給它的父視圖
- 在視圖層次結(jié)構(gòu)的最頂級(jí)視圖挣轨,如果也不能處理收到的事件或消息胞此,則其將事件或消息傳遞給window對(duì)象進(jìn)行處理
- 如果window對(duì)象也不處理隙券,則其將事件或消息傳遞給UIApplication對(duì)象
- 如果UIApplication也不能處理該事件或消息固阁,則將其丟棄
監(jiān)聽(tīng)觸摸事件的做法
-
如果想監(jiān)聽(tīng)一個(gè)view上面的觸摸事件迂卢,之前的做法是
- 自定義一個(gè)view
- 實(shí)現(xiàn)view的touches方法溜哮,在方法內(nèi)部實(shí)現(xiàn)具體處理代碼
-
通過(guò)touches方法監(jiān)聽(tīng)view觸摸事件屿良,有很明顯的幾個(gè)缺點(diǎn)
- 必須得自定義view
- 由于是在view內(nèi)部的touches方法中監(jiān)聽(tīng)觸摸事件悠栓,因此默認(rèn)情況下霉涨,無(wú)法讓其他外界對(duì)象監(jiān)聽(tīng)view的觸摸事件
- 不容易區(qū)分用戶(hù)的具體手勢(shì)行為
iOS 3.2之后按价,蘋(píng)果推出了手勢(shì)識(shí)別功能(Gesture Recognizer),在觸摸事件處理方面笙瑟,大大簡(jiǎn)化了開(kāi)發(fā)者的開(kāi)發(fā)難度
UIGestureRecognizer
為了完成手勢(shì)識(shí)別楼镐,必須借助于手勢(shì)識(shí)別器----UIGestureRecognizer
利用UIGestureRecognizer,能輕松識(shí)別用戶(hù)在某個(gè)view上面做的一些常見(jiàn)手勢(shì)
-
UIGestureRecognizer是一個(gè)抽象類(lèi)往枷,定義了所有手勢(shì)的基本行為框产,使用它的子類(lèi)才能處理具體的手勢(shì)
- UITapGestureRecognizer(敲擊)
- UIPinchGestureRecognizer(捏合,用于縮放)
- UIPanGestureRecognizer(拖拽)
- UISwipeGestureRecognizer(輕掃)
- UIRotationGestureRecognizer(旋轉(zhuǎn))
- UILongPressGestureRecognizer(長(zhǎng)按)
UITapGestureRecognizer
- 每一個(gè)手勢(shì)識(shí)別器的用法都差不多错洁,比如UITapGestureRecognizer的使用步驟如下
- 創(chuàng)建手勢(shì)識(shí)別器對(duì)象
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] init];
- 設(shè)置手勢(shì)識(shí)別器對(duì)象的具體屬性
// 連續(xù)敲擊2次
tap.numberOfTapsRequired = 2;
// 需要2根手指一起敲擊
tap.numberOfTouchesRequired = 2;
- 添加手勢(shì)識(shí)別器到對(duì)應(yīng)的view上
[self.iconView addGestureRecognizer:tap];
- 監(jiān)聽(tīng)手勢(shì)的觸發(fā)
[tap addTarget:self action:@selector(tapIconView:)];
手勢(shì)識(shí)別的狀態(tài)
typedef NS_ENUM(NSInteger, UIGestureRecognizerState) {
// 沒(méi)有觸摸事件發(fā)生秉宿,所有手勢(shì)識(shí)別的默認(rèn)狀態(tài)
UIGestureRecognizerStatePossible,
// 一個(gè)手勢(shì)已經(jīng)開(kāi)始但尚未改變或者完成時(shí)
UIGestureRecognizerStateBegan,
// 手勢(shì)狀態(tài)改變
UIGestureRecognizerStateChanged,
// 手勢(shì)完成
UIGestureRecognizerStateEnded,
// 手勢(shì)取消,恢復(fù)至Possible狀態(tài)
UIGestureRecognizerStateCancelled,
// 手勢(shì)失敗屯碴,恢復(fù)至Possible狀態(tài)
UIGestureRecognizerStateFailed,
// 識(shí)別到手勢(shì)識(shí)別
UIGestureRecognizerStateRecognized = UIGestureRecognizerStateEnded
};