觸摸事件,手勢識別

•在iOS中不是任何對象都能處理事件,只有繼承了UIResponder的對象才能接收並處理事件。咱們稱之爲「響應者對象」
•UIApplication、UIViewController、UIView都繼承自UIResponder,所以它們都是響應者對象,都可以接收並處理事件
•UIResponder內部提供瞭如下方法來處理事件
Ø觸摸事件

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;緩存

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;ui

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;atom

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;orm

 

Ø加速計事件

- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event;對象

- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event;排序

- (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event;繼承

 

Ø遠程控制事件

- (void)remoteControlReceivedWithEvent:(UIEvent *)event;隊列

Ø手指按下
Ø- (void)touchesBegan:
Ø手指移動
Ø- (void)touchesMoved:
Ø手指擡起
Ø- (void)touchesEnded:
Ø意外中斷事件(電話打擾)
Ø- (void)touchesCancelled:
•UIView是UIResponder的子類,能夠覆蓋下列4個方法處理不一樣的觸摸事件
Ø一根或者多根手指開始觸摸view,系統會自動調用view的下面方法

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event事件

Ø
Ø一根或者多根手指在view上移動,系統會自動調用view的下面方法(隨着手指的移動,會持續調用該方法)

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)eventip

Ø
Ø一根或者多根手指離開view,系統會自動調用view的下面方法

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

Ø
Ø觸摸結束前,某個系統事件(例如電話呼入)會打斷觸摸過程,系統會自動調用view的下面方法

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event

 

提示:touches中存放的都是UITouch對象

•* NSSet:
•1> 無序的、不重複的。存放到 NSSet 中的內容並不會排序與添加順序也沒有關係
•2> 經過 anyObject 來訪問單個元素
•3> 遍歷 NSSet 中的每一個元素
•4> 好處: 效率高。
•5> 應用場景:
•* 好比重用 Cell 的時候, 從緩存池中隨便獲取一個就能夠了, 無需按照指定順序來獲取
•* 當須要把數據存放到一個集合中, 而後判斷集合中是否有某個對象的時候
•* NSArray
•1> 有序的、能夠有重複對象。對象的順序是按照添加的順序來保存的
•2> 好處: 有序訪問
•3> 應用場景: 在絕大多數須要依賴順序的狀況下(好比 tableView 的數據源集合,在實際操做中要根據下標來獲取對象)
•3> 經過下標來訪問
•- 介紹 UITouch 對象
•@property(nonatomic,readonly) NSUInteger          tapCount;   // touch down within a certain point within a certain amount of time
•@property(nonatomic,readonly,retain) UIView      *view;
•- (CGPoint)locationInView:(UIView *)view;
•- (CGPoint)previousLocationInView:(UIView *)view;
•- 案例: 跟着鼠標走的 UIView
•1> 自定義一個 UIView 添加到界面上
•2> 在 touchMove 方法中, 獲取當前觸摸點, 讓自定義 View 的 center 等於當前觸摸點
•3>  完善: 計算出當前觸摸點, 前一個觸摸點,計算偏移量.設置 UIView 中的中心點進行偏移或者使用 transform
•當用戶用一根手指觸摸屏幕時,會建立一個與手指相關聯的UITouch對象
•一根手指對應一個UITouch對象
•UITouch的做用
Ø保存着跟手指相關的信息,好比觸摸的位置、時間、階段

 

•當手指移動時,系統會更新同一個UITouch對象,使之可以一直保存該手指在的觸摸位置
•當手指離開屏幕時,系統會銷燬相應的UITouch對象
•提示:iPhone開發中,要避免使用雙擊事件!
•觸摸產生時所處的窗口

@property(nonatomic,readonly,retain) UIWindow    *window;

 

•觸摸產生時所處的視圖

@property(nonatomic,readonly,retain) UIView      *view;

 

•短期內點按屏幕的次數,能夠根據tapCount判斷單擊、雙擊或更多的點擊

@property(nonatomic,readonly) NSUInteger          tapCount;

•記錄了觸摸事件產生或變化時的時間,單位是秒

@property(nonatomic,readonly) NSTimeInterval      timestamp;

 

•當前觸摸事件所處的狀態

@property(nonatomic,readonly) UITouchPhase        phase;

•- (CGPoint)locationInView:(UIView *)view;
Ø返回值表示觸摸在view上的位置
Ø這裏返回的位置是針對view的座標系的(以view的左上角爲原點(0, 0))
Ø調用時傳入的view參數爲nil的話,返回的是觸摸點在UIWindow的位置
Ø
•- (CGPoint)previousLocationInView:(UIView *)view;
Ø該方法記錄了前一個觸摸點的位置

 

•每產生一個事件,就會產生一個UIEvent對象
•UIEvent:稱爲事件對象,記錄事件產生的時刻和類型
•常見屬性
Ø事件類型

@property(nonatomic,readonly) UIEventType     type;

@property(nonatomic,readonly) UIEventSubtype  subtype;

 

Ø事件產生的時間

@property(nonatomic,readonly) NSTimeInterval  timestamp;

 

•UIEvent還提供了相應的方法能夠得到在某個view上面的觸摸對象(UITouch)
•一次完整的觸摸過程,會經歷3個狀態:
Ø觸摸開始:- (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

 

•4個觸摸事件處理方法中,都有NSSet *touches和UIEvent *event兩個參數
Ø一次完整的觸摸過程當中,只會產生一個事件對象,4個觸摸方法都是同一個event參數
Ø
Ø若是兩根手指同時觸摸一個view,那麼view只會調用一次touchesBegan:withEvent:方法,touches參數中裝着2個UITouch對象
Ø
Ø若是這兩根手指一前一後分開觸摸同一個view,那麼view會分別調用2次touchesBegan:withEvent:方法,而且每次調用時的touches參數中只包含一個UITouch對象
Ø
Ø根據touches中UITouch的個數能夠判斷出是單點觸摸仍是多點觸摸
•發生觸摸事件後,系統會將該事件加入到一個由UIApplication管理的事件隊列中
•UIApplication會從事件隊列中取出最前面的事件,並將事件分發下去以便處理,一般,先發送事件給應用程序的主窗口(keyWindow)
•主窗口會在視圖層次結構中找到一個最合適的視圖來處理觸摸事件,這也是整個事件處理過程的第一步
•主窗口會調用hitTest:withEvent:方法在視圖繼承樹中找到一個最合適的子視圖來處理觸摸事件,該子視圖即爲hit-test視圖
•找到合適的視圖控件後,就會調用視圖控件的touches方法來做具體的事件處理
ØtouchesBegan…
ØtouchesMoved…
ØtouchedEnded…
•觸摸事件的傳遞是從父控件傳遞到子控件
Ø點擊了綠色的view:

UIApplication -> UIWindow -> 白色 -> 綠色

Ø點擊了藍色的view:

UIApplication -> UIWindow -> 白色 -> 橙色 -> 藍色

Ø點擊了黃色的view:

UIApplication -> UIWindow -> 白色 -> 橙色 -> 藍色 -> 黃色

 

•若是父控件不能接收觸摸事件,那麼子控件就不可能接收到觸摸事件(掌握)
•如何找到最合適的控件來處理事件?
Ø本身是否能接收觸摸事件?userInteractionEnabled 等等
Ø觸摸點是否在本身身上?
Ø從後往前遍歷子控件,重複前面的兩個步驟
Ø若是沒有符合條件的子控件,那麼就本身最適合處理
1.不接收用戶交互

userInteractionEnabled = NO

 

2.隱藏

hidden = YES

 

3.透明

alpha = 0.0 ~ 0.01

 

4. 若是子視圖的位置超出了父視圖的有效範圍, 那麼子視圖也是沒法與用戶交互的, 即便設置了父視圖的 clipsToBounds = NO, 能夠看懂, 可是也是沒法與用戶交互的

 

提示:UIImageView的userInteractionEnabled默認就是NO,所以UIImageView以及它的子控件默認是不能接收觸摸事件的

 

•用戶點擊屏幕後產生的一個觸摸事件,通過一些列的傳遞過程後,會找到最合適的視圖控件來處理這個事件
•找到最合適的視圖控件後,就會調用控件的touches方法來做具體的事件處理
ØtouchesBegan…
ØtouchesMoved…
ØtouchedEnded…
Ø
•這些touches方法的默認作法是將事件順着響應者鏈條向上傳遞,將事件交給上一個響應者進行處理
1.若是view的控制器存在,就傳遞給控制器;若是控制器不存在,則將其傳遞給它的父視圖
2.在視圖層次結構的最頂級視圖,若是也不能處理收到的事件或消息,則其將事件或消息傳遞給window對象進行處理
3.若是window對象也不處理,則其將事件或消息傳遞給UIApplication對象
4.若是UIApplication也不能處理該事件或消息,則將其丟棄
•若是想監聽一個view上面的觸摸事件,以前的作法是
Ø自定義一個view
Ø實現view的touches方法,在方法內部實現具體處理代碼
Ø
•經過touches方法監聽view觸摸事件,有很明顯的幾個缺點
Ø必須得自定義view
Ø因爲是在view內部的touches方法中監聽觸摸事件,所以默認狀況下,沒法讓其餘外界對象監聽view的觸摸事件
Ø不容易區分用戶的具體手勢行爲
•iOS 3.2以後,蘋果推出了手勢識別功能(Gesture Recognizer),在觸摸事件處理方面,大大簡化了開發者的開發難度
•爲了完成手勢識別,必須藉助於手勢識別器----UIGestureRecognizer
•利用UIGestureRecognizer,能輕鬆識別用戶在某個view上面作的一些常見手勢
•UIGestureRecognizer是一個抽象類,定義了全部手勢的基本行爲,使用它的子類才能處理具體的手勢
ØUITapGestureRecognizer(敲擊)
ØUIPinchGestureRecognizer(捏合,用於縮放)
ØUIPanGestureRecognizer(拖拽)
ØUISwipeGestureRecognizer(輕掃)
ØUIRotationGestureRecognizer(旋轉)
ØUILongPressGestureRecognizer(長按)
•每個手勢識別器的用法都差很少,好比UITapGestureRecognizer的使用步驟以下
Ø建立手勢識別器對象

UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] init];

 

Ø設置手勢識別器對象的具體屬性

// 連續敲擊2次

tap.numberOfTapsRequired = 2;

// 須要2根手指一塊兒敲擊

tap.numberOfTouchesRequired = 2;

 

Ø添加手勢識別器到對應的view上

[self.iconView addGestureRecognizer:tap];

 

Ø監聽手勢的觸發

[tap addTarget:self action:@selector(tapIconView:)];

typedef NS_ENUM(NSInteger, UIGestureRecognizerState) {

    // 沒有觸摸事件發生,全部手勢識別的默認狀態

    UIGestureRecognizerStatePossible,

    // 一個手勢已經開始但還沒有改變或者完成時

    UIGestureRecognizerStateBegan,

    // 手勢狀態改變

    UIGestureRecognizerStateChanged,

    // 手勢完成

    UIGestureRecognizerStateEnded,

    // 手勢取消,恢復至Possible狀態

    UIGestureRecognizerStateCancelled,

    // 手勢失敗,恢復至Possible狀態

    UIGestureRecognizerStateFailed,

    // 識別到手勢識別

    UIGestureRecognizerStateRecognized = UIGestureRecognizerStateEnded

};

相關文章
相關標籤/搜索