如果這篇文章幫助到了您,希望您能點擊一下喜歡或者評論,你們的支持是我前進的強大動力.謝謝!
觸摸事件(Multitouch events)
- 手指觸摸了(或者手機加速事件,遠程遙控事件等...這里先只介紹觸摸事件)某個響應者對象時,會調用調用響應者對象的一些方法
- 響應者對象都能夠接收并處理事件
- 在iOS中不是任何對象都能處理事件,只有繼承了UIResponder的對象才能接收并處理事件。我們稱之為“響應者對象”
- UIApplication、UIViewController、UIView都繼承自UIResponder,因此它們都是響應者對象,都能夠接收并處理事件
繼承了UIResponder如何處理事件
因為UIResponder內部提供了以下方法來處理事件
- 比如觸摸事件會調用以下方法:
- (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;
- 加速計事件會調用:(下面暫時不介紹)
- (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;
如何監聽UIView的觸摸事件?
想要監聽UIView的觸摸事件,首先第一步要自定義UIView,
因為只有實現了UIResponder的事件方法才能夠監聽事件.
UIView的觸摸事件主要有:
一根或者多根手指開始觸摸view,系統會自動調用view的下面方法.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
一根或者多根手指在view上移動時,系統會自動調用view的下面方法
(隨著手指的移動,會持續調用該方法,也就是說這個方法會調用很多次)
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
一根或者多根手指離開view,系統會自動調用view的下面方法
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
- 參數touches介紹
- 是一個NSSet集合,里面裝的是UITouch對象
- 什么是UITouch對象?
當用戶用一根手指觸摸屏幕時,會創建一個與手指相關聯的UITouch對象
一根手指對應一個UITouch對象
UITouch的作用:保存著跟手指相關的信息,比如觸摸的位置、時間、階段
當手指移動時,系統會更新同一個UITouch對象,使之能夠一直保存該手指在的觸摸位置
當手指離開屏幕時,系統會銷毀相應的UITouch對象 - UITouch屬性
觸摸產生時所處的窗口 @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;
- UITouch的方法
- (CGPoint)locationInView:(UIView *)view;
返回值表示觸摸在view上的位置
這里返回的位置是針對view的坐標系的(以view的左上角為原點(0, 0))
調用時傳入的view參數為nil的話,返回的是觸摸點在UIWindow的位置
- (CGPoint)previousLocationInView:(UIView *)view;
該方法記錄了前一個觸摸點的位置
- event參數介紹
- 每產生一個事件,就會產生一個UIEvent對象
- UIEvent:稱為事件對象,記錄事件產生的時刻和類型
- 常見屬性
```objc
事件類型
@property(nonatomic,readonly) UIEventType type;
@property(nonatomic,readonly) UIEventSubtype subtype;
事件產生的時間
@property(nonatomic,readonly) NSTimeInterval timestamp;
UIEvent還提供了相應的方法可以獲得在某個view上面的觸摸對象(UITouch)
事件的產生與傳遞
-
當發生一個觸摸事件后,系統會將該事件加入到一個由UIApplication管理的事件隊列中.
UIApplication會從事件隊列中取出最前面的事件,交給主窗口.
主窗口會在視圖層次結構中找到一個'最合適的視圖'來處理觸摸事件
觸摸事件的傳遞是從父控件傳遞到子控件的.
如果一個父控件不能接收事件,那么它里面的了子控件也不能夠接收事件.- 一個控件不能夠接收事件情況下
1.不接收用戶交互時不能夠處理事件
userInteractionEnabled = NO
2.當一個控件隱藏的時候不能夠接收事件
Hidden = YES的時候
3.當一個控件為透明的時候也不能夠接收事件 (alpha <= 0.01)
注意:UIImageView的userInteractionEnabled默認就是NO,
因此UIImageView以及它的子控件默認是不能接收觸摸事件的
- 一個控件不能夠接收事件情況下
如何尋找最合適的View(重點)
1.先判斷自己是否能夠接收觸摸事件,如果能再繼續往下判斷,
2.再判斷觸摸的當前點在不在自己的身上.
3.如果在自己身上,它會從后往前遍歷子控件,遍歷出每一個子控件后,重復前面的兩個步驟.
4.如果沒有符合條件的子控件,那么它自己就是最適合的View.-
觸摸事件處理的詳細過程
用戶點擊屏幕后產生的一個觸摸事件,經過一系列的傳遞過程后,會找到最合適的視圖控件來處理這個事件找到最合適的視圖控件后,就會調用控件的touches方法來作具體的事件處理 touchesBegan… touchesMoved… touchedEnded… touchesBegan方法的默認做法是將事件'順著響應者鏈條'向上傳遞,將事件交給上一個響應者進行處理
-
什么是響應者鏈條
- 通俗的來說就是由多個響應者對象連接起來的鏈條.
所以事件傳遞與響應的完整過程就是:
1.在產生一個事件時,系統會將該事件加入到一個由UIApplication管理的事件隊列中,
2.UIApplication會從事件隊列中取出最前面的事件,將它傳遞給先發送事件給應用程序的主窗口.
3.主窗口會調用hitTest方法尋找最適合的視圖控件,找到后就會調用視圖控件的touches方法來做具體的事情.
4.當調用touches方法,它的默認做法, 就會將事件順著響應者鏈條往上傳遞,
5.傳遞給上一個響應者,接著就會調用上一個響應者的touches方如何去尋找上一個響應者
1.如果當前的View是控制器的View,那么控制器就是上一個響應者.
2.如果當前的View不是控制器的View,那么它的父控件就是上一個響應者.
3.在視圖層次結構的最頂級視圖,如果也不能處理收到的事件或消息,則其將事件或消息傳遞給window對象進行處理
4.如果window對象也不處理,則其將事件或消息傳遞給UIApplication對象
5.如果UIApplication也不能處理該事件或消息,則將其丟棄
hitTest與pointInside方法
- hitTest方法
作用:尋找最適合的View
參數:當前手指所在的點.產生的事件
返回值:返回誰, 誰就是最適合的View.
什么時候用調用:只要一個事件,傳遞給一個控件時, 就會調用這個控件的hitTest方法
-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event- hitTest方法底層實現
作用:當一個事件傳遞給當前View的時候就會調用這個方法.
第一個參數:當前手指在屏幕上的點
第二個參數:當前產生的事件
-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
1.查看自己能不能接收事件
if(self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01){
return nil;
}
2.判斷當前的點在不在自己身上.
if(![self pointInside:point withEvent:event]){
return nil;
}
查看自己是不是最適合的view
從后往前遍歷自己的子控制器.
int count = (int)self.subviews.count;
for (int i = count - 1; i >=0; i--) {
取出子控制器.
UIView *childView = self.subviews[i];
要把當前的點轉換成子控件上的坐標點.
CGPoint childP = [self convertPoint:point toView:childView];
UIView *view = [childView hitTest:childP withEvent:event];
如果有值,直接返回,返回的就是最適合的View.
if (view) {
return view;
}
}
沒有找到比自己更適合的View.自己就是最適合的View
return self;
}
- PointInside方法
作用:判斷point在不在方法調用者上-------先要轉換坐標系[self convertPoint:point toView:childView];
point:必須是方法調用者的坐標系
什么時候調用:hitTest方法底層會調用這個方法,判斷點在不在控件上.
-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{
return YES;
}
To有錯誤和意見請大家指出,謝謝大家,一起進步!