我們的App與用戶進(jìn)行交互,基本上是依賴于各種各樣的觸發(fā)事件和運(yùn)動(dòng)事件。例如,用戶點(diǎn)擊界面上的按鈕,我們需要觸發(fā)一個(gè)按鈕點(diǎn)擊事件,并進(jìn)行相應(yīng)的處理,以給用戶一個(gè)響應(yīng)。UIView的三大職責(zé)之一就是處理觸發(fā)事件和運(yùn)動(dòng)事件,一個(gè)視圖是一個(gè)事件響應(yīng)者,可以處理點(diǎn)擊等觸發(fā)事件,而這些觸發(fā)事件和運(yùn)動(dòng)事件就是在UIResponder類中定義的。
一個(gè)UIResponder類為那些需要響應(yīng)并處理事件的對(duì)象定義了一組接口。這些事件主要分為兩類:觸摸事件(touch events)和運(yùn)動(dòng)事件(motion events)。UIResponder類為這兩類事件都定義了一組接口,這個(gè)我們將在下面詳細(xì)介紹,并探討一下。
在UIKit中,UIApplication、UIView、UIViewController這幾個(gè)類都是直接繼承自UIResponder類。因此UIKit中的視圖、控件、視圖控制器,以及我們自定義的視圖及視圖控制器都有響應(yīng)事件的能力。這些對(duì)象通常被稱為響應(yīng)對(duì)象,或者是響應(yīng)者(以下我們統(tǒng)一使用響應(yīng)者)。
本文將詳細(xì)介紹一個(gè)UIResponder類提供的基本功能。不過在此之前,我們先來了解一下事件響應(yīng)鏈機(jī)制。
響應(yīng)鏈
大多數(shù)事件的分發(fā)都是依賴響應(yīng)鏈的。響應(yīng)鏈?zhǔn)怯梢幌盗墟溄釉谝黄鸬捻憫?yīng)者組成的。一般情況下,一條響應(yīng)鏈開始于第一響應(yīng)者,結(jié)束于application對(duì)象。如果一個(gè)響應(yīng)者不能處理事件,則會(huì)將事件沿著響應(yīng)鏈傳到下一響應(yīng)者。
那這里就會(huì)有三個(gè)問題:
- 響應(yīng)鏈?zhǔn)呛螘r(shí)構(gòu)建的
1.系統(tǒng)是如何確定第一響應(yīng)者的
2.確定第一響應(yīng)者后,系統(tǒng)又是按照什么樣的順序來傳遞事件的
構(gòu)建響應(yīng)鏈
3.我們都知道在一個(gè)App中,所有視圖是按一定的結(jié)構(gòu)層次組織起來的,即樹狀層次結(jié)構(gòu)。除了根視圖外,每個(gè)視圖都有一個(gè)父視圖;而每個(gè)視圖都可以有0個(gè)或多個(gè)子視圖。而在這個(gè)樹狀結(jié)構(gòu)構(gòu)建的同時(shí),也構(gòu)建了一條完整的事件響應(yīng)鏈。
確定第一響應(yīng)者
當(dāng)用戶觸發(fā)某一事件(觸摸事件或運(yùn)動(dòng)事件)后,UIKit會(huì)創(chuàng)建一個(gè)事件對(duì)象(UIEvent),該對(duì)象包含一些處理事件所需要的信息。然后事件對(duì)象被放到一個(gè)事件隊(duì)列中。這些事件按照先進(jìn)先出的順序來處理。當(dāng)處理事件時(shí),程序的UIApplication對(duì)象會(huì)從隊(duì)列頭部取出一個(gè)事件對(duì)象,將其分發(fā)出去。通常首先是將事件分發(fā)給程序的主window對(duì)象,對(duì)于觸摸事件來講,window對(duì)象會(huì)首先嘗試將事件分發(fā)給觸摸事件發(fā)生的那個(gè)視圖上。這一視圖通常被稱為hit-test視圖,而查找這一視圖的過程就叫做hit-testing。
系統(tǒng)使用hit-testing來找到觸摸事件下的視圖,它檢測(cè)一個(gè)觸摸事件是否發(fā)生在相應(yīng)視圖對(duì)象的邊界之內(nèi)(即視圖的frame屬性,這也是為什么子視圖如果在父視圖的frame之外時(shí),是無(wú)法響應(yīng)事件的)。如果在,則會(huì)遞歸檢測(cè)其所有的子視圖。包含觸摸點(diǎn)的視圖層次架構(gòu)中最底層的視圖就是hit-test視圖。在檢測(cè)出hit-test視圖后,系統(tǒng)就將事件發(fā)送給這個(gè)視圖來進(jìn)行處理。
我們通過一個(gè)示例來演示hit-testing的過程。圖1是一個(gè)視圖層次結(jié)構(gòu),

假設(shè)用戶點(diǎn)擊了視圖E,系統(tǒng)按照以下順序來查找hit-test視圖:
點(diǎn)擊事件發(fā)生在視圖A的邊界內(nèi),所以檢測(cè)子視圖B和C;
點(diǎn)擊事件不在視圖B的邊界內(nèi),但在視圖C的邊界范圍內(nèi),所以檢測(cè)子圖片D和E;
點(diǎn)擊事件不在視圖D的邊界內(nèi),但在視圖E的邊界范圍內(nèi);
視圖E是包含觸摸點(diǎn)的視圖層次架構(gòu)中最底層的視圖(倒樹結(jié)構(gòu)),所以它就是hit-test視圖。
hit-test視圖可以最先去處理觸摸事件,如果hit-test視圖不能處理事件,則事件會(huì)沿著響應(yīng)鏈往上傳遞,直到找到能處理它的視圖。
事件傳遞
最有機(jī)會(huì)處理事件的對(duì)象是hit-test視圖或第一響應(yīng)者。如果這兩者都不能處理事件,UIKit就會(huì)將事件傳遞到響應(yīng)鏈中的下一個(gè)響應(yīng)者。每一個(gè)響應(yīng)者確定其是否要處理事件或者是通過nextResponder方法將其傳遞給下一個(gè)響應(yīng)者。這一過程一直持續(xù)到找到能處理事件的響應(yīng)者對(duì)象或者最終沒有找到響應(yīng)者。最后到UIApplication對(duì)象,若果也不能處理,這個(gè)事件就會(huì)被拋棄。
下圖演示了這樣一個(gè)事件傳遞的流程:

當(dāng)系統(tǒng)檢測(cè)到一個(gè)事件時(shí),將其傳遞給初始對(duì)象,這個(gè)對(duì)象通常是一個(gè)視圖。然后,會(huì)按以下路徑來處理事件(我們以上面左圖為例):
1.初始視圖(initial view)嘗試處理事件。如果它不能處理事件,則將事件傳遞給其父視圖。
2.初始視圖的父視圖(superview)嘗試處理事件。如果這個(gè)父視圖還不能處理事件,則繼續(xù)將視圖傳遞給上層視圖。
3.上層視圖(topmost view)會(huì)嘗試處理事件。如果這個(gè)上層視圖還是不能處理事件,則將事件傳遞給視圖所在的視圖控制器。
4.視圖控制器會(huì)嘗試處理事件。如果這個(gè)視圖控制器不能處理事件,則將事件傳遞給窗口(window)對(duì)象。
5.窗口(window)對(duì)象嘗試處理事件。如果不能處理,則將事件傳遞給單例UIApplication對(duì)象。
6.如果UIApplication對(duì)象不能處理事件,則丟棄這個(gè)事件。
從上面可以看到,視圖、視圖控制器、窗口對(duì)象和UIApplication對(duì)象都能處理事件。另外需要注意的是,手勢(shì)也會(huì)影響到事件的傳遞。
以上便是響應(yīng)鏈的一些基本知識(shí)。有了這些知識(shí),我們便可以來看看UIResponder提供給我們的一些方法了。
管理響應(yīng)鏈
UIResponder提供了幾個(gè)方法來管理響應(yīng)鏈,包括讓響應(yīng)對(duì)象成為第一響應(yīng)者、放棄第一響應(yīng)者、檢測(cè)是否是第一響應(yīng)者以及傳遞事件到下一響應(yīng)者的方法,我們分別來介紹一下。
- 上面提到在響應(yīng)鏈中負(fù)責(zé)傳遞事件的方法屬性和方法,其聲明如下:
#if UIKIT_DEFINE_AS_PROPERTIES
@property(nonatomic, readonly, nullable) UIResponder *nextResponder;
#else
//上面提到在響應(yīng)鏈中負(fù)責(zé)傳遞事件的方法是nextResponder,其聲明如下
- (nullable UIResponder*)nextResponder;
#endif
#if UIKIT_DEFINE_AS_PROPERTIES
@property(nonatomic, readonly) BOOL canBecomeFirstResponder; // default is NO
#else
//是否將目標(biāo)對(duì)象設(shè)置為第一響應(yīng)者的資格
- (BOOL)canBecomeFirstResponder; // default is NO
#endif
//成為第一響應(yīng)者
- (BOOL)becomeFirstResponder;
#if UIKIT_DEFINE_AS_PROPERTIES
@property(nonatomic, readonly) BOOL canResignFirstResponder; // default is YES
#else
//是否將目標(biāo)對(duì)象設(shè)置為失去第一響應(yīng)者的資格
- (BOOL)canResignFirstResponder; // default is YES
#endif
- (BOOL)resignFirstResponder;
#if UIKIT_DEFINE_AS_PROPERTIES
@property(nonatomic, readonly) BOOL isFirstResponder;
#else
- (BOOL)isFirstResponder;
#endif
// Generally, all responders which do custom touch handling should override all four of these methods.
// Your responder will receive either touchesEnded:withEvent: or touchesCancelled:withEvent: for each
// touch it is handling (those touches it received in touchesBegan:withEvent:).
// *** You must handle cancelled touches to ensure correct behavior in your application. Failure to
// do so is very likely to lead to incorrect behavior or crashes.
#UIResponder內(nèi)部提供了以下方法來處理事件觸摸事件
// UIView是UIResponder的子類,可以覆蓋下列4個(gè)方法處理不同的觸摸事件
// 一根或者多根手指開始觸摸view,系統(tǒng)會(huì)自動(dòng)調(diào)用view的下面方法
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
// 一根或者多根手指在view上移動(dòng),系統(tǒng)會(huì)自動(dòng)調(diào)用view的下面方法(隨著手指的移動(dòng),會(huì)持續(xù)調(diào)用該方法)
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
// 觸摸結(jié)束前,某個(gè)系統(tǒng)事件(例如電話呼入)會(huì)打斷觸摸過程,系統(tǒng)會(huì)自動(dòng)調(diào)用view的下面方法
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
// 觸摸結(jié)束前,某個(gè)系統(tǒng)事件(例如電話呼入)會(huì)打斷觸摸過程,系統(tǒng)會(huì)自動(dòng)調(diào)用view的下面方法
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesEstimatedPropertiesUpdated:(NSSet<UITouch *> *)touches NS_AVAILABLE_IOS(9_1);
// Generally, all responders which do custom press handling should override all four of these methods.
// Your responder will receive either pressesEnded:withEvent or pressesCancelled:withEvent: for each
// press it is handling (those presses it received in pressesBegan:withEvent:).
// pressesChanged:withEvent: will be invoked for presses that provide an analog value
// (like thumbsticks or analog push buttons)
// *** You must handle cancelled presses to ensure correct behavior in your application. Failure to
// do so is very likely to lead to incorrect behavior or crashes.
- (void)pressesBegan:(NSSet<UIPress *> *)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0);
- (void)pressesChanged:(NSSet<UIPress *> *)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0);
- (void)pressesEnded:(NSSet<UIPress *> *)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0);
- (void)pressesCancelled:(NSSet<UIPress *> *)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0);
//加速計(jì)事件
- (void)motionBegan:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(3_0);
- (void)motionEnded:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(3_0);
- (void)motionCancelled:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(3_0);
//遠(yuǎn)程控制事件
- (void)remoteControlReceivedWithEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(4_0);
- (BOOL)canPerformAction:(SEL)action withSender:(nullable id)sender NS_AVAILABLE_IOS(3_0);
// Allows an action to be forwarded to another target. By default checks -canPerformAction:withSender: to either return self, or go up the responder chain.
- (nullable id)targetForAction:(SEL)action withSender:(nullable id)sender NS_AVAILABLE_IOS(7_0);
@property(nullable, nonatomic,readonly) NSUndoManager *undoManager NS_AVAILABLE_IOS(3_0);
- (UIResponder *)nextResponder
UIResponder類并不自動(dòng)保存或設(shè)置下一個(gè)響應(yīng)者,該方法的默認(rèn)實(shí)現(xiàn)是返回nil。子類的實(shí)現(xiàn)必須重寫這個(gè)方法來設(shè)置下一響應(yīng)者。UIView的實(shí)現(xiàn)是返回管理它的UIViewController對(duì)象(如果它有)或者其父視圖。而UIViewController的實(shí)現(xiàn)是返回它的視圖的父視圖;UIWindow的實(shí)現(xiàn)是返回app對(duì)象;而UIApplication的實(shí)現(xiàn)是返回nil。所以,響應(yīng)鏈?zhǔn)窃跇?gòu)建視圖層次結(jié)構(gòu)時(shí)生成的。
一個(gè)響應(yīng)對(duì)象可以成為第一響應(yīng)者,也可以放棄第一響應(yīng)者。為此,UIResponder提供了一系列方法,我們分別來介紹一下。
如果想判定一個(gè)響應(yīng)對(duì)象是否是第一響應(yīng)者,則可以使用以下方法:
- (BOOL)isFirstResponder
如果我們希望將一個(gè)響應(yīng)對(duì)象作為第一響應(yīng)者,則可以使用以下方法:
- (BOOL)becomeFirstResponder
如果對(duì)象成為第一響應(yīng)者,則返回YES;否則返回NO。默認(rèn)實(shí)現(xiàn)是返回YES。子類可以重寫這個(gè)方法來更新狀態(tài),或者來執(zhí)行一些其它的行為。
一個(gè)響應(yīng)對(duì)象只有在當(dāng)前響應(yīng)者能放棄第一響應(yīng)者狀態(tài)(canResignFirstResponder)且自身能成為第一響應(yīng)者(canBecomeFirstResponder)時(shí)才會(huì)成為第一響應(yīng)者。
這個(gè)方法相信大家用得比較多,特別是在希望UITextField獲取焦點(diǎn)時(shí)。另外需要注意的是只有當(dāng)視圖是視圖層次結(jié)構(gòu)的一部分時(shí)才調(diào)用這個(gè)方法。如果視圖的window屬性不為空時(shí),視圖才在一個(gè)視圖層次結(jié)構(gòu)中;如果該屬性為nil,則視圖不在任何層次結(jié)構(gòu)中。
上面提到一個(gè)響應(yīng)對(duì)象成為第一響應(yīng)者的一個(gè)前提是它可以成為第一響應(yīng)者的資格,我們可以使用canBecomeFirstResponder方法來檢測(cè),
- (BOOL)canBecomeFirstResponder
需要注意的是我們不能向一個(gè)不在視圖層次結(jié)構(gòu)中的視圖發(fā)送這個(gè)消息,其結(jié)果是未定義的。
與上面兩個(gè)方法相對(duì)應(yīng)的是響應(yīng)者放棄第一響應(yīng)者的方法,其定義如下:
- (BOOL)resignFirstResponder
- (BOOL)canResignFirstResponder
resignFirstResponder默認(rèn)也是返回YES。需要注意的是,如果子類要重寫這個(gè)方法,則在我們的代碼中必須調(diào)用super的實(shí)現(xiàn)。
canResignFirstResponder默認(rèn)也是返回YES。不過有些情況下可能需要返回NO,如一個(gè)輸入框在輸入過程中可能需要讓這個(gè)方法返回NO,以確保在編輯過程中能始終保證是第一響應(yīng)者。
管理輸入視圖
所謂的輸入視圖,是指當(dāng)對(duì)象為第一響應(yīng)者時(shí),顯示另外一個(gè)視圖用來處理當(dāng)前對(duì)象的信息輸入,如UITextView和UITextField兩個(gè)對(duì)象,在其成為第一響應(yīng)者是,會(huì)顯示一個(gè)系統(tǒng)鍵盤,用來輸入信息。這個(gè)系統(tǒng)鍵盤就是輸入視圖。輸入視圖有兩種,一個(gè)是inputView,另一個(gè)是inputAccessoryView。這兩者如圖

與inputView相關(guān)的屬性有如下兩個(gè):
@property(nonatomic, readonly, retain) UIView *inputView
@property(nonatomic, readonly, retain) UIInputViewController *inputViewController
這兩個(gè)屬性提供一個(gè)視圖(或視圖控制器)用于替代為UITextField和UITextView彈出的系統(tǒng)鍵盤。我們可以在子類中將這兩個(gè)屬性重新定義為讀寫屬性來設(shè)置這個(gè)屬性。如果我們需要自己寫一個(gè)鍵盤的,如為輸入框定義一個(gè)用于輸入身份證的鍵盤(只包含0-9和X),則可以使用這兩個(gè)屬性來獲取這個(gè)鍵盤。
與inputView類似,inputAccessoryView也有兩個(gè)相關(guān)的屬性:
@property(nonatomic, readonly, retain) UIView *inputAccessoryView
@property(nonatomic, readonly, retain) UIInputViewController *inputAccessoryViewController
設(shè)置方法與前面相同,都是在子類中重新定義為可讀寫屬性,以設(shè)置這個(gè)屬性。
另外,UIResponder還提供了以下方法,在對(duì)象是第一響應(yīng)者時(shí)更新輸入和訪問視圖,
- (void)reloadInputViews
調(diào)用這個(gè)方法時(shí),視圖會(huì)立即被替換,即不會(huì)有動(dòng)畫之類的過渡。如果當(dāng)前對(duì)象不是第一響應(yīng)者,則該方法是無(wú)效的。
響應(yīng)觸摸事件
UIResponder提供了如下四個(gè)大家都非常熟悉的方法來響應(yīng)觸摸事件:
// 當(dāng)一個(gè)或多個(gè)手指觸摸到一個(gè)視圖或窗口
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
// 當(dāng)與事件相關(guān)的一個(gè)或多個(gè)手指在視圖或窗口上移動(dòng)時(shí)
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
// 當(dāng)一個(gè)或多個(gè)手指從視圖或窗口上抬起時(shí)
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
// 當(dāng)一個(gè)系統(tǒng)事件取消一個(gè)觸摸事件時(shí)
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
這四個(gè)方法默認(rèn)都是什么都不做。不過,UIKit中UIResponder的子類,尤其是UIView,這幾個(gè)方法的實(shí)現(xiàn)都會(huì)把消息傳遞到響應(yīng)鏈上。因此,為了不阻斷響應(yīng)鏈,我們的子類在重寫時(shí)需要調(diào)用父類的相應(yīng)方法;而不要將消息直接發(fā)送給下一響應(yīng)者。
默認(rèn)情況下,多點(diǎn)觸摸是被禁用的。為了接受多點(diǎn)觸摸事件,我們需要設(shè)置響應(yīng)視圖的multipleTouchEnabled屬性為YES。
響應(yīng)移動(dòng)事件
與觸摸事件類似,UIResponder也提供了幾個(gè)方法來響應(yīng)移動(dòng)事件:
// 移動(dòng)事件開始
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event
// 移動(dòng)事件結(jié)束
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
// 取消移動(dòng)事件
- (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event
與觸摸事件不同的是,運(yùn)動(dòng)事件只有開始與結(jié)束操作;它不會(huì)報(bào)告類似于晃動(dòng)這樣的事件。這幾個(gè)方法的默認(rèn)操作也是什么都不做。不過,UIKit中UIResponder的子類,尤其是UIView,這幾個(gè)方法的實(shí)現(xiàn)都會(huì)把消息傳遞到響應(yīng)鏈上。
響應(yīng)遠(yuǎn)程控制事件
遠(yuǎn)程控制事件來源于一些外部的配件,如耳機(jī)等。用戶可以通過耳機(jī)來控制視頻或音頻的播放。接收響應(yīng)者對(duì)象需要檢查事件的子類型來確定命令(如播放,子類型為UIEventSubtypeRemoteControlPlay),然后進(jìn)行相應(yīng)處理。
為了響應(yīng)遠(yuǎn)程控制事件,UIResponder提供了以下方法,
- (void)remoteControlReceivedWithEvent:(UIEvent *)event
我們可以在子類中實(shí)現(xiàn)該方法,來處理遠(yuǎn)程控制事件。不過,為了允許分發(fā)遠(yuǎn)程控制事件,我們必須調(diào)用UIApplication的beginReceivingRemoteControlEvents方法;而如果要關(guān)閉遠(yuǎn)程控制事件的分發(fā),則調(diào)用endReceivingRemoteControlEvents方法。
在我們的應(yīng)用中,經(jīng)常會(huì)處理各種菜單命令,如文本輸入框的”復(fù)制”、”粘貼”等。UIResponder為此提供了兩個(gè)方法來支持此類操作。首先使用以下方法可以啟動(dòng)或禁用指定的命令:
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
該方法默認(rèn)返回YES,我們的類可以通過某種途徑處理這個(gè)命令,包括類本身或者其下一個(gè)響應(yīng)者。子類可以重寫這個(gè)方法來開啟菜單命令。例如,如果我們希望菜單支持”Copy”而不支持”Paser”,則在我們的子類中實(shí)現(xiàn)該方法。需要注意的是,即使在子類中禁用某個(gè)命令,在響應(yīng)鏈上的其它響應(yīng)者也可能會(huì)處理這些命令。
另外,我們可以使用以下方法來獲取可以響應(yīng)某一行為的接收者:
- (id)targetForAction:(SEL)action withSender:(id)sender
在對(duì)象需要調(diào)用一個(gè)action操作時(shí)調(diào)用該方法。默認(rèn)的實(shí)現(xiàn)是調(diào)用canPerformAction:withSender:方法來確定對(duì)象是否可以調(diào)用action操作。如果可以,則返回對(duì)象本身,否則將請(qǐng)求傳遞到響應(yīng)鏈上。如果我們想要重寫目標(biāo)的選擇方式,則應(yīng)該重寫這個(gè)方法。下面這段代碼演示了一個(gè)文本輸入域禁用拷貝/粘貼操作:
- (id)targetForAction:(SEL)action withSender:(id)sender{ UIMenuController *menuController = [UIMenuController sharedMenuController];
if (action == @selector(selectAll:) || action == @selector(paste:) ||action == @selector(copy:) || action == @selector(cut:)) {
if (menuController) {
[UIMenuController sharedMenuController].menuVisible = NO;
}
return nil;
}
return [super targetForAction:action withSender:sender];
}
結(jié)語(yǔ)
以我測(cè)試代碼結(jié)束,應(yīng)該是不錯(cuò)的
- (void)viewDidLoad {
[super viewDidLoad];
label=[[UILabel alloc]initWithFrame:CGRectMake(100, 100, SCREEN_WIDTH-200, 100)];
label.text=@"開始你幾十次那就瞌睡蟲你桑蠶絲次數(shù)次時(shí)間長(zhǎng)是程思佳傳送才加上就程思佳測(cè)試才加上產(chǎn)品搜救出平時(shí)";
label.numberOfLines=0;
label.userInteractionEnabled = YES;
[self.view addSubview:label];
[label addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(labelClick)]];
}
-(void)labelClick{
NSLog(@"測(cè)試");
UIMenuController *menuController = [UIMenuController sharedMenuController];
menuController.menuItems = @[ [[UIMenuItem alloc] initWithTitle:@"頂" action:@selector(ding:)], [[UIMenuItem alloc] initWithTitle:@"回復(fù)" action:@selector(reply:)], [[UIMenuItem alloc] initWithTitle:@"舉報(bào)" action:@selector(warn:)] ];
// 菜單最終顯示的位置
[menuController setTargetRect:label.bounds inView:label];
// 顯示菜單
[menuController setMenuVisible:YES animated:YES];
}
//讓Label具備成為第一響應(yīng)者的資格(默認(rèn)為NO),需手動(dòng)開啟
- (BOOL)canBecomeFirstResponder{
return YES;
}
-(void)ding:(id)send{
NSLog(@"ding");
}
-(void)reply:(id)send{
NSLog(@"reply");
}
-(void)warn:(id)send{
NSLog(@"warn");
}
////判斷當(dāng)前的方法,是否顯示需要顯示的項(xiàng)
//- (BOOL)canPerformAction:(SEL)action withSender:(id)sender{
// if ( (action == @selector(copy:) && label.text)
// ||(action == @selector(cut:) && label.text) || action == @selector(paste:) || action == @selector(ding:) || action == @selector(reply:) || action == @selector(warn:)){
// return YES;
// }
// return NO;
//
//}
//方法來獲取可以響應(yīng)某一行為的接收者:
- (id)targetForAction:(SEL)action withSender:(id)sender{ UIMenuController *menuController = [UIMenuController sharedMenuController];
if (action == @selector(selectAll:) || action == @selector(paste:) ||action == @selector(copy:) || action == @selector(cut:)) {
if (menuController) {
[UIMenuController sharedMenuController].menuVisible = NO;
}
return nil;
}
return [super targetForAction:action withSender:sender];
}