iOS中的事件可以分為三大類(lèi):
1> 觸摸事件
2> 加速計(jì)事件
3> 遠(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)處理事件;
1> 觸摸事件
一次完成的觸摸過(guò)程,會(huì)經(jīng)歷3個(gè)狀態(tài);
UIView的觸摸事件處理
1、一根或多根手指開(kāi)始觸摸view,系統(tǒng)會(huì)自動(dòng)調(diào)用view下面的方法:
?
1
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;? //觸摸開(kāi)始
2、一根或者多根手指在view上移動(dòng),系統(tǒng)會(huì)自動(dòng)調(diào)用view下面的方法(隨著手指的移動(dòng),會(huì)持續(xù)調(diào)用該方法):
?
1
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;? //觸摸移動(dòng)
3、一根或者多根手指離開(kāi)view,系統(tǒng)會(huì)自動(dòng)調(diào)用view下面的方法:
?
1
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;? //觸摸結(jié)束
4、觸摸結(jié)束前,某個(gè)系統(tǒng)事件(例如電話(huà)呼入)會(huì)打斷觸摸過(guò)程,系統(tǒng)會(huì)自動(dòng)調(diào)用view下面的方法
?
1
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event; //觸摸取消(可能會(huì)經(jīng)歷)
4個(gè)觸摸事件的處理方法中,都有 NSSet *touches 和 UIEvent *event 兩個(gè)參數(shù);
1、一次完整的觸摸過(guò)程,只會(huì)產(chǎn)生一個(gè)事件對(duì)象,4個(gè)觸摸方法都是同一個(gè)event參數(shù);
2、如果兩根手指同時(shí)觸摸一個(gè)view,那么view只會(huì)調(diào)用一次 touchesBegan:withEvent: 方法,touches參數(shù)中裝著兩個(gè)UITouch對(duì)象;
3、如果這兩根手指一前一后分開(kāi)觸摸同一個(gè)view,那么view會(huì)分別調(diào)用兩次 touchesBegan:withEvent:方法, 并且每次調(diào)用時(shí)的touches參數(shù)只包含一個(gè)UITouch對(duì)象;
4、根據(jù)touches中UITouch個(gè)數(shù)可以判斷出使單點(diǎn)觸摸還是多點(diǎn)觸摸。
提示:touches中存放的都是UITouch對(duì)象。
2> 加速計(jì)事件
?
1
2
3
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event;
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event;
- (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event;
3> 遠(yuǎn)程控制事件
?
1
- (void)remoteControlReceivedWithEvent:(UIEvent *)event;
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í)所處的窗口
?
1
@property(nonatomic,readonly,retain) UIWindow *window;
觸摸產(chǎn)生時(shí)所處的視圖
?
1
@property(nonatomic,readonly,retain) UIView *view;
短時(shí)間內(nèi)點(diǎn)按屏幕的次數(shù),可以根據(jù)tapCount判斷單擊、雙擊或更多地點(diǎn)擊
?
1
@property(nonatomic,readonly) NSUInteger tapCount;
記錄了觸摸事件產(chǎn)生或變化時(shí)的時(shí)間,單位是秒
?
1
@property(nonatomic,readonly) NSTimeInterval timestamp;
當(dāng)前觸摸事件所處的狀態(tài)
?
1
2
3
4
5
6
7
8
@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的方法:
?
1
- (CGPoint)locationInView:(UIView *)view;
1> 返回值表示觸摸在view上的位置;
2> 這里返回的位置是針對(duì)view坐標(biāo)系的,(以view的左上角為原點(diǎn)(0,0));
3> 調(diào)用時(shí)傳入的view參數(shù)為nil 的話(huà),返回的是觸摸點(diǎn)在UIWindow的位置。
?
1
- (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)屬性:
1> 事件類(lèi)型
?
@property(nonatomic,readonly) UIEventType? type;
@property(nonatomic,readonly) UIEventSubtype? subtype;
/*
typedef
NS_ENUM(NSInteger, UIEventType) {
UIEventTypeTouches,
UIEventTypeMotion,
UIEventTypeRemoteControl,
};
typedef
NS_ENUM(NSInteger, UIEventSubtype) {
// available in iPhone OS 3.0
UIEventSubtypeNone? ? ? ? ? ? ? ? ? ? ? ? ? ? ? = 0,
// for UIEventTypeMotion, available in iPhone OS 3.0
UIEventSubtypeMotionShake? ? ? ? ? ? ? ? ? ? ? = 1,
// for UIEventTypeRemoteControl, available in iOS 4.0
UIEventSubtypeRemoteControlPlay? ? ? ? ? ? ? ? = 100,
UIEventSubtypeRemoteControlPause? ? ? ? ? ? ? ? = 101,
UIEventSubtypeRemoteControlStop? ? ? ? ? ? ? ? = 102,
UIEventSubtypeRemoteControlTogglePlayPause? ? ? = 103,
UIEventSubtypeRemoteControlNextTrack? ? ? ? ? ? = 104,
UIEventSubtypeRemoteControlPreviousTrack? ? ? ? = 105,
UIEventSubtypeRemoteControlBeginSeekingBackward = 106,
UIEventSubtypeRemoteControlEndSeekingBackward? = 107,
UIEventSubtypeRemoteControlBeginSeekingForward? = 108,
UIEventSubtypeRemoteControlEndSeekingForward? ? = 109,
};
*/
2> 事件產(chǎn)生的時(shí)間
?
1
@property(nonatomic,readonly) NSTimeInterval? timestamp;
UIEvent還提供了相應(yīng)的方法可以獲得在某個(gè)view上面的觸摸對(duì)象(UITouch)。
觸摸事件的產(chǎn)生:
1> 發(fā)生觸摸事件后,系統(tǒng)會(huì)將該事件加入到一個(gè)由UIApplication管理的事件隊(duì)列中;
2> UIApplication會(huì)從事件隊(duì)列中取出最前面的事件,并將事件分發(fā)下去以便處理,通常,先發(fā)送事件給應(yīng)用程序的主窗口(keyWindow);
3> 主窗口會(huì)在視圖層次結(jié)構(gòu)中找到一個(gè)最合適的視圖控件來(lái)處理觸摸事件,這也是整個(gè)事件處理過(guò)程的第一步;
4> 找到合適的視圖控件后,就會(huì)調(diào)用視圖控件的touches方法來(lái)做具體的事件處理。
觸摸事件的傳遞:
觸摸事件的傳遞是從父控件傳遞到子控件;
如果父控件不能接收觸摸事件,那么子控件就不可能接收到觸摸事件。
UIView不接收觸摸事件的三種情況:
1> 不接受用戶(hù)交互 :userInteractionEnable = NO;
2> 隱藏 :hidden = YES;
3> 透明:alpha = 0.0 ~ 0.01
提示:UIImageView的userInteractionEnable默認(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)者鏈的事件傳遞過(guò)程:
1> 如果view的控制器存在,就傳遞給控制器;如果控制器不存在,則將其傳遞給它的父視圖;
2> 在視圖層次結(jié)構(gòu)最頂級(jí)的視圖,如果也不能處理收到的事件或消息,則其將事件或消息傳遞給window對(duì)象進(jìn)行處理。
3> 如果window對(duì)象也不處理,則其將事件或消息傳遞給UIApplication對(duì)象;
4> 如果UIApplication也不能處理該事件或消息,則將其丟棄。
監(jiān)聽(tīng)觸摸事件的做法
如果想監(jiān)聽(tīng)一個(gè)view上面的觸摸事件,之前的做法是:
1> 自定義一個(gè)view;
2> 實(shí)現(xiàn)view的touches方法,在方法內(nèi)部實(shí)現(xiàn)具體處理代碼。
通過(guò)touches方法監(jiān)聽(tīng)view觸摸事件,有很明顯的幾個(gè)缺點(diǎn):
1> 必須得自定義view;
2> 由于是在view內(nèi)部的touches方法中監(jiān)聽(tīng)觸摸事件,因此默認(rèn)情況下,無(wú)法讓其他外界對(duì)象監(jiān)聽(tīng)view的觸摸事件;
3> 不容易區(qū)分用戶(hù)的具體手勢(shì)行為。
iOS 3.2之后,蘋(píng)果推出了手勢(shì)識(shí)別功能(Gesture Recognizer),在觸摸事件處理方面,大大簡(jiǎn)化了開(kāi)發(fā)者的開(kāi)發(fā)難度。
UIGestureRescognizer
為了完成手勢(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)按)
每一個(gè)手勢(shì)識(shí)別器的用法都差不多,比如UITapGestureRecognizer的使用步驟如下:
1> 創(chuàng)建手勢(shì)識(shí)別器對(duì)象;
?
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] init];
2> 設(shè)置手勢(shì)識(shí)別器對(duì)象的具體屬性;
?
// 連續(xù)敲擊2次
tap.numberOfTapsRequired = 2;
// 需要2根手指一起敲擊
tap.numberOfTouchesRequired = 2;
3> 添加手勢(shì)識(shí)別器到對(duì)應(yīng)的view上;
?
1
[self.iconView addGestureRecognizer:tap];
4> 監(jiān)聽(tīng)手勢(shì)的觸發(fā)
?
1
[tap addTarget:self action:@selector(tapIconView:)];
手勢(shì)識(shí)別的狀太
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
};