標籤:
簡單來說就是 :一級一級的找到響應的視圖,如果沒有就傳給UIWindow執行個體和UIApplication執行個體,要是他們也處理不了,就丟棄這次事件...
對於IOS裝置使用者來說,他們操作裝置的方式主要有三種:觸控螢幕幕、晃動裝置、通過遙控設施控制裝置。對應的事件類型有以下三種:
1、觸屏事件(Touch Event)
2、運動事件(Motion Event)
3、遠端控制事件(Remote-Control Event)
響應者鏈條概念: iOS系統檢測到手指觸摸(Touch)操作時會將其打包成一個UIEvent對象,並放入當前活動Application的事件隊 列,單例的UIApplication會從事件隊列中取出觸摸事件並傳遞給單例的UIWindow來處理,UIWindow對象首先會使用 hitTest:withEvent:方法尋找此次Touch操作初始點所在的視圖(View),即需要將觸摸事件傳遞給其處理的視圖,這個過程稱之為 hit-test view。
響應者對象(Responder Object) 指的是 有響應和處理事件能力的對象。 響應者鏈就是由一系列的響應者對象 構成的一個階層。
UIResponder 是所有響應對象的基類,在UIResponder類中定義了處理上述各種事件的介面。我們熟悉的 UIApplication、 UIViewController、 UIWindow 和所有繼承自UIView的UIKit類都直接或間接的繼承自UIResponder,所以它們 的執行個體都是可以構成響應者鏈的響應者對象。
UIWindow執行個體對象會首先在它的內容視圖上調用hitTest:withEvent:,此方法會在其視圖層級結構中的每個視圖上調用 pointInside:withEvent:(該方法用來判斷點擊事件發生的位置是否處於當前視圖範圍內,以確定使用者是不是點擊了當前視圖),如果 pointInside:withEvent:返回YES,則繼續逐級調用,直到找到touch操作發生的位置,這個視圖也就是要找的hit-test view。
hitTest:withEvent:方法的處理流程如下:
首先調用當前視圖的pointInside:withEvent:方法判斷觸摸點是否在當前視圖內;
若返回NO,則hitTest:withEvent:返回nil;
若返回YES,則向當前視圖的所有子視圖(subviews)發送hitTest:withEvent:訊息,所有子視圖的遍曆順序是從最頂層視圖一直到到最底層視圖,即從subviews數組的末尾向前遍曆,直到有子視圖返回非Null 物件或者全部子視圖遍曆完畢;
若第一次有子視圖返回非Null 物件,則hitTest:withEvent:方法返回此對象,處理結束;
如所有子視圖都返回非,則hitTest:withEvent:方法返回自身(self)。
假如使用者點擊了View E,下面結合圖二介紹hit-test view的流程:
1、A是UIWindow的根視圖,因此,UIWindwo對象會首相對A進行hit-test;
2、顯然使用者點擊的範圍是在A的範圍內,因此, pointInside:withEvent:返回了YES,這時會繼續檢查A的子視圖;
3、這時候會有兩個分支,B和C:
點擊的範圍不再B內,因此B分支的 pointInside:withEvent:返回NO,對應的hitTest:withEvent:返回nil;
點擊的範圍在C內,即C的 pointInside:withEvent:返回YES;
4、這時候有D和E兩個分支:
點擊的範圍不再D內,因此D 的 pointInside:withEvent:返回NO,對應的hitTest:withEvent:返回nil;
點擊的範圍在E內,即E的 pointInside:withEvent:返回YES,由於E沒有子視圖(也可以理解成對E的子視圖進行hit-test時返回了nil),因此,E的 hitTest:withEvent:會將E返回,再往回回溯,就是C的 hitTest:withEvent:返回E--->>A的hitTest:withEvent:返回E。
至此,本次點擊事件的第一響應者就通過響應者鏈的事件分發邏輯成功的找到了。
不難看出,這個處理流程有點類似二分搜尋的思想,這樣能以最快的速度,最精確地定位出能響應觸摸事件的UIView。
***上面找到了事件的第一響應者,接下來就該沿著尋找第一響應者的相反順序來處理這個事件,如果UIWindow單例和UIApplication都無法處理這一事件,則該事件會被丟棄。***
說明:
1、如果最終 hit-test沒有找到第一響應者,或者第一響應者沒有處理該事件,則該事件會沿著響應者鏈向上回溯,如果UIWindow執行個體和UIApplication執行個體都不能處理該事件,則該事件會被丟棄;
2、hitTest:withEvent:方法將會忽略隱藏(hidden=YES)的視圖,禁止使用者操作(userInteractionEnabled=YES)的視圖,以及alpha層級小於0.01(alpha<0.01)的視圖。如果一個子視圖的地區超過父視圖的bound地區(父視圖的clipsToBounds 屬性為NO,這樣超過父視圖bound地區的子視圖內容也會顯示),那麼正常情況下對子視圖在父視圖之外地區的觸摸操作不會被識別,因為父視圖的pointInside:withEvent:方法會返回NO,這樣就不會繼續向下遍曆子視圖了。當然,也可以重寫pointInside:withEvent:方法來處理這種情況。
3、我們可以重寫 hitTest:withEvent:來達到某些特定的目的,下面的連結就是一個有趣的應用舉例,當然實際應用中很少用到這些。
再深入詳細一些:
iOS的事件大致分為三種:觸摸事件、加速計事件、遠端控制事件
首先要理解以下幾個概念:
一、響應者對象:
在iOS中不是任何對象都能處理事件,只有繼承了UIResponder的對象才能接收並處理事件。我們稱之為“響應者對象”。
UIApplication、UIViewController、UIView都繼承自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;
三、UITouch
使用者同時觸摸的手指,每一根就代表一個UITouch對象,它儲存著跟手指相關的資訊,比如觸摸的位置、時間、階段。
當手指移動(move)時,系統會更新同一個UITouch對象,使之能夠一直儲存該手指在的觸摸位置。
當手指離開(end)螢幕時,系統會銷毀相應的UITouch對象.
四、UIEvent
觸摸事件發生,必須會產生一個UIEvent對象,稱為事件對象,記錄事件產生的時刻和類型。
五、首先處理事件的響應者
發生觸摸事件後,系統會將該事件加入到一個由UIApplication管理的事件隊列中。
UIApplication會從事件隊列中取出最前面的事件,並將事件分發下去以便處理,通常,先發送事件給應用程式的主視窗(keyWindow)
主視窗會在視圖階層中找到一個最合適的視圖來處理觸摸事件,這也是整個事件處理過程的第一步
找到合適的視圖控制項後,就會調用視圖控制項的touches方法來作具體的事件處理
touchesBegan…
touchesMoved…
touchedEnded…
下面是PPT樣本,說的很明白。
UIView不接收觸摸事件的三種情況
1.alpha= 0.0 ~ 0.01
2.hidden屬性=YES
3.userInteractionEnabled=YES
注意:UIImageView的userInteractionEnabled預設就是NO,因此UIImageView以及它的子控制項預設是不能接收觸摸事件的
六、響應者鏈條
合適的控制項調用touches方法後,會預設地將事件順著響應者鏈條向上傳遞,將事件交給上一個響應者進行處理。
尋找上一個響應者的過程,看起來是尋找首要合適控制項的逆過程。
當前響應者的touches方法中如果調用[super touches],就會對用上一個響應者touches方法,同時可以將UITouch對象和UIEvent對象向上傳遞。
這樣就能夠將一次觸摸事件的事件對象和觸摸對象的資訊傳遞給多個響應者。
問題:上一個響應者(nextResponder)是誰?
判斷步驟:
1>如果當前的view是控制器的view,控制器就是上一個響應者
2>如果當前的view不是控制器的view,那麼父控制項就是上一個響應者
3>如果當前響應者是控制器,那麼上一個響應者是UIWindow;如果UIWindow也不處理,就再往上傳給UIApplication
4>如果UIApplication也不處理,那麼這條訊息就被會廢棄
七、觸摸事件處理過程
1.使用者觸摸後,系統先將事件對象(event)由上往下傳遞(父控制項傳給子控制項),找到最合適的控制項來處理事件(遞迴尋找當前控制項的最適合子控制項)
2.調用合適控制項的touches相關方法
3.如果調用了super的touches相關方法,就會將事件順著響應者鏈條往上傳遞,傳給上一個響應者
4.接著就會調用上一個響應者的touches方法
5.只要當前響應者的touches方法中調用了super的touches方法,還會繼續往上遞迴調用,直到不再調用super的touches方法
iOS開發--響應者鏈條