天天看點

《MonoTouch開發實踐指南》一2.2 通過示例将Object-C與MonoTouch進行對比

下面通過示例來說明上面講到的一些概念。前面提到的uiactionsheet将會在示例中使用。通過objective-c與c#的對比,将有助于清楚地了解如何使用monotouch開發應用程式。

注意 通常,使用monotouch開發應用程式不需要xcode或objective-c,這裡這樣做的目的是作為基礎知識輔助說明monotouch的設計。如果有興趣想了解更多的相關技術,推薦閱讀stephen g. kochan寫的《programming in objective-c 2.0》。

示例将會如圖2-2所示,允許改變應用程式的背景圖檔。

《MonoTouch開發實踐指南》一2.2 通過示例将Object-C與MonoTouch進行對比

首先使用xcode建立示例應用程式。在開發時,可以借鑒之前使用monodevelop開發應用程式時的步驟,并對比它們之間的不同。打開xcode并建立一個名為lmt2-1的基于視窗的應用程式。在ib中輕按兩下mainwindow.xib檔案打開它。如圖2-3所示,在ib中将library視窗中的uiimageview拖到該視窗。uiimageview将用來顯示背景圖檔,因為要在代碼中改變背景圖檔,是以需要為它建立一個outlet。

《MonoTouch開發實踐指南》一2.2 通過示例将Object-C與MonoTouch進行對比

ib中要連接配接的outlet在objective-c的頭檔案中定義,稍後要在xcode中進行添加。不過,在添加之前,先要在ib中完成界面設計,将一個按鈕拖放到圖檔視圖的頂部。

注意 按鈕通常與uibarbuttonitem一起放在uitoolbar内。

按鈕的作用是打開uiactionsheet,然後在uiactionsheet中切換圖檔視圖中的圖檔。要将響應按鈕觸碰動作的處理代碼與按鈕連接配接起來,需要在頭檔案定義一個操作。在xcode定義好這些之後,傳回ib中進行連接配接。現在,回到xcode。

在xcode中,選擇appdelegate的頭檔案(lmt2_1appdelegate.h),然後在檔案裡添加代碼清單2-1中的代碼。

代碼清單2-1 objective-c頭檔案中的outlet和action

代碼中為uiimageview增加了一個稱為imageview的執行個體變量,并使用@property指令聲明它是iboutlet,這樣在xib檔案中,ib就可以使用這個執行個體變量來連接配接圖檔視圖。

注意 iboutlet隻是一個空的宏,不需要為它編寫代碼,它的主要作用是與ib內建。同樣,ibaction也不會有傳回值,它隻是作為ib的一個提示。

還需要在頭檔案中定義動作,以便在ib中連接配接按鈕的觸碰事件。此外,在括号中定義的可選參數(nonatomic和retain),會讓編譯器根據記憶體管理器和線程來生成實際的屬性。可以看到,這裡确實沒有屬性,它隻是圍繞getter和setter方法的“糖衣文法”(syntactic sugar)。完成xcode的代碼後,轉到實作檔案(lmt2_1appdelegate.m)添加屬性和動作方法的代碼。

另一半的objective-c屬性需要使用@synthesize指令,在實作檔案添加該指令後,編譯器會在頭檔案中生成屬性的定義。稍後在使用monotouch實作此過程的時候,會發現monotouch更簡單、更便捷。現在開始添加屬性和方法。

在lmt2_1appdelegate.m檔案中,在xcode模闆已有的視窗語句下添加imageview的synthesize語句。此外,添加changepicture:方法,并在方法内添加如代碼清單2-2中所列出的記錄字元串的存根實作。要記住,對編譯器來說,ibaction是沒有傳回值的。在介紹uiactionsheet的時候,還要回到這裡的實作操作。現在,儲存xcode中的檔案後,切換回ib中的mainwindow.xib。

代碼清單2-2 lmt2_appdelegate.m

在ib中,現在要将outlet連接配接到imageview,這樣才可以在appdelegate類中通過代碼改變背景圖檔。還要設定target-action,這樣才能在觸碰按鈕的時候調用changepicture:方法。現在開始完成這些步驟,要注意區分xcode和objective-c與monotouch之間的不同。

首先,将appdelegate的imageview的outlet連接配接到xib中的uiimageview。如圖2-4所示,在mainwindow.xib和connections inspector中選擇appdelegate,然後将imageview outlet拖到視窗的uiimageview中。

《MonoTouch開發實踐指南》一2.2 通過示例将Object-C與MonoTouch進行對比

接着,需要連接配接的是appdelegate代碼中建立的changepicture:方法。這是前面講過的target-action模式的一個例子。當在monotouch中實作這些功能時,會看到在c#中是如何使用動作及如何将代碼連接配接到c#風格的事件的。要将objective-c的處理代碼連接配接到按鈕上的事件,先在mainwindow.xib中選擇該按鈕,然後在connections inspector中将touchupinside事件拖到appdelegate。當松開滑鼠按鈕,ib會彈出一個包含所有使用ibaction聲明的方法菜單。此時這個菜單中隻有一個changepicture:方法。選擇changepicture,注意在connections inspector中,按鈕的touchupinside事件與目标類appdelegate中的changepicture:動作建立了連接配接。儲存後,在xcode中運作應用程式。

應用程式運作後,從xcode運作調試器控制台(run→console)。在觸碰應用程式的按鈕後,會在控制台看到如圖2-5所示的日志資訊。

《MonoTouch開發實踐指南》一2.2 通過示例将Object-C與MonoTouch進行對比

現在已經通過objective-c的target-action模式連接配接了事件,下一步要做的是使用monodevelop實作同樣的功能。

現在,留意一下monotouch是如何使用c#的方式實作事件響應的target-action模式的。打開monodevelop并建立一個名為lmt2-2的基于iphone視窗的新工程。在ib中打開mainwindow.xib,添加一個uiimageview和一個按鈕,修改按鈕上的文本為“change image”。現在要為圖檔視圖添加一個outlet以便代碼可以通路到它,是以在此立即建立這個outlet。因為是由appdelegate的代碼通路這個outlet,是以要在appdelegate中添加這個outlet。在library視窗和ib的标簽頁内再次選擇appdelegate,在outlets區域内添加一個名為imageview的outlet,修改它的類型為uiimageview。

注意 monotouch中的uikit版本和基礎架構分别包含在命名空間monotouch.uikit和monotouch.foundation中。

在objective-c中,是在appdelegate的頭檔案中添加outlet的,而在monotouch中,一旦xib檔案中的對象(如uiimageview執行個體)和outlet之間建立連接配接,就會通過在局部類中生成的屬性将outlet連接配接到代碼。屬性用connectattribute進行聲明,它的作用與objective-c中的iboutlet一樣,即與ib內建。

現在,在mainwindow.xib視窗中選擇appdelegate,如之前做過的一樣,将imageview的outlet連接配接到在視窗中的uiimageview。如果在ib中儲存檔案後,切換到monodevelop中檢視mainwindow.xib.designer.cs檔案,就會看到局部類和剛才提及的屬性。現在,連接配接按鈕。

第1章示範了如何使用c#風格的事件來連接配接uibutton事件。現在可以試着使用與剛才在objective-c中使用的target-aciton模式來達到相同的效果。要記住,目标是appdelegate,要在appdelegate中建立動作方法來響應按鈕的touchupinside事件。利用monodevelop與ib內建,可以達到相同的效果。在ib的library視窗中,選擇appdelegate的actions标簽(在outlets标簽頁的右邊第一個),用“changepicture:”選擇器添加一個動作。然後,選擇視窗中的按鈕,将connections inspector中的touch up inside事件拖到mainwindow.xib中的appdelegate上進行連接配接。松開滑鼠,彈出一個之前在xcode和ib中出現過的changepicture:選擇器菜單。選擇changepicture:選擇器,并在ib中儲存所有修改。如果回頭看看在mainwindow.xib.designer.cs檔案中的局部類,就會看到新代碼已添加,exportattribute聲明的作用是将ib中添加的動作處理代碼挂接到changepicture選擇器。現在添加實作代碼(類似占位符的代碼,後面将會替換為uiactionsheet)。在main.cs檔案中,添加以下代碼到appdelegate類:

運作應用程式并觸碰按鈕,将會看到寫在monodevelop中的application output标簽頁中的文本資訊(如圖2-6所示)。

《MonoTouch開發實踐指南》一2.2 通過示例将Object-C與MonoTouch進行對比

提示 使用c#事件可以避免不必要的動作。就如第1章所看到的,這樣做的好處是,可以使用c#風格的事件來實作,進而避免使用動作方法來實作。隻需要為按鈕建立一個outlet就可通過程式通路注冊的事件。monotouch支援建立c#事件委托的顯式回調方法,以及匿名方法和lambda表達式。

正如所看到的,monotouch可以使用與objective-c相同的模式實作事件處理。此外,monotouch提供了更靈活的c#風格的事件以供選擇。

現在要做的是為uiactionsheet編碼以改變圖檔。還是先通過xcode實作,然後再通過monotouch實作,以比較兩者在實作objective-c委托模式之間的不同。不過,在實作之前,要先花點時間了解monotouch是如何實作appdelegate和如何處理它的。

在本書前幾章的簡單示例中,還不能對應用程式進行結構化設計,這将在第3章進行講述。然而,在開發應用程式中總會用到appdelegate。在目前的示例中,所有的應用程式代碼都是在它内部添加的,實際上它的作用是通過其他類來處理應用程式邏輯。appdelegate是一個在應用程式的生命周期内用來響應uiapplication各類操作并進行處理的類,這些處理包括應用程式終止、應用程式啟動完成、接收系統發出的記憶體警告等。在objective-c中,appdelegate定義為一個名為uiapplicationdelegate的協定。記住objective-c協定基本類似于c#接口,除了一些方法指定為可選的外。如果打開xcode中的lmt2_1appdelegate.h檔案,會看到以下代碼:

@interface lmt2_1appdelegate : nsobject

這是objective-c在相關lmt2_1appdelegate.m檔案中實作lmt2_1appdelegate類定義的方式,它派生于nsobject(cocoatouch中其他類的基類,類似于.net中的object類)并遵循uiapplicationdelegate協定。這意味着實作将包含協定的所有請求方法以及可選方法。這是在lmt2_appdelegate.m檔案中實作的方法application:didfinishlaunchingwithoptions:,應用程式完成啟動後會調用該方法。與monotouch進行比較,就會發現appdelegate是作為一個類實作的。如果作為接口實作,那會工作不了,因為它不能給可選方法附加協定類型,因而它必須實作為一個如代碼清單2-3所示的包含各種虛函數的類。這就是在monotouch中看到的模式,将objective-c協定轉換為派生于基類的類,并通過重寫虛函數插入實作代碼。

代碼清單2-3 monotouch appdelegate類

注意 所有有exportattribute定義的方法,在重寫虛函數時,不需要知道實際的選擇器或提供屬性。

正如前面所看到的,在monotouch的實作中,monodevelop和ib之間相結合産生的所有代碼都結束在一個局部類定義中,這為實作應用程式代碼提供了很大的自由度,無須使用工具接口。繼承的層次結構可由開發人員在局部類定義中自行設定。在appdelegate的情況下,monodevelop代碼模闆從uiapplicationdelegate派生出appdelegate,在main.cs檔案中,代碼如下:

模闆還重寫了finishedlaunching方法,這裡可以實作之前在objective-c實作的(就像前面在xcode應用程式中看到的)application:didfinishlaunchin-gwithoptions:。

注意 在tirania.org/tmp/rosetta.html中,可以找到一個非常有用的名為“monotouch rosetta stone”的文檔,它列出了monotouch中與objective-c選擇器對應的c#實作。

appdelegate是cocoatouch中使用的delegation模式的一個示例,monotouch完全支援。現在要做的是在應用程式中使用uiactionsheet,這是ios sdk中另一種delegation例子。

現在回到xcode(聽起來像電影)建立uiactionsheet并設定不同按鈕以便改變imageview中的圖檔。目前,示例一直是在設計視窗和appdelegate中完成的,不算複雜。如前所述,這不是太規範的設計,具體會在第3章中講述。現在需要一個視圖來作為ui的容器,一直以來,為了簡單起見都直接跳過了,沒有讨論。這個視圖就是早已存在的imageview,将用來顯示actionsheet。在lmt2_1appdelegate.m檔案中,添加如代碼清單2-4所示的代碼到changpicture:方法來建立actionsheet。

代碼清單2-4 建立uiactionsheet的代碼

代碼将建立并顯示一個actionsheet,它内部除了包含"image 1"和"image 2"兩個按鈕外,還包含一個"cancel"(取消)按鈕。使用otherbuttontitles定義的按鈕的索引分别是0和1,而取消按鈕的索引是2。按鈕的單擊操作将使用索引進行區分,這樣的處理方式是objective-c delegation(委托)的又一個例子。注意代碼中建立actionsheet時設定的delegation(委托)屬性為self,說明它将引用目前對象,與c#中的this一樣。也就是說,傳遞給actionsheet的對象就是lmt2_1appdelegate執行個體,因為目前類就是這個。是以,lmt2_1appdelegate需要遵循uiactionsheetdelegate協定。現在設定uiactionsheetdelegate協定,打開對應的頭檔案并在尖括号内uiapplicationdelegate協定定義的後面加入協定。現在要實作uiactionsheetdelegate協定的帶有選擇器的actionsheet: clickedbuttonatindex:方法以便接收uiactionsheet的按鈕單擊後的回調處理。要完成這個,在@end:前的頭檔案添加以下代碼:

當uiactionsheet的按鈕單擊後,實作方法将執行并傳回單擊按鈕的索引。處理代碼可根據索引改變圖檔。

在lmt2_1appdelegate.m檔案中添加代碼清單2-5所示的代碼完成實作。

代碼清單2-5 actionsheet:clickedbuttonatindex:方法的實作代碼

代碼清單2-5引用了image1.jpg和iamge2.jpg這兩個圖檔。

将圖檔從finder拖放到group & files下的lmt2-1工程的根目錄下,xcode自動将它們打包到應用程式釋出包。松開滑鼠後,選擇copy items into destination group抯 folder以便将它們複制到與xcode工程相同的位置。

在xcode中生成并運作應用程式,然後單擊按鈕打開actionsheet并從中選擇一個按鈕來改變圖檔。當選擇actionsheet的按鈕時,actionsheet的委托被調用,進而處理按鈕單擊的代碼也會調用。可以看到,objective-c中的委托設計模式非常類似于其他語言的回調接口,譬如c#。不過,由于可選方法需要支援協定,是以monotouch通過類來實作。如以上看到的,在objective-c中,appdelegate的單一實作在示例中要遵循名為uiapplicationdelegate和uiactionsheetdelegate的協定。在monotouch中,要處理這個可能會不知所措,無從下手,因而需要使用類來實作,但不允許多重繼承。現在回到monodevelop并完成實作。

在monotouch中,會在構造函數中映射代碼清單2-4中objective-c的initwithtitle: delegate:cancelbuttontitle:destructivebuttontitle:otherb-uttontitles:方法。此外,在c#中顯示uiactionsheet仍然可以調用showinview方法。不過,這時候委托指針指向的不是目前執行個體,因為它已經是uiapplicationdelegate的子類,而是從uiactionsheetdelegate派生的嵌套類,這樣就可以保證委托實作封裝在類(appdelegate)内,以便建立的類執行個體可以進行委托處理(uiactionsheet)。其實,也不必非得這樣嵌入委托類,還可以在這個類的外部建立它。不過,像嵌入委托這樣的處理方式在monotouch中是經常用到的,而且它表現得很好。根據以上說明,就可實作如代碼清單2-6所示的changepicture方法。

代碼清單2-6 在monotouch中顯示uiactionsheet

changepictureactionsheetdelegate就是剛才講到的嵌套類。在objective-c中,使用actionsheet:clickedbuttonatindex:方法來處理uiactionsheet按鈕的單擊操作。在monotouch中,前面已經提過,要通過重寫虛函數來實作。實作方法的定義代碼如下:

public virtual void clicked (uiactionsheet actionsheet, int buttonindex)

與objective-c示例一樣,實作将使用單擊按鈕的索引來切換圖檔視圖的圖檔。在目前版本的monotouch中要注意,cancel(取消)按鈕的索引是最後一個索引,而不是objective-c中的第一個索引。如果需要,也可以通過uiactionsheet的cancelbuttonindex屬性來修改cancel(取消)按鈕的索引值。為了簡單起見,這裡将使用預設值,changepictureactionsheetdelegate類的實作代碼如代碼清單2-7所示。

注意 下一版本的monotouch會固定cancel(取消)按鈕的位置。

代碼清單2-7 在changepictureactionsheetdelegate類實作單擊操作

現在重複在xcode中的操作,将圖檔複制到工程目錄下。在finder中将圖檔拖到monodevelop解決方案樹下,然後單擊如圖2-8所示的對話框中的copy按鈕。在解決方案樹中的圖檔上右擊(如果使用的單按鈕滑鼠,請按下ctrl鍵再單擊),然後設定為build action to content,這樣就可以在monodevelop生成應用程式時把圖檔打包到應用程式。

注意 如果在monodevelop中建立檔案夾,等同于在磁盤上建立檔案夾,這與在xcode中在磁盤根目錄下預設為虛拟目錄不同。

最後完成如代碼清單2-8所示的main.cs檔案。如果現在生成并運作應用程式,切換圖檔,那麼效果與在objective-c中看到的一樣。

代碼清單2-8 main.cs的最後版本

現在,已經了解了如何在monotouch中使用c#開發應用程式,并與在xcode中使用objective-c開發iphone應用程式的核心設計模式做了比較。下面要了解的是monotouch如何建立這些并讓它們工作。

繼續閱讀