1.iOS中的事件
iOS中的事件可以分為3大類型:
觸摸事件
加速計(jì)事件
遠(yuǎn)程控制事件
這里我們只討論iOS中的觸摸事件。
1.1響應(yīng)者對象(UIResponder)
在iOS中不是任何對象都能處理事件, 只有繼承了UIResponder的對象才能接收并處理事件,我們稱為響應(yīng)者對象
UIApplication,UIViewController,UIView都繼承自UIResponder,因此他們都是響應(yīng)者對象, 都能夠接收并處理事件
UIResponder內(nè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;
2.iOS中事件的產(chǎn)生和傳遞
1.發(fā)生觸摸事件后,系統(tǒng)會(huì)將該事件加入到一個(gè)由UIApplication管理的隊(duì)列事件中
2.UIApplication會(huì)從事件隊(duì)列中取出最前面的事件,并將事件分發(fā)下去以便處理,通常會(huì)先發(fā)送事件給應(yīng)用程序的主窗口(keyWindow)
3.主窗口會(huì)在視圖層次結(jié)構(gòu)中找到一個(gè)最合適的視圖來處理觸摸事件
4.找到合適的視圖控件后,就會(huì)調(diào)用視圖控件的touches方法來作事件的具體處理:touchesBegin... touchesMoved...touchesEnded等
5.這些touches方法默認(rèn)的做法是將事件順著響應(yīng)者鏈條向上傳遞,將事件叫個(gè)上一個(gè)相應(yīng)者進(jìn)行處理
面我們舉個(gè)例子來演示下具體的傳遞過程,如圖:
一般事件的傳遞是從父控件傳遞到子控件的
例如:點(diǎn)擊了綠色的View,傳遞過程如下:UIApplication->Window->白色View->綠色View
點(diǎn)擊藍(lán)色的View,傳遞過程如下:UIApplication->Window->白色View->橙色View->藍(lán)色View
如果父控件接受不到觸摸事件,那么子控件就不可能接收到觸摸事件
UIView不能接收觸摸事件的三種情況:
1.不接受用戶交互:userInteractionEnabled = NO;
2.隱藏:hidden = YES;
3.透明:alpha = 0.0~0.01
如何找到最合適的控件來處理事件呢?有以下準(zhǔn)則
1.自己是否能接受觸摸事件
2.觸摸點(diǎn)是否在自己身上
3.從后往前遍歷子控件,重復(fù)上面的兩個(gè)步驟
4.如果沒有符合條件的子控件,那么自己最適合處理
例如:
說明一下控件的添加順序:白1->綠2->橙2->藍(lán)3->紅3->黃4
這里點(diǎn)擊了橙色的那塊區(qū)域,事件傳遞判斷過程如下:
1.UIApplication從事件隊(duì)列中取出事件分發(fā)給UIWindow
2.UIWindow判斷自己是否能接受觸摸事件,可以
3.UIWindow判斷觸摸點(diǎn)是否在自己身上,是的。
4.UIWindow從后往前便利自己的子控件,取出白1
5.白1都滿足最上面兩個(gè)條件,遍歷子控件橙2
6.橙2都滿足最上面兩個(gè)條件,遍歷子控件,先取出紅3
7.紅3不滿足條件2,取出藍(lán)3
8.藍(lán)3也不滿足條件2,最后最合適的控件是橙2
2.1( 重難點(diǎn))如何尋找最合適的view
應(yīng)用如何找到最合適的控件來處理事件?
1.首先判斷主窗口(keyWindow)自己是否能接受觸摸事件
2.觸摸點(diǎn)是否在自己身上
3.從后往前遍歷子控件,重復(fù)前面的兩個(gè)步驟(首先查找數(shù)組中最后一個(gè)元素)
4.如果沒有符合條件的子控件,那么就認(rèn)為自己最合適處理
詳述:1.主窗口接收到應(yīng)用程序傳遞過來的事件后,首先判斷自己能否接手觸摸事件。如果能,那么在判斷觸摸點(diǎn)在不在窗口自己身上
2.如果觸摸點(diǎn)也在窗口身上,那么窗口會(huì)從后往前遍歷自己的子控件(遍歷自己的子控件只是為了尋找出來最合適的view)
3.遍歷到每一個(gè)子控件后,又會(huì)重復(fù)上面的兩個(gè)步驟(傳遞事件給子控件,1.判斷子控件能否接受事件,2.點(diǎn)在不在子控件上)
4.如此循環(huán)遍歷子控件,直到找到最合適的view,如果沒有更合適的子控件,那么自己就成為最合適的view。
找到最合適的view后,就會(huì)調(diào)用該view的touches方法處理具體的事件。所以,只有找到最合適的view,把事件傳遞給最合適的view后,才會(huì)調(diào)用touches方法進(jìn)行接下來的事件處理。
找不到最合適的view,就不會(huì)調(diào)用touches方法進(jìn)行事件處理。
注意:之所以會(huì)采取從后往前遍歷子控件的方式尋找最合適的view只是為了做一些循環(huán)優(yōu)化。因?yàn)橄啾容^之下,后添加的view在上面,降低循環(huán)次數(shù)。
3.事件響應(yīng)
上文介紹了事件的傳遞過程,找到合適的控件之后就要進(jìn)行響應(yīng)了,這里先介紹一下響應(yīng)者鏈條:響應(yīng)者鏈條其實(shí)就是很多響應(yīng)者對象(繼承自UIResponder的對象)一起組合起來的鏈條稱之為響應(yīng)者鏈條
一般默認(rèn)做法是控件將事件順著響應(yīng)者鏈條向上傳遞,將事件交給上一個(gè)響應(yīng)者進(jìn)行處理。那么如何判斷當(dāng)前響應(yīng)者的上一個(gè)響應(yīng)者是誰呢?有以下兩個(gè)規(guī)則:
1.判斷當(dāng)前是否是控制器的View,如果是控制器的View,上一個(gè)響應(yīng)者就是控制器
2.如果不是控制器的View,上一個(gè)響應(yīng)者就是父控件
響應(yīng)過程如下圖:
如果控制器也不響應(yīng)響應(yīng)touches方法,就交給UIWindow。如果UIWindow也不響應(yīng),交給UIApplication,如果都不響應(yīng)事件就作廢了。
最后總結(jié)來說一次完整的觸摸事件的傳遞響應(yīng)過程為:
UIApplication-->UIWindow-->遞歸找到最合適處理的控件-->控件調(diào)用touches方法-->判斷是否實(shí)現(xiàn)touches方法-->沒有實(shí)現(xiàn)默認(rèn)會(huì)將事件傳遞給上一個(gè)響應(yīng)者-->找到上一個(gè)響應(yīng)者-->找不到方法作廢
一句話總結(jié)整個(gè)過程是:觸摸或者點(diǎn)擊一個(gè)控件,然后這個(gè)事件會(huì)從上向下(從父->子)找最合適的view處理,找到這個(gè)view之后看他能不能處理,能就處理,不能就按照事件響應(yīng)鏈向上(從子->父)傳遞給父控件
事件的傳遞和響應(yīng)的區(qū)別:
事件的傳遞是從上到下(父控件到子控件),事件的響應(yīng)是從下到上(順著響應(yīng)者鏈條向上傳遞:子控件到父控件。