Пошаговое руководство. Фоновое расположение в Xamarin.iOS
В этом примере мы создадим приложение расположения iOS, которое выводит сведения о текущем расположении: широте, долготе и других параметрах на экран. В этом приложении показано, как правильно выполнять обновления расположения, пока приложение является активным или фоновым.
В этом пошаговом руководстве описываются некоторые основные понятия фонового фона, включая регистрацию приложения в качестве фонового приложения, приостановку обновлений пользовательского интерфейса при фоновом режиме и работу с WillEnterBackground
методами.WillEnterForeground
AppDelegate
Настройка приложения
Сначала создайте приложение с одним представлением приложения > iOS > (C#). Вызовите его расположение и убедитесь, что выбраны iPad и iPhone.
Приложение расположения определяется как фоновое приложение в iOS. Зарегистрируйте приложение в качестве приложения location, изменив файл Info.plist для проекта.
В разделе Обозреватель решений дважды щелкните файл Info.plist, чтобы открыть его, и прокрутите внизу списка. Установите флажок " Включить фоновые режимы " и флажки "Обновления расположения".
В Visual Studio для Mac будет выглядеть примерно так:
В Visual Studio info.plist необходимо обновить вручную, добавив следующую пару "ключ-значение":
<key>UIBackgroundModes</key> <array> <string>location</string> </array>
Теперь, когда приложение зарегистрировано, оно может получить данные о расположении с устройства. В iOS
CLLocationManager
класс используется для доступа к сведениям о расположении и может вызывать события, предоставляющие обновления расположения.В коде создайте новый класс
LocationManager
, который предоставляет одно место для различных экранов и кода для подписки на обновления расположения.LocationManager
В классе сделайте экземпляр вызываемогоCLLocationManager
:LocMgr
public class LocationManager { protected CLLocationManager locMgr; public LocationManager () { this.locMgr = new CLLocationManager(); this.locMgr.PausesLocationUpdatesAutomatically = false; // iOS 8 has additional permissions requirements if (UIDevice.CurrentDevice.CheckSystemVersion (8, 0)) { locMgr.RequestAlwaysAuthorization (); // works in background //locMgr.RequestWhenInUseAuthorization (); // only in foreground } if (UIDevice.CurrentDevice.CheckSystemVersion (9, 0)) { locMgr.AllowsBackgroundLocationUpdates = true; } } public CLLocationManager LocMgr { get { return this.locMgr; } } }
Приведенный выше код задает ряд свойств и разрешений для класса CLLocationManager :
PausesLocationUpdatesAutomatically
— Это логическое значение, которое можно задать в зависимости от того, разрешено ли системе приостановить обновления расположения. На некоторых устройствахtrue
оно по умолчанию, которое может привести к остановке получения обновлений фонового расположения через около 15 минут.RequestAlwaysAuthorization
— Этот метод следует передать, чтобы предоставить пользователю приложения возможность разрешить доступ к расположению в фоновом режиме.RequestWhenInUseAuthorization
Также можно передать, если вы хотите предоставить пользователю возможность разрешить доступ к расположению только в том случае, если приложение находится на переднем плане.AllowsBackgroundLocationUpdates
— Это логическое свойство, введенное в iOS 9, которое можно задать, чтобы приложение получало обновления расположения при приостановке.
Внимание
Для iOS 8 (и более поздней версии) также требуется запись в файле Info.plist для отображения пользователя в рамках запроса авторизации.
Добавьте ключи Info.plist для типов разрешений, необходимых приложению ,
NSLocationWhenInUseUsageDescription
NSLocationAlwaysUsageDescription
и (или)NSLocationAlwaysAndWhenInUseUsageDescription
с строкой, которая будет отображаться пользователю в оповещении, запрашивающей доступ к данным о расположении.Для iOS 9 требуется, чтобы при использовании
AllowsBackgroundLocationUpdates
Info.plist был включен ключUIBackgroundModes
со значениемlocation
. Если вы выполнили шаг 2 этого пошагового руководства, это уже должно быть в файле Info.plist.LocationManager
В классе создайте метод, который вызываетсяStartLocationUpdates
с помощью следующего кода. В этом коде показано, как начать получать обновления расположения изCLLocationManager
:if (CLLocationManager.LocationServicesEnabled) { //set the desired accuracy, in meters LocMgr.DesiredAccuracy = 1; LocMgr.LocationsUpdated += (object sender, CLLocationsUpdatedEventArgs e) => { // fire our custom Location Updated event LocationUpdated (this, new LocationUpdatedEventArgs (e.Locations [e.Locations.Length - 1])); }; LocMgr.StartUpdatingLocation(); }
В этом методе происходит несколько важных действий. Сначала мы проверяем, имеет ли приложение доступ к данным о расположении на устройстве. Мы проверяем это путем вызова
LocationServicesEnabled
CLLocationManager
. Этот метод возвращает значение false , если пользователь отказал приложению в доступе к сведениям о расположении.Затем сообщите диспетчеру расположений, как часто обновляться.
CLLocationManager
предоставляет множество параметров фильтрации и настройки данных расположения, включая частоту обновлений. В этом примере задайтеDesiredAccuracy
для обновления каждый раз, когда расположение изменяется на метр. Дополнительные сведения о настройке частоты обновления расположения и других настроек см . в справочнике по классу CLLocationManager в документации Apple.Наконец, вызовите
StartUpdatingLocation
CLLocationManager
экземпляр. Это сообщает диспетчеру расположений получить первоначальное исправление в текущем расположении и начать отправку обновлений.
До сих пор диспетчер расположений был создан, настроен с типами данных, которые мы хотим получить, и определил исходное расположение. Теперь коду необходимо отобразить данные расположения в пользовательском интерфейсе. Это можно сделать с помощью пользовательского события, которое принимает в CLLocation
качестве аргумента:
// event for the location changing
public event EventHandler<LocationUpdatedEventArgs>LocationUpdated = delegate { };
Следующим шагом является подписка на обновления расположения из него CLLocationManager
и создание настраиваемого LocationUpdated
события, когда новые данные расположения становятся доступными, передавая расположение в качестве аргумента. Для этого создайте новый класс LocationUpdateEventArgs.cs. Этот код доступен в основном приложении и возвращает расположение устройства при возникновении события:
public class LocationUpdatedEventArgs : EventArgs
{
CLLocation location;
public LocationUpdatedEventArgs(CLLocation location)
{
this.location = location;
}
public CLLocation Location
{
get { return location; }
}
}
Пользовательский интерфейс
Используйте построитель интерфейсов Xcode для создания экрана, в котором будут отображаться сведения о расположении. Дважды щелкните файл Main.storyboard , чтобы начать работу.
На раскадровке перетащите на экран несколько меток, чтобы они действовали в качестве заполнителей для сведений о расположении. В этом примере существуют метки широты, долготы, высоты, курса и скорости.
Дополнительные сведения см. в статье Проектирование пользовательских интерфейсов с помощью Xcode.
На панели решения дважды щелкните
ViewController.cs
файл и измените его, чтобы создать новый экземпляр LocationManager и вызватьStartLocationUpdates
его. Измените код следующим образом:#region Computed Properties public static bool UserInterfaceIdiomIsPhone { get { return UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Phone; } } public static LocationManager Manager { get; set;} #endregion #region Constructors public ViewController (IntPtr handle) : base (handle) { // As soon as the app is done launching, begin generating location updates in the location manager Manager = new LocationManager(); Manager.StartLocationUpdates(); } #endregion
Это приведет к запуску обновлений расположения при запуске приложения, хотя данные не будут отображаться.
Теперь, когда обновления расположения получены, обновите экран с информацией о расположении. Следующий метод получает расположение из нашего
LocationUpdated
события и отображает его в пользовательском интерфейсе:#region Public Methods public void HandleLocationChanged (object sender, LocationUpdatedEventArgs e) { // Handle foreground updates CLLocation location = e.Location; LblAltitude.Text = location.Altitude + " meters"; LblLongitude.Text = location.Coordinate.Longitude.ToString (); LblLatitude.Text = location.Coordinate.Latitude.ToString (); LblCourse.Text = location.Course.ToString (); LblSpeed.Text = location.Speed.ToString (); Console.WriteLine ("foreground updated"); } #endregion
Нам по-прежнему нужно подписаться на LocationUpdated
событие в AppDelegate и вызвать новый метод для обновления пользовательского интерфейса. Добавьте следующий код ViewDidLoad,
сразу после StartLocationUpdates
вызова:
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
// It is better to handle this with notifications, so that the UI updates
// resume when the application re-enters the foreground!
Manager.LocationUpdated += HandleLocationChanged;
}
Теперь, когда приложение запускается, оно должно выглядеть примерно так:
Обработка активных и фоновых состояний
Приложение выводит обновления расположения, пока он находится на переднем плане и активен. Чтобы продемонстрировать, что происходит, когда приложение входит в фон, переопределите
AppDelegate
методы, отслеживающие изменения состояния приложения, чтобы приложение записывает в консоль при переходе между передним планом и фоном:public override void DidEnterBackground (UIApplication application) { Console.WriteLine ("App entering background state."); } public override void WillEnterForeground (UIApplication application) { Console.WriteLine ("App will enter foreground"); }
Добавьте в выходные данные приложения в следующий код для
LocationManager
непрерывной печати обновленных данных о расположении, чтобы убедиться, что сведения о расположении по-прежнему доступны в фоновом режиме:public class LocationManager { public LocationManager () { ... LocationUpdated += PrintLocation; } ... //This will keep going in the background and the foreground public void PrintLocation (object sender, LocationUpdatedEventArgs e) { CLLocation location = e.Location; Console.WriteLine ("Altitude: " + location.Altitude + " meters"); Console.WriteLine ("Longitude: " + location.Coordinate.Longitude); Console.WriteLine ("Latitude: " + location.Coordinate.Latitude); Console.WriteLine ("Course: " + location.Course); Console.WriteLine ("Speed: " + location.Speed); } }
Существует одна из оставшихся проблем с кодом: попытка обновить пользовательский интерфейс, когда приложение фоном приведет к его прекращению. Когда приложение переходит в фон, код должен отменить подписку из обновлений расположения и прекратить обновление пользовательского интерфейса.
iOS предоставляет нам уведомления, когда приложение будет переходить к другим состояниям приложения. В этом случае мы можем подписаться на
ObserveDidEnterBackground
уведомление.В следующем фрагменте кода показано, как использовать уведомление, чтобы сообщить представлению об остановке обновлений пользовательского интерфейса. Это будет идти в
ViewDidLoad
:UIApplication.Notifications.ObserveDidEnterBackground ((sender, args) => { Manager.LocationUpdated -= HandleLocationChanged; });
При запуске приложения выходные данные будут выглядеть примерно так:
Приложение выводит обновления на экран при работе на переднем плане и продолжает выводить данные в окно вывода приложения во время работы в фоновом режиме.
Остается только одна проблема: экран запускает обновления пользовательского интерфейса при первой загрузке приложения, но не имеет способа знать, когда приложение повторно ввело передний план. Если фоновое приложение возвращается на передний план, обновления пользовательского интерфейса не возобновляются.
Чтобы устранить эту проблему, вложен вызов для запуска обновлений пользовательского интерфейса в другом уведомлении, что приведет к срабатыванию приложения при входе в активное состояние:
UIApplication.Notifications.ObserveDidBecomeActive ((sender, args) => {
Manager.LocationUpdated += HandleLocationChanged;
});
Теперь пользовательский интерфейс начнет обновляться при первом запуске приложения и возобновляет обновление в любой момент, когда приложение возвращается на передний план.
В этом пошаговом руководстве мы создали хорошо функционирующее фоновое приложение iOS, которое печатает данные о расположении на экране и в окне вывода приложения.