Как сделать приложение для Android и iOS на C# на Mac
Опубликовано: 2022-03-11Жила-была компания, у которой были все лучшие инструменты, и писать программы для их платформы было круто. Но постепенно они стали безразличны к собственным проблемам. Они не встревожились, когда их системы вышли из строя, а скорее приняли это состояние вселенной как факт жизни. Они верили, что их программы совершенны сами по себе, безмятежны и элегантны, а их цель очевидна.
О боже, если бы они только знали, как они ошибались…
Давно пора было, когда они осознали свои ошибки, и их генеральный директор заплакал, чтобы вернуть всех разработчиков, которые покинули свою платформу и уплыли. Это была компания Microsoft , и я, например, был убежден, что их судьба решена и что они медленно, но верно исчезнут с передовой технологического ландшафта.
Я так счастлив, что ошибся!
За последние несколько лет Microsoft вытащила несколько тузов из рукавов. Да, они испортили Skype (до сих пор ненавижу их за это), провалили со смартфонами и почти преуспели с планшетами. Но, они также сделали некоторые действительно удивительные вещи. Отказавшись от подхода закрытой империи, они открыли исходный код .NET, присоединились к Linux Foundation, выпустили SQL Server для Linux и создали новый замечательный инструмент под названием Visual Studio для Mac.
Правильно, настоящая Microsoft IDE не для Windows, а для Mac. Представь это!
Написание вашего первого кроссплатформенного приложения для Android и iOS с использованием C# на Mac
Вы можете использовать Visual Studio для Mac для создания приложений практически любого типа. Это может быть iOS, tvOS, Android, Mac, .NET Core или даже ASP.NET. Поскольку все крутые ребята сейчас пишут мобильные приложения, давайте посмотрим, что нужно в Visual Studio для Mac, чтобы создать приложение C#, которое будет работать на Android и iOS.
Первое, что вам нужно сделать, это выбрать шаблон приложения. Начнем с простого «приложения с одним просмотром».
После ввода имени пакета и начальной загрузки приложения Visual Studio создаст решение с тремя проектами. Первый проект будет общей библиотекой, в которой вы должны хранить независимый от платформы код, а два других будут приложениями для Android и iOS.
Вы можете использовать меню «Выполнить» или команды на панели приложений, чтобы запустить приложение.
Поздравляем! Теперь вы разработчик iOS и Android, несмотря на то, что вы никогда не писали ни строчки кода на Objective-C, Swift или Java.
Однако мы еще не многого добились с нашим приложением. Давайте сделаем вещи более интересными и включим карты и сервисы определения местоположения.
Использование карт и служб определения местоположения
Имейте в виду, что VS для Mac все еще находится в «Предварительной версии», и вы не найдете много помощи и документации по его использованию. Лучшим местом для ссылок о том, как что-то делать, по-прежнему является официальная документация Xamarin.
Visual Studio для Mac не использует то же решение и структуру приложения, что и инструменты Xamarin, которые вы могли видеть на ПК. В большинстве случаев вам нужно будет поэкспериментировать и обойти несколько препятствий, чтобы заставить их примеры работать. Будем надеяться, что Microsoft останется на высоте и предоставит потрясающую коллекцию ресурсов MSDN после выпуска финальной версии VS для Mac.
Отображение текущего местоположения на iOS
Доступ к ресурсам мобильного устройства, таким как текущее местоположение, требует, чтобы пользователи «вручную» предоставляли вашему приложению разрешения на использование этих ресурсов. iOS использует файл info.plist
для хранения этих настроек. VS для Mac предоставляет визуальный интерфейс для редактирования этого файла. Первое, что нам нужно сделать, это добавить значение для параметра с именем NSLocationWhenInUseUsageDescription
.
Примечание. VS покажет длинное имя для «NSLocationWhenInUseUsageDescription», когда вы установите имя свойства. Это ожидаемо, и не беспокойтесь об этом.
Наше загрузочное приложение было создано с помощью простой кнопки, которая считала клики. Первое, что вам нужно сделать, это удалить его и заменить содержимое экрана картой. Для этого найдите файл Main.storyboard
в обозревателе решений и дважды щелкните его, чтобы открыть в редакторе.
Раскадровки представлены Apple и приняты Xamarin. Дополнительные сведения см. в документации Apple или документации Xamarin.
Удалите кнопку и добавьте на страницу компонент Map View.
Убедитесь, что вы правильно назвали свой компонент «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
К сожалению, Google и Microsoft решили немного усложнить эту простую задачу, чем это было с iOS. Чтобы использовать карты в приложении для Android, вам необходимо создать ключ API Карт Google и добавить его в файл AndroidManifest.xml
.
Ребята из Xamarin создали довольно простое руководство по получению ключа API Карт Google. Пожалуйста, следуйте инструкциям в их руководстве, прежде чем продолжить. Когда вы закончите, ваш AndroidManifest.xml
должен содержать такой параметр:
<meta-data android:name="com.google.android.maps.v2.API_KEY" android:value="YOUR KEY" />
Теперь вы готовы добавить карту в свое приложение.
Отличительной особенностью VS для 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); }
При вызове GetSystemService из метода OnCreate()
ваша MainActivity
будет активирована как ILocationListener
и, таким образом, сможет обрабатывать все перечисленные выше события.
Запустите приложение для Android, и вы должны установить карту в соответствии с вашим местоположением, как показано на следующем изображении.
Использование общих библиотек для iOS и Android
Одной из замечательных особенностей VS для Mac является возможность совместного использования кода приложениями iOS и Android. В идеале мы могли бы иметь всю бизнес-логику приложения в общей библиотеке, ограничивая любой специфичный для iOS и Android код частью пользовательского интерфейса.
Давайте создадим общий класс, который будет асинхронно выполнять 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
Измените файл ViewController.cs
в проекте iOS, чтобы он соответствовал следующему коду:
(Убедитесь, что вы используете правильное пространство имен из вашего проекта)
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, требует, чтобы все взаимодействия с пользовательским интерфейсом происходили в основном потоке приложения. Это означает, что любое изменение элементов пользовательского интерфейса также должно происходить из основного потока. Вот тут-то и появляются RunOnUiThread
и InvokeOnMainThread
. Поскольку HTTP-запросы выполнялись в отдельном потоке, а doneCallback()
вызывался вне основного потока, нам пришлось использовать эти методы, чтобы получить доступ к кнопкам и изменить метку.
Разработчики C# захватывают Android и iOS
В Visual Studio для Mac еще есть несколько недостатков, которые нужно исправить, но с первого взгляда я очень взволнован ее будущим. Потребность в мобильных приложениях растет с каждым днем, и с выпуском Visual Studio для Mac корпорация Майкрософт позволила армии отличных разработчиков C# удовлетворить эту потребность.