Поделиться через


Пошаговое руководство. Фоновое расположение в Xamarin.iOS

В этом примере мы создадим приложение расположения iOS, которое выводит сведения о текущем расположении: широте, долготе и других параметрах на экран. В этом приложении показано, как правильно выполнять обновления расположения, пока приложение является активным или фоновым.

В этом пошаговом руководстве описываются некоторые основные понятия фонового фона, включая регистрацию приложения в качестве фонового приложения, приостановку обновлений пользовательского интерфейса при фоновом режиме и работу с WillEnterBackground методами.WillEnterForegroundAppDelegate

Настройка приложения

  1. Сначала создайте приложение с одним представлением приложения > iOS > (C#). Вызовите его расположение и убедитесь, что выбраны iPad и i Телефон.

  2. Приложение расположения определяется как фоновое приложение в iOS. Зарегистрируйте приложение в качестве приложения location, изменив файл Info.plist для проекта.

    В разделе Обозреватель решений дважды щелкните файл Info.plist, чтобы открыть его, и прокрутите внизу списка. Поместите проверка как в режимы включения фона, так и в поле "Расположение" Обновления проверка boxes.

    В Visual Studio для Mac будет выглядеть примерно так:

    Поместите проверка как в режиме включения фона, так и в папке

    В Visual Studio info.plist необходимо обновить вручную, добавив следующую пару "ключ-значение":

    <key>UIBackgroundModes</key>
    <array>
      <string>location</string>
    </array>
    
  3. Теперь, когда приложение зарегистрировано, оно может получить данные о расположении с устройства. В iOS CLLocationManager класс используется для доступа к сведениям о расположении и может вызывать события, предоставляющие обновления расположения.

  4. В коде создайте новый класс 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 для отображения пользователя в рамках запроса авторизации.

  5. Добавьте ключи Info.plist для типов разрешений, необходимых приложению , NSLocationWhenInUseUsageDescriptionNSLocationAlwaysUsageDescriptionи (или) NSLocationAlwaysAndWhenInUseUsageDescription с строкой, которая будет отображаться пользователю в оповещении, запрашивающей доступ к данным о расположении.

  6. Для iOS 9 требуется, чтобы при использовании AllowsBackgroundLocationUpdatesInfo.plist был включен ключ UIBackgroundModes со значением location. Если вы выполнили шаг 2 этого пошагового руководства, это уже должно быть в файле Info.plist.

  7. 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();
    }
    

    В этом методе происходит несколько важных действий. Во-первых, мы выполняем проверка, чтобы узнать, имеет ли приложение доступ к данным о расположении на устройстве. Мы проверяем это путем вызова LocationServicesEnabledCLLocationManager. Этот метод возвращает значение false , если пользователь отказал приложению в доступе к сведениям о расположении.

  8. Затем сообщите диспетчеру расположений, как часто обновляться. CLLocationManager предоставляет множество параметров фильтрации и настройки данных расположения, включая частоту обновлений. В этом примере задайте DesiredAccuracy для обновления каждый раз, когда расположение изменяется на метр. Дополнительные сведения о настройке частоты обновления расположения и других настроек см . в справочнике по классу CLLocationManager в документации Apple.

  9. Наконец, вызовите StartUpdatingLocationCLLocationManager экземпляр. Это сообщает диспетчеру расположений получить первоначальное исправление в текущем расположении и начать отправку обновлений.

До сих пор диспетчер расположений был создан, настроен с типами данных, которые мы хотим получить, и определил исходное расположение. Теперь коду необходимо отобразить данные расположения в пользовательском интерфейсе. Это можно сделать с помощью пользовательского события, которое принимает в 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; }
    }
}

Пользовательский интерфейс

  1. Используйте построитель интерфейсов Xcode для создания экрана, в котором будут отображаться сведения о расположении. Дважды щелкните файл Main.storyboard , чтобы начать работу.

    На раскадровке перетащите на экран несколько меток, чтобы они действовали в качестве заполнителей для сведений о расположении. В этом примере существуют метки широты, долготы, высоты, курса и скорости.

    Дополнительные сведения см. в статье Проектирование пользовательских интерфейсов с помощью Xcode.

  2. На панели решения дважды щелкните 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
    

    Это приведет к запуску обновлений расположения при запуске приложения, хотя данные не будут отображаться.

  3. Теперь, когда обновления расположения получены, обновите экран с информацией о расположении. Следующий метод получает расположение из нашего 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;

}

Теперь, когда приложение запускается, оно должно выглядеть примерно так:

Пример запуска приложения

Обработка активных и фоновых состояний

  1. Приложение выводит обновления расположения, пока он находится на переднем плане и активен. Чтобы продемонстрировать, что происходит, когда приложение входит в фон, переопределите 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);
        }
    }
    
  2. Существует одна из оставшихся проблем с кодом: попытка обновить пользовательский интерфейс, когда приложение фоном приведет к его прекращению. Когда приложение переходит в фон, код должен отменить подписку из обновлений расположения и прекратить обновление пользовательского интерфейса.

    iOS предоставляет нам уведомления, когда приложение будет переходить к другим состояниям приложения. В этом случае мы можем подписаться на ObserveDidEnterBackground уведомление.

    В следующем фрагменте кода показано, как использовать уведомление, чтобы сообщить представлению об остановке обновлений пользовательского интерфейса. Это будет идти в ViewDidLoad:

    UIApplication.Notifications.ObserveDidEnterBackground ((sender, args) => {
        Manager.LocationUpdated -= HandleLocationChanged;
    });
    

    При запуске приложения выходные данные будут выглядеть примерно так:

    Пример выходных данных расположения в консоли

  3. Приложение выводит обновления на экран при работе на переднем плане и продолжает выводить данные в окно вывода приложения во время работы в фоновом режиме.

Остается только одна проблема: экран запускает обновления пользовательского интерфейса при первой загрузке приложения, но не имеет способа знать, когда приложение повторно ввело передний план. Если фоновое приложение возвращается на передний план, обновления пользовательского интерфейса не возобновляются.

Чтобы устранить эту проблему, вложен вызов для запуска обновлений пользовательского интерфейса в другом уведомлении, что приведет к срабатыванию приложения при входе в активное состояние:

UIApplication.Notifications.ObserveDidBecomeActive ((sender, args) => {
  Manager.LocationUpdated += HandleLocationChanged;
});

Теперь пользовательский интерфейс начнет обновляться при первом запуске приложения и возобновляет обновление в любой момент, когда приложение возвращается на передний план.

В этом пошаговом руководстве мы создали хорошо функционирующее фоновое приложение iOS, которое печатает данные о расположении на экране и в окне вывода приложения.