UIResponder
介紹
UIResponder 這個類定義了很多用來處理響應和時間處理的類。他的子類有UIApplication,UIView以及UIWindow等。
IOS中分爲兩類事件:觸摸事件,和移動事件。最原始的事件處理方是touchesBegan:withEvent:
,touchesMoved:withEvent:
, touchesEnded:withEvent:
,
and touchesCancelled:withEvent:。
無論任何時候手指只要觸摸屏幕或是在屏幕上移動拖拽甚至離開屏幕都會導致一個UIEvent對象產生。
Responder Chain
在UIResponder中有一個非常重要的概念叫做Responder Chain,個人的理解是這是按照一定規則組織的響應、處理事件的一條鏈表。在瞭解UIResponder之前還得在瞭解一個概念Hit-Testing。在IOS中通常使用hit-testing去找到那個被觸摸的視圖。這個視圖叫hit-test view,當IOS找到hit-test view後就把touch event交個那個視圖來處理。下面畫個圖來說明一下,當點擊視圖E時看一下hit-testing的工作過程。
1.確定改觸摸事件發生在view A範圍內,接下來測試view B以及view C。
2.檢查發現事件不再view B範圍內發生,接下來檢查view C發現觸摸事件發生在了view C中,所以檢查 view D,view E。
3.最後發現事件發生在view E的範圍內所以view E成爲了hit-test view。
下面是關於調用hit-test的官方說明:
The hitTest:withEvent:
method
returns the hit test view for a given CGPoint
and UIEvent
.
The hitTest:withEvent:
method begins
by calling thepointInside:withEvent:
method
on itself. If the point passed into hitTest:withEvent:
is
inside the bounds of the view, pointInside:withEvent:
returns YES
.
Then, the method recursively calls hitTest:withEvent:
on
every subview that returns YES
.
Responder
Chain 是由responder對象組成的
responder chain是由一系列responder對象連接起來的,他從第一個responder對象開始一直到application對象結束。如果第一個responder不能夠處理該事件則該事件會被髮送到下一個在該responder
chain中的responder來處理。
當自己定義的一個類想讓他成爲first responder時需要做兩件事:
2.接受 becomeFirstResponder 消息,如果必要的話可讓對象給自己發送該消息。
在這裏有一個地方需要注意,當把一個對象變爲first responder是要確保這個對象的圖形界面已經建立起來,也就是說要在viewDidAppear中調用becomeFirstResponder方法。如果在veiwWillAppear方法中調用becomeFirstResponder將會得到一個NO。
Responder
Chain 遵循一個特定的傳播路徑
如果hit-test view不能夠處理該事件則UIKit會將事件傳遞給下一個Responder。下圖則顯示了事件在Responder Chain中傳播的兩種方式:
對於左邊的app中事件傳播路徑如下:
1.初始的界面嘗試去處理事件後者消息,打他處理不了則把事件交給它上一層視圖處理,因爲最開始的界面在他的view
controller裏的視圖層次裏不是最上層的。(這裏的上下是按照樹的結構而言的,下圖解釋:)
2.上層視圖嘗試處理事件,如果他不能處理則將事件交給他的上層視圖處理,原因同上。
3.在view controller中最上層的視圖嘗試處理,他也不能處理則交給他的view controller來處理。
4.view controller也無法處理則交給window來處理。
5.window無法處理交給app object來處理
6.app object無法處理則將該事件丟棄掉。
右邊的傳播方式稍有不同:
1.一個視圖在他的view controller 的視圖層中向上傳播一個事件直到它到達最頂層視圖。
2.最頂層視圖無法處理將event交給他的view controller來處理。
3.view controller 傳遞事件到他的最頂層視圖的上一層視圖,接下來重複1-3的步驟直到事件到達root view controller。
4.root view controller將事件傳遞到window object。
5.window 將事件傳遞給app object。
注意:事件,消息不要自己向上傳送而要調用父類中的方法來處理,讓UIKit來處理消息在responder chain中的傳遞。
Responder chain Demo:
-
-
-
#import "PMBViewController.h"
-
#import "PMBViewOne.h"
-
#import "PMBVIewTwo.h"
-
#import "PMBVIewThree.h"
-
-
@interface PMBViewController ()
-
@property (weak, nonatomic) IBOutlet PMBVIewThree *viewThree;
-
-
@property (weak, nonatomic) IBOutlet PMBVIewTwo *viewTwo;
-
-
@property (weak, nonatomic) IBOutlet PMBViewOne *viewOne;
-
-
-
@end
-
-
@implementation PMBViewController
-
-
- (void)viewDidLoad
-
{
-
[super viewDidLoad];
-
-
}
-
-
-
- (void)didReceiveMemoryWarning
-
{
-
[super didReceiveMemoryWarning];
-
-
}
-
@end
-
-
-
#import "PMBViewOne.h"
-
-
@implementation PMBViewOne
-
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
-
[super touchesBegan:touches withEvent:event];
-
[[[touches anyObject] view] setBackgroundColor:[UIColor redColor]];
-
}
-
@end
-
-
-
#import "PMBVIewTwo.h"
-
-
@implementation PMBVIewTwo
-
@end
-
-
-
#import "PMBVIewThree.h"
-
-
@implementation PMBVIewThree
-
-
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
-
[super touchesBegan:touches withEvent:event];
-
[[[touches anyObject] view] setBackgroundColor:[UIColor greenColor]];
-
}
-
-
@end