觸摸粹懒、事件和響應(yīng)者那些事

這篇文章是我在閱讀相關(guān)蘋(píng)果官方文檔后總結(jié)整理出來(lái)的一些平持馗叮可能不太注意到,但是又比較有用的知識(shí)點(diǎn)凫乖。如有錯(cuò)誤确垫,歡迎指出。

事件傳遞

事件本質(zhì)

什么是事件帽芽?官方文檔的解釋是:

Events in iOS represent fingers touching views of an application or the user shaking the device. One or more fingers touch down on one or more views, perhaps move around, and then lift from the view or views. As this is happening, iPhone’s Multi-Touch system registers these touches as events and sends them to the currently active application for processing

當(dāng)觸摸事件發(fā)生時(shí)删掀,系統(tǒng)會(huì)把觸摸注冊(cè)為一個(gè)事件(Event),傳遞給系統(tǒng)處理导街。一個(gè)完整的手勢(shì)過(guò)程披泪,是從第一根手指觸碰到屏幕開(kāi)始,到最后一根手指離開(kāi)屏幕為止搬瑰。

當(dāng)然款票,手機(jī)的搖晃也要算是Event(它屬于UIEventType里的motion),不過(guò)那是另一回事了泽论,我們后邊會(huì)再說(shuō)艾少。

屏幕上的每一個(gè)觸摸點(diǎn)用UITouch來(lái)表示。在整個(gè)手勢(shì)過(guò)程中翼悴,每一個(gè)UITouch對(duì)象會(huì)被系統(tǒng)持有缚够,但是它的狀態(tài)是可變的,分別要經(jīng)歷touchesBegan, touchesMovedtouchesEnded 三個(gè)狀態(tài)抄瓦。
當(dāng)然潮瓶,個(gè)別的時(shí)候,一個(gè)UITouch會(huì)經(jīng)歷第四個(gè)狀態(tài):touchesCanceled钙姊。一個(gè)事件被取消通常是由于一個(gè)外部事件(例如來(lái)電)的產(chǎn)生毯辅,讓系統(tǒng)終止了本次touch事件。

UITouch

每一個(gè)UITouch對(duì)象表示一根手指對(duì)屏幕的觸摸煞额,包含位置思恐、大小、移動(dòng)狀況以及觸摸的力度(力度僅在支持3Dtouch或者Apple Pencil的設(shè)備上管用)膊毁。
UITouch類(lèi)有以下我覺(jué)得比較重要的屬性和方法:

  • locationInView: 表示觸摸點(diǎn)在給定視圖對(duì)應(yīng)坐標(biāo)系下的位置胀莹。如果view參數(shù)傳nil,那么給出的是touch在window對(duì)于坐標(biāo)系中的位置婚温。
  • previousLocation(in:) 表示前一次該touch在給定視圖中的方位描焰。
  • view 表示touch對(duì)象被“傳送”到的view。注意,這個(gè)view并不一定是touch對(duì)象本身所在的view(即荆秦,不一定是用戶(hù)手指點(diǎn)中的view)篱竭。例如,當(dāng)一個(gè)gestureRecognizer接收到一個(gè)觸摸事件時(shí)步绸,view為nil掺逼,因?yàn)闆](méi)有view在接收這個(gè)觸摸。
  • preciseLocation(in:) 這個(gè)表示一個(gè)touch在給定視圖中的精確方位瓤介。注意吕喘,不要把返回的CGPoint用于hitTest。有的時(shí)候hitTest返回值顯示touch在給定view中刑桑,但是preciseLocation方法返回的值卻表明touch不在view中氯质。
  • phase:表示UITouch對(duì)象的幾個(gè)階段。按照順序依次變化:began, moved, stationary, ended/canceled

響應(yīng)者鏈

UIResponder

UIResponder是一個(gè)抽象類(lèi)漾月,被蘋(píng)果稱(chēng)為事件處理的“主心骨”病梢。具體到事件發(fā)生時(shí),繼承自UIResponder的對(duì)象主要有兩個(gè)方面的職責(zé):

  1. 通過(guò)覆寫(xiě)四個(gè)關(guān)于touches的方法梁肿,攔截并處理事件(如果該對(duì)象需要響應(yīng)事件的話)蜓陌。
  2. 將事件順著響應(yīng)者鏈向上傳遞(如果該對(duì)象不需要響應(yīng)該事件的話)。

另外吩蔑,inputView也可以作為事件的響應(yīng)(在這里我把它理解為“輸入響應(yīng)”)钮热。例如,當(dāng)我們點(diǎn)擊一個(gè)textView烛芬,這個(gè)view會(huì)變成First Responder隧期,并顯示它的 inputView。關(guān)于inputView和firstResponder我們會(huì)另起一篇文章來(lái)詳細(xì)描述它赘娄。

接下來(lái)我們看幾個(gè)UIResponder當(dāng)中重要的屬性和方法:

  • nextResponder

顧名思義仆潮,它表示響應(yīng)者鏈中的下一個(gè)響應(yīng)者。值得注意的是遣臼,UIResponder本身并不存儲(chǔ)或者預(yù)先設(shè)置任何值給nextResponder性置,該屬性默認(rèn)設(shè)置為nil。到底誰(shuí)是nextResponder還需要繼承自它的類(lèi)自己來(lái)覆寫(xiě)揍堰。例如鹏浅,一個(gè)View的nextResponder可能是它的superView(如果有的話),也可能是viewController(如果該view就是根視圖)屏歹。一個(gè)ViewController的nextResponder可能是UIWindow(如果其根視圖是這個(gè)window的root view的話)隐砸,也可能是另一個(gè)viewControllerB(如果viewController嵌套在viewControllerB中顯示的話)。UIWindow的nextResponder就是UIApplication蝙眶。UIAPPlication的nextResponder就是appDelegate(當(dāng)且僅當(dāng)這個(gè)delegate是UIResponder的實(shí)例而非一個(gè)view季希,viewController,或者app object本身)。

  • isFirstResponder
    字面意思胖眷,“是否是第一響應(yīng)者”武通。關(guān)于這個(gè)第一響應(yīng)者,目前我暫時(shí)無(wú)法獲得一個(gè)準(zhǔn)確的定義珊搀,但基本可以肯定的是,此處的第一響應(yīng)者和事件傳遞過(guò)程中尋找的“最合適的響應(yīng)者”并非同一回事尾菇。因此境析,這部分暫時(shí)略過(guò),等找到準(zhǔn)確定義之后再發(fā)文說(shuō)明派诬。

  • canBecomeFirstResponder
    字面意思劳淆,表示一個(gè)對(duì)象是否能夠成為第一響應(yīng)者。UIKit會(huì)把某些事件默赂,例如motion event沛鸵,分發(fā)給“第一響應(yīng)者”。默認(rèn)返回No缆八。

  • becomeFirstResponder
    讓消息接收者成為第一響應(yīng)者曲掰。這個(gè)方法相信咱們?cè)陂_(kāi)發(fā)過(guò)程中都快要寫(xiě)爛了——遇到textField,調(diào)用此方法奈辰,讓系統(tǒng)彈出鍵盤(pán)栏妖。文檔指出,對(duì)某個(gè)對(duì)象調(diào)用該方法后奖恰,并不能保證該對(duì)象一定能夠成為firstResponder吊趾,因?yàn)椋琔IKit會(huì)首先對(duì)當(dāng)前的firstResponder發(fā)送resignFirstResponder消息瑟啃,然而后者可能會(huì)失斅鄯骸(例如自定義的對(duì)象重寫(xiě)了resignFirstResponder,通過(guò)return NO拒絕退出第一響應(yīng)者狀態(tài))蛹屿。
    如果當(dāng)前firstResponder成功地resign了屁奏,UIKit還要調(diào)用當(dāng)前對(duì)象canBecomeFirstResponder方法,而如上文所言蜡峰,后者默認(rèn)返回NO了袁。
    再如果,canBecomeFirstResponder返回了YES——那么該對(duì)象將成為第一響應(yīng)者湿颅。至此载绿,所有發(fā)送給第一響應(yīng)者的事件都被指派給這個(gè)對(duì)象,且系統(tǒng)將會(huì)試圖展示該對(duì)象的inputView油航。

    • (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *) event
      默認(rèn)情況下崭庸,不管你用了幾根手指去點(diǎn)這個(gè)view,touches集合中僅包含一個(gè)UITouch 對(duì)象。如果你希望接收到多個(gè)手指的觸控怕享,記得調(diào)用view.isMultipleTouchEnabled=true执赡。
      有兩個(gè)注意點(diǎn):
  1. 該方法的默認(rèn)實(shí)現(xiàn)是將事件沿響應(yīng)者鏈向上傳遞。因此函筋,如果你想要覆寫(xiě)該方法沙合,要確保調(diào)用了super的touchesBegan方法以傳遞任何你自身不處理的事件
  2. 如果你覆寫(xiě)該方法的時(shí)候沒(méi)有調(diào)用super跌帐,那么你需要在你的自定義類(lèi)中同時(shí)調(diào)用其他touches相關(guān)的方法首懈,哪怕在這些方法中什么都不做。

事件攔截

UIEvent

  1. 它用于表示用戶(hù)和APP的一個(gè)交互的對(duì)象谨敛。
  2. 永遠(yuǎn)不要retain一個(gè)UIEvent對(duì)象究履,或者是其內(nèi)部的屬性。如果的確需要retain某個(gè)UIEvent自帶的屬性脸狸,應(yīng)對(duì)后者使用copy操作最仑。
  3. 包含四個(gè)type: touches, motion, remote-Control, presses。motion是由UIKit觸發(fā)的(要和Core Motion Framework的motion event區(qū)分開(kāi)來(lái)炊甲。)remote-Control指的是用戶(hù)通過(guò)外部配件(比如耳機(jī)泥彤、遙控器)對(duì)設(shè)備發(fā)出的操作指令。Press事件指的是用戶(hù)通過(guò)游戲控制器蜜葱、遙控器等的實(shí)體按鍵來(lái)和設(shè)備進(jìn)行的交互行為全景。所有的這些可以通過(guò)UIEvent的type和subtype屬性來(lái)加以判斷。

接下來(lái)介紹一些重要的屬性和方法:

func touches(for view: UIView) -> Set<UITouch>?

返回該事件中牵囤,屬于指定view上的所有touch爸黄。

func touches(for window: UIWindow) -> Set<UITouch>?

和上面類(lèi)似。

func touches(for gesture: UIGestureRecognizer) -> Set<UITouch>?

返回該手勢(shì)識(shí)別器所接收到的所有UITouch對(duì)象揭鳞。

func coalescedTouches(for touch: UITouch) -> [UITouch]?

這個(gè)方法是在iOS9之后提出的炕贵,它利用了一種叫做“觸摸合并”的技術(shù)。由于系統(tǒng)對(duì)touch的采樣在touchesMoved方法中進(jìn)行野崇,而后者的調(diào)用頻率最高也才60次/秒(如果主線程有其他高耗時(shí)的操作称开,該方法的調(diào)用頻率甚至更低),這樣乓梨,就不可避免地會(huì)出現(xiàn)“漏點(diǎn)”的情況鳖轰。而在新的iPad Pro2代上,界面刷新率達(dá)到了120Hz(使用Apple pencil時(shí)刷新率一度飆升至200Hz)扶镀,因此蕴侣,使用傳統(tǒng)的touchesMoved必然會(huì)造成一個(gè)奇觀:用戶(hù)的手指在前面劃線片酝,畫(huà)出來(lái)的線在后邊追趕用戶(hù)的手指……抑或是用戶(hù)命名畫(huà)了一條弧線呻引,得到的卻是一條“折線”……
基于此,蘋(píng)果提出了觸摸擬合技術(shù)虐急,它可以讓你獲取到所有在兩次touchesMoved調(diào)用之間的UITouch對(duì)象。
使用方法如下:
''override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?)"
''{
'' if let coalescedTouches = event.coalescedTouchesForTouch(touch) {
'' print("coalescedTouches:", coalescedTouches.count)
''
'' for coalescedTouch in coalescedTouches
'' {
'' //Additional operations
'' }
''
'' }
'' }

`func predictedTouches(for touch: UITouch) -> [UITouch]?

同樣是在iOS9以后狞膘,同樣是為了減少延遲揩懒,蘋(píng)果還推出了觸摸預(yù)測(cè)技術(shù),它根據(jù)先前觸摸的點(diǎn)挽封,使用一套非常精密的算法已球,來(lái)大致預(yù)測(cè)下一個(gè)被觸摸的點(diǎn)所在的坐標(biāo)。因此场仲,開(kāi)發(fā)者可以使用預(yù)測(cè)出來(lái)的點(diǎn)來(lái)提前做好UI更新的準(zhǔn)備和悦。
使用方法和func coalescedTouches(for touch: UITouch) -> [UITouch]?基本一樣,在此不多贅述渠缕。

UITouch,UIEvent褒繁,UIResponder亦鳞,UIGestureRecognizer的區(qū)別與聯(lián)系

這一部分就算作是本文的小結(jié)了。我們來(lái)梳理一下四者的區(qū)別和聯(lián)系:

  • UITouch:表示一根手指在屏幕上的觸摸棒坏、移動(dòng)燕差,其生命周期從手指觸摸屏幕時(shí)開(kāi)始,到手指離開(kāi)屏幕(或者被cancel)為止坝冕。
  • UIGestureRecognizer: 手勢(shì)識(shí)別器徒探。一個(gè)手勢(shì)可能要一根or多根手指來(lái)完成,因此一個(gè)手勢(shì)包含多個(gè)UITouch對(duì)象喂窟。
  • UIEvent:表示“事件”测暗,有三個(gè)大類(lèi):觸摸事件、動(dòng)作事件磨澡、遠(yuǎn)程事件碗啄。一個(gè)觸摸事件(touch event)包含了一個(gè)或多個(gè)與該事件有關(guān)的觸摸對(duì)象,后者用UITouch對(duì)象來(lái)表示稳摄。
  • UIResponder:響應(yīng)事件的一個(gè)“抽象類(lèi)”稚字,需要響應(yīng)事件的類(lèi)必須繼承自它。多個(gè)響應(yīng)者組成響應(yīng)者鏈厦酬。
    另外胆描,一個(gè)完整的觸摸序列,是從第一根手指按下開(kāi)始仗阅,到最后一根手指一開(kāi)屏幕為止昌讲。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市霹菊,隨后出現(xiàn)的幾起案子剧蚣,更是在濱河造成了極大的恐慌支竹,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鸠按,死亡現(xiàn)場(chǎng)離奇詭異礼搁,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)目尖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)馒吴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人瑟曲,你說(shuō)我怎么就攤上這事饮戳。” “怎么了洞拨?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵扯罐,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我烦衣,道長(zhǎng)歹河,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任花吟,我火速辦了婚禮秸歧,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘衅澈。我一直安慰自己键菱,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開(kāi)白布今布。 她就那樣靜靜地躺著经备,像睡著了一般。 火紅的嫁衣襯著肌膚如雪险耀。 梳的紋絲不亂的頭發(fā)上弄喘,一...
    開(kāi)封第一講書(shū)人閱讀 49,046評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音甩牺,去河邊找鬼蘑志。 笑死,一個(gè)胖子當(dāng)著我的面吹牛贬派,可吹牛的內(nèi)容都是我干的急但。 我是一名探鬼主播,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼搞乏,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼波桩!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起请敦,我...
    開(kāi)封第一講書(shū)人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤镐躲,失蹤者是張志新(化名)和其女友劉穎储玫,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體萤皂,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡撒穷,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了裆熙。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片端礼。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖入录,靈堂內(nèi)的尸體忽然破棺而出蛤奥,到底是詐尸還是另有隱情,我是刑警寧澤僚稿,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布凡桥,位于F島的核電站,受9級(jí)特大地震影響蚀同,放射性物質(zhì)發(fā)生泄漏唬血。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一唤崭、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧脖律,春花似錦谢肾、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至微姊,卻和暖如春酸茴,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背兢交。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工薪捍, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人配喳。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓酪穿,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親晴裹。 傳聞我的和親對(duì)象是個(gè)殘疾皇子被济,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345

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

  • 好奇觸摸事件是如何從屏幕轉(zhuǎn)移到APP內(nèi)的?困惑于Cell怎么突然不能點(diǎn)擊了涧团?糾結(jié)于如何實(shí)現(xiàn)這個(gè)奇葩響應(yīng)需求只磷?亦或是...
    Lotheve閱讀 56,665評(píng)論 51 597
  • 在iOS開(kāi)發(fā)中經(jīng)常會(huì)涉及到觸摸事件经磅。本想自己總結(jié)一下,但是遇到了這篇文章钮追,感覺(jué)總結(jié)的已經(jīng)很到位预厌,特此轉(zhuǎn)載。作者:L...
    WQ_UESTC閱讀 5,989評(píng)論 4 26
  • 本文來(lái)自:http://ios.jobbole.com/84081/ 前言: 按照時(shí)間順序畏陕,事件的生命周期是這樣的...
    HackerOnce閱讀 2,831評(píng)論 1 10
  • 在接下來(lái)的兩章中配乓,您將創(chuàng)建 TouchTracker,該應(yīng)用程序中用戶(hù)可以通過(guò)觸摸屏幕來(lái)畫(huà)畫(huà)惠毁。 在本章中犹芹,您將創(chuàng)建...
    titvax閱讀 675評(píng)論 0 0
  • 響應(yīng)者對(duì)象 在iOS中不是任何對(duì)象都能處理事件,只有繼承了UIResponder的對(duì)象才能接收并處理事件鞠绰。我們稱(chēng)之...
    JonesCxy閱讀 689評(píng)論 0 0