簡(jiǎn)單分享一下iOS開(kāi)發(fā)中事件在view之間如何傳遞,如何響應(yīng)事件,如有錯(cuò)誤,請(qǐng)大家指正。
一、事件的產(chǎn)生

應(yīng)用外層的事件傳遞 看一下示意圖就好了!不多說(shuō)
二、事件的傳遞
2.1.響應(yīng)者對(duì)象
在iOS中不是任何對(duì)象都能處理事件,只有繼承了UIResponder的對(duì)象才能接受并處理事件,我們稱(chēng)之為響應(yīng)者對(duì)象。以下都是繼承自UIResponder的,所以都能接收并處理事件。
- UIApplication
- AppDelegate
- UIWindow
- UIViewController
- UIView
觸摸事件傳遞到應(yīng)用進(jìn)程之后
2.2如何進(jìn)行事件傳遞
- 系統(tǒng)會(huì)將該事件加入到一個(gè)由UIApplication管理的事件隊(duì)列中(注:隊(duì)列是先進(jìn)先出,先產(chǎn)生的事件先處理)
- UIApplication取出最前面的事件,向下分發(fā);通常,會(huì)先發(fā)送給App的主窗口window
- 主窗口會(huì)將事件繼續(xù)傳遞到視圖層,直到在視圖結(jié)構(gòu)中找到一個(gè)最合適的視圖來(lái)處理觸摸事件,這也是整個(gè)事件處理過(guò)程的第一步。
- 找到合適的視圖控件后,就會(huì)調(diào)用視圖控件的touches方法來(lái)作具體的事件處理。這也是整個(gè)事件處理過(guò)程的第一步。
注意:
1. 觸摸事件的傳遞是從父控件傳遞到子控件,
即:UIApplication->window->尋找處理事件最合適的view
2.如果父控件不能接受觸摸事件,那么子控件就不可能接收到觸摸事件
2.3如何找到最適合的View
1.首先判斷能否接收觸摸事件,如果不能,事件將不可能傳遞給子視圖
2.判斷事件是否在自己身上
3.子控件數(shù)組中從后往前遍歷子控件,重復(fù)前面的兩個(gè)步驟(所謂從后往前遍歷子控件,就是首先查找子控件數(shù)組中最后一個(gè)控件(即最上層的控件),然后執(zhí)行1、2步驟)
4.例如在view中有一個(gè)fitView,view將事件傳遞給fitView,fitView會(huì)再遍歷一遍自身中的子控件,直至沒(méi)有更合適的view為止。
5.如果沒(méi)有符合條件的子控件,那么就認(rèn)為自己最合適處理這個(gè)事件,也就是自己是最合適的view。
2.3.1找到最適合的view的內(nèi)部剖析
找到最適合的view,需要用到兩個(gè)重要發(fā)方法
/// 只要事件一傳遞給該一個(gè)控件, 該控件就會(huì)調(diào)用此方法
/// 該方法的作用: 尋找并返回最合適的view(能夠響應(yīng)事件的那個(gè)最合適的view)
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView?
/// 該方法判斷觸摸點(diǎn)在不在當(dāng)前view上
override func point(inside point: CGPoint, with event: UIEvent?)
1.可以重寫(xiě)此方法,攔截事件的傳遞,返回指定的view來(lái)處理事件
2.如果方法返回nil,表示控件本身和其子控件都不是最合適的view,也就是在自己身上沒(méi)有找到更合適的view。那么最合適的view就是該控件的父控件。
注意:不管子控件是不是最合適的view,系統(tǒng)默認(rèn)都要先把事件傳遞給子控件,經(jīng)過(guò)子控件調(diào)用自己的hitTest(_ point: CGPoint, with event: UIEvent?)方法驗(yàn)證后才知道有沒(méi)有更合適的view。即便父控件是最合適的view了,子控件的hitTest:withEvent:方法還是會(huì)調(diào)用,不然怎么知道有沒(méi)有更合適的!
不能接收觸摸事件的三種情況:
- view.userInteractionEnabled = NO 不允許交互
- view.isHidden = true 隱藏之后,啥也看不到了,還存在毛的交互
- view.alpha<=0.01 透明度小于0.01,也接受不到觸摸的事件了
三、事件的響應(yīng)
3.1 事件響應(yīng)的過(guò)程
- 用戶(hù)點(diǎn)擊屏幕后產(chǎn)生的一個(gè)觸摸事件,經(jīng)過(guò)一系列的傳遞過(guò)程后,會(huì)找到最合適的視圖控件來(lái)處理這個(gè)事件
- 找到最適合的view之后,就會(huì)調(diào)用該view的touchesBegan…touchesMoved…touchedEnded…方法來(lái)作具體的事件處理;(默認(rèn)情況下,view是不處理事件,而是將事件順著響應(yīng)者鏈條傳遞給上一個(gè)響應(yīng)者,如果在view中重寫(xiě)touch方法去做事件處理,那么事件就不會(huì)再傳遞)
- 如果view不處理事件,則將事件傳遞給上一個(gè)響應(yīng)者,如果上一個(gè)響應(yīng)者也不處理事件,則繼續(xù)再向上拋;直到拋給view controller的view;如果view controller的view依然不處理,則拋給view controller
- 如果view controller不處理,則拋給window;如果window不處理,拋給UIApplication,如果UIApplication不處理,則事件就廢棄。
注意:
1、如果當(dāng)前view是控制器的view,那么控制器就是上一個(gè)響應(yīng)者
2、如果當(dāng)前這個(gè)view不是控制器的view,那么父控件就是上一個(gè)響應(yīng)者
響應(yīng)者對(duì)象:能處理事件的對(duì)象,也就是繼承自UIResponder的對(duì)象
在UIResponder類(lèi)中有以下方法
/// 觸摸事件
open func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
open func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?)
open func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?)
open func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?)
/// 按壓事件 (3D Touch事件)
@available(iOS 9.0, *)
open func pressesBegan(_ presses: Set<UIPress>, with event: UIPressesEvent?)
@available(iOS 9.0, *)
open func pressesChanged(_ presses: Set<UIPress>, with event: UIPressesEvent?)
@available(iOS 9.0, *)
open func pressesEnded(_ presses: Set<UIPress>, with event: UIPressesEvent?)
@available(iOS 9.0, *)
open func pressesCancelled(_ presses: Set<UIPress>, with event: UIPressesEvent?)
/// 加速計(jì)事件
@available(iOS 3.0, *)
open func motionBegan(_ motion: UIEvent.EventSubtype, with event: UIEvent?)
@available(iOS 3.0, *)
open func motionEnded(_ motion: UIEvent.EventSubtype, with event: UIEvent?)
@available(iOS 3.0, *)
open func motionCancelled(_ motion: UIEvent.EventSubtype, with event: UIEvent?)
/// 遠(yuǎn)程控制事件
@available(iOS 4.0, *)
open func remoteControlReceived(with event: UIEvent?)
基于這些方法,處理事件的響應(yīng)和傳遞
3.2什么是響應(yīng)者鏈?
在iOS中無(wú)論是最后面的UIWindow還是最前面的某個(gè)按鈕,它們的擺放是有前后關(guān)系的,一個(gè)控件可以放到另一個(gè)控件上面或下面,那么用戶(hù)點(diǎn)擊某個(gè)控件時(shí)是觸發(fā)上面的控件還是下面的控件呢,這種先后關(guān)系構(gòu)成一個(gè)鏈條就叫“響應(yīng)者鏈”。也可以說(shuō),響應(yīng)者鏈?zhǔn)怯啥鄠€(gè)響應(yīng)者對(duì)象連接起來(lái)的鏈條。在iOS中響應(yīng)者鏈的關(guān)系可以用下圖表示:

根據(jù)圖示,描述事件的傳遞與響應(yīng):
1、當(dāng)一個(gè)事件發(fā)生后,事件會(huì)從父控件傳給子控件,也就是說(shuō)由Application -> Window -> View -> View -> initial view,這是事件的傳遞,也就是尋找最合適的view的過(guò)程。
2、然后是事件的響應(yīng)。首先看initial view能否處理這個(gè)事件,如果不能則會(huì)將事件傳遞給其上級(jí)視圖(inital view的superView);如果上級(jí)視圖仍然無(wú)法處理則會(huì)繼續(xù)往上傳遞;一直傳遞到視圖控制器view controller,首先判斷視圖控制器的根視圖view是否能處理此事件;如果不能則接著判斷該視圖控制器能否處理此事件,如果還是不能則繼續(xù)向上傳 遞;
3、圖示二表示黃色view是view controller1的視圖view,藍(lán)色view是view controller2的視圖,此時(shí)的傳遞方式是 如果黃view不能處理事件,則將事件傳遞給其本身的視圖控制器(view controller1)如果黃view的視圖控制器不處理事件,則繼續(xù)交給父視圖控制器的根視圖(即藍(lán)色View),如果藍(lán)色View不處理事件,則繼續(xù)傳遞給其根視圖控制器處理;
4、如果根視圖控制器不處理事件,則傳遞到window,如果window還是不能處理此事件則繼續(xù)交給application處理,如果最后application還是不能處理此事件則將其丟棄
5、在事件的響應(yīng)中,如果某個(gè)控件實(shí)現(xiàn)了touches...方法,則這個(gè)事件將由該控件來(lái)接受,如果調(diào)用了[supertouches….],就會(huì)將事件順著響應(yīng)者鏈條往上傳遞,傳遞給上一個(gè)響應(yīng)者(默認(rèn));接著就會(huì)調(diào)用上一個(gè)響應(yīng)者的touches….方法
事件的傳遞和響應(yīng)的區(qū)別:
事件的傳遞(拿到最合適的view)是從上到下(父控件到子控件),事件的響應(yīng)(拿到最合適的view 處理事件)是從下到上(順著響應(yīng)者鏈條向上傳遞:子控件到父控件)。