響應者對象(UIResponder)
在iOS中不是任何對象都能處理事件,只有繼承了UIResponder的對象才能接受并處理事件,我們稱之為“響應者對象”。以下都是繼承自UIResponder的,所以都能接收并處理事件。
- UIApplication
- UIViewController
- UIView
那么為什么繼承自UIResponder的類就能夠接收并處理事件呢?
因為UIResponder中提供了以下4個對象方法來處理觸摸事件。
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;
iOS 事件響應鏈全過程分兩步:
一.事件的傳遞:從用戶觸摸位置找到最上層view,這個過程是從底層向上層尋找的過程。
iOS使用hit-testing尋找觸摸的view。
Hit-Testing 叫做命中測試。
Hit-Testing通過檢查觸摸點是否在關聯(lián)的view邊界內,如果在,則遞歸地檢查該view的所有子view。
在層級上處于lowest(就是離用戶最近的view)且邊界范圍包含觸摸點的view成為hit-test view。確定hit-test view后,它傳遞觸摸事件給該view。
下面是官方的小例子:

- 觸摸點在view A中,所以要先檢查子view B和C。
- 觸摸點不在view B中,但在C中,所以檢查C的子view D和E。
- 觸摸點不在D中,但在E中。View E是這個層級上處于lowest的- view的邊界范圍包含觸摸點,所以它成為了hit-test view。
Hit-test view是處理觸摸事件的第一選擇,如果hit-test view不能處理事件,該事件將從事件響應鏈中尋找響應器,直到系統(tǒng)找到一個處理事件的對象。若不能處理,則就有事件傳遞鏈了,繼續(xù)看下面的事件傳遞鏈。
Hit-Test命中測試方法判斷一個view能否作為Responder還有3個條件:
- 透明度<0.01
- hidden=YES的時候
- userInteractionEnabled = NO
以上這3個情況,該view(以及該view所有的子視圖) 命中測試方法會忽略掉,也就不會有事件響應了。
二.事件響應:從上層沿著響應鏈傳到底層,和第一步剛好是相反的。
響應者對象:能處理事件的對象,也就是繼承自UIResponder的對象
作用:能很清楚的看見每個響應者之間的聯(lián)系,并且可以讓一個事件多個對象處理。
如何判斷上一個響應者
1> 如果當前這個view是控制器的view,那么控制器就是上一個響應者
2> 如果當前這個view不是控制器的view,那么父控件就是上一個響應者
下面是參考官方的響應流程圖:

左半圖:
initial view若不能處理事件,則傳到其父視圖view
view若不能處理,則傳到其父視圖,因為它還不是最上層視圖
這里view的父視圖是view controller的view,因為這個view也不能處理事件,因此傳給view controller
若view controller也不能處理此事件,則傳到window
若window也不能處理此事件,則傳到app單例對象Application
若UIApplication單例對象也不能處理,則表示無效事件
右半圖:
initial view一直傳遞直到最上層view(原話:A view passes an event up its view controller’s view hierarchy until it reaches the topmost view.)
topmost view傳遞事件到它所在的控制器(原話:The topmost view passes the event to its view controller.)
view controller傳遞事件到topmost view的父視圖,重復前三步,走到到達root controller(原話:passes the event to its topmost view’s superview. Steps 1-3 repeat until the event reaches the root view controller.)
由root控制器傳遞事件到window(原話:The root view controller passes the event to the window object.)
若window也不能處理此事件,則傳到app單例對象Application
若UIApplication單例對象也不能處理,則表示無效事件
事件處理的整個流程總結:
- 觸摸屏幕產生觸摸事件后,觸摸事件會被添加到由UIApplication管理的事件隊列中(即,首先接收到事件的是UIApplication)。
- UIApplication會從事件隊列中取出最前面的事件,把事件傳遞給應用程序的主窗口(keyWindow)。
- 主窗口會在視圖層次結構中找到一個最合適的視圖來處理觸摸事件。(至此,第一步已完成)
- 最合適的view會調用自己的touches方法處理事件
- touches默認做法是把事件順著響應者鏈條向上拋。
如何做到一個事件多個對象處理:
因為系統(tǒng)默認做法是把事件上拋給父控件,所以可以通過重寫自己的touches方法和父控件的touches方法來達到一個事件多個對象處理的目的。
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
// 1.自己先處理事件...
NSLog(@"do somthing...");
// 2.再調用系統(tǒng)的默認做法,再把事件交給上一個響應者處理
[super touchesBegan:touches withEvent:event];
}
事件的傳遞和響應的區(qū)別:
事件的傳遞是從上到下(父控件到子控件),事件的響應是從下到上(順著響應者鏈條向上傳遞:子控件到父控件。