Tutorial: Ubicación en segundo plano en Xamarin.iOS
En este ejemplo, crearemos una aplicación de ubicación de iOS que imprime información sobre nuestra ubicación actual: latitud, longitud y otros parámetros en la pantalla. Esta aplicación mostrará cómo realizar correctamente las actualizaciones de ubicación mientras la aplicación está activa o en segundo plano.
En este tutorial se explican algunos conceptos clave de segundo plano, incluido el registro de una aplicación como una aplicación necesaria en segundo plano, la suspensión de las actualizaciones de la interfaz de usuario cuando la aplicación está en segundo plano y el trabajo con los WillEnterBackground
métodos y WillEnterForeground
AppDelegate
.
Configuración de la aplicación
En primer lugar, cree una nueva iOS > Aplicación > Aplicación de vista única (C#). Llámela Ubicación y asegúrese de que se hayan seleccionado tanto iPad como iPhone.
Una aplicación de ubicación se califica como una aplicación necesaria en segundo plano en iOS. Registre la aplicación como una aplicación de ubicación mediante la edición del archivo Info.plist para el proyecto.
En el Explorador de soluciones, haga doble clic en el archivo Info.plist para abrirlo y desplácese hasta la parte inferior de la lista. Coloque una comprobación junto a las casillas Habilitar modos en segundo plano y Actualizaciones de ubicación.
En Visual Studio para Mac, tendrá un aspecto similar al siguiente:
En Visual Studio, Info.plist debe actualizarse manualmente mediante la adición del siguiente par clave-valor:
<key>UIBackgroundModes</key> <array> <string>location</string> </array>
Ahora que la aplicación está registrada, puede obtener datos de ubicación del dispositivo. En iOS, la clase
CLLocationManager
se usa para acceder a información de ubicación y puede generar eventos que proporcionen actualizaciones de ubicación.En el código, cree una nueva clase denominada
LocationManager
que proporcione un solo lugar para que varias pantallas y código se suscriban a actualizaciones de ubicación. En la claseLocationManager
, cree una instancia delCLLocationManager
llamadaLocMgr
: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; } } }
El código anterior establece una serie de propiedades y permisos en la clase CLLocationManager:
PausesLocationUpdatesAutomatically
: es un valor booleano que se puede establecer en función de si se permite al sistema pausar las actualizaciones de ubicación. En algún dispositivo, el valor predeterminado estrue
, lo que puede hacer que el dispositivo deje de obtener actualizaciones de ubicación en segundo plano transcurridos unos 15 minutos.RequestAlwaysAuthorization
: debe pasar este método para dar al usuario de la aplicación la opción de permitir que se acceda a la ubicación en segundo plano.RequestWhenInUseAuthorization
también se puede pasar si desea dar al usuario la opción de permitir que se acceda a la ubicación solo cuando la aplicación está en primer plano.AllowsBackgroundLocationUpdates
: se trata de una propiedad booleana, introducida en iOS 9 que se puede establecer para permitir que una aplicación reciba actualizaciones de ubicación al suspenderse.
Importante
iOS 8 (y versiones posteriores) también requiere una entrada en el archivo Info.plist para mostrar al usuario como parte de la solicitud de autorización.
Agregue claves de Info.plist para los tipos de permisos que requiere la aplicación (
NSLocationAlwaysUsageDescription
,NSLocationWhenInUseUsageDescription
y/oNSLocationAlwaysAndWhenInUseUsageDescription
) con una cadena que se mostrará al usuario en la alerta que solicita acceso a los datos de ubicación.iOS 9 requiere que al usar
AllowsBackgroundLocationUpdates
Info.plist, incluya la claveUIBackgroundModes
con el valorlocation
. Si ha completado el paso 2 de este tutorial, ya debería estar en el archivo Info.plist.Dentro de la clase
LocationManager
, cree un método llamadoStartLocationUpdates
con el código siguiente. Este código muestra cómo empezar a recibir actualizaciones de ubicación deCLLocationManager
: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(); }
Varias cosas importantes suceden en este método. En primer lugar, se realiza una comprobación para ver si la aplicación tiene acceso a los datos de ubicación en el dispositivo. Para comprobarlo, llame a
LocationServicesEnabled
enCLLocationManager
. Este método devolverá false si el usuario ha denegado el acceso de la aplicación a la información de ubicación.A continuación, indique al administrador de ubicaciones la frecuencia con la que actualizar.
CLLocationManager
proporciona muchas opciones para filtrar y configurar datos de ubicación, incluida la frecuencia de las actualizaciones. En este ejemplo, establezcaDesiredAccuracy
para que se actualice cada vez que cambie la ubicación por un medidor. Para obtener más información sobre cómo configurar la frecuencia de actualización de ubicación y otras preferencias, consulte la referencia de clase CLLocationManager en la documentación de Apple.Por último, llame a
StartUpdatingLocation
en la instancia deCLLocationManager
. Esto indica al administrador de ubicación que obtenga una corrección inicial en la ubicación actual y empiece a enviar actualizaciones.
Hasta ahora, el administrador de ubicación se ha creado, configurado con los tipos de datos que queremos recibir y ha determinado la ubicación inicial. Ahora el código debe representar los datos de ubicación en la interfaz de usuario. Podemos hacerlo con un evento personalizado que toma CLLocation
como argumento:
// event for the location changing
public event EventHandler<LocationUpdatedEventArgs>LocationUpdated = delegate { };
El siguiente paso consiste en suscribirse a las actualizaciones de ubicación de CLLocationManager
y generar el evento LocationUpdated
personalizado cuando los nuevos datos de ubicación estén disponibles, pasando la ubicación como argumento. Para ello, cree una nueva clase LocationUpdateEventArgs.cs. Se puede acceder a este código dentro de la aplicación principal y devuelve la ubicación del dispositivo al generarse el evento:
public class LocationUpdatedEventArgs : EventArgs
{
CLLocation location;
public LocationUpdatedEventArgs(CLLocation location)
{
this.location = location;
}
public CLLocation Location
{
get { return location; }
}
}
Interfaz de usuario
Use Interface Builder de Xcode para compilar la pantalla en la que se mostrará información de ubicación. Haga doble clic en el archivo Main.storyboard para comenzar.
En el guion gráfico, arrastre varias etiquetas a la pantalla para que actúen como marcadores de posición para la información de ubicación. En este ejemplo, hay etiquetas de latitud, longitud, altitud, curso y velocidad.
Para obtener más información, consulte Diseño de interfaces de usuario con Xcode.
En el Panel de solución, haga doble clic en el archivo
ViewController.cs
y edítelo para crear una nueva instancia de LocationManager y llamar aStartLocationUpdates
en él. Cambie el código para que tenga un aspecto similar al siguiente:#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
Esto iniciará las actualizaciones de ubicación en el inicio de la aplicación, aunque no se mostrarán datos.
Ahora que se reciben las actualizaciones de ubicación, actualice la pantalla con la información de ubicación. El método siguiente obtiene la ubicación de nuestro evento
LocationUpdated
y la muestra en la interfaz de usuario:#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
Todavía es necesario suscribirse al evento LocationUpdated
en AppDelegate y llamar al nuevo método para actualizar la interfaz de usuario. Agregue el código siguiente en ViewDidLoad,
justo después de la llamada a 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;
}
Ahora, al ejecutarse la aplicación, debería tener un aspecto similar al siguiente:
Control de estados activos y en segundo plano
La aplicación genera actualizaciones de ubicación mientras está en primer plano y activa. Para demostrar lo que sucede cuando la aplicación entra en segundo plano, invalide los métodos
AppDelegate
que realizan un seguimiento de los cambios en el estado de la aplicación para que la aplicación escriba en la consola cuando realice la transición entre el primer y segundo plano:public override void DidEnterBackground (UIApplication application) { Console.WriteLine ("App entering background state."); } public override void WillEnterForeground (UIApplication application) { Console.WriteLine ("App will enter foreground"); }
Agregue el código siguiente en
LocationManager
para imprimir continuamente los datos de ubicación actualizados en la salida de la aplicación para comprobar que la información de ubicación sigue estando disponible en segundo plano: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); } }
Hay un problema restante con el código: al intentar actualizar la interfaz de usuario cuando la aplicación esté en segundo plano, iOS la finalizará. Cuando la aplicación entra en segundo plano, el código debe cancelar la suscripción a las actualizaciones de ubicación y dejar de actualizar la interfaz de usuario.
iOS nos proporciona notificaciones cuando la aplicación está a punto de realizar la transición a un estado de la aplicación diferente. En este caso, podemos suscribirnos a la notificación
ObserveDidEnterBackground
.El siguiente fragmento de código muestra cómo usar una notificación para permitir a la vista saber cuándo detener las actualizaciones de la interfaz de usuario. Esto irá en
ViewDidLoad
:UIApplication.Notifications.ObserveDidEnterBackground ((sender, args) => { Manager.LocationUpdated -= HandleLocationChanged; });
Cuando se ejecute la aplicación, la salida tendrá un aspecto similar al siguiente:
La aplicación imprime actualizaciones de ubicación en la pantalla cuando se trabaja en primer plano y continúa imprimiendo datos en la ventana de salida de la aplicación mientras se trabaja en segundo plano.
Solo queda un problema pendiente: la pantalla inicia las actualizaciones de la interfaz de usuario cuando la aplicación se carga por primera vez, pero no tiene forma de saber cuándo ha vuelto a entrar en primer plano la aplicación. Si la aplicación en segundo plano vuelve a estar en primer plano, no se reanudarán las actualizaciones de la interfaz de usuario.
Para corregir esto, anide una llamada para iniciar las actualizaciones de la interfaz de usuario dentro de otra notificación, que se activará cuando la aplicación entre en estado activo:
UIApplication.Notifications.ObserveDidBecomeActive ((sender, args) => {
Manager.LocationUpdated += HandleLocationChanged;
});
Ahora la interfaz de usuario comenzará a actualizarse cuando se inicie la aplicación por primera vez y reanudará la actualización cada vez que vuelva a entrar en primer plano.
En este tutorial, hemos creado una aplicación iOS con un buen comportamiento y compatible en segundo plano que imprime datos de ubicación tanto en la pantalla como en la ventana de salida de la aplicación.