讀古今文學網 > iOS編程基礎:Swift、Xcode和Cocoa入門指南 > 11.6 動作 >

11.6 動作

所謂動作就是由UIControl子類(一個控件)實例發出的一條消息,用於通知你該控件上發生了一個重要的用戶事件。UIControl子類都是非常簡單的界面對象,用戶可以直接與其交互,比如,按鈕(UIButton)和分割控件(UISegmentedControl)等。

重要的用戶事件(控件事件)列在了UIControl類文檔Constants中的UIControlEvents下。不同控件實現了不同的控件事件;比如,分割控件的Value Changed事件表示用戶輕拍並選擇了不同的分段,按鈕的Touch Up Inside事件則表示用戶輕拍了按鈕。控件事件本身並沒有外在效果;控件會形象地做出響應(比如,被輕拍的按鈕看起來就像按下去了一樣),不過它並不會自動共享事件發生的信息。如果想知道一個控件事件是何時發生的以便能夠在代碼中對其做出響應,你就需要讓該控件事件觸發一條動作消息。

下面是其運作方式。控件會維護一個內部分發表:對於每個控件事件來說都可以有任意數量的目標-動作對,其中動作是個消息選擇器(即方法名),目標則是消息將會發送到的對象。當控件事件發生時,控件會查詢其分發表,尋找與該控件事件相關的所有目標-動作對,並將每一條動作消息發送給相應的目標(如圖11-1所示)。

圖11-1:目標-動作架構

有兩種方式可以操縱控件的動作分發表:

動作連接

可以在nib中配置動作連接。第7章曾介紹過如何做到這一點,不過並未介紹底層機制。現在一切都很明顯了:nib編輯器中形成的動作連接是配置控件動作分發表的一種可視化方式。

代碼

可以通過代碼直接操縱控件的動作分發表。這裡所用的關鍵方法是UIControl的實例方法addTarget:action:forControlEvents:,其中target:是個對象,action:是個選擇器(在Swift中是個字符串),controlEvents:是通過位掩碼指定的。與通知中心不同,控件還提供了用於內省分發表的方法。

對於UIControl的target:與關於NSNotifications的selector:也要小心。當控件事件觸發時,目標一定要存在才行,它要有一個與動作選擇器相對應的方法,Objective-C必須要能調用該方法。否則就會出問題。

回憶一下第7章介紹的關於控件與動作的示例。我們有一個buttonPressed:方法:


@IBAction func buttonPressed(sender:AnyObject) {
    let alert = UIAlertController(
        title: \"Howdy!\", message: \"You tapped me!\", preferredStyle: .Alert)
    alert.addAction(
        UIAlertAction(title: \"OK\", style: .Cancel, handler: nil))
    self.presentViewController(alert, animated: true, completion: nil)
}
  

該方法的目的在於當用戶輕拍了界面上的某個按鈕時它會被調用。在第7章中,我們通過在nib中創建了一個動作連接來做到這一點:將按鈕的Touch Up Inside事件連接到了ViewController的buttonPressed:方法。實際上,我們構造了一個目標-動作對,並將該目標-動作對添加到了按鈕的Touch Up Inside控件事件分發表中。

相對於在nib中進行操作,我們可以通過代碼達成所願。假設我們從來沒有繪製過這個動作連接;相反,我們有一個從視圖控制器到按鈕的名為button的插座變量連接。當nib加載後,視圖控制器就可以像如下代碼一樣配置按鈕的分發表:


self.button.addTarget(self,
    action: \"buttonPressed:\",
    forControlEvents: .TouchUpInside)   

一個控件事件可以有多個目標-動作對。你可能有意這麼配置,但也有可能無意而為之。不小心為一個控件事件指定一個目標-動作對但又沒有移除現有的目標-動作對是非常容易犯的一個錯誤,這會導致一些非常奇怪的行為。比如,如果在nib中構造了一個動作連接並且又通過代碼配置了分發表,那麼輕拍按鈕就會導致buttonPressed:被調用兩次。

動作選擇器的簽名可以是如下3種形式之一:

·完整形式接收兩個參數:

·控件,通常是AnyObject類型。

·生成控件事件的UIEvent。

·一種簡寫形式,也是最常使用的一種形式,省略了第2個參數。buttonPressed:就是個例子;它只接收一個參數sender。當buttonPressed:通過來自於按鈕的動作消息被調用時,sender就是對該按鈕的引用。

·還有一種簡寫形式,它會將這兩個參數全部省略。

UIEvent是什麼,作用又是什麼呢?當用戶使用手指進行操作時就會生成觸摸事件(輕拍屏幕、移動手指、將手指從屏幕移開)。UIEvent是最底層的對象,用於實現觸摸事件與應用之間的通信。UIEvent基本上就是個時間戳(一個Double)外加上一個觸摸事件(UITouch)的集合(Set)。動作機制對你屏蔽了觸摸事件的複雜性,不過通過接收UIEvent,你依然可以處理這些複雜性。

奇怪的是,沒有一個動作選擇器參數提供了一種方式來獲悉哪個控件事件觸發了當前動作選擇器的調用!比如,要想區分Touch Up Inside控件事件與Touch Up Outside控件事件,其相應的目標-動作對就必須指定兩個不同的動作處理器;如果將其分發給相同的動作處理器,那麼該處理器就無法判斷發生的是哪個控件事件了。