iOS控件之UIResponder類

iOS控件之UIResponder類html

 

在iOS中UIResponder類是專門用來響應用戶的操做處理各類事件的,咱們知道UIApplication、UIView、UIViewController這幾個類是直接繼承自UIResponder,UIWindow是直接繼承自UIView的一個特殊的View,因此這些類均可以響應事件。固然咱們自定義的繼承自UIView的View以及自定義的繼承自UIViewController的控制器均可以響應事件。iOS裏面一般將這些能響應事件的對象稱之爲響應者。

 

iOS中事件Event能夠簡單劃分爲兩種,一種是系統事件(如來電、內存警告等),另外的咱們就稱非系統事件(觸摸事件、運動事件、遠程控制事件)。
不管是系統事件仍是非系統事件,iOS系統都是最早知道事件的,而後將事件加入到當前正在運行的app的UIApplication的事件隊列中。UIApplication會按照先進先出的順序從事件隊列中取出事件,而後分發出去尋求響應。
 
UIApplication對接收到的系統事件會分發給它的代理AppDelegate對象進行處理,其餘的非系統事件會傳給UIApplication的keyWindow去尋找合適的view進行響應。
 
本文主要討論非系統事件,包括觸摸事件(Touch Events)、運動事件(Motion Events)、遠程控制事件(Remote Control Events,如插入耳機調節音量觸發的事件)。關於系統事件能夠參考這篇文章http://www.cnblogs.com/shaoting/p/5029092.html
 
 
下面咱們根據UIResponder.h頭文件來具體介紹關於響應事件的各個方面。

這個部分咱們主要介紹3種事件類型,即觸摸事件,運動事件,遠程控制事件。當用戶觸發某一事件時,UIKit會建立一個UIEvent事件對象(關於iOS事件對象能夠參考這篇文章),事件對象會加入到一個FIFO先進先出的隊列中,UIApplication對象處理事件時,會從隊列頭部取出一個事件對象進行分發。微信

 

1.觸摸事件

@interface UIResponder : NSObject - (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;//系統事件干擾

這4個方法是觸摸事件的最原始處理的4個方法,分別表明觸摸屏幕,在屏幕上移動,離開屏幕以及受到系統優先級別高的事件的干擾(好比來電話)取消觸摸事件。另外UIKit框架對於觸摸事件爲咱們提供了UIGestureRecognizer手勢識別這個類,基本上能知足咱們的大部分需求(能夠參考這篇文章)。這裏介紹的是最底層的處理方法,好比能夠用來實現繪圖類型的APP.
上面提到UIApplication對象從隊列中取出事件對象進行分發,對於觸摸事件來講,UIApplication會首先把事件交給keyWindow,Window會將事件交給UIGestureRecognizer處理,若是UIGestureRecognizer識別了傳遞過來的事件,則交給相對應的target去處理(關於iOS手勢事件能夠參考這篇文章),事件不會再傳遞,若是UIGestureRecognizer並無識別傳遞過來的事件(多是沒有視圖添加手勢,也可能手勢識別不成功),事件會傳遞到視圖樹形結構,會分紅尋找接受者和事件響應這兩個步驟。
1.在iOS視圖樹形結構中找到最終的接收者,也就是觸摸事件發生的那個最上層的View上,這一過程稱爲hit-testing(測試命中),經過一層層的遍歷找到最終的命中視圖稱爲hit-test view.
UIView中有兩個方法用來肯定hit-test view.app

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event; - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event;

 
 

這是一張我從apple官方文檔裏面的截圖,這裏全部的顯示的View都是加載到主window上,假設咱們觸摸到屏幕上ViewD的區域,當咱們沒有重載UIView的hitTest:withEvent:pointInside:withEvent:這兩個方法時,系統默認的處理以下:框架

  • keyWindow調用pointInside:withEvent:判斷觸摸點是否在其frame範圍內,返回Yes,遍歷keyWindow的subView->ViewA.
  • ViewA調用pointInside:withEvent:判斷觸摸點是否在其frame範圍內,返回Yes,遍歷ViewA的subView->ViewB、ViewC(關於ViewB和ViewC先執行哪一個,是根據ViewA添加子控件的前後順序,老是先執行後添加的subView.假設添加ViewB後添加ViewC)
  • ViewC調用pointInside:withEvent:判斷觸摸點是否在其frame範圍內,返回Yes,遍歷ViewC的subView->ViewD、ViewE
  • ViewE調用pointInside:withEvent:判斷觸摸點是否在其frame範圍內,返回NO,ViewE的hitTest:withEvent:返回nil(若是是先執行ViewB的狀況,假設ViewB還有子節點subView,因爲ViewB的pointInside:withEvent:返回NO,ViewB的hitTest:withEvent:`直接返回nil是不會再去遍歷ViewB的子節點的)
  • ViewD調用pointInside:withEvent:判斷觸摸點是否在其frame範圍內,返回Yes而且沒有子節點subView,ViewD的hitTest:withEvent:返回ViewD自己,即爲最終的hit-test view(不會再遍歷ViewB)iewB

    須要注意的是:View.isHidden=YES View.alpha<=0.01 View.userInterfaceEnable=NO View.enable = NO(指繼承自UIControl的View)的這4種狀況下,View的pointInside返回NO,hitTest方法返回nil
    默認UIImageViewuserInterfaceEnable=NOide

2.找到了hit-test view,下一個步驟就是響應事件。說明一下,對於觸摸事件來講,不管View是否處理事件,即便是application經過[application beginIgnoringInteractionEvents]忽略了觸摸事件,上面hit-testing的過程依然存在,它隻影響第二個步驟事件響應的過程。下面咱們將介紹iOS響應者鏈條(Responder chain)測試


 
這是我從官方文檔裏面截取的一張關於響應者鏈條的截圖。咱們先看上圖左邊的狀況:標註爲①的地方即爲步驟1找到的hit-test view 它做爲第一響應者來響應這個事件,若是該view沒有經過重寫或者封裝touch系列方法來處理該事件,默認touch的實現就是調用父類的touch方法,將事件傳遞下去。在這裏由1->傳遞到它的父類2,2是控制器的根view,->傳遞到vc控制器->傳遞到窗口window->傳遞到application

再看上圖右邊的狀況:標註爲①的地方即爲步驟1找到的hit-test view,同時它是控制器的根view而且還有父視圖,事件傳遞到控制器->再傳遞到父視->傳遞到控制器,再傳遞到父視圖窗口->application。其實上圖左邊部分也能夠理解爲窗口是控制器根視圖的父視圖。若是整個響應者鏈條結束,都沒有對事件作處理,那麼該事件會被丟棄。ui

總結一下響應者鏈條的傳遞過程是:由第一響應者(對於觸摸事件來講是hist-test view)開始向上傳遞。若是該視圖是控制器的根視圖,先傳遞給控制器,再傳遞給父視圖,若是不是控制器的根視圖,直接傳遞給父視圖。
只要在響應者的處理方法裏面調用父類的方法,就可讓多個視圖和控制器響應同一個事件,響應者鏈條的根本目的是:共享事件,讓多個視圖和控制器能夠對同一事件作不一樣的處理。spa

 

2.運動事件

- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event NS_AVAILABLE_IOS(3_0); - (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event NS_AVAILABLE_IOS(3_0); - (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event NS_AVAILABLE_IOS(3_0);

這3個方法是運動事件的最原始處理的3個方法,這裏處理的運動事件特指shake事件,手機搖動觸發手機內部的加速度傳感器,能夠用來實現搖一搖計算運動的步數等等應用。相似於觸摸事件,這3個方法分別表明事件開始、事件結束和受到系統干擾取消事件。
加速度計accelerometer其實是由三個加速度計組成,分別用於測量X,Y和Z軸直線路徑速度的變化。結合全部三個加速度計能夠檢測設備朝任何方向的運動和獲取設備的當前方向。對於shake事件來講,咱們不關心3個方向上的運動,只做爲一個事件對象來處理。若是隻是處理設備的大方向,並不須要知道方向向量,如橫屏豎屏屏幕旋轉,咱們可使用UIDevice類(參考文章)。若是咱們須要知道3個方向上的運動作更細緻化的處理,如上了多少層樓等運動類型APP,可使用的核心運動框架訪問加速度計,陀螺儀和設備的運動類來作處理(Core Motion參考文章)
上面介紹的響應者鏈條對shake事件一樣適用,只不過,沒有hit-testing過程,若是當前顯示的視圖界面沒有一個view聲明爲第一響應者(調用becomeFirstResponder申明而且View須要重寫canBecomeFirstResponder方法返回YES,默認返回爲NO),默認當前視圖控制器爲第一響應者,並將事件沿着響應者鏈條傳遞,直到被處理。若是有視圖聲明爲第一響應者,就從該視圖開始傳遞事件直到被處理,若是該事件最終沒有被處理而且UIApplication的applicationSupportsShakeToEdit屬性爲YES(默認就是YES),當鍵盤顯示的時候,系統會有一個是否撤銷正在輸入的警告。就是微信和QQ上在輸入的時候搖動手機提示撤銷輸入的那種效果。關於更多撤銷方面的操做參考NSUndoManager3d

 

3.遠程控制事件(本文暫不討論)

 

4.UI控件不能響應事件的幾種狀況

注意:UIView.enable = NO;時也不能響應事件!代理

 

 

以上內容大多轉自http://www.jianshu.com/p/2dda99a0e09a

相關文章
相關標籤/搜索