在iOS中,只有繼承了UIResponder的對象纔可以接受並處理時間,這些對象被稱爲響應者對象
> UIApplication
> UIViewController
> UIView
這些都繼承自UIResponder,因此他們都是響應者對象,都能夠接收並且處理事件,UIResponder提供了以下方法來處理事件
觸摸事件
// 開始點擊
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
// 當手指在view上移動的時候
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
// 當手指離開這個view的時候
- (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;
------- 當要支持多根手指點擊設置屬性 : mutableTouch --------------
事件對象:UIEvent
UIEvent表示用戶交互的事件對象,有以下屬性
@property(nonatomic,readonly) UIEventType type;
@property(nonatomic,readonly) UIEventSubtype subtype;
其中type表示當前響應事件的類型,分別有多點觸控、搖一搖、遠程操控、3DTouch,在一個用戶點擊事件處理的過程中,UIEvent對象時唯一的
點擊對象:UITouch
UITouch表示單個點擊,其類文件中存在枚舉類型UITouchPhase的屬性,用來表示當前點擊的狀態。這些狀態包括點擊開始、移動、停止不動、結束、取消五個狀態,每次點擊發生的時候,點擊對象都放在一個集合中傳入UIResponder的回調方法中,我們通過集合中對象獲取用戶點擊的位置
============屬性===============
觸摸產生時所處的窗口
@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;
============方法===============
// 返回值表示觸摸在view上的位置
// 這裏返回的位置是針對view的座標系的(以view的左上角爲原點(0, 0))
// 調用時傳入的view參數爲nil的話,返回的是觸摸點在UIWindow的位置
-(CGPoint)locationInView:(UIView *)view;
// 該方法記錄了前一個觸摸點的位置
-(CGPoint)previousLocationInView:(UIView *)view;
-(CGPoint)locationInView:(UIView *)view 獲取當前點擊座標點
-(CGPoint) previousLocationInView: (UIview *) view 獲取上個點擊位置的座標點
除了touch回調的幾個點擊事件,手勢UIGestureRecognizer對象也附加在view上,來實現其他手勢事件,在view添加單擊手勢之後,原來的touchesEnded就無效了
UITouch的作用,保存着跟手指相關的信息,比如位置、時間、階段,當手指離開時,系統會銷燬響應的UITouch對象。
事件的傳遞和原理
1 > 發生觸摸事件後,系統會將該事件加入到一個由UIApplication管理的事件隊列中(爲什麼是隊列而不是棧呢?因爲隊列是FIFO,也就是先進先出,先產生的事件先處理纔對,棧是先進後出的。),UIApplication會從事件中取出最前面的事件,發送給程序的主窗口--->keyWindow,主窗口會在視圖層次結構中找到一個最適合的試圖來處理事件。
2 > 找到最合適的試圖空間後,會調用試圖控件的觸摸事件的方法
示例:
點擊了綠色:UIApplication --> UIWindow --> 白色 ---> 綠色
點擊了藍色:UIApplication --> UIWindow --> 白色 ---> 橙色 ---> 藍色
如果父控件不能接受觸摸事件,那麼子控件就不可能接受到觸摸事件
一個試圖無法接收觸摸事件可能是以下情況
1、不接收用戶交互
userInteractionEnabled = NO
2、隱藏
hidden = yes;
3、透明度
alpha = 0;
其中:UIImageView的userInteractionEnable默認爲NO,因此UIImageVIew以及它的子控件默認不能接受觸摸事件,如果想讓某個view不能處理事件,那麼就設置這三個屬性
那麼怎麼找到最合適的控件來處理事件呢?
1️⃣、首先判斷主窗口是否能接受事件
2️⃣、判斷觸摸點是否在自己身上
3️⃣、子控件數組中從後往前遍歷子控件,重複1️⃣、2️⃣兩個步驟
4️⃣、比如有個LHView,那麼把這個事件交給這個view,再遍歷LHView的子控件,直至沒有更合適的爲止
5️⃣、如果沒有符合條件的子控件,那麼就認爲自己最適合處理這個事件
當一個試圖接收到事件傳遞時,就會調用 - hitText方法,這個方法用於判斷找出最合適的view,找到最合適的view就會調用響應的處理方法
代碼如下
-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
//1、判斷當前的view能否接收事件
if (self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01) return nil;
//2、判斷點在不在當前控件
NSInteger pointCount = self.subviews.count;
//3、從後往前遍歷子控件
for (NSInteger i = pointCount - 1; i >= 0; i--) {
UIView * childView = self.subviews[i];
//把當前控件的座標轉換成子控件上的座標
CGPoint childPoint = [self convertPoint:point toView:childView];
UIView * fitView = [childView hitTest:childPoint withEvent:event];
if (fitView) {
return fitView;
}
}
//循環結束,沒有找到比自己更合適的view
return self;
}
總結:
事件的傳遞與響應(響應者鏈條)
1、當一個事件發生後,事件會從父控件傳給子控件,也就是說有UIApplication -> UIWindow -> UIView -> initial view 的傳遞,尋找最合適的view的過程,當一個試圖接收到事件之後就會調用 -(UIView *)hitTest:(CGpoint) point withEvent:(UIEvent *)event,經過5個步驟來尋找的過程。
2、事件的響應:首先看當前的initial view是否處理這個事件,如果不則會將事件傳遞給上級試圖,如果上級試圖仍然無法處理則會繼續往上傳遞,一直傳遞到控制器 viewController。會判斷試圖控制器的根試圖能否處理此事件,此時還是不能處理則交給UIWindow,如果還不能處理則交給最後的UIApplication,它如果還不能處理的話就丟棄
3、在事件的響應中,如果某個空間實現了touches....的方法,則這個事件將有此控件來接收,如果調用了【super touches。。。】;就會將事件順着響應者鏈條網上往上傳遞,傳遞給上一個響應者,接着就會調用上一個響應者的touches方法。
====馬上結束====
試問:能否做到一個事件多個對象處理
系統默認做法是把事件上拋給父控件,所以可以通過重寫自己的touches方法和父控件的touches方法來達到這個目的
試問:事件的傳遞和響應的區別
事件的傳遞時從上到下,從父控件到子控件,事件的響應是從下往上,順着響應者鏈條向上傳遞,子控件到父控件
====== 這回真的是馬上結束 ======
-(UIResponder *)nextResponder;
它返回接收者下一個響應,沒有就返回nil
結束