iOS 事件的傳遞和響應(yīng)機制

聲明:

這篇是根據(jù)看史上最詳細的iOSZ之事件的傳遞和響應(yīng)機制-原理篇做的一個筆記。

筆記一:響應(yīng)者對象(UIResponder)

UIResponder類可以接收和處理事件因為它提供了4個對象方法來處理接觸事件

UIResponder內(nèi)部提供了以下方法來處理事件觸摸事件

// 一根或者多根手指開始觸摸view,系統(tǒng)會自動調(diào)用view的下面方法
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;

// 一根或者多根手指在view上移動,系統(tǒng)會自動調(diào)用view的下面方法(隨著手指的移動,會持續(xù)調(diào)用該方法)
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;

// 一根或者多根手指離開view,系統(tǒng)會自動調(diào)用view的下面方法
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;

//觸摸結(jié)束前
- (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;

總結(jié):
UIApplication,UIViewController,UIView都是繼承于UIResponder,所以它們也可以重寫事件來接收和處理事件。

聯(lián)想:
CALayer 和 UIView的最大區(qū)別是 UIview可以處理事件 因為view是responder的子類

筆記二:UITouch對象

下面是UITouch的屬性和方法:

 @interface UITouch : NSObject

NSTimeInterval   timestamp; //觸摸發(fā)生的時間
UITouchPhase        phase;  //返回一個階段常量,指出觸摸開始、繼續(xù)、結(jié)束或被取消,分別對應(yīng)UITouchPhaseBegan、UITouchPhaseMoved等
NSUInteger          tapCount;   //輕按屏幕的次數(shù) 可以根據(jù)這個判斷單擊 雙擊 多擊
UITouchType         type NS_AVAILABLE_IOS(9_0); //觸摸的類型

CGFloat majorRadius ; //觸摸的半徑
CGFloat majorRadiusTolerance;//觸摸的半徑的誤差值

//引起觸摸的視圖或Window.
UIWindow   *window;
UIView        *view;

NSArray <UIGestureRecognizer *> *gestureRecognizers ;//手勢數(shù)組
//  現(xiàn)在觸摸的坐標//函數(shù)返回一個CGPoint類型的值,
//  表示觸摸在view這個視圖上的位置,這里返回的位置是針對view的坐標系的。
//  調(diào)用時傳入的view參數(shù)為空的話,返回的時觸摸點在整個窗口的位置。
- (CGPoint)locationInView:(nullable UIView *)view;

//上一次觸摸的坐標//該方法記錄了前一個坐標值函數(shù)返回也是一個CGPoint類型的值
//表示觸摸在view這個視圖上的位置,這里返回的位置是針對view的坐標系的。
//調(diào)用時傳入的view參數(shù)為空的話,返回的時觸摸點在整個窗口的位置。
- (CGPoint)previousLocationInView:(nullable UIView *)view;

////現(xiàn)在觸摸的精確的坐標
- (CGPoint)preciseLocationInView:(nullable UIView *)view;
//上一次觸摸的精確的坐標
- (CGPoint)precisePreviousLocationInView:(nullable UIView *)view

//觸摸的力度
CGFloat force;
//觸摸的最大的力度
CGFloat maximumPossibleForce;

//沿著x軸正向的方位角,當(dāng)與x軸正向方向相同時,該值為0;當(dāng)view參數(shù)為nil時,
//默認為keyWindow返回觸針的方位角(弧度)。
- (CGFloat)azimuthAngleInView:(nullable UIView *)view NS_AVAILABLE_IOS(9_1);

//當(dāng)前觸摸對象的方向上的單位向量當(dāng)view參數(shù)為nil時,
//默認為keyWindow返回在觸針的方位角的方向指向的單位矢量。
- (CGVector)azimuthUnitVectorInView:(nullable UIView *)view NS_AVAILABLE_IOS(9_1);
//當(dāng)筆平行于平面時,該值為0
//當(dāng)筆垂直于平面時,該值為Pi / 2
//觸針的高度(單位為弧度)
CGFloat altitudeAngle ;

//當(dāng)每個觸摸對象的觸摸特性發(fā)生變化時,
//該值將會單獨增加,返回值是NSNumber 索引號,
//讓您關(guān)聯(lián)與原來的觸摸更新的聯(lián)系
NSNumber * _Nullable estimationUpdateIndex 

//當(dāng)前觸摸對象估計的觸摸特性,返回值是UITouchPropertyies一組觸摸屬性,
//這些屬性將得到更新
UITouchProperties estimatedProperties 

//一組期望在未來的更新報文的觸摸性能。
UITouchProperties estimatedPropertiesExpectingUpdates;
UITouch對象:

1 當(dāng)前用戶一根手指觸摸屏幕市 會創(chuàng)建一個與手指相關(guān)的UITouch對象 一根手指對應(yīng)著一個UITouch對象
2如果兩根手指同時觸摸一個view,那么view只會調(diào)用一個touchesBegan方法 touches參數(shù)中裝著2個UITouch對象
3如果這兩根手指一前一后分開觸摸同一個view,那么view會分別調(diào)用2次touchesBegan 方法并且每次調(diào)用的touches參數(shù)只包含一個UITouchd對象

UITouch的作用

1 保存著跟手指有關(guān)的信息
2 保存手指的觸摸位置
3 當(dāng)手指離開屏幕系統(tǒng)會銷毀相應(yīng)的UITouch對象

筆記三 IOS的事件產(chǎn)生和傳遞

1 事件的觸摸事件的傳遞是從父控件傳遞到子控件 UIApplication -> Window -> 合適的View
2 應(yīng)用如何找到最合適的控件來處理事件?

1.首先判斷主窗口(keyWindow)自己是否能接受觸摸事件
2.判斷觸摸點是否在自己身上
3.子控件數(shù)組中從后往前遍歷子控件,重復(fù)前面的兩個步驟(所謂從后往前遍歷子控件,就是 首先查找子控件數(shù)組中最后一個元素,然后執(zhí)行1、2步驟)
4.view,比如叫做fitView,那么會把這個事件交給這個fitView,再遍歷這個fitView的子控 件,直至沒有更合適的view為止。
5.如果沒有符合條件的子控件,那么就認為自己最合適處理這個事件,也就是自己是最合適的view。

3 UIView不能接收觸摸事件的三種情況:

1 不允許交互:userInteractionEnabled = NO
2 隱藏:如果把父控件隱藏,那么子控件也會隱藏,隱藏的控件不能接受事件
3 透明度:如果設(shè)置一個控件的透明度<0.01,會直接影響子控件的透明度。alpha:0.0~0.01為透明

借用看到的圖片 覺得很清晰的表達了上面的流程:

1055199-48ec84d3d176374e.png

筆記4 如何尋找最合適的view

1 首先判斷主窗口自己能否接受觸摸事件
2 觸摸點是否在自己的身上
3 從后遍歷子控件 重復(fù)前面兩個步驟
4 如果沒有符合的子控件就認為自己最合適

筆記5 尋找最合適的view的兩個方法

5.1 hitTest:withEvent:方法

1 什么時候調(diào)用?
只要事件一傳遞給一個控件,這個控件就會調(diào)用他自己的hitTest:withEvent方法

2 可以根據(jù)這個方法攔截事件

3 特殊情況:
誰都不能處理事件,窗口也不能處理。

重寫window的hitTest:withEvent:方法return nil
只能有窗口處理事件。

控制器的view的hitTest:withEvent:方法return nil或者window的hitTest:withEvent:方法return self
return nil的含義:
hitTest:withEvent:中return nil的意思是調(diào)用當(dāng)前hitTest:withEvent:方法的view不是合適的view,子控件也不是合適的view。如果同級的兄弟控件也沒有合適的view,那么最合適的view就是父控件。

5.1 pointInside:withEvent:方法

pointInside:withEvent:方法判斷點在不在當(dāng)前view上(方法調(diào)用者的坐標系上)如果返回YES,代表點在方法調(diào)用者的坐標系上;返回NO代表點不在方法調(diào)用者的坐標系上,那么方法調(diào)用者也就不能處理事件。

筆記6 事件的響應(yīng)

1事件的響應(yīng)

1用戶點擊屏幕后產(chǎn)生一個觸摸事件經(jīng)過一系列的傳遞 會找到最合適的視圖 也就是筆記4和5 為事件的傳遞過程
2 找到合適的視圖后 會通過touch方法將事件順著響應(yīng)鏈條向上傳遞,將事件交給上一個響應(yīng)者處理

2 響應(yīng)鏈條

響應(yīng)者鏈條:在iOS程序中無論是最后面的UIWindow還是最前面的某個按鈕,它們的擺放是有前后關(guān)系的,一個控件可以放到另一個控件上面或下面,那么用戶點擊某個控件時是觸發(fā)上面的控件還是下面的控件呢,這種先后關(guān)系構(gòu)成一個鏈條就叫“響應(yīng)者鏈”。也可以說,響應(yīng)者鏈是由多個響應(yīng)者對象連接起來的鏈條。在iOS中響應(yīng)者鏈的關(guān)系可以用下圖表示:

1055199-2a49a16e1e483b5c.png
3 響應(yīng)者對象:能處理事件的對象,也就是繼承自UIResponder的對象
4如何判斷上一個響應(yīng)者

1> 如果當(dāng)前這個view是控制器的view,那么控制器就是上一個響應(yīng)者
2> 如果當(dāng)前這個view不是控制器的view,那么父控件就是上一個響應(yīng)者

5 響應(yīng)鏈的事件傳遞過程

1 如果當(dāng)前view是控制器的view 那么控制器就是上一個響應(yīng)者 事件就傳遞給控制器。如果當(dāng)前view不是控制器的view 那么父視圖就是當(dāng)前view的上一個響應(yīng)者,事件就會傳遞給它的父視圖

2 在視圖層次結(jié)構(gòu)的最頂級視圖,如果也不能處理的事件或消息 則將其事件或消息傳遞給window對象進行處理

3 如果window對象也不處理 則其講事件或消息傳遞給UIApplication對象

4 如果UIApplication也不能處理就將改事件或消息給丟棄

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ 
// 默認會把事件傳遞給上一個響應(yīng)者,上一個響應(yīng)者是父控件,交給父控件處理
[super touchesBegan:touches withEvent:event]; 
// 注意不是調(diào)用父控件的touches方法,而是調(diào)用父類的touches方法
// super是父類 superview是父控件 
}

6事件的傳遞與響應(yīng):

1、當(dāng)一個事件發(fā)生后,事件會從父控件傳給子控件,也就是說由UIApplication -> UIWindow -> UIView -> initial view,以上就是事件的傳遞,也就是尋找最合適的view的過程。

2、接下來是事件的響應(yīng)。首先看initial view能否處理這個事件,如果不能則會將事件傳遞給其上級視圖(inital view的superView);如果上級視圖仍然無法處理則會繼續(xù)往上傳遞;一直傳遞到視圖控制器view controller,首先判斷視圖控制器的根視圖view是否能處理此事件;如果不能則接著判斷該視圖控制器能否處理此事件,如果還是不能則繼續(xù)向上傳 遞;(對于第二個圖視圖控制器本身還在另一個視圖控制器中,則繼續(xù)交給父視圖控制器的根視圖,如果根視圖不能處理則交給父視圖控制器處理);一直到 window,如果window還是不能處理此事件則繼續(xù)交給application處理,如果最后application還是不能處理此事件則將其丟棄

3、在事件的響應(yīng)中,如果某個控件實現(xiàn)了touches...方法,則這個事件將由該控件來接受,如果調(diào)用了[supertouches….];就會將事件順著響應(yīng)者鏈條往上傳遞,傳遞給上一個響應(yīng)者;接著就會調(diào)用上一個響應(yīng)者的touches….方法

總結(jié):
事件的傳遞是從上到下(父控件到子控件),事件的響應(yīng)是從下到上(順著響應(yīng)者鏈條向上傳遞:子控件到父控件。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容