不要慫一起看看響應者鏈和事件傳遞

在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

結束

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章