事件
移動(dòng)應(yīng)用中常用的事件
1. 觸摸事件。
2. 加速計(jì)事件。例如:手機(jī)搖一搖
3. 遠(yuǎn)程控制事件。例如:耳機(jī)控制

處理事件的條件:
- 在iOS中,不是任何對(duì)象都能處理事件,只有繼承了UIResponder的對(duì)象才能接收并處理事件,稱(chēng)之為“響應(yīng)者對(duì)象”。
- UIApplication,UIViewController,UIView都繼承自UIResponder,因此它們都是響應(yīng)者對(duì)象,都能接收并處理事件。
處理方法:
UIResponder內(nèi)部提供的事件處理方法,可以在類(lèi)中重寫(xiě)下列方法來(lái)處理事件:
// 觸摸事件
- (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;
// 加速計(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),通過(guò)覆蓋下列四個(gè)方法處理觸摸事件
覆蓋下列方法后,系統(tǒng)會(huì)自動(dòng)調(diào)用來(lái)進(jìn)行事件的處理
一根或者多根手指開(kāi)始觸摸View:
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event;
一根或者多根手指在View移動(dòng),隨著手指的移動(dòng),系統(tǒng)會(huì)不斷調(diào)用此方法:
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event;
一根或者多根手指離開(kāi)View:
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event;
觸摸結(jié)束之前,某個(gè)系統(tǒng)事件(例如電話呼入)打斷觸摸過(guò)程
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event;
UITouch
- 當(dāng)用戶用一根手指觸摸屏幕時(shí),會(huì)創(chuàng)建一個(gè)與手指相關(guān)聯(lián)的UITouch對(duì)象。
- 一根手指對(duì)應(yīng)一個(gè)UITouch對(duì)象,多根手指觸摸時(shí),會(huì)創(chuà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ì)象。
UITouch的常用屬性
//觸摸產(chǎn)生時(shí)所處的窗口
@property(nullable,nonatomic,readonly,strong) UIWindow *window;
//觸摸產(chǎn)生時(shí)所處的視圖
@property(nullable,nonatomic,readonly,strong) UIView *view;
//記錄了觸摸事件產(chǎn)生時(shí)或變化時(shí)的時(shí)間,單位時(shí)秒
@property(nonatomic,readonly) NSTimeInterval timestamp;
//當(dāng)前觸摸事件所處的狀態(tài)
@property(nonatomic,readonly) UITouchPhase phase;
//短時(shí)間內(nèi)點(diǎn)擊屏幕的次數(shù),可以根據(jù)此屬性判斷單擊,雙擊,或更多點(diǎn)擊
@property(nonatomic,readonly) NSUInteger tapCount;
//iOS9開(kāi)始支持的屬性,觸摸類(lèi)型,包括直接觸摸,間接觸摸,觸控筆觸摸
@property(nonatomic,readonly) UITouchType type;
typedef NS_ENUM(NSInteger, UITouchType) {
UITouchTypeDirect, //直接觸摸
UITouchTypeIndirect, //間接觸摸
UITouchTypeStylus, //觸控筆觸摸
};
@property(nonatomic,readonly) CGFloat majorRadius //觸摸的半徑;
UITouch的常用方法
//返回觸摸點(diǎn)在view上的位置,針對(duì)view的坐標(biāo)系統(tǒng)(以view的左上角為原點(diǎn)),傳入的view為空時(shí),返回的觸摸點(diǎn)在UIWindow的位置
- (CGPoint)locationInView:(nullable UIView *)view;
//返回前一個(gè)觸摸點(diǎn)的位置
- (CGPoint)previousLocationInView:(nullable UIView *)view;
//精確的觸摸點(diǎn)位置 iOS9.1之后
- (CGPoint)preciseLocationInView:(nullable UIView *)view NS_AVAILABLE_IOS(9_1);
//精確的前一個(gè)觸摸點(diǎn)的位置 iOS9.1之后
- (CGPoint)precisePreviousLocationInView:(nullable UIView *)view NS_AVAILABLE_IOS(9_1);
UIEvent
- 每產(chǎn)生一個(gè)事件,就會(huì)產(chǎn)生一個(gè)UIEvent對(duì)象,稱(chēng)為事件對(duì)象,記錄事件產(chǎn)生的時(shí)刻和類(lèi)型。
UIEvent常用屬性
//事件類(lèi)型
@property(nonatomic,readonly) UIEventType type NS_AVAILABLE_IOS(3_0);
@property(nonatomic,readonly) UIEventSubtype subtype NS_AVAILABLE_IOS(3_0);
//事件產(chǎn)生的時(shí)間
@property(nonatomic,readonly) NSTimeInterval timestamp;
UIEvent常用方法
//獲得窗口上的UITouch對(duì)象
- (nullable NSSet <UITouch *> *)touchesForWindow:(UIWindow *)window;
//獲得view上的UITouch對(duì)象
- (nullable NSSet <UITouch *> *)touchesForView:(UIView *)view;
touches和event參數(shù)
一次完整的觸摸過(guò)程,會(huì)經(jīng)歷3個(gè)狀態(tài):
觸摸開(kāi)始:
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event;
觸摸移動(dòng):
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event;
觸摸結(jié)束:
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event;
觸摸消失(可能會(huì)經(jīng)歷):
- (void)touchesCancelled:(NSSet<UITouch *> *)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ì)象。
- 如果兩根手指一前一后觸摸同一個(gè)view,那么view會(huì)分別調(diào)用兩次touchesBegan:withEvent:方法,并且每次調(diào)用時(shí)的touches參數(shù)只包含一個(gè)UITouch對(duì)象。
- 根據(jù)touches中的UITouch的個(gè)數(shù)可以判斷出是單點(diǎn)觸摸還是多點(diǎn)觸摸
事件的產(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)做具體的事件處理
即: 觸摸(事件發(fā)生) ——> UIApplication管理的事件隊(duì)列 ——> 主窗口(keyWindow) ——> 合適的視圖 ——> 視圖通過(guò)方法來(lái)處理事件

找到做合適的控件兩個(gè)步驟:
1.自己是否能接收事件。 2.觸摸點(diǎn)是否在自己身上。如果父控件不能接收事件,那么子控件就不可能接收到事件。
UIView不能接收觸摸事件的三種情況:
1.不接收用戶交互
屬性u(píng)serInteractionEnabled = NO;
2.視圖隱藏
屬性hidden = YES;
3.透明
屬性alpha = 0.0 ~ 0.01;
PS:UIImageView的userInteractionEnabled 默認(rèn)為 NO,因此UIImageView以及它的子控件默認(rèn)是不能接收事件的。如果沒(méi)有合適的響應(yīng)者,系統(tǒng)將丟棄這一事件。
響應(yīng)者鏈條
- 響應(yīng)者鏈條:響應(yīng)者鏈條是有多個(gè)響應(yīng)者對(duì)象連接起來(lái)的鏈條。(繼承自UIRespngder,能夠處理事件的對(duì)象叫做響應(yīng)者對(duì)象)
- 好處:通過(guò)響應(yīng)者鏈條,能夠讓多個(gè)控件處理同一個(gè)觸摸事件。(在touches方法內(nèi)部,用super調(diào)用touches方法)
- 用戶點(diǎn)擊屏幕后產(chǎn)生的一個(gè)觸摸事件,經(jīng)過(guò)一系列的傳遞過(guò)程之后,會(huì)找到最合適的控件來(lái)處理這個(gè)事件。

響應(yīng)者鏈條的事件傳遞過(guò)程:
- 響應(yīng)者鏈條的事件傳遞過(guò)程的順序與觸摸事件的傳遞過(guò)程相反。
- 響應(yīng)者鏈條的傳遞由最合適的控件開(kāi)始,由子視圖往父視圖傳遞。
- 如果view的控制器存在,就傳遞給控制器,如果控制器不存在,就傳遞給其他父視圖。
- 在視圖層次結(jié)構(gòu)的最頂級(jí)視圖,如果不能處理收到的事件或消息,則將事件或消息給window處理。
- 如果window對(duì)象也不處理,則將事件或消息傳遞給UIApplication對(duì)象。
- 如果UIApplication對(duì)象也不能處理,則將其丟棄。
- 找到最合適的控件之后,會(huì)調(diào)用touches方法來(lái)做具體的事件處理。
touchesBegan……
touchesMoves……
touchesEnded……
這些touches方法的默認(rèn)做法是將事件順著響應(yīng)者鏈條向上傳遞,將事件交給上一個(gè)響應(yīng)者處理。
手勢(shì)識(shí)別器
- 之前處理觸摸事件的做法:
如果想監(jiān)聽(tīng)一個(gè)view上的觸摸事件
1.自定義一個(gè)view。
2.實(shí)現(xiàn)view的touches方法,在方法內(nèi)部實(shí)現(xiàn)具體代碼來(lái)處理。
缺點(diǎn):
1.必須自定義view。
2.由于是在view內(nèi)部的touches方法中堅(jiān)挺觸摸事件,因此默認(rèn)情況下,無(wú)法讓其他外界對(duì)象監(jiān)聽(tīng)view的觸摸事件。
3.不容易區(qū)分用戶的具體手勢(shì)行為。
iOS3.2之后,蘋(píng)果推出手勢(shì)識(shí)別功能(Gesture Recognizer)。
- 如果要完成手勢(shì)識(shí)別,必須借助于手勢(shì)識(shí)別器—UIGestureRecognizer
UIGestureRecognizer是一個(gè)抽象類(lèi),定義了所有手勢(shì)的基本行為,使用它的子類(lèi)才能處理具體的手勢(shì)。
UITapGestureRecognizer(敲擊)
UIPinchGestureRecognizer(捏合,用于縮放)
UIPanGestureRecognizer(拖拽)
UISwipeGestureRecognizer(輕掃)
UIRotationGestureRecognizer(旋轉(zhuǎn))
UILongGestureRecognizer(長(zhǎng)按)
手勢(shì)識(shí)別器的使用:
//1.創(chuàng)建敲擊手勢(shì)識(shí)別器
UITapGestureRecognizer *tapGR = [[UITapGestureRecognizer alloc] init];
//2.給要進(jìn)行處理觸摸事件的view添加手勢(shì)識(shí)別,這里給self.view添加手勢(shì)識(shí)別
[self.view addGestureRecognizer:tapGR];
//3.添加監(jiān)聽(tīng)方法
tapGR addTarget:self action:@selector(tapMethod:) //實(shí)現(xiàn)tapMethod:方法來(lái)處理具體的操作