iOS中的事件可以分爲三大類:
1> 觸摸事件
2> 加速計事件
3> 遠程控制事件
響應者對象
在iOS中不是任何對象都能處理事件,只有繼承了UIResponder的對象才能接收並處理事件.我們稱之爲"響應者對象".
UIApplication,UIViewController,UIView都繼承自UIResponder,因此它們都是響應者對象,都能夠接收並處理事件.
UIResponder
UIResponder內部提供了方法來處理事件;
1> 觸摸事件
一次完成的觸摸過程,會經歷3個狀態;
UIView的觸摸事件處理
1、一根或多根手指開始觸摸view,系統會自動調用view下面的方法:
1
|
- ( void )touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event; //觸摸開始 |
2、一根或者多根手指在view上移動,系統會自動調用view下面的方法(隨着手指的移動,會持續調用該方法):
1
|
- ( void )touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event; //觸摸移動 |
3、一根或者多根手指離開view,系統會自動調用view下面的方法:
1
|
- ( void )touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event; //觸摸結束 |
4、觸摸結束前,某個系統事件(例如電話呼入)會打斷觸摸過程,系統會自動調用view下面的方法
1
|
- ( void )touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event; //觸摸取消(可能會經歷) |
4個觸摸事件的處理方法中,都有 NSSet *touches 和 UIEvent *event 兩個參數;
1、一次完整的觸摸過程,只會產生一個事件對象,4個觸摸方法都是同一個event參數;
2、如果兩根手指同時觸摸一個view,那麼view只會調用一次 touchesBegan:withEvent: 方法,touches參數中裝着兩個UITouch對象;
3、如果這兩根手指一前一後分開觸摸同一個view,那麼view會分別調用兩次 touchesBegan:withEvent:方法, 並且每次調用時的touches參數只包含一個UITouch對象;
4、根據touches中UITouch個數可以判斷出使單點觸摸還是多點觸摸。
提示:touches中存放的都是UITouch對象。
2> 加速計事件
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> 遠程控制事件
1
|
- ( void )remoteControlReceivedWithEvent:(UIEvent *)event; |
UITouch
當用戶用一根手指觸摸屏幕時,會創建一個與手指相關聯的UITouch對象;一根手指對應一個UITouch對象;
UITouch的作用:
保存跟手指相關的信息,比如觸摸的位置、時間、階段;
當手指移動時,系統會更新同一個UITouch對象,使之能夠一直保存該手指的觸摸位置;
當手指離開屏幕時,系統會銷燬相應的UITouch對象。
提示:iPhone開發中,要避免使用雙擊事件。
UITouch的屬性:
觸摸產生時所處的窗口
1
|
@property(nonatomic,readonly,retain) UIWindow *window; |
觸摸產生時所處的視圖
1
|
@property(nonatomic,readonly,retain) UIView *view; |
短時間內點按屏幕的次數,可以根據tapCount判斷單擊、雙擊或更多地點擊
1
|
@property(nonatomic,readonly) NSUInteger tapCount; |
記錄了觸摸事件產生或變化時的時間,單位是秒
1
|
@property(nonatomic,readonly) NSTimeInterval timestamp; |
當前觸摸事件所處的狀態
1
2
3
4
5
6
7
8
|
@property(nonatomic,readonly) UITouchPhase phase; /* UITouchPhase是一個枚舉類型,包含: UITouchPhaseBegan(觸摸開始) UITouchPhaseMoved(接觸點移動) UITouchPhaseStationary(接觸點無移動) UITouchPhaseEnded(觸摸結束) UITouchPhaseCancelled(觸摸取消)*/ |
UITouch的方法:
1
|
- (CGPoint)locationInView:(UIView *)view; |
1> 返回值表示觸摸在view上的位置;
2> 這裏返回的位置是針對view座標系的,(以view的左上角爲原點(0,0));
3> 調用時傳入的view參數爲nil 的話,返回的是觸摸點在UIWindow的位置。
1
|
- (CGPoint)previousLocationInView:(UIView *)view; |
該方法記錄了前一個觸摸點的位置;
UIEvent
每產生一個事件,就會產生一個UIEvent對象;
UIEvent:稱爲事件對象,記錄事件產生的時刻和類型。
常見屬性:
1> 事件類型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
@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> 事件產生的時間
1
|
@property(nonatomic,readonly) NSTimeInterval timestamp; |
UIEvent還提供了相應的方法可以獲得在某個view上面的觸摸對象(UITouch)。
觸摸事件的產生:
1> 發生觸摸事件後,系統會將該事件加入到一個由UIApplication管理的事件隊列中;
2> UIApplication會從事件隊列中取出最前面的事件,並將事件分發下去以便處理,通常,先發送事件給應用程序的主窗口(keyWindow);
3> 主窗口會在視圖層次結構中找到一個最合適的視圖控件來處理觸摸事件,這也是整個事件處理過程的第一步;
4> 找到合適的視圖控件後,就會調用視圖控件的touches方法來做具體的事件處理。
觸摸事件的傳遞:
觸摸事件的傳遞是從父控件傳遞到子控件;
如果父控件不能接收觸摸事件,那麼子控件就不可能接收到觸摸事件。
UIView不接收觸摸事件的三種情況:
1> 不接受用戶交互 :userInteractionEnable = NO;
2> 隱藏 :hidden = YES;
3> 透明:alpha = 0.0 ~ 0.01
提示:UIImageView的userInteractionEnable默認就是NO,因此UIImageView以及它的子控件默認是不能接收觸摸事件的。
觸摸事件處理的詳細過程:
用戶點擊屏幕後產生的一個觸摸事件,經過一些列的傳遞過程後,會找到最合適的視圖控件來處理這個事件
找到最合適的視圖控件後,就會調用控件的touches方法來作具體的事件處理
touchesBegan…
touchesMoved…
touchedEnded…
這些touches方法的默認做法是將事件順着響應者鏈條向上傳遞,將事件交給上一個響應者進行處理
響應者鏈的事件傳遞過程:
1> 如果view的控制器存在,就傳遞給控制器;如果控制器不存在,則將其傳遞給它的父視圖;
2> 在視圖層次結構最頂級的視圖,如果也不能處理收到的事件或消息,則其將事件或消息傳遞給window對象進行處理。
3> 如果window對象也不處理,則其將事件或消息傳遞給UIApplication對象;
4> 如果UIApplication也不能處理該事件或消息,則將其丟棄。
監聽觸摸事件的做法
如果想監聽一個view上面的觸摸事件,之前的做法是:
1> 自定義一個view;
2> 實現view的touches方法,在方法內部實現具體處理代碼。
通過touches方法監聽view觸摸事件,有很明顯的幾個缺點:
1> 必須得自定義view;
2> 由於是在view內部的touches方法中監聽觸摸事件,因此默認情況下,無法讓其他外界對象監聽view的觸摸事件;
3> 不容易區分用戶的具體手勢行爲。
iOS 3.2之後,蘋果推出了手勢識別功能(Gesture Recognizer),在觸摸事件處理方面,大大簡化了開發者的開發難度。
UIGestureRescognizer
爲了完成手勢識別,必須藉助於手勢識別器:UIGestureRecognizer 。
利用UIGestureRecognizer,能輕鬆識別用戶在某個view上面做的一些常見手勢。
UIGestureRecognizer是一個抽象類,定義了所有的手勢基本行爲,使用它的子類才能處理具體的手勢
1
2
3
4
5
6
|
UITapGestureRecognizer(敲擊) UIPinchGestureRecognizer(捏合,用於縮放) UIPanGestureRecognizer(拖拽) UISwipeGestureRecognizer(輕掃) UIRotationGestureRecognizer(旋轉) UILongPressGestureRecognizer(長按) |
每一個手勢識別器的用法都差不多,比如UITapGestureRecognizer的使用步驟如下:
1> 創建手勢識別器對象;
1
|
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] init]; |
2> 設置手勢識別器對象的具體屬性;
1
2
3
4
|
// 連續敲擊2次 tap.numberOfTapsRequired = 2; // 需要2根手指一起敲擊 tap.numberOfTouchesRequired = 2; |
3> 添加手勢識別器到對應的view上;
1
|
[self.iconView addGestureRecognizer:tap]; |
4> 監聽手勢的觸發
1
|
[tap addTarget:self action:@selector(tapIconView:)]; |
手勢識別的狀態
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
typedef NS_ENUM(NSInteger, UIGestureRecognizerState) { // 沒有觸摸事件發生,所有手勢識別的默認狀態 UIGestureRecognizerStatePossible, // 一個手勢已經開始但尚未改變或者完成時 UIGestureRecognizerStateBegan, // 手勢狀態改變 UIGestureRecognizerStateChanged, // 手勢完成 UIGestureRecognizerStateEnded, // 手勢取消,恢復至Possible狀態 UIGestureRecognizerStateCancelled, // 手勢失敗,恢復至Possible狀態 UIGestureRecognizerStateFailed, // 識別到手勢識別 UIGestureRecognizerStateRecognized = UIGestureRecognizerStateEnded }; |