一> 基礎(chǔ)概述
1> ios當(dāng)中常用的事件分為三種:
- 觸摸事件
- 加速計事件
- 遠程控制事件
2> 什么是響應(yīng)者對象:
- 繼承了UIResponds的對象我們稱它為響應(yīng)者對象, UIApplication伍派、UIViewController诉植、UIView都繼承自UIResponder, 因此它們都是響應(yīng)者對象昵观,都能夠接收并處理事件
3> 為什么說繼承了UIResponder就能夠處理事件?
- 因為UIResponder內(nèi)部提供了以下方法來處理事件
-
比如觸摸事件會調(diào)用以下方法:
- (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;
-
加速計事件會調(diào)用:
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event;
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event;
- (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event;
-
遠程控制事件會調(diào)用 :
- (void)remoteControlReceivedWithEvent:(UIEvent *)event;
-
4> 如何監(jiān)聽UIView的觸摸事件?
想要監(jiān)聽UIViiew的觸摸事件,首先第一步要自定義UIView, 因為只有實現(xiàn)了UIResponder的事件方法才能夠監(jiān)聽事件.
UIView的觸摸事件主要有:
a) 一根或者多根手指開始觸摸view,系統(tǒng)會自動調(diào)用view的下面方法 :- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
b) 一根或者多根手指在view上移動時啊犬,系統(tǒng)會自動調(diào)用view的下面方法 (隨著手指的移動灼擂,會持續(xù)調(diào)用該方法,也就是說這個方法會調(diào)用很多次) :- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
c) 一根或者多根手指離開view剔应,系統(tǒng)會自動調(diào)用view的下面方法 :- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
-
一次完整的觸摸過程语御,會經(jīng)歷3個狀態(tài):
- 觸摸開始 :
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- 觸摸移動:
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
- 觸摸結(jié)束:
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
- 觸摸取消(可能會經(jīng)歷):
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;
- 一次完整的觸摸過程中应闯,只會產(chǎn)生一個事件對象,4個觸摸方法都是同一個event參數(shù); 如果兩根手指同時觸摸一個view船万,那么view只會調(diào)用一次
touchesBegan:withEvent:
方法骨田,touches參數(shù)中裝著2個UITouch對象; 如果這兩根手指一前一后分開觸摸同一個view,那么view會分別調(diào)用2次touchesBegan:withEvent:方法舱呻,并且每次調(diào)用時的touches參數(shù)中只包含一個UITouch對象
- 觸摸開始 :
二> UITouch對象
1> UITouch對象概述
- 當(dāng)用戶用一根手指觸摸屏幕時抵卫,會創(chuàng)建一個與手指相關(guān)聯(lián)的UITouch對象
- 一根手指對應(yīng)一個UITouch對象
2> UITouch的作用
- 保存著跟手指相關(guān)的信息介粘,比如觸摸的位置、時間雅采、階段
- 當(dāng)手指移動時,系統(tǒng)會更新同一個UITouch對象宝鼓,使之能夠一直保存該手指在的觸摸位置
- 當(dāng)手指離開屏幕時巴刻,系統(tǒng)會銷毀相應(yīng)的UITouch對象
3> UITouch對象屬性
-
window
: 觸摸產(chǎn)生時所處的窗口 -
view
: 觸摸產(chǎn)生時所處的視圖 -
tapCount
: 短時間內(nèi)點按屏幕的次數(shù)胡陪,可以根據(jù)tapCount判斷單擊、雙擊或更多的點擊 -
timestamp
: 記錄了觸摸事件產(chǎn)生或變化時的時間邑雅,單位是秒 -
phase
: 當(dāng)前觸摸事件所處的狀態(tài)
4> UITouch對象方法
-
- (CGPoint)locationInView:(UIView *)view;
: 返回值表示觸摸在view上的位置; 這里返回的位置是針對view的坐標(biāo)系的(以view的左上角為原點(0, 0))調(diào)用時傳入的view參數(shù)為nil的話淮野,返回的是觸摸點在UIWindow的位置 -
- (CGPoint)previousLocationInView:(UIView *)view;
: 該方法記錄了前一個觸摸點的位置
三> UIEvent對象
1> UIEvent對象概述
- 每產(chǎn)生一個事件吹泡,就會產(chǎn)生一個UIEvent對象
- UIEvent:稱為事件對象,記錄事件產(chǎn)生的時刻和類型
2> UIEvent對象屬性
-
事件類型
@property(nonatomic,readonly) UIEventType type;
@property(nonatomic,readonly) UIEventSubtype subtype;
事件產(chǎn)生的時間 :
@property(nonatomic,readonly) NSTimeInterval timestamp;
3> UIEvent對象方法
- UIEvent還提供了相應(yīng)的方法可以獲得在某個view上面的觸摸對象(UITouch)
事件的產(chǎn)生和傳遞
-
第一步: 尋找最適合的View
- 發(fā)生觸摸事件后,系統(tǒng)會將該事件加入到一個由UIApplication管理的事件隊列中
- UIApplication會從事件隊列中取出最前面的事件泪漂,并將事件分發(fā)下去以便處理萝勤,通常呐伞,先發(fā)送事件給應(yīng)用程序的主窗口(keyWindow)
- 主窗口會在視圖層次結(jié)構(gòu)中找到一個最合適的視圖來處理觸摸事件,這也是整個事件處理過程的第一步
- 找到合適的視圖控件后趟径,就會調(diào)用視圖控件的touches方法來作具體的事件處理
- 觸摸事件的傳遞是從父控件傳遞到子控件
- 如果父控件不能接收觸摸事件癣防,那么子控件就不可能接收到觸摸事件
- 如何找到最合適的控件來處理事件蕾盯?
- 自己是否能接收觸摸事件?
- 觸摸點是否在自己身上望拖?
- 從后往前遍歷子控件说敏,重復(fù)前面的兩個步驟
- 如果沒有符合條件的子控件盔沫,那么就自己最適合處理
- UIView不接收觸摸事件的三種情況:
- 不接收用戶交互 :
userInteractionEnabled = NO
- 隱藏 :
hidden = YES
- 透明 :
alpha = 0.0 ~ 0.01
- 不接收用戶交互 :
- 觸摸事件的傳遞是從父控件傳遞到子控件
-
第二步: 如何尋找最適合的View
- 當(dāng)一個事件傳遞給當(dāng)前的View時候, 系統(tǒng)就會調(diào)用
-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
方法- 作用 : 去尋找最適合的View
- 何時調(diào)用 : 當(dāng)一個事件傳遞給當(dāng)前View,就會調(diào)用
- 返回值 : 返回的是誰,誰就是最適合的View(就會調(diào)用最適合的View的touch方
- 在系統(tǒng)調(diào)用hist, 系統(tǒng)內(nèi)部會調(diào)用pointInside方法
- 作用 : 判斷當(dāng)前點在不在它調(diào)用View,(誰調(diào)用pointInside,這個View就是誰)
- 何時調(diào)用 : 它是在hitTest方法當(dāng)中調(diào)用的
- 注意:point點必須得要跟它方法調(diào)用者在同一個坐標(biāo)系里面
- hitTest方法底層實現(xiàn)
- 當(dāng)一個事件傳遞給當(dāng)前的View時候, 系統(tǒng)就會調(diào)用
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
//1.判斷自己能否接收事件
if(self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01) {
return nil;
}
//2.判斷當(dāng)前點在不在當(dāng)前View.
if (![self pointInside:point withEvent:event]) {
return nil;
}
//3.從后往前遍歷自己的子控件.讓子控件重復(fù)前兩步操作,(把事件傳遞給,讓子控件調(diào)用hitTest)
int count = (int)self.subviews.count;
for (int i = count - 1; i >= 0; i--) {
//取出每一個子控件
UIView *chileV = self.subviews[i];
//把當(dāng)前的點轉(zhuǎn)換成子控件從標(biāo)系上的點.
CGPoint childP = [self convertPoint:point toView:chileV];
UIView *fitView = [chileV hitTest:childP withEvent:event];
//判斷有沒有找到最適合的View
if(fitView){
return fitView;
}
}
//4.沒有找到比它自己更適合的View.那么它自己就是最適合的View
return self;
}
觸摸事件處理的詳細過程
用戶點擊屏幕后產(chǎn)生的一個觸摸事件,經(jīng)過一系列的傳遞過程后惩歉,會找到最合適的視圖控件來處理這個事件
找到最合適的視圖控件后撑蚌,就會調(diào)用控件的touches方法來作具體的事件處理
touchesBegan…
touchesMoved…
touchedEnded…這些touches方法的默認做法是將事件順著響應(yīng)者鏈條向上傳遞搏屑,將事件交給上一個響應(yīng)者進行處理
- 響應(yīng)者鏈條:是由多個響應(yīng)者對象連接起來的鏈條
- 作用:能很清楚的看見每個響應(yīng)者之間的聯(lián)系辣恋,并且可以讓一個事件多個對象處理伟骨。
- 響應(yīng)者對象:能處理事件的對象
四> 示例代碼
一) UIView的拖拽
- 代碼思路:
- 自定義UIView,實現(xiàn)監(jiān)聽方法.
- 確定在TouchMove方法當(dāng)中進行操作,因為用戶手指在視圖上移動的時候才需要移動視圖携狭。
- 獲取當(dāng)前手指的位置和上一個手指的位置.
- 當(dāng)前視圖的位置 = 上一次視圖的位置 + 手指的偏移量
- 關(guān)健代碼實現(xiàn):
- 拿到UITouch就能獲取當(dāng)前點 :
UITouch *touch = [touches anyObject];
- 獲取當(dāng)前點 :
CGPoint curP = [touch locationInView:self];
- 獲取上一個點 :
CGPoint preP = [touch previousLocationInView:self];
- 獲取手指x軸偏移量 :
CGFloat offsetX = curP.x - preP.x;
- 獲取手指y軸偏移量 :
CGFloat offsetY = curP.y - preP.y;
- 移動當(dāng)前view :
self.transform = CGAffineTransformTranslate(self.transform, offsetX, offsetY);
- 拿到UITouch就能獲取當(dāng)前點 :