iOS控件之UIResponder

在iOS中UIResponder類是專門用來響應用戶的操作處理各種事件的续滋,包括觸摸事件(Touch Events)、運動事件(Motion Events)鲤嫡、遠程控制事件(Remote Control Events,如插入耳機調節(jié)音量觸發(fā)的事件)粟害。我們知道UIApplication茫陆、UIView仗岖、UIViewController這幾個類是直接繼承自UIResponder,UIWindow是直接繼承自UIView的一個特殊的View,所以這些類都可以響應事件。當然我們自定義的繼承自UIView的View以及自定義的繼承自UIViewController的控制器都可以響應事件览妖。iOS里面通常將這些能響應事件的對象稱之為響應者轧拄。

下面我們根據(jù)UIResponder.h頭文件來具體介紹關于響應事件的各個方面。

一. UIResponder類

這個部分我們主要介紹3種事件類型讽膏,即觸摸事件檩电,運動事件,遠程控制事件府树。當用戶觸發(fā)某一事件時俐末,UIKit會創(chuàng)建一個UIEvent事件對象(關于iOS事件對象可以參考這篇文章),事件對象會加入到一個FIFO先進先出的隊列中,UIApplication對象處理事件時奄侠,會從隊列頭部取出一個事件對象進行分發(fā)鹅搪。

1.觸摸事件
@interface UIResponder : NSObject
- (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;//系統(tǒng)事件干擾

這4個方法是觸摸事件的最原始處理的4個方法,分別代表觸摸屏幕遭铺,在屏幕上移動丽柿,離開屏幕以及受到系統(tǒng)優(yōu)先級別高的事件的干擾(比如來電話)取消觸摸事件。另外UIKit框架對于觸摸事件為我們提供了UIGestureRecognizer手勢識別這個類魂挂,基本上能滿足我們的大部分需求(可以參考這篇文章)甫题。這里介紹的是最底層的處理方法,比如可以用來實現(xiàn)繪圖類型的APP.
上面提到UIApplication對象從隊列中取出事件對象進行分發(fā)涂召,對于觸摸事件來說坠非,UIApplication會首先把事件交給keyWindow,Window會將事件交給UIGestureRecognizer處理,如果UIGestureRecognizer識別了傳遞過來的事件果正,則交給相對應的target去處理(關于iOS手勢事件可以參考這篇文章)炎码,事件不會再傳遞,如果UIGestureRecognizer并沒有識別傳遞過來的事件(可能是沒有視圖添加手勢秋泳,也可能手勢識別不成功)潦闲,事件會傳遞到視圖樹形結構,會分成尋找接受者和事件響應這兩個步驟迫皱。
1.在iOS視圖樹形結構中找到最終的接收者歉闰,也就是觸摸事件發(fā)生的那個最上層的View上,這一過程稱為hit-testing(測試命中),通過一層層的遍歷找到最終的命中視圖稱為hit-test view.
UIView中有兩個方法用來確定hit-test view.
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event;

hit-testing

這是一張我從apple官方文檔里面的截圖卓起,這里所有的顯示的View都是加載到主window上和敬,假設我們觸摸到屏幕上ViewD的區(qū)域,當我們沒有重載UIView的hitTest:withEvent:pointInside:withEvent:這兩個方法時,系統(tǒng)默認的處理如下:

  • keyWindow調用pointInside:withEvent:判斷觸摸點是否在其frame范圍內(nèi)戏阅,返回Yes昼弟,遍歷keyWindow的subView->ViewA.
  • ViewA調用pointInside:withEvent:判斷觸摸點是否在其frame范圍內(nèi),返回Yes奕筐,遍歷ViewA的subView->ViewB舱痘、ViewC(關于ViewB和ViewC先執(zhí)行哪個蚕键,是根據(jù)ViewA添加子控件的先后順序,總是先執(zhí)行后添加的subView.假設添加ViewB后添加ViewC)
  • ViewC調用pointInside:withEvent:判斷觸摸點是否在其frame范圍內(nèi)衰粹,返回Yes锣光,遍歷ViewC的subView->ViewD、ViewE
  • ViewE調用pointInside:withEvent:判斷觸摸點是否在其frame范圍內(nèi)铝耻,返回NO,ViewE的hitTest:withEvent:返回nil(如果是先執(zhí)行ViewB的情況誊爹,假設ViewB還有子節(jié)點subView,由于ViewB的pointInside:withEvent:返回NO,ViewB的hitTest:withEvent:`直接返回nil是不會再去遍歷ViewB的子節(jié)點的)
  • ViewD調用pointInside:withEvent:判斷觸摸點是否在其frame范圍內(nèi),返回Yes并且沒有子節(jié)點subView,ViewD的hitTest:withEvent:返回ViewD本身瓢捉,即為最終的hit-test view(不會再遍歷ViewB)iewB

需要注意的是:View.isHidden=YES View.alpha<=0.01 View.userInterfaceEnable=NO View.enable = NO(指繼承自UIControl的View)的這4種情況下,View的pointInside返回NO频丘,hitTest方法返回nil
默認UIImageViewuserInterfaceEnable=NO

2.找到了hit-test view,下一個步驟就是響應事件泡态。說明一下搂漠,對于觸摸事件來說,無論View是否處理事件某弦,即使是application通過[application beginIgnoringInteractionEvents]忽略了觸摸事件桐汤,上面hit-testing的過程依然存在,它只影響第二個步驟事件響應的過程靶壮。下面我們將介紹iOS響應者鏈條(Responder chain)

responder chain

這是我從官方文檔里面截取的一張關于響應者鏈條的截圖怔毛。我們先看上圖左邊的情況:標注為①的地方即為步驟1找到的hit-test view 它作為第一響應者來響應這個事件,如果該view沒有通過重寫或者封裝touch系列方法來處理該事件腾降,默認touch的實現(xiàn)就是調用父類的touch方法拣度,將事件傳遞下去。在這里由1->傳遞到它的父類2螃壤,2是控制器的根view,->傳遞到vc控制器->傳遞到窗口window->傳遞到application
再看上圖右邊的情況:標注為①的地方即為步驟1找到的hit-test view,同時它是控制器的根view并且還有父視圖抗果,事件傳遞到控制器->再傳遞到父視->傳遞到控制器,再傳遞到父視圖窗口->application奸晴。其實上圖左邊部分也可以理解為窗口是控制器根視圖的父視圖冤馏。如果整個響應者鏈條結束,都沒有對事件做處理蚁滋,那么該事件會被丟棄宿接。

總結一下響應者鏈條的傳遞過程是:由第一響應者(對于觸摸事件來說是hist-test view)開始向上傳遞赘淮。如果該視圖是控制器的根視圖辕录,先傳遞給控制器,再傳遞給父視圖梢卸,如果不是控制器的根視圖走诞,直接傳遞給父視圖。
只要在響應者的處理方法里面調用父類的方法蛤高,就可以讓多個視圖和控制器響應同一個事件蚣旱,響應者鏈條的根本目的是:共享事件碑幅,讓多個視圖和控制器可以對同一事件做不同的處理。

2.運動事件
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event NS_AVAILABLE_IOS(3_0);
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event NS_AVAILABLE_IOS(3_0);
- (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event NS_AVAILABLE_IOS(3_0);

這3個方法是運動事件的最原始處理的3個方法塞绿,這里處理的運動事件特指shake事件沟涨,手機搖動觸發(fā)手機內(nèi)部的加速度傳感器,可以用來實現(xiàn)搖一搖計算運動的步數(shù)等等應用异吻。類似于觸摸事件裹赴,這3個方法分別代表事件開始、事件結束和受到系統(tǒng)干擾取消事件诀浪。
加速度計accelerometer實際上是由三個加速度計組成棋返,分別用于測量X,Y和Z軸直線路徑速度的變化。結合所有三個加速度計可以檢測設備朝任何方向的運動和獲取設備的當前方向雷猪。對于shake事件來說睛竣,我們不關心3個方向上的運動,只作為一個事件對象來處理求摇。如果只是處理設備的大方向射沟,并不需要知道方向向量,如橫屏豎屏屏幕旋轉与境,我們可以使用UIDevice類(參考文章)躏惋。如果我們需要知道3個方向上的運動做更細致化的處理,如上了多少層樓等運動類型APP嚷辅,可以使用的核心運動框架訪問加速度計簿姨,陀螺儀和設備的運動類來做處理(Core Motion參考文章)
上面介紹的響應者鏈條對shake事件同樣適用,只不過簸搞,沒有hit-testing過程扁位,如果當前顯示的視圖界面沒有一個view聲明為第一響應者(調用becomeFirstResponder申明并且View需要重寫canBecomeFirstResponder方法返回YES,默認返回為NO)趁俊,默認當前視圖控制器為第一響應者域仇,并將事件沿著響應者鏈條傳遞,直到被處理寺擂。如果有視圖聲明為第一響應者暇务,就從該視圖開始傳遞事件直到被處理,如果該事件最終沒有被處理并且UIApplication的applicationSupportsShakeToEdit屬性為YES(默認就是YES),當鍵盤顯示的時候,系統(tǒng)會有一個是否撤銷正在輸入的警告怔软。就是微信和QQ上在輸入的時候搖動手機提示撤銷輸入的那種效果垦细。關于更多撤銷方面的操作參考NSUndoManager

3.遠程控制事件
- (void)remoteControlReceivedWithEvent:(UIEvent *)event NS_AVAILABLE_IOS(4_0);

遠程控制事件一般用于多媒體事件,播放暫停上一曲下一曲快進快退等挡逼。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末括改,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子家坎,更是在濱河造成了極大的恐慌嘱能,老刑警劉巖吝梅,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異惹骂,居然都是意外死亡苏携,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門对粪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來兜叨,“玉大人,你說我怎么就攤上這事衩侥」酰” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵茫死,是天一觀的道長跪但。 經(jīng)常有香客問我,道長峦萎,這世上最難降的妖魔是什么屡久? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮爱榔,結果婚禮上被环,老公的妹妹穿的比我還像新娘。我一直安慰自己详幽,他們只是感情好筛欢,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著唇聘,像睡著了一般版姑。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上迟郎,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天剥险,我揣著相機與錄音,去河邊找鬼宪肖。 笑死表制,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的控乾。 我是一名探鬼主播么介,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼阱持!你這毒婦竟也來了夭拌?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤衷咽,失蹤者是張志新(化名)和其女友劉穎鸽扁,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體镶骗,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡桶现,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了鼎姊。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片骡和。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖相寇,靈堂內(nèi)的尸體忽然破棺而出慰于,到底是詐尸還是另有隱情,我是刑警寧澤唤衫,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布婆赠,位于F島的核電站,受9級特大地震影響佳励,放射性物質發(fā)生泄漏休里。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一赃承、第九天 我趴在偏房一處隱蔽的房頂上張望妙黍。 院中可真熱鬧,春花似錦瞧剖、人聲如沸拭嫁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽噩凹。三九已至,卻和暖如春毡咏,著一層夾襖步出監(jiān)牢的瞬間驮宴,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工呕缭, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留堵泽,地道東北人。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓恢总,卻偏偏與公主長得像迎罗,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子片仿,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

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