第一響應(yīng)者 (The First Responder)
什么是第一響應(yīng)者导披?簡單的講贰您,第一響應(yīng)者是一個UIWindow對象接收到一個事件后刁赖,第一個來響應(yīng)的該事件的對象逛钻。注意:這個第一響應(yīng)者與觸摸檢測到的第一個響應(yīng)的UIView并不是一個概念贬蛙。第一響應(yīng)者一般情況下用于處理非觸摸事件(手機搖晃雨女、耳機線控的遠程空間)或非本窗口的觸摸事件(鍵盤觸摸事件),通俗點講其實就是管別人閑事的響應(yīng)者阳准。在IOS中氛堕,當(dāng)然管閑事并不是所有控件都愿意的,這么說好像并不是很好理解野蝇,或著是站在編程人員的角度來看待這個問題讼稚,程序員負(fù)責(zé)告訴系統(tǒng)哪個對象可以成為第一響應(yīng)者(canBecomeFirstResponder),如果方法canBecomeFirstResponder返回YES绕沈,這個響應(yīng)者對象才有資格稱為第一響應(yīng)者锐想。有資格并不代表一定可以成為第一響應(yīng)者,還要becomeFirstResponder正式成為第一響應(yīng)者乍狐。同時也有對應(yīng)的canResignFirstResponder和resignFirstResponder是否可以解除第一響應(yīng)者赠摇。
值得注意的是,一個UIWindow對象在某一時刻只能有一個響應(yīng)者對象可以成為第一響應(yīng)者澜躺。我們可以通過isFirstResponder來判斷某一個對象是否為第一響應(yīng)者蝉稳。
我們可以用蘋果的私有方法來獲得第一響應(yīng)者
UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow];
UIView *firstResponder = [keyWindow performSelector:@selector(firstResponder)];
下面我們用搖一搖事件來說一說第一響應(yīng)者響應(yīng)事件流程
代碼
在視圖控制器中
- (void)viewDidLoad {
[super viewDidLoad];
HeaderView *headerView = [[HeaderView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 200)];
headerView.backgroundColor = [UIColor redColor];
[self.view addSubview:headerView];
}
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event {
NSLog(@"~~~~~~~~~motionBegan");
UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow];
UIView *firstResponder = [keyWindow performSelector:@selector(firstResponder)];
}
在晃動手機后,我們可以看到斷點打印的firstResponder為空掘鄙。
此時的調(diào)用流程為:
這個時候卻響應(yīng)了搖一搖事件耘戚,為什么呢?系統(tǒng)這個時候會找到當(dāng)前視圖操漠,即self.view收津,如果self.view如果不能響應(yīng),則會依據(jù)響應(yīng)鏈找到能夠響應(yīng)該事件的響應(yīng)者浊伙,在這里self.view.nextResponse就是ViewController撞秋,所以會響應(yīng)這個搖一搖事件。
然后我們在HerderView里面加入一些代碼
@interface HeaderView ()
@end
@implementation HeaderView
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// [self becomeFirstResponder];
}
return self;
}
//- (BOOL)canBecomeFirstResponder {
//
// return YES;
//}
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event {
UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow];
UIView *firstResponder = [keyWindow performSelector:@selector(firstResponder)];
}
然后搖一搖事件嚣鄙,發(fā)現(xiàn)HeaderView并沒有響應(yīng)搖一搖事件吻贿,而是還是由ViewController來響應(yīng)的。
然后我們將注釋的部分打開哑子,注意這里讓HeaderView成為響應(yīng)者舅列,注意一定要寫canBecomeFirstResponder函數(shù)肌割,不然HeaderView就算寫了becomeFirstResponder也不能成為第一響應(yīng)者,然后搖一搖可以看到HeaderView的motionBegan調(diào)用了帐要,HeaderView響應(yīng)了這個事件把敞,同時這個時候的第一響應(yīng)者為HeaderView,函數(shù)調(diào)用流程為:
然后將HeaderView的motionBegan函數(shù)注釋掉,搖一搖,然后發(fā)現(xiàn)ViewController的motionBegan函數(shù)調(diào)用了娱颊,這個時候事件由第一響應(yīng)者HeaderView傳遞給了ViewController,事件的調(diào)用流程:
在這里我們看到調(diào)用流程里有forwardMethodIntld函數(shù)耽装,推測他就是在響應(yīng)鏈中的響應(yīng)者無法響應(yīng)事件時進行轉(zhuǎn)發(fā)到下一個響應(yīng)者的函數(shù),
在這里我們可以分析一下
- 在HeaderView沒有成為第一響應(yīng)者简烤,搖一搖的時候有一個forwardMethodIntld函數(shù)剂邮,這個時候由self.view進行開始響應(yīng),但是self.view并不能響應(yīng)該事件横侦,然后向上傳遞給ViewController挥萌,而ViewController能夠響應(yīng)事件,然后事件到這里被處理枉侧,不會繼續(xù)傳遞下去引瀑,所以進行了一次轉(zhuǎn)發(fā)。
- 在HeaderView成為第一響應(yīng)者榨馁,然后也有搖一搖事件motionBegan響應(yīng)的時候憨栽,事件直接找到第一響應(yīng)者,發(fā)現(xiàn)第一響應(yīng)者能夠響應(yīng)該事件翼虫,事件到此結(jié)束屑柔,所以這里沒有forwardMethodIntld函數(shù),也就沒有進行事件的轉(zhuǎn)發(fā)珍剑。
- 在HeaderView成為第一響應(yīng)者掸宛,然后沒有響應(yīng)搖一搖事件motionBegan的時候,ViewController響應(yīng)了該事件招拙,發(fā)現(xiàn)這里有兩個forwardMethodIntld函數(shù)唧瘾,原因是找到第一響應(yīng)者HeaderView的時候,發(fā)現(xiàn)并不能響應(yīng)該事件别凤,然后由響應(yīng)鏈的上一層self.view響應(yīng)饰序,self.view也不能響應(yīng)該事件,然后找到self.view的上一層规哪,即ViewController求豫,然后響應(yīng)該事件,所以這里進行了兩次事件的轉(zhuǎn)發(fā)。