運用的前提是掌握
掌握的本質(zhì)是理解
本篇內(nèi)容將圍繞iOS中事件及其傳遞機制進行學(xué)習(xí)和分析,。在iOS中,,事件分為三類:
- 觸控事件(單點、多點觸控以及各種手勢操作)
- 傳感器事件(重力,、加速度傳感器等)
- 遠程控制事件(遠程遙控iOS設(shè)備多媒體播放等)
這三類事件共同構(gòu)成了iOS設(shè)備豐富的操作方式和使用體驗,,本次就首先來針對第一類事件:觸控事件,進行學(xué)習(xí)和分析,。
Gesture Recognizers
Gesture Recognizers是一類手勢識別器對象,,它可以附屬在你指定的View上,并且為其設(shè)定指定的手勢操作,,例如是點擊,、滑動或者是拖拽。當(dāng)觸控事件 發(fā)生時,,設(shè)置了Gesture Recognizers的View會先通過識別器去攔截觸控事件,,如果該觸控事件是事先為View設(shè)定的觸控監(jiān)聽事件,那么Gesture Recognizers將會發(fā)送動作消息給目標(biāo)處理對象,,目標(biāo)處理對象則對這次觸控事件進行處理,,先看看如下流程圖。
在iOS中,,View就是我們在屏幕上看到的各種UI控件,,當(dāng)一個觸控事件發(fā)生時,Gesture Recognizers會先獲取到指定的事件,然后發(fā)送動作消息(action message)給目標(biāo)對象(target),,目標(biāo)對象就是ViewController,,在ViewController中通過事件方法完成對該事件的處理。Gesture Recognizers能設(shè)置諸如單擊,、滑動,、拖拽等事件,通過Action-Target這種設(shè)計模式,,好處是能動態(tài)為View添加各種事件監(jiān)聽,,而不用去實現(xiàn)一個View的子類去完成這些功能。
以上過程就是我們在開發(fā)中在方法中常見的設(shè)置action和設(shè)置target,,例如為UIButton設(shè)置監(jiān)聽事件等,。
常用手勢識別類
在UIKit框架中,系統(tǒng)為我們事先定義好了一些常用的手勢識別器,,包括點擊,、雙指縮放、拖拽,、滑動、旋轉(zhuǎn)以及長按,。通過這些手勢識別器我們可以構(gòu)造豐富的操作方式,。
在上表中可以看到,UIKit框架中已經(jīng)提供了諸如UITapGestureRecognizer在內(nèi)的六種手勢識別器,,如果你需要實現(xiàn)自定義的手勢識別器,,也可以通過繼承UIGestureRecognizer類并重寫其中的方法來完成,這里我們就不詳細討論了,。
每一個Gesture Recognizer關(guān)聯(lián)一個View,,但是一個View可以關(guān)聯(lián)多個Gesture Recognizer,因為一個View可能還能響應(yīng)多種觸控操作方式,。當(dāng)一個觸控事件發(fā)生時,,Gesture Recognizer接收一個動作消息要先于View本身,結(jié)果就是Gesture Recognizer作為View處理觸控事件的代表,,或者叫代理,。當(dāng)Gesture Recognizer接收到指定的事件時,它就會發(fā)送一條動作消息(action message)給ViewController并處理,。
連續(xù)和不連續(xù)動作
觸控動作同時分為連續(xù)動作(continuous)和不連續(xù)動作(discrete),,連續(xù)動作例如滑動和拖拽,它會持續(xù)一小段時間,,而不連續(xù)動作例如單擊,,它瞬間就會完成,在這兩類事件的處理上又稍有不同。對于不連續(xù)動作,,Gesture Recognizer只會給ViewContoller發(fā)送一個單一的動作消息(action message),而對于連續(xù)動作,,Gesture Recognizer會發(fā)送多條動作消息給ViewController,直到所有的事件都結(jié)束,。
為一個View添加GestureRecognizer有兩種方式,,一種是通過InterfaceBuilder實現(xiàn),另一種就是通過代碼實現(xiàn),,我們看看通過代碼來如何實現(xiàn),。
MyViewContoller.m1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| - (void)viewDidLoad {
[super viewDidLoad];
// 創(chuàng)建并初始化手勢對象
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc]
initWithTarget:self action:@selector(respondToTapGesture:)];
// 指定操作為單擊一次
tapRecognizer.numberOfTapsRequired = 1;
// 為當(dāng)前View添加GestureRecognizer
[self.view addGestureRecognizer:tapRecognizer];
// ...
}
|
通過上述代碼,我們實現(xiàn)了為當(dāng)前MyViewController的View添加一個單擊事件,,首先構(gòu)造了UITapGestureRecognizer對象,,指定了target為當(dāng)前ViewController本身,action就是后面自己實現(xiàn)的處理方法,,這里就呼應(yīng)了前文提到的Action-Target模式,。
在事件處理過程中,這兩種方式所處的狀態(tài)又各有不同,,首先,,所有的觸控事件最開始都是處于可用狀態(tài)(Possible),對應(yīng)UIKit里面的UIGestureRecognizerStatePossible類,,如果是不連續(xù)動作事件,,則狀態(tài)只會從Possible轉(zhuǎn)變?yōu)橐炎R別狀態(tài)(Recognized,UIGestureRecognizerStateRecognized)或者是失敗狀態(tài)(Failed,UIGestureRecognizerStateFailed)。例如一次成功的單擊動作,,就對應(yīng)了Possible-Recognized這個過程,。
如果是連續(xù)動作事件,如果事件沒有失敗并且連續(xù)動作的第一個動作被成功識別(Recognized),,則從Possible狀態(tài)轉(zhuǎn)移到Began(UIGestureRecognizerStateBegan)狀態(tài),,這里表示連續(xù)動作的開始,接著會轉(zhuǎn)變?yōu)镃hanged(UIGestureRecognizerStateChanged)狀態(tài),,在這個狀態(tài)下會不斷循環(huán)的處理連續(xù)動作,,直到動作執(zhí)行完成變轉(zhuǎn)變?yōu)镽ecognized已識別狀態(tài),最終該動作會處于完成狀態(tài)(UIGestureRecognizerStateEnded),,另外,,連續(xù)動作事件的處理狀態(tài)會從Changed狀態(tài)轉(zhuǎn)變?yōu)镃anceled(UIGestureRecognizerStateCancelled)狀態(tài),原因是識別器認為當(dāng)前的動作已經(jīng)不匹配當(dāng)初對事件的設(shè)定了,。每個動作狀態(tài)的變化,,Gesture Recognizer都會發(fā)送消息(action message)給Target,也就是ViewController,,它可以根據(jù)這些動作消息進行相應(yīng)的處理,。例如一次成功的滑動手勢動作就包括按下,、移動、抬起的過程,,分別對應(yīng)了Possible-Began-Changed-Recognized這個過程,。
UITouch & UIEvent
在屏幕上的每一次動作事件都是一次Touch,在iOS中用UITouch對象表示每一次的觸控,,多個Touch組成一次Event,,用UIEvent來表示一次事件對象。
在上述過程中,,完成了一次雙指縮放的事件動作,,每一次手指狀態(tài)的變化都對應(yīng)事件動作處理過程中得一個階段。通過Began-Moved-Ended這幾個階段的動作(Touch)共同構(gòu)成了一次事件(Event),。在事件響應(yīng)對象UIResponder中有對應(yīng)的方法來分別處理這幾個階段的事件,。
- touchesBegan:withEvent:
- touchesMoved:withEvent:
- touchesEnded:withEvent:
- touchesCancelled:withEvent:
后面的參數(shù)分別對應(yīng)UITouchPhaseBegan、UITouchPhaseMoved,、UITouchPhaseEnded,、UITouchPhaseCancelled這幾個類。用來表示不同階段的狀態(tài),。
事件傳遞
如上圖,,iOS中事件傳遞首先從App(UIApplication)開始,接著傳遞到Window(UIWindow),,在接著往下傳遞到View之前,,Window會將事件交給GestureRecognizer,如果在此期間,,GestureRecognizer識別了傳遞過來的事件,則該事件將不會繼續(xù)傳遞到View去,,而是像我們之前說的那樣交給Target(ViewController)進行處理,。
響應(yīng)者鏈(Responder Chain)
通常,一個iOS應(yīng)用中,,在一塊屏幕上通常有很多的UI控件,,也就是有很多的View,那么當(dāng)一個事件發(fā)生時,,如何來確定是哪個View響應(yīng)了這個事件呢,,接下來我們就一起來看看。
響應(yīng)者對象(Responsder Object)
響應(yīng)者對象是能夠響應(yīng)并且處理事件的對象,,UIResponder是所有響應(yīng)者對象的父類,,包括UIApplication、UIView和UIViewController都是UIResponder的子類,。也就意味著所有的View和ViewController都是響應(yīng)者對象,。
第一響應(yīng)者(First Responder)
第一響應(yīng)者是第一個接收事件的View對象,,我們在Xcode的Interface Builder畫視圖時,可以看到視圖結(jié)構(gòu)中就有First Responder,。
這里的First Responder就是UIApplication了,。另外,我們可以控制一個View讓其成為First Responder,,通過實現(xiàn) canBecomeFirstResponder方法并返回YES可以使當(dāng)前View成為第一響應(yīng)者,,或者調(diào)用View的becomeFirstResponder方法也可以,例如當(dāng)UITextField調(diào)用該方法時會彈出鍵盤進行輸入,,此時輸入框控件就是第一響應(yīng)者,。
事件傳遞機制
如上所說,,,如果hit-test view不能處理當(dāng)前事件,,那么事件將會沿著響應(yīng)者鏈(Responder Chain)進行傳遞,知道遇到能處理該事件的響應(yīng)者(Responsder Object),。通過下圖,,我們來看看兩種不同情況下得事件傳遞機制。
左邊的情況,,接收事件的initial view如果不能處理該事件并且她不是頂層的View,,則事件會往它的父View進行傳遞。initial view的父View獲取事件后如果仍不能處理,,則繼續(xù)往上傳遞,,循環(huán)這個過程。如果頂層的View還是不能處理這個事件的話,,則會將事件傳遞給它們的ViewController,,如果ViewController也不能處理,則傳遞給Window(UIWindow),,此時Window不能處理的話就將事件傳遞給Application(UIApplication),,最后如果連Application也不能處理,則廢棄該事件,。
右邊圖的流程唯一不同就在于,,如果當(dāng)前的ViewController是由層級關(guān)系的,那么當(dāng)子ViewController不能處理事件時,,它會將事件繼續(xù)往上傳遞,,直到傳遞到其Root ViewController,后面的流程就跟之前分析的一樣了,。
這就是事件響應(yīng)者鏈的傳遞機制,,通過這些內(nèi)容,我們可以更深入的了解事件在iOS中得傳遞機制,,對我們在實際開發(fā)中更好的理解事件操作的原理有很大的幫助,,也對我們實現(xiàn)復(fù)雜布局進行事件處理時增添了多一份的理解,。