如何在 Mac 上用 C# 製作 Android 和 iOS 應用程序
已發表: 2022-03-11曾幾何時,有一家公司擁有所有最好的工具,為他們的平台編寫軟件非常棒。 但慢慢地,他們對自己的問題變得漠不關心。 當他們的系統崩潰時,他們並沒有驚慌失措,而是接受了宇宙的這種狀態作為生命的事實。 他們相信他們的節目本身就是完美的,寧靜而優雅,他們的目的不言而喻。
哦,男孩,如果他們只知道他們錯了……
當他們意識到自己的錯誤並且他們的 CEO 哭著要帶回所有離開他們平台並揚帆遠航的開發人員時,已經遲到了。 這家公司就是微軟,我相信他們的命運已經註定,他們會慢慢地但肯定會從技術領域的最前沿消失。
我很高興我錯了!
在過去的幾年裡,微軟已經從袖子裡拿出了幾張王牌。 是的,他們搞砸了 Skype(我仍然討厭他們),在智能手機上失敗了,在平板電腦上幾乎成功了。 但是,他們也做了一些非常了不起的事情。 他們放棄了封閉的帝國方法,開源了 .NET,加入了 Linux 基金會,發布了適用於 Linux 的 SQL Server,並創建了這個名為 Visual Studio for Mac 的偉大新工具。
沒錯,一個真正的Microsoft IDE,不是用於 Windows,而是用於 Mac。 想像一下!
在 Mac 上使用 C# 編寫您的第一個跨平台 Android 和 iOS 應用程序
您可以使用 Visual Studio for Mac 創建幾乎任何類型的應用程序。 它可以是 iOS、tvOS、Android、Mac、.NET Core,甚至是 ASP.NET。 由於所有酷孩子現在都在編寫移動應用程序,讓我們看看在 Visual Studio for Mac 中創建將在 Android 和 iOS 上運行的 C# 應用程序需要什麼。
您需要做的第一件事是選擇應用程序模板。 讓我們從一個簡單的“單一視圖應用程序”開始。
填寫包名稱並引導您的應用程序後,Visual Studio 將創建一個包含三個項目的解決方案。 第一個項目將是一個共享庫,您應該在其中保留與平台無關的代碼,另外兩個將是 Android 和 iOS 應用程序。
您可以使用“運行”菜單或應用程序欄中的命令來啟動您的應用程序。
恭喜! 您現在是一名 iOS 和 Android 開發人員,儘管您從未編寫過一行 Objective-C、Swift 或 Java 代碼。
不過,我們的應用程序還沒有真正取得太多成就。 讓我們讓事情變得更有趣,並結合地圖和位置服務。
使用地圖和定位服務
請記住,用於 Mac 的 VS 仍處於“預覽”狀態,您在使用它時找不到太多幫助和文檔。 關於如何做事的最佳參考仍然是官方 Xamarin 文檔。
Visual Studio For Mac 不使用與你可能在 PC 上看到的 Xamarin 工具相同的解決方案和應用程序結構。 在大多數情況下,您需要嘗試並繞過一些障礙才能使他們的示例正常工作。 讓我們希望,一旦 VS for Mac 的最終版本發布,Microsoft 將保持領先地位,並提供令人敬畏的 MSDN 資源集合。
在 iOS 上顯示當前位置
訪問移動設備資源(例如當前位置)需要用戶“手動”授予您的應用程序使用這些資源的權限。 iOS 使用文件info.plist
來存儲這些設置。 VS for Mac 提供了一個可視化的界面來編輯這個文件。 我們需要做的第一件事是為名為NSLocationWhenInUseUsageDescription
的設置添加一個值。
注意:設置屬性名稱時,VS 會顯示“NSLocationWhenInUseUsageDescription”的長名稱。 這是意料之中的,不用擔心。
我們的引導應用程序是使用一個簡單的按鈕創建的,該按鈕用於計算點擊次數。 您要做的第一件事是將其刪除並用地圖替換屏幕內容。 為此,請在解決方案瀏覽器中查找Main.storyboard
文件並雙擊它以在編輯器中打開它。
情節提要由 Apple 引入並被 Xamarin 採用。 有關詳細信息,請參閱 Apple 文檔或 Xamarin 文檔。
移除按鈕並將地圖視圖組件添加到頁面。
確保正確命名您的“mapView”組件。
現在剩下的就是清理ViewController.cs
文件,並修改ViewDidLoad()
方法以匹配以下內容:
using CoreLocation; public override void ViewDidLoad() { base.ViewDidLoad(); // Perform any additional setup after loading the view, typically from a nib. CLLocationManager locationManager = new CLLocationManager(); locationManager.RequestWhenInUseAuthorization(); mapView.ShowsUserLocation = true; }
您可以使用“快速修復”功能讓 VS 自動添加對 CoreLocation 庫的引用,也可以手動添加它。
運行 iOS 應用程序後,您應該會看到訪問您的位置的請求。 一旦獲得許可,您的地圖將加載一個標準的藍點,顯示您所在的位置(或您使用 iOS 模擬器偽裝的位置 :))。
在 Android 上顯示當前位置
不幸的是,谷歌和微軟決定讓這個簡單的任務比 iOS 更複雜一些。 為了在 Android 應用程序中使用地圖,您需要創建 Google Maps API 密鑰,並將其添加到您的AndroidManifest.xml
文件中。
Xamarin 開發人員為獲取 Google Maps API 密鑰創建了一個非常簡單的指南。 在繼續之前,請按照他們指南中的步驟操作。 完成後,您的AndroidManifest.xml
應包含如下設置:
<meta-data android:name="com.google.android.maps.v2.API_KEY" android:value="YOUR KEY" />
您現在已準備好將地圖添加到您的應用程序。
VS for Mac 的偉大之處在於它由 NuGet 提供支持,就像它的老大哥一樣。 由於默認情況下不包含地圖處理庫,因此您需要安裝Xamarin.Forms.Maps
包。
但是,沒有“地圖視圖”組件可以拖到“活動”中。 相反,將地圖添加到屏幕需要手動更改 Resources->layout->Main.axml 文件。 您可以使用設計器視圖刪除之前創建的按鈕,然後切換到“代碼視圖”並在您的LinearLayout
中添加以下片段代碼:
<fragment xmlns:andro android: android:layout_width="match_parent" android:layout_height="match_parent" class="com.google.android.gms.maps.MapFragment" />
與 iOS 一樣,您需要配置您的應用程序以請求適當的權限。 為此,請打開AndroidManifest.xml
進行編輯,然後單擊編輯器左下方的“應用程序”按鈕。 VS 將向您顯示用於設置這些值的可視界面。 您需要啟用其中一些,如下所示。
現在是時候編寫一些真正的代碼了。 找到MainActivity.cs
文件,打開它進行編輯,並進行以下更改:
添加命名空間引用:
using Android.Gms.Maps.Model; using Android.Gms.Maps; using Android.Locations; Make your MainActivity also a ILocationListener. public class MainActivity : Activity, ILocationListener Implement the ILocationListener methods within your MainActivity: public void OnProviderEnabled(string provider) {} public void OnProviderDisabled(string provider) {} public void OnStatusChanged(string provider, Availability status, Bundle extras) {} public void OnLocationChanged(Android.Locations.Location location) { LatLng latLng = new LatLng(location.Latitude, location.Longitude); CameraPosition.Builder builder = CameraPosition.InvokeBuilder(); builder.Target(latLng); builder.Zoom(15); builder.Bearing(155); builder.Tilt(10); CameraPosition cameraPosition = builder.Build(); CameraUpdate cameraUpdate = CameraUpdateFactory.NewCameraPosition(cameraPosition); MapFragment mapFrag = (MapFragment)FragmentManager.FindFragmentById(Resource.Id.map); GoogleMap map = mapFrag.Map; if (map != null) { map.MoveCamera(cameraUpdate); } }
添加以下兩個變量作為類級變量:

LocationManager locMgr; string locationProvider;
並清理OnCreate()
方法,如下所示:
protected override void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState); // Set our view from the "main" layout resource SetContentView(Resource.Layout.Main); locMgr = GetSystemService(LocationService) as LocationManager; Criteria locationCriteria = new Criteria(); locationCriteria.Accuracy = Accuracy.Coarse; locationCriteria.PowerRequirement = Power.Medium; locationProvider = locMgr.GetBestProvider(locationCriteria, true); locMgr.RequestLocationUpdates(locationProvider, 2000, 1, this); }
通過從OnCreate()
方法中調用 GetSystemService,您的MainActivity
將作為ILocationListener
激活,從而能夠處理上面列出的所有事件。
運行您的 Android 應用程序,您應該將地圖定位到您的位置,類似於下圖。
使用適用於 iOS 和 Android 的共享庫
VS for Mac 的最大特點之一是可以在 iOS 和 Android 應用程序之間共享代碼。 理想情況下,我們可以將應用程序的所有業務邏輯放在一個共享庫中,將任何 iOS 和 Android 特定代碼限制為 UI 的一部分。
讓我們創建一個共享類,它將異步執行 HTTP 請求並在調試控制台中顯示內容。
使用以下代碼在名為RestClient.cs
的共享庫中創建一個新類文件:
(確保使用項目中的正確命名空間)
using System; using System.Net; namespace testshared { public delegate void callback(string responseText); class ReqState { public ReqState(HttpWebRequest req, callback cb) { request = req; callback = cb; } public HttpWebRequest request { get; set; } public callback callback; } public class RestClient { public RestClient() {} public void FetchPage(string url, callback cb) { HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); request.BeginGetResponse(new AsyncCallback(FinishWebRequest), new ReqState(request, cb)); } private void FinishWebRequest(IAsyncResult result) { ReqState reqState = (result.AsyncState as ReqState); HttpWebResponse response = reqState.request.EndGetResponse(result) as HttpWebResponse; using (var reader = new System.IO.StreamReader(response.GetResponseStream())) { string responseText = reader.ReadToEnd(); reqState.callback(responseText); } } } }
在 iOS 上使用庫
修改 iOS 項目中的ViewController.cs
文件以匹配以下代碼:
(確保使用項目中的正確命名空間)
using System; using UIKit; using System.Diagnostics; namespace testshared.iOS { public partial class ViewController : UIViewController { RestClient rest = new RestClient(); public ViewController(IntPtr handle) : base(handle) {} public override void ViewDidLoad() { base.ViewDidLoad(); // Perform any additional setup after loading the view, typically from a nib. Button.AccessibilityIdentifier = "myButton"; Button.TouchUpInside += delegate { Button.SetTitle("Loading...", UIControlState.Normal); rest.FetchPage("http://www.google.com", doneCallback); }; } public void doneCallback(string content) { InvokeOnMainThread(() => { Debug.Write(content); Button.SetTitle("All Done", UIControlState.Normal); }); } public override void DidReceiveMemoryWarning() { base.DidReceiveMemoryWarning(); // Release any cached data, images, etc that aren't in use. } } }
運行您的 iOS 應用程序,單擊按鈕並檢查 Visual Studio 中的“應用程序輸出”選項卡。 它應該顯示如下內容:
在 Android 上使用庫
Android 應用程序所需的更改與 iOS 應用程序所需的更改非常相似。 修改MainActivity.cs
文件以匹配以下內容:
(確保使用項目中的正確命名空間)
using Android.App; using Android.Widget; using Android.OS; namespace testshared.Droid { [Activity(Label = "testshared", MainLauncher = true, Icon = "@mipmap/icon")] public class MainActivity : Activity { RestClient rest = new RestClient(); protected override void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState); // Set our view from the "main" layout resource SetContentView(Resource.Layout.Main); // Get our button from the layout resource, // and attach an event to it Button button = FindViewById<Button>(Resource.Id.myButton); button.Click += delegate { button.Text = $"Loading..."; rest.FetchPage("http://www.google.com", doneCallback); }; } public void doneCallback(string content) { RunOnUiThread(() => { Button button = FindViewById<Button>(Resource.Id.myButton); button.Text = "All done"; System.Diagnostics.Debug.WriteLine(content); }); } } }
注意: Android 和 iOS 兩個平台的系統架構都要求所有 UI 交互都發生在主應用程序線程上。 這意味著對 UI 元素的任何更改也應該在主線程中發生。 這就是RunOnUiThread
和InvokeOnMainThread
的用武之地。由於 HTTP 請求在單獨的線程中執行,並且在主線程之外調用了doneCallback()
,因此我們必須使用這些方法才能訪問按鈕並更改標籤。
C# 開發人員正在接管 Android 和 iOS
Visual Studio for Mac 仍有一些問題需要解決,但從第一眼看到它,我對它的未來感到非常興奮。 對移動應用程序的需求每天都在增長,借助 Visual Studio for Mac,Microsoft 進一步使一支優秀的 C# 開發人員大軍能夠滿足這一需求。