ios中的事件处理、响应者链条以及第一响应者
在ios中,事件UIEvent類來表示,當(dāng)一個(gè)事件發(fā)生時(shí),系統(tǒng)會(huì)搜集的相關(guān)事件信息,創(chuàng)建一個(gè)UIEvent對象,最后將該事件轉(zhuǎn)發(fā)給應(yīng)用程序?qū)ο?UIApplication)。日常生活中,主要有三種類型的事件:觸摸事件,加速計(jì)事件以及遠(yuǎn)程遙控事件。下面是官方的一張圖片:
當(dāng)用戶通過以上方式觸發(fā)一個(gè)事件時(shí),會(huì)將相應(yīng)的事件對象添加到UIApplication的事件隊(duì)列中。UIApplication會(huì)循環(huán)的從隊(duì)列中拿出第一個(gè)事件來處理。首先將該事件分發(fā)給UIApplication 的主窗口對象(KeyWindow),然后由主窗口決定如何將事件交給最合適的響應(yīng)者(UIResponder)來處理取決于事件的類型。這里主要分兩種情況:
1、觸摸事件:UIApplication通過一個(gè)觸摸檢測來決定最合適來處理該事件的響應(yīng)者,一般情況下,這個(gè)響應(yīng)者是UIView對象。
2、加速計(jì)事件或遠(yuǎn)程遙控事件:UIApplication尋找UIWindow中的第一響應(yīng)者。找到第一響應(yīng)者(The First Responder)后,會(huì)將該事件對象派發(fā)給該響應(yīng)者以便處理。
下面分別討論上述兩種情況。
一、觸摸事件中的觸摸檢測
首先我們需要明確一個(gè)UIView對象能夠接收觸摸事件至少要保證以下三個(gè)條件:
1、userInteractionEnabled屬性為YES,該屬性表示允許控件同用戶交互。
2、Hidden屬性為NO。控件都看不見,還觸摸啥?
3、opacity屬性值0 ~0.01,不能透明過分了吧?
接下來的我們僅僅認(rèn)為該三個(gè)基本屬性都滿足要求,方便描述,當(dāng)然對于不滿足要求的自然是不能接收觸摸說事件的。
當(dāng)用戶手指觸摸到屏幕中的某一塊區(qū)域時(shí),UIWindow查找其子控件,然后通過調(diào)用所有自控件的方法:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
來通過指定的觸摸點(diǎn)獲取最合適的UIView來處理該觸摸事件。如何通過觸摸點(diǎn)獲取UIView原理其實(shí)非常簡單,只需要檢查該觸摸點(diǎn)是否在該控件所在的矩形區(qū)域內(nèi)就可以了,其實(shí)hitTest:withEvent方法內(nèi)部也是調(diào)用方法:
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
如果檢測到傳入的控件包含該觸摸點(diǎn)就返回YES。
當(dāng)通過hitTest方法檢測獲取到UIView后,會(huì)繼續(xù)對該UIView對象做一次檢測操作,也就是查找subViews的subViews做觸摸檢測。最終該方法會(huì)返回一個(gè)最合適的控件來響應(yīng)該事件。再次申明,如果之前的三個(gè)條件不滿足,那么該UIView以及其subViews都不可以響應(yīng)該觸摸事件。
找到響應(yīng)者后,響應(yīng)者可以重寫以下方法來對觸摸事件做響應(yīng):
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[super touchesBegan:touches withEvent:event];//讓下一個(gè)響應(yīng)者可以有機(jī)會(huì)繼續(xù)處理
}
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[super touchesBegan:touches withEvent:event];
}
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[super touchesBegan:touches withEvent:event];
}
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[super touchesBegan:touches withEvent:event];
}
在響應(yīng)方法內(nèi)部,我們也可以將這個(gè)觸摸事件繼續(xù)傳遞給父控件的對應(yīng)方法處理。然后父控件還可以將該事件繼續(xù)向上傳遞,直到傳遞給UIApplication對象。這一系列的響應(yīng)者對象就構(gòu)成了一個(gè)響應(yīng)者鏈條。
二、第一響應(yīng)者 (The First Responder)
什么是第一響應(yīng)者?簡單的講,第一響應(yīng)者是一個(gè)UIWindow對象接收到一個(gè)事件后,第一個(gè)來響應(yīng)的該事件的對象。注意:這個(gè)第一響應(yīng)者與之前討論的觸摸檢測到的第一個(gè)響應(yīng)的UIView并不是一個(gè)概念。第一響應(yīng)者一般情況下用于處理非觸摸事件(手機(jī)搖晃、耳機(jī)線控的遠(yuǎn)程空間)或非本窗口的觸摸事件(鍵盤觸摸事件),通俗點(diǎn)講其實(shí)就是管別人閑事的響應(yīng)者。在IOS中,當(dāng)然管閑事并不是所有控件都愿意的,這么說好像并不是很好理解,或著是站在編程人員的角度來看待這個(gè)問題,程序員負(fù)責(zé)告訴系統(tǒng)哪個(gè)對象可以成為第一響應(yīng)者(canBecomeFirstResponder),如果方法canBecomeFirstResponder返回YES,這個(gè)響應(yīng)者對象才有資格稱為第一響應(yīng)者。有資格并不代表一定可以成為第一響應(yīng)者,就好像符合要求并不一定能夠應(yīng)聘成功一樣,所以還差一個(gè)聘用環(huán)節(jié),那就是becomeFirstResponder正式成為第一響應(yīng)者。
請?jiān)徫业倪@些可能不太正常的想法,個(gè)人感覺上面的過程又有點(diǎn)像招聘流程,簡歷篩選就是canBecomeFirstResponder,becomeFirstResponder就是正式成為公司的員工。那么既然公司由聘用,那么對應(yīng)的就有辭退咯!對應(yīng)的方法就是canResignFirstResponder,這個(gè)表示第一響應(yīng)者是否可以被辭退,有些牛逼到逆天的員工并不是說辭退就辭退的,爭取有一天可以成為這個(gè)逆天員工,好吧,我又扯遠(yuǎn)了。還有一個(gè)方法就是resignFirstResponder,正式辭退該員工。
值得注意的是,一個(gè)UIWindow對象在某一時(shí)刻只能有一個(gè)響應(yīng)者對象可以成為第一響應(yīng)者。我們可以通過isFirstResponder來判斷某一個(gè)對象是否為第一響應(yīng)者。
大家先看下面的一個(gè)手機(jī)界面:
界面中包含兩個(gè)輸入框,一個(gè)切換第一響應(yīng)者的按鈕。我為兩個(gè)輸入框綁定了開始編輯事件,然后在事件中打印第一響應(yīng)者相關(guān)的信息,代碼如下:
NSString * NSStringFromBoolValue(BOOL boolValue){
return boolValue ? @"YES" : @"NO";
}
- (IBAction)editingBegin:(id)sender {
NSLog(@"top : 是否可以成為第一響應(yīng)者=>%@,是否第一響應(yīng)者=>%@",NSStringFromBoolValue(self.topInputView.canBecomeFirstResponder),NSStringFromBoolValue(self.topInputView.isFirstResponder));
NSLog(@"down : 是否可以成為第一響應(yīng)者=>%@,是否第一響應(yīng)者=>%@",NSStringFromBoolValue(self.downInputView.canBecomeFirstResponder),NSStringFromBoolValue(self.downInputView.isFirstResponder));
}
當(dāng)點(diǎn)擊第一個(gè)輸入框時(shí),打印如下:
我們可以看到兩個(gè)輸入框都可以成為第一響應(yīng)者。但是只有第一個(gè)輸入框才是第一響應(yīng)者。
當(dāng)點(diǎn)擊第二個(gè)輸入框時(shí),打印如下:
我們可以看到兩個(gè)輸入框都是第一響應(yīng)者,但是只有下面那個(gè)輸入框才是第一響應(yīng)者。
我們注意到,兩個(gè)輸入框的下方有一個(gè)按鈕用于切換第一響應(yīng)者。按鈕的響應(yīng)事件方法為:
- (IBAction)switch:(id)sender {
/**
* 1、如果頂部輸入框是第一響應(yīng)者就將第一響應(yīng)者切換為下方的輸入框
2、如果頂部輸入框不是第一響應(yīng)者,就將其設(shè)置為第一響應(yīng)者。
*/
if(self.topInputView.isFirstResponder){
[self.downInputView becomeFirstResponder];
}else{
[self.topInputView becomeFirstResponder];
}
NSLog(@"父控件中的第一響應(yīng)者:%@",[self.uiview findFirstResponder]);
NSLog(@"top : 是否可以成為第一響應(yīng)者=>%@,是否第一響應(yīng)者=>%@",NSStringFromBoolValue(self.topInputView.canBecomeFirstResponder),
NSStringFromBoolValue(self.topInputView.isFirstResponder));
NSLog(@"down : 是否可以成為第一響應(yīng)者=>%@,是否第一響應(yīng)者=>%@",NSStringFromBoolValue(self.downInputView.canBecomeFirstResponder),
NSStringFromBoolValue(self.downInputView.isFirstResponder));
}
在點(diǎn)擊了第一個(gè)輸入框后,我們點(diǎn)擊切換第一響應(yīng)者,屏幕打印如下:
我們可以看到,這個(gè)時(shí)候下方的輸入框成為第一響應(yīng)者,并且觸發(fā)了開始編輯事件,所以有兩次打印。切換后,焦點(diǎn)也切換到第二個(gè)輸入框中,我們通過鍵盤輸入時(shí),內(nèi)容會(huì)在第二個(gè)輸入框中出現(xiàn)。
時(shí)常有人碰到希望點(diǎn)擊空白區(qū)域,隱藏鍵盤的問題。例如輸入框輸入一半,覺得不想再編輯了,可以點(diǎn)擊空白區(qū)域來隱藏鍵盤,這個(gè)時(shí)候其實(shí)只需要告訴系統(tǒng),這個(gè)第一響應(yīng)者的位置我不想要了,我想辭職,這就夠了!下面是點(diǎn)擊第一個(gè)輸入框后,然后點(diǎn)擊空白區(qū)域,觸發(fā)touchesBegin事件,最后topInputView辭職的過程,通過這種方式就可以隱藏鍵盤了。代碼如下:
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[self.topInputView resignFirstResponder];
}
今天就先寫到這里,后續(xù)會(huì)補(bǔ)充一些細(xì)節(jié)性的東西。
總結(jié)
以上是生活随笔為你收集整理的ios中的事件处理、响应者链条以及第一响应者的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java实现bt文件下载、制作、解析、磁
- 下一篇: Win10:如何修改双网卡的优先级?