一說開發 iOS app,你立馬就會想到蘋果的開發語言 Objective C/Swift 和 Xcode。但是,這並不是唯一的選擇,我們完全可以使用別的語言和框架。
一種主流的替換方案是 Xamarin,這是一個跨平台框架,允許你開發 iOS、Android 和 OSX、Windows app,它使用的是 C# 和 Visual Studio。最大的好處在於,Xamarin 允許你在 iOS 和 Android app 間共享代碼。
Xamarin 與其他跨平台框架相比有一個最大的好處:使用 Xamarin,你的項目能夠編譯出本地代碼,並使用本地 APIs。也就是說,用 Xamarin 編寫的 app 和用 Xcode 編出來的 app 毫無區別。更多細節,請閱讀這篇文章Xamarin vs. Native App Development。
但是 Xamarin 還有一個巨大的缺點,就是它的價格。每個平台 1000 美元/年的價格,可能要讓你戒掉每天都喝的拿鐵或法布奇諾才能負擔得起……程序員如果不喝咖啡是件很危險的事情。因為高昂的價格,Xamarin 至今只在預算豐沛的企業項目中才會采用。
但是,自從微軟收購 Xamarin 之後這種情況發生了改變,微軟將它集成到新版的 Visual Studio 中,甚至是免費的社區版。而社區版對個人開發者和小團隊是免費的。
免費?真是太好了!
除了價格以外(或者根本不考慮價格),Xamarin 還擁有其它好處,包括允許程序員:
利用原有的 c# 庫和工具編寫手機 app。 在不同平台的 app 之間共用代碼。 在 ASP.net 後台和前端 app 之間共用代碼。Xamarin 還允許你根據你的需求改變工具。如果想最大化地跨平台共用代碼,請使用 Xamarin Forms,它非常適合於不針對特定平台的特性或者需要單獨定制 UI 的 app。
如果你的 app 需要調用針對特定平台的功能或界面,請使用 Xamarin.iOS、Xamarin.Android 或其他平台的模塊,這樣就可以直接調用本地 API 和框架。這些模塊能夠創建高度定制的 UI,同時在通用代碼上支持跨平台。
在本教程中,你將使用 Xamarin.iOS 創建 iPhone app,這個 app 用於列出用戶的照片庫。
本教程不需要任何 iOS 或 Xamarin 開發經驗,但最好具備 C# 基礎。
要用 Xamarin 和 Visual Studio 開發 iOS app,理想的情況下你需要兩台電腦:
一台 Windows 電腦,用於運行 Visual Studio,並編寫代碼。 一台裝有 Xcode 的 Mac 電腦,用於充當 buid 主機。並不是專門用它來進行編譯,而是在開發和調試過程中,用它來接受自 Windows 電腦發出網絡請求。兩台電腦的物理距離越近越好,因為當你在 windows 電腦上編譯和運行時,iOS 模擬器會在 Mac 電腦上運行。
你可能會問“如果我沒有兩台電腦怎麼辦?”
對於只使用 Mac 平台的用戶,Xamarin 也提供了 OSX 下的 IDE,但本教程主要目的是演示全新的 Visual Studio。如果你不喜歡這樣,你可以在 Mac 上跑一個 Windows 虛擬機。VMWare Fushion 或者免費開源的 VirtualBox 都可以。
如果使用 Windows 虛擬機,你需要保證 Windows 能夠通過網絡訪問 Mac。也就是說,你需要在 Windows 下 Ping 到 Mac 的 IP 地址。
對於純 Windows 用戶,那麼現在、馬上去買一台 Mac。我在這裡等你!如果不行,就使用 MacinCloud 或 Macminicolo 之類的雲服務吧。
本教程假設你有單獨的 Mac 和 Windows 電腦,當然,對於在使用 Mac 下使用 Windows 主機的人來說,本教程也是適用的。
下載、安裝 Xcode 到你的 Mac 電腦上,如果你還沒有這樣做的話。這個和從應用商店安裝其他 app 並無不同,只不過有好幾 G 大,需要的時間長一點。
裝完 Xcode 之後,下載 Xamarin Studio 到 Mac 電腦上。不用填寫 email 地址,下載是免費的。插一句:是不是覺得很爽——這不需要犧牲你的任何一種咖啡為代價!
下載完成後後,打開安裝包,雙擊 Install Xamarin.app。接收協議條款,點擊 continue。
安裝器會自動找到已經安裝的工具並檢查當前操作系統版本。它會顯示一個開發環境列表。勾上 Xamarin.iOS,點擊 continue。
然後你會看到一個確認清單,列出了將要安裝的內容。點擊 continue。然後你會看到一個概要,以及一個啟動 Xamarin Studio 的選項。直接點 Quit 完成安裝。
在本教程中,你可以使用任意版本的 Visual Studio,甚至是免費的社區版。社區版的功能並不完全,但完全不影響你開發復雜的 app。
你的 Windows 當你必須滿足 Visual Studio 的最小系統需求。要獲得比較順暢的開發體驗,至少需要 3 GB 的內存。
如果你沒有安裝過 Visual Studio,你可以從社區版網頁上點擊綠色的社區版2015按鈕,下載社區版的安裝器。
運行安裝器,開始安裝,選擇定制安裝選項。在特性列表中,展開跨平台手機開發,然後選擇 C#/.NET(Xamarin v4.0.3)(本教程編寫時的版本,很可能會有不同)。
點擊 Next,等待安裝完成。時間有點長,你可以站起來走一下,消化掉安裝 Xcode 時吃的餅干 :]
如果你已經裝過 Visual Studio 但沒有 Xamarin tools,進入 Windows 的 Programs and Features,找到 Visual Studio 2015,選擇它,點擊 Change,然後選擇 Modify。
在 Cross Platform Mobile Development 下面找到 Xamarin,即 C#/.NET (Xamarin v4.0.3),勾選它,點擊 Upate 以進行安裝。
呼——裝的東西真多,但總算是搞定了!
打開 Visual Studio,選擇 File\New\Project。在 Visual C# 下面,展開 iOS,選擇 iPhone -> Single View App 模板。這個模板創建一個只有一個 view controller 的 app,view controller 是 iOS app 中用於管理視圖的類。
無論 Name 還是 Solution Name,都請輸入 ImageLocation。選擇項目保存路徑,然後點擊 OK,新項目就創建好了。
Visual Studio 會提示你需要指定一台 Mac 電腦作為 Xamarin 的 buid 主機:<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCtTaIE1hYyC158TUyc+jrLTyv6rPtc2zxqu6w8no1sOjrNGh1PG5ss/toaMgtPK/qtS2s8y1x8K8oaMNCjxwPr2rJmxkcXVvO9TK0O23w87KJnJkcXVvO7jEzqombGRxdW87vfbV4tCp08O7pyZyZHF1bzujrMi7uvPM7bzTvavTw9Pat8POyiBNYWMgyc+1xCBYYW1hcmluILrNIFhjb2RlILXE08O7p6GjPC9wPg0KPHA+PGltZyBhbHQ9"" src="/uploadfile/Collfiles/20161009/201610090948021136.png" title="\" />
關閉窗口,返回 Windows 電腦。
回到 Visual Studio,在要求你指定一台 Mac 作為 build 主機時,選擇你的 Mac 電腦,然後點擊 Connect。輸入用戶名密碼,點擊 login。
你可以查看工具欄,檢查是否已經連接成功。
從 Solution Platform 下拉框中選擇 iPhone Simulator,這將自動選擇 build 主機的一個模擬器。如果要改變為其他模擬器,可以點擊當前模擬器右邊的小箭頭。
按下綠色的 Debug 箭頭或者 F5 快捷鍵,編譯運行程序。
編譯完成後,你卻不能在 Windows 上看到任何效果。因為它運行在你的 Mac (build 主機) 上。這就是為什麼最好將兩台機器盡量靠近的理由!
在前幾天的 Evolve 大會上,Xamarin 宣布將推出 iOS Simulator Remoting ,它能夠讓你和運行在蘋果 iOS 模擬器上的 app 進行交互,就像在 Windows PC 上運行了一個模擬器一樣。但在目前,你仍然需要和運行在 Mac 上的模擬器打交道。
在模擬器上,你會看到一個啟動畫面閃現,然後顯示一個空的窗口。恭喜你!你的 Xamarin 能夠正常工作了。
要停止 app,可以點擊紅色的 Stop 按鈕(Shift+F5 快捷鍵)。
這個 app 會顯示給用戶一個 Collection View,以展示用戶相冊中的縮略圖片。Collection View 是一個 iOS 控件,以網格形式顯示多個條目。
要編輯 app 故事板中的“場景”,請從解決方案管理器中打開 Main.storyboard。
在工具箱中的搜索欄中,輸入 collection 字樣進行過濾。將 Data View 下面的 Collection View 對拖到空白的視圖中央。
選擇 Collection View,你會看到它四周出現一些空心的小圓圈。如果你看到的是 T 字而不是小圓圈,請再點它一次,即可切換到小圓圈。
點擊並拖動每個小圓圈直到看見藍色線條後放開鼠標按鍵,控件的邊緣就自動對齊到線條所在的地方。
然後設置 Collection View 的自動布局約束,自動布局約束用於告訴 app 當設備旋屏時視圖應當如何重新改變大小。在故事板上邊的工具欄中,點擊 CONSTRAINTS 字樣右邊的綠色的加號按鈕。這將自動為 Collection View 創建約束。
自動創建的約束大部分正確,但也需要對其中一些進行調整。在屬性窗口中,切換到 Layout 標簽,拉到 Constraints 一欄。
邊距中的兩個約束是正確的,但寬高約束不正確。刪除 Width 和 Height 約束(點擊它們右邊的 X 按鈕)。
注意,Collection View 此時變成橙色。這表明約束不正確。
點擊 Collection View 以選中它。如果你看到之前一樣的圓圈,再點擊它一次切換到綠色的 T 字圖標。點擊並拖放 Collection View 上端的 T 字直到綠色的名為 Top Layout Guide 的外框處。放開鼠標左鍵,這將創建一條相對於視圖頂部的約束。
然後,點擊並向左拖放 Collection View 左邊的 T 字直到看到一條藍色的虛線。放開鼠標左鍵,這將創建一條相對於視圖左邊緣的約束。
這時,你的約束應該是這樣的:
看見 Collection View 中的小方塊了嗎?在這個方塊中有一個紅色的驚歎號。這就是一個 Collection View Cell,表示 Collection View 中的一個單元格。
要配置這個 cell 的大小,需要在 Collection View 中進行。選中 Collection View,上拉到 Layout 標簽的頂部。在 Cell Size 小節,將其 Width 和 Height 設置為 100。
然後,點擊 Collection View Cell 中的紅色驚歎號,這將彈出一個提示窗口,說你還沒有為 cell 分配一個 reuse identifier。因此,選中 cell,打開 Widget 標簽,下拉到 Collection Reusable View 小節,將 Identifier 設置為 ImageCellIdentifier。這將讓這個錯誤消失。
繼續下拉,來到 Interaction 小節,將 Background Color 設置為 Predefined 中的藍色。
現在,場景效果變成:
上拉到 Widget 節頂部,將 Class 設置為 PhotoCollectionImageCell。
Visual Studio 會自動創建同一類名的類,繼承 UICollectionViewCell,並自動創建一個 PhotoCollectionImageCell.cs 文件。唉,什麼時候 Xcode 才能和 Visual Studio 一樣?!
你還需要創建一個類,充當 UICollectionViewDataSource,為 Collection View 提供數據。
在解決方案管理器的 ImageLocation 上右擊,選擇 Add \ Class, 類名為 PhotoCollectionDataSource.cs 然後點擊 Add。
打開新創建的 PhotoCollectionDataSource.cs,然後在文件頂部寫入:
using UIKit;
這將導入 iOS UIKit 框架。
然後是類定義:
public class PhotoCollectionDataSource : UICollectionViewDataSource { }
還記得你為 Collection View Cell 定義的 reuse identifier 嗎?在這裡我們將會用到它。在類定義中加入:
private static readonly string photoCellIdentifier = "ImageCellIdentifier";
UICollectionViewDataSource 類中有兩個抽象方法必須實現。在類中加入:
public override UICollectionViewCell GetCell(UICollectionView collectionView, NSIndexPath indexPath) { var imageCell = collectionView.DequeueReusableCell(photoCellIdentifier, indexPath) as PhotoCollectionImageCell; return imageCell; } public override nint GetItemsCount(UICollectionView collectionView, nint section) { return 7; }
GetCell() 方法負責提供一個用於在 Collection View 中顯示的 cell。
DequeueReusableCell 方法會重用那些不再使用的 cell,例如那些已經不需要在屏幕上顯示的 cell,然後返回該 cell。如果沒有可重用的 cell,則會創建一個新的 cell。
GetItemsCount 方法負責告訴 Collection View 需要顯示多少(7 個) cell。
然後需要在 ViewController 類中添加一個 Collection View 的引用,ViewController 就是管理著 Collection View 的那個 Scene。回到 Main.storyboard,選擇 Collection View,來到 Widget 標簽,將 Name 設為 collectionView。
Visual Studio 會自動在 ViewController 類中創建一個名為 collectionView 的實例變量。
注意,在 ViewController.cs 中你無法看到這個實例變量。要看到這個變量,你需要點擊 ViewController.cs 左邊的右箭頭,以打開 ViewController.designer.cs。這裡才看得見 Visual Studio 為你創建的實例變量。
從解決方案管理器中打開 ViewController.cs,在類中添加如下字段:
private PhotoCollectionDataSource photoDataSource;
在 ViewDidLoad()最後,添加代碼,初始化數據源並將它綁定到 Collection View:
photoDataSource = new PhotoCollectionDataSource(); collectionView.DataSource = photoDataSource;
這樣,photoDataSource 就能夠為 Collection View 提供數據了。
編譯運行程序。你會看到 Collection View 顯示了 7 個藍色方塊。
好極了—— 一切順利!
藍色方塊搞定了,接下來是將數據源變成從設備中獲取的圖片,然後在 Collection View 中顯示它們。你將用 Photos 框架訪問來自 Photos app 的照片、視頻。
接下來,你需要在 cell 中加入一個 Image View。打開 Main.stroyboard,選擇 Collection View Cell。在 Widget 標簽,下拉並設置 Background Color 為默認。
在工具箱中,搜索 image view,然後拖一個 Image View 到 cell 中。
Image View 的默認大小比 cell 大,要修改其大小,選擇這個 Image View 然後在 Properties \ Layout 標簽的 View 小節下面,將 X 和 Y 設為 0 ,Width 和 Height 設為 100。
切換到 Widget 標簽,將 Name 設置為 cellImageView。Visual Studio 會自動創建一個名為 cellImageView 的變量。
拉到 View 小節,將 Mode 設為 Aspect Fill。這將防止圖片被縮放。
注意:在 PhotoCollectionImageCell.cs 中無法看到 cellImageView 變量。這個類是分部類,這個變量在另外一個文件中。
在解決方案管理器中,點擊 PhotoCollectionImageCell.cs 左邊的箭頭,展開它。打開 PhotoCollectionImageCell.designer.cs,你將看到 cellImageView 變量聲明。
這個文件是自動創建的,不要去改變它。否則,它們會在你不知道的情況下被覆蓋,或者導致類和故事板之間的綁定被打斷,從而導致運行時錯誤。
這個變量不是公有的,因此別的類無法訪問它。因此,你需要提供一個訪問它的方法,以便我們能夠改變 Image View 上顯示的圖片。
打開 PhotoCollectionImageCell.cs 添加如下方法:
public void SetImage(UIImage image) { cellImageView.Image = image; }
現在你可以讓 PhotoCollectionDataSource 去抓取照片了。
在 PhotoCollectionDataSource.cs 的頂部:
using Photos;
在 PhotoCollectionDataSource 增加變量:
private PHFetchResult imageFetchResult; private PHImageManager imageManager;
imageFetchResult 變量用於存儲照片對應 Asset 的數組,然後通過 imageManager 對象來獲取照片數據。
在 GetCell() 方法前,添加構造方法:
public PhotoCollectionDataSource() { imageFetchResult = PHAsset.FetchAssets(PHAssetMediaType.Image, null); imageManager = new PHImageManager(); }
這個構造方法從 Photos app 中抓取所有圖片資源,並將結果放到 imageFetchResult 變量中。然後初始化 imageManager,app 用它來查詢每一張照片的具體數據。
在構造方法下面,添加析構方法,將 imageManager 對象釋放:
~PhotoCollectionDataSource() { imageManager.Dispose(); }
在 GetItemsCount 和 GetCell 方法中用新數據源中的圖片替換原來的空 cell。修改 GetItemsCount() 方法為:
public override nint GetItemsCount(UICollectionView collectionView, nint section) { return imageFetchResult.Count; }
修改 GetCell 方法為:
public override UICollectionViewCell GetCell(UICollectionView collectionView, NSIndexPath indexPath) { var imageCell = collectionView.DequeueReusableCell(photoCellIdentifier, indexPath) as PhotoCollectionImageCell; // 1 var imageAsset = imageFetchResult[indexPath.Item] as PHAsset; // 2 imageManager.RequestImageForAsset(imageAsset, new CoreGraphics.CGSize(100.0, 100.0), PHImageContentMode.AspectFill, new PHImageRequestOptions(), // 3 (UIImage image, NSDictionary info) => { // 4 imageCell.SetImage(image); }); return imageCell; }
以上代碼分別進行說明如下:
indexPath 表明當前將返回哪一個 cell 。其 Item 屬性表示了 cell 的索引。 我們根據這個索引獲得圖片資源並將之轉換為 PHAsset 對象。 用 imageManager 對象去請求獲取 PHAsset 所對應的圖片,同時指定了所需圖片的大小和縮放模式。 許多 iOS 框架中的方法都會在執行耗時任務時使用延遲執行,當任務完成時再調用委托方法。以 RequestImageForAsset 方法為例,當請求完成時,委托方法將被調用,所請求的圖片和相關信息將通過參數傳遞到委托方法。 最後,設置 cell 中的圖片。編譯運行。你會被詢問需要訪問權限。
如果你選擇 OK,app 什麼也不會顯示。搞毛啊!
iOS 認為照片屬於用戶的敏感信息,需要經過用戶授權。但是當用戶同意授權之後, app 也必須注冊接收相應的通知,以便重新刷新視圖。也就是你接下來的工作。
首先,你需要在 PhotoCollectionDataSource 類中增加一個方法以便當照片庫內容發生改變後重新抓取數據。在類中加入以下方法:
public void ReloadPhotos() { imageFetchResult = PHAsset.FetchAssets(PHAssetMediaType.Image, null); }
然後,打開 ViewController.cs 導入 photos 框架:
using Photos;
在 ViewDidLoad() 方法中:
// 1 PHPhotoLibrary.SharedPhotoLibrary.RegisterChangeObserver((changeObserver) => { //2 InvokeOnMainThread(() => { // 3 photoDataSource.ReloadPhotos(); collectionView.ReloadData(); }); });
上述代碼負責:
將 app 注冊為接收照片庫改變通知,當照片庫內容改變時調用指定代碼。 InvokeOnMainThread() 方法在主線程中刷新 UI,否則會導致 app 崩潰。 調用 photoDataSource.ReloadPhotos() 重新獲取照片,調用 collectionView.ReloadData() 讓 Collection View 重繪。最後,我們來解決前面的問題,在 app 還沒有得到相冊訪問權限時,請求用戶授權。
在 ViewDidLoad() 方法中,在初始化 photoDataSource 之前加入:
if (PHPhotoLibrary.AuthorizationStatus == PHAuthorizationStatus.NotDetermined) { PHPhotoLibrary.RequestAuthorization((PHAuthorizationStatus newStatus) => { }); }
這裡需要檢查當前授權狀態,如果用戶未授權,提示用戶進行授權。
為了再次提示用戶授權,你需要通過 Simulator \ Reset Content and Settings 重置模擬器。
編譯運行。你會看到照片訪問授權的提示,如果你選擇 OK,這個 app 會在 Collection View 中顯示照片的縮略圖!
你可以從這裡下載完整的 Visual Studio 項目。
在本教程中,你學習了如何配置 Xamarin 以及如何用它來創建 iOS app。
在 Xamarin 指南網站 有幾個優秀的學習資源。要了解更多關於創建跨平台 app 的內容,請查看 Xamarin 教程關於創建同一應用的 iOS 和 Android app。
微軟收購 Xamarin 後做出了一些令人贊歎的改變。 在微軟的 Build 打回和 Xamarin Evolve 中你會看到這種傾向。Xamarin 發布了最近 Evolve 大會的會議視頻,這些視頻詳細介紹了關於如何使用 Xamarin 的信息和未來的產品方向。
你會用 Xamarin 創建 app 嗎?如果你對本文有任何問題建議,請在下面留言。