使用 Cocoa 為初學者編寫 Mac OS X 程式/構建 GUI
上一頁:一些 Cocoa 基本原則 | 下一頁:容器 - 陣列和字典
到目前為止,我們已經使用 Interface Builder 為我們的“Hello World”示例建立了一個非常簡單的介面。現在,我們將更詳細地瞭解它,以便我們瞭解如何構建更復雜、更有用的使用者介面。
我們已經以非常籠統的方式討論了目標和動作的概念;現在我們將瞭解 Interface Builder 如何廣泛地利用這一點來將圖形控制元件連線到您編寫的程式碼段,這些程式碼段實現了您應用程式的有趣功能。
以“Hello World”為起點,讓我們在程式碼中新增一個簡單的動作,以便我們可以瞭解它是如何工作的。這個動作非常基本 - 它只是在移動滑塊控制元件時設定文字的字型大小。在 Xcode 中,單擊“GCHelloView.h”,以便它出現在編輯器中。現在將以下行新增到類定義中,位於其他方法下方,但在“@end”語句之前
- (IBAction) textSizeAction:(id) sender;
然後進行儲存,以確保將此更改儲存到檔案。在這裡,我們將方法的返回型別宣告為 IBAction。事實上,這只是一個宏,它只是“void”,但任何標記為 IBAction 的東西都可以被 Interface Builder 自動檢測為一個 *動作例程*,即一個可以連線到支援目標/動作機制的任何控制元件的例程。我們稍後會回到 Xcode 來實現此方法,但首先讓我們在 IB 中將其連線起來。
如果 IB 未執行,請雙擊“MainMenu.nib”啟動它。將視窗排列在 IB 中,以便您可以從 Xcode 拖動檔案“GCHelloView.h”到 IB。這會觸發 IB 讀取檔案,因此它將獲取動作方法並將其新增到 GCHelloView 物件可用動作的列表中。接下來,切換回“例項”面板,並透過雙擊其圖示將“視窗”置於最前面(或者如果您能看到它,只需單擊視窗即可)。透過稍微拉大視窗,在視窗底部留出一些空間。在部件調色盤中,選擇“控制元件”面板(從左側數第二個),然後將水平滑塊控制元件從調色盤拖動到視窗中。確保您將滑塊放在您騰出的空間中,而不是放在 GCHelloView 中。
選擇滑塊控制元件,然後開啟“檢查器”(如果不可見,請使用“工具”->“顯示檢查器”)。確保彈出選單設定為“屬性”。將屬性設定為以下值
- 最小值 - 9.0
- 最大值 - 72.0
- 當前值 - 48.0
此外,選中“滑動時持續傳送動作”和“啟用”複選框。其他設定應保持其預設值。
接下來,我們需要為滑塊控制元件指定一個目標,即與應將動作傳送到的物件建立連線。IB 以圖形方式設定目標。我們從動作的傳送方到目標進行控制拖動。一條線將連線這兩個物件。現在就這樣做 - 從滑塊到 GCHelloView 進行控制拖動。檢查器將切換到“連線”部分,並列出 GCHelloView 的所有可用動作方法。突出顯示“textSizeAction:”並單擊“連線”以建立連線。(注意 - 如果“textSizeAction:”沒有出現在列表中,您可以手動新增它。在“類”列表中選擇 GCHelloView。使用檢查器屬性切換到“動作”,單擊“新增”,然後鍵入方法名稱 - 不要忘記冒號!您可能需要這樣做,因為之前將檔案拖動到 IB 中的操作(應該為您處理此操作)似乎並不總是可靠地工作。新增完方法後,請再次嘗試控制拖動步驟)。
儲存更改,然後返回到 Xcode。現在我們需要實現動作方法。找到並選擇 GCHelloView.m 檔案。將以下方法新增到實現體的主體中
- (IBAction) textSizeAction:(id) sender
{
[self setText:[self text] withSize:[sender floatValue]];
}

現在構建並執行專案。拖動滑塊... 當您拖動時,文字的大小應該會發生變化。
上面的程式碼實現了動作。它在滑塊拖動到新位置時被呼叫,並向其自己的 setText:withSize: 方法傳送訊息,傳遞現有的文字([self text])和一個從滑塊的值本身獲得的大小。我們在 IB 中設定了 9 到 72 的值範圍 - 這就變成了文字的字號。動作方法的“sender”引數始終是導致動作的物件 - 在這種情況下,是滑塊。因此,我們可以簡單地呼叫其“floatValue”方法來找出其當前值,並將其直接作為文字大小傳遞。更改在您拖動時立即可見,因為我們之前在 setText:withSize: 方法中添加了程式碼行 [self setNeedsDisplay:YES],這會導致 Cocoa 呼叫我們的 drawRect: 方法,該方法使用新大小重新繪製文字。
儘管很簡單,但此示例非常典型地展示了所有控制元件如何與您應用程式中的程式碼段互動。您在某個合適的目標物件中編寫一個動作方法(它始終具有 IBAction 返回型別,以及一個單獨的“sender”物件引數),然後在 IB 中連線該動作和目標。
選單命令的工作方式與滑塊或按鈕非常相似。它們具有一個目標和一個動作。當選擇選單項時,它會將動作傳送到目標。您可以像我們對滑塊所做的那樣設定它們 - 從選單項到目標進行控制拖動,選擇動作並單擊“連線”。
然而,有時我們不希望選單命令轉到特定的固定目標,而是希望它取決於上下文。例如,我們的應用程式可能有多個類似的視窗開啟,例如文件。當用戶選擇“複製”或“貼上”命令時,它應該始終將目標設定為當前活動文件。如果我們將此選單命令繫結到特定目標,那麼這些命令將無法按照使用者通常期望的方式工作。
為了解決這個問題,Cocoa 保持一個“命令鏈”,它會根據上下文而改變。最前面的視窗將包含一個目標,該目標應該是第一個響應命令的物件。如果它可以響應,則會響應。否則,命令將傳遞到鏈中的下一個物件,這可能是視窗中的另一個檢視,也可能是視窗本身。如果可以處理命令,則會處理。否則,它會再次向上傳遞,這一次傳遞到應用程式物件。如果應用程式無法處理命令,則會丟棄並忽略它。
在任何特定時間可以響應命令的鏈中的第一個物件稱為 **第一個響應者**,在主 IB 視窗中將顯示一個表示此物件的圖示。因此,我們只需要將其設定為我們動作的目標,我們就可以解決選單項的更改目標問題。您會發現您可以像任何其他可設定為目標的物件一樣,對該目標進行控制拖動。“動作”列表中將列出第一個響應者的所有已知於 IB 的物件的所有動作。您可以連結到任何動作。如果在當前命令鏈中找到動作,則該動作將找到其目標並執行。如果在特定時間無法在命令鏈中找到特定的動作,將會發生什麼?好吧,如果傳送了動作,則不會發生任何事情,因為沒有人響應它,但實際上,在這種情況下,Cocoa 會自動停用選單項!因此,找不到目標的動作會自動變灰,這為我們節省了大量管理選單啟用工作,因為上下文會發生變化。
“Hello World”沒有多個視窗,因此在這個階段我們無法令人信服地演示這一點,但很快我們將建立一個更復雜的應用程式,這種方法將大放異彩。
執行“Hello World”。嘗試調整視窗大小。這可能不是您期望的行為 - 所有內容都保持與底部固定距離。讓我們來看看如何使檢視按照我們想要的方式調整大小。
在 Interface Builder 中,將視窗置於最前端並選中 GCHelloView。使用 Inspector,透過彈出選單切換到“Size”面板。圖示表明檢視的哪些邊緣應該與視窗的邊緣嚴格繫結,哪些可以靈活調整。透過點選連結,可以將它們從一種狀態切換到另一種狀態。GCHelloView 應該隨著視窗大小改變而改變大小,因此將內部連結設定為靈活,外部連結設定為固定。現在選中滑塊。允許它與頂部和右側邊緣保持靈活連結,但與左側和底部邊緣保持固定連結。內部應固定。儲存檔案並使用 Xcode 構建並執行專案。現在,您將看到 GCHelloView(由藍色邊框顯示)在調整視窗大小後會伸縮。
您可能會注意到,然而,文字似乎並沒有按照我們的預期那樣表現,它與檢視底部的距離是固定的。我們習慣於文字保持與頂部邊緣的固定距離。造成這種情況的原因是 Cocoa 的預設座標遵循底層的 Quartz 圖形系統,其中 y 值的增大是向上移動螢幕,而不是向下移動。雖然這是數學上的約定,但計算機程式設計師通常習慣於相反的方式。Cocoa 允許任何檢視以這種方式進行“翻轉”,使用一個簡單的覆蓋方法。所以我們現在就來改變它。
在 Xcode 中,選擇 GCHelloView.m 並將以下方法新增到實現中
- (BOOL) isFlipped
{
return YES;
}
這是一個對 NSView 方法的覆蓋,因此我們不需要在我們的類中宣告它。我們只需返回 YES 即可讓 Cocoa 知道我們希望這個檢視的 y 座標從上到下。再次構建並執行,您將看到這帶來的區別。由於在 drawRect: 中使用的 y 座標的選擇,文字將位於檢視的相當靠下的位置 - 您可能想要將其更改為類似於 10 的值,這樣它就會靠近頂部。