iOS---事件傳遞和響應(yīng)機(jī)制

iOS 中的事件

  • 觸摸事件
  • 加速計(jì)事件
  • 遠(yuǎn)程控制事件

響應(yīng)者對(duì)象(UIResponder)

只有繼承 UIResponder 的對(duì)象才能響應(yīng)事件

  • UIApplication
  • UIViewControl
  • UIView
UIResponder內(nèi)部提供了以下方法來(lái)處理事件觸摸事件
- (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;
加速計(jì)事件
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event;
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event;
- (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event;
遠(yuǎn)程控制事件
- (void)remoteControlReceivedWithEvent:(UIEvent *)event;



touchesBegan 這個(gè)方法拄查,如果是兩個(gè)手指同時(shí)觸摸外恕,這個(gè)方法只會(huì)調(diào)用一次擎宝,方法中包含兩個(gè) UITouch 的對(duì)象 炒考, 如果是一前一后的觸摸武花,就會(huì)調(diào)用兩次這個(gè)方法球涛,方法中包含一個(gè) UITouch 對(duì)象

注意:
1.想要處理 UIView 的觸摸事件,就要繼承 UIView ,然后重寫(xiě) UIView 的觸摸事件方法
2.UIViewControl 在其觸摸事件方法中就可以處理

UITouch 對(duì)象

UITouch 的屬性

觸摸產(chǎn)生時(shí)所處的窗口
@property(nonatomic,readonly,retain) UIWindow *window;

觸摸產(chǎn)生時(shí)所處的視圖
@property(nonatomic,readonly,retain) UIView *view
;

短時(shí)間內(nèi)點(diǎn)按屏幕的次數(shù)褥紫,可以根據(jù)tapCount判斷單擊、雙擊或更多的點(diǎn)擊
@property(nonatomic,readonly) NSUInteger tapCount;

記錄了觸摸事件產(chǎn)生或變化時(shí)的時(shí)間瞪慧,單位是秒@property(nonatomic,readonly) NSTimeInterval timestamp;

當(dāng)前觸摸事件所處的狀態(tài)
@property(nonatomic,readonly) UITouchPhase phase;


UITouch 的方法

(CGPoint)locationInView:(UIView *)view;
// 返回值表示觸摸在view上的位置
// 這里返回的位置是針對(duì)view的坐標(biāo)系的(以view的左上角為原點(diǎn)(0, 0))
// 調(diào)用時(shí)傳入的view參數(shù)為nil的話(huà)髓考,返回的是觸摸點(diǎn)在UIWindow的位置

(CGPoint)previousLocationInView:(UIView *)view;
// 該方法記錄了前一個(gè)觸摸點(diǎn)的位置


  • 當(dāng)用戶(hù)用一根手指觸摸屏膜時(shí),就會(huì)創(chuàng)建一個(gè) UITouch 對(duì)象
  • 一根手指對(duì)應(yīng)一個(gè) UITouch 對(duì)象
  • 當(dāng)手指移動(dòng)時(shí)弃酌,系統(tǒng)會(huì)更新 UITouch 對(duì)象的位置等信息
  • 當(dāng)手指離開(kāi)時(shí)氨菇,相應(yīng)的 UITouch 對(duì)象就會(huì)銷(xiāo)毀


IOS 的事件產(chǎn)生和傳遞

事件產(chǎn)生

  1. 發(fā)生觸摸事件后,系統(tǒng)會(huì)把事件加入到由 UIApplication 管理的事件隊(duì)列
  2. UIApplication 會(huì)從事件隊(duì)列中取出最前面的事件妓湘,并將事件分發(fā)下去處理查蓉,通常是先發(fā)給應(yīng)用程序的KeyWindow
  3. KeyWindow 會(huì)找其視圖層結(jié)構(gòu)找到一個(gè)最適合的View 來(lái)處理事件


事件的傳遞

UIApplication -> Window -> 尋找最合適的View

如何尋找最合適的 View?

  1. 首先判斷 KeyWind 是否能接受觸摸事件
  2. 判斷觸摸點(diǎn)是否在窗口身上
  3. 從后往前遍歷子控件榜贴,重復(fù)上述兩個(gè)步驟
  4. 遍歷到最合適的 View
  5. 如果沒(méi)有最合適的 View 豌研,那么就由 KeyWind 處理



??注意點(diǎn):

  1. 透明度 < 0.01 的控件,是不能接受觸摸事件
  2. 如果父控件不能接收事件唬党,那么子控件也不能
  3. 不允許交互:userInteractionEnabled = NO
  4. 不管控件能否接收觸摸事件鹃共,主要有觸摸,就會(huì)產(chǎn)生事件驶拱,只是這個(gè)事件會(huì)不會(huì)被處理

如何尋找最合適的 View 的底層揭秘

hitTest:withEvent:
pointInside

hitTest:withEvent:方法

  • 調(diào)用:主要有事件傳遞給一個(gè)控件霜浴,控件就會(huì)調(diào)用該方法
  • 作用:找到最合適的 View ,并返回該 View

攔截事件的處理

  • 正因?yàn)閔itTest:withEvent:方法可以返回最合適的view屯烦,所以可以通過(guò)重寫(xiě)hitTest:withEvent:方法坷随,返回指定的view作為最合適的view。
  • 不管點(diǎn)擊哪里驻龟,最合適的view都是hitTest:withEvent:方法中返回的那個(gè)view温眉。
  • 通過(guò)重寫(xiě)hitTest:withEvent:,就可以攔截事件的傳遞過(guò)程翁狐,想讓誰(shuí)處理事件誰(shuí)就處理事件类溢。


如果想指定 View 作為最適合的 View,有以下兩種方法

  1. 在父控件的hitTest:withEvent:返回子控件
  2. 在子控件的hitTest:withEvent:返回自

??注意點(diǎn):
推薦方法一露懒,不推薦方法二的原因是有可能不能成功返回子控件闯冷,如果觸摸點(diǎn)不在子控件,而在另一個(gè)子控件懈词,那么沒(méi)辦法返回該子控件蛇耀。

事件傳遞的正真順序

  1. 產(chǎn)生觸摸事件
  2. UIApplication 事件隊(duì)列
  3. [UIWindow hitTest:withEvent:];
  4. 返回更合適的 View
  5. [子控件 hitTest:withEvent:];
  6. 返回更合適的 View


#import "WSWindow.h"
@implementation WSWindow
// 什么時(shí)候調(diào)用:只要事件一傳遞給一個(gè)控件,那么這個(gè)控件就會(huì)調(diào)用自己的這個(gè)方法
// 作用:尋找并返回最合適的view
// UIApplication -> [UIWindow hitTest:withEvent:]尋找最合適的view告訴系統(tǒng)
// point:當(dāng)前手指觸摸的點(diǎn)
// point:是方法調(diào)用者坐標(biāo)系上的點(diǎn)
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
    // 1.判斷下窗口能否接收事件
     if (self.userInteractionEnabled == NO || self.hidden == YES ||  self.alpha = 0; i--)     { 
    // 獲取子控件
    UIView *childView = self.subviews[i]; 
    // 坐標(biāo)系的轉(zhuǎn)換,把窗口上的點(diǎn)轉(zhuǎn)換為子控件上的點(diǎn) 
    // 把自己控件上的點(diǎn)轉(zhuǎn)換成子控件上的點(diǎn) 
    CGPoint childP = [self convertPoint:point toView:childView]; 
    UIView *fitView = [childView hitTest:childP withEvent:event]; 
    if (fitView) {
    // 如果能找到最合適的view 
    return fitView; 
    }
    } 
    // 4.沒(méi)有找到更合適的view坎弯,也就是沒(méi)有比自己更合適的view 
    return self;
    }
    // 作用:判斷下傳入過(guò)來(lái)的點(diǎn)在不在方法調(diào)用者的坐標(biāo)系上
    // point:是方法調(diào)用者坐標(biāo)系上的點(diǎn)
    //- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
    //{
    // return NO;
    //}
    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ 
    NSLog(@"%s",__func__);
    }
    @end


pointInside:withEvent:方法

pointInside:withEvent:方法判斷點(diǎn)在不在當(dāng)前view上(方法調(diào)用者的坐標(biāo)系上)如果返回YES纺涤,代表點(diǎn)在方法調(diào)用者的坐標(biāo)系上;返回NO代表點(diǎn)不在方法調(diào)用者的坐標(biāo)系上译暂,那么方法調(diào)用者也就不能處理事件


事件響應(yīng)

響應(yīng)者鏈條:是由多個(gè)響應(yīng)者連接起來(lái)的鏈條。

事件響應(yīng)的流程

1.找到最合適的 View 的時(shí)候撩炊,就會(huì)調(diào)用自己 touchs 的方法處理事件外永。
2.touches默認(rèn)做法是把事件順著響應(yīng)者鏈條向上拋.
2.1 首先看 initail View 能不能處理事件
2.2 不能就把事件傳遞給父控件,繼續(xù)判斷
2.3 沒(méi)有一個(gè)控件能處理就拋給 Window
2.4 Window 處理不了拧咳,最后拋給 UIApplication
2.5 UIApplication 處理不了伯顶,把事件丟棄。
如果以上一個(gè)響應(yīng)者重寫(xiě)了 touches 方法骆膝,就能處理事件

如何做到一個(gè)事件多個(gè)對(duì)象處理
因?yàn)橄到y(tǒng)默認(rèn)做法是把事件上拋給父控件祭衩,所以可以通過(guò)重寫(xiě)自己的touches方法和父控件的touches方法來(lái)達(dá)到一個(gè)事件多個(gè)對(duì)象處理的目的。

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ 
// 1.自己先處理事件...
NSLog(@"do somthing...");
// 2.再調(diào)用系統(tǒng)的默認(rèn)做法谭网,再把事件交給上一個(gè)響應(yīng)者處理
[super touchesBegan:touches withEvent:event]; 
}


事件傳遞和事件響應(yīng)的過(guò)程總結(jié)

  1. 事件產(chǎn)生汪厨,添加到 UIApplication 的事件隊(duì)列
  2. UIApplication 把事件隊(duì)列最前的事件傳遞給UIWindow
  3. UIWindow 調(diào)用 hitTest:withEvent: 返回一個(gè)合適的 View
  4. View 繼續(xù)調(diào)用 hitTest:withEvent: 找到一個(gè)最適合的 View
  5. 事件傳遞給最適合的 View 就開(kāi)始響應(yīng)事件
  6. 如果最適合 View 不能處理事件,把事件拋給父控件
  7. 一直都沒(méi)有控件可以處理事件愉择,就把事件拋給 UIWindow
  8. UIWindow 處理不了就拋給 UIApplication
  9. 最后處理不了就把事件拋棄劫乱。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市锥涕,隨后出現(xiàn)的幾起案子衷戈,更是在濱河造成了極大的恐慌,老刑警劉巖层坠,帶你破解...
    沈念sama閱讀 221,635評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件殖妇,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡破花,警方通過(guò)查閱死者的電腦和手機(jī)谦趣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)座每,“玉大人前鹅,你說(shuō)我怎么就攤上這事∏褪幔” “怎么了舰绘?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,083評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)葱椭。 經(jīng)常有香客問(wèn)我捂寿,道長(zhǎng),這世上最難降的妖魔是什么孵运? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,640評(píng)論 1 296
  • 正文 為了忘掉前任秦陋,我火速辦了婚禮,結(jié)果婚禮上治笨,老公的妹妹穿的比我還像新娘踱侣。我一直安慰自己粪小,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布抡句。 她就那樣靜靜地躺著,像睡著了一般杠愧。 火紅的嫁衣襯著肌膚如雪待榔。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,262評(píng)論 1 308
  • 那天流济,我揣著相機(jī)與錄音锐锣,去河邊找鬼。 笑死绳瘟,一個(gè)胖子當(dāng)著我的面吹牛雕憔,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播糖声,決...
    沈念sama閱讀 40,833評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼斤彼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了蘸泻?” 一聲冷哼從身側(cè)響起琉苇,我...
    開(kāi)封第一講書(shū)人閱讀 39,736評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎悦施,沒(méi)想到半個(gè)月后并扇,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,280評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡抡诞,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評(píng)論 3 340
  • 正文 我和宋清朗相戀三年穷蛹,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片昼汗。...
    茶點(diǎn)故事閱讀 40,503評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡肴熏,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出乔遮,到底是詐尸還是另有隱情扮超,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布蹋肮,位于F島的核電站出刷,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏坯辩。R本人自食惡果不足惜馁龟,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望漆魔。 院中可真熱鬧坷檩,春花似錦却音、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,340評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至句灌,卻和暖如春夷陋,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背胰锌。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,460評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工骗绕, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人资昧。 一個(gè)月前我還...
    沈念sama閱讀 48,909評(píng)論 3 376
  • 正文 我出身青樓酬土,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親格带。 傳聞我的和親對(duì)象是個(gè)殘疾皇子撤缴,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容