Compartilhar via


Passo a passo – Localização em segundo plano no Xamarin.iOS

Neste exemplo, vamos criar um aplicativo de localização do iOS que imprime informações sobre nossa localização atual: latitude, longitude e outros parâmetros na tela. Este aplicativo demonstrará como executar corretamente as atualizações de localização enquanto o aplicativo estiver ativo ou em segundo plano.

Este passo a passo explica alguns conceitos importantes de segundo plano, incluindo registrar um aplicativo como um aplicativo necessário em segundo plano, suspender atualizações de interface do usuário quando o aplicativo está em segundo plano e trabalhar com os WillEnterBackground métodos and WillEnterForeground AppDelegate .

Configuração do aplicativo

  1. Primeiro, crie um novo aplicativo de exibição única do aplicativo > iOS > (C#). Chame-o de Localização e certifique-se de que o iPad e o iPhone foram selecionados.

  2. Um aplicativo de localização se qualifica como um aplicativo necessário em segundo plano no iOS. Registre o aplicativo como um aplicativo de localização editando o arquivo Info.plist para o projeto.

    Em Gerenciador de Soluções, clique duas vezes no arquivo Info.plist para abri-lo e role até a parte inferior da lista. Marque as caixas de seleção Ativar modos em segundo plano e Atualizações de localização.

    No Visual Studio para Mac, ele será semelhante a este:

    Marque as caixas de seleção Ativar modos em segundo plano e Atualizações de localização

    No Visual Studio, Info.plist precisa ser atualizado manualmente adicionando o seguinte par chave/valor:

    <key>UIBackgroundModes</key>
    <array>
      <string>location</string>
    </array>
    
  3. Agora que o aplicativo está registrado, ele pode obter dados de localização do dispositivo. No iOS, a CLLocationManager classe é usada para acessar informações de localização e pode gerar eventos que fornecem atualizações de localização.

  4. No código, crie uma nova classe chamada LocationManager that fornece um único local para várias telas e código para assinar atualizações de localização. LocationManager Na classe, faça uma instância do CLLocationManager chamado 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; }
        }
    }
    

    O código acima define várias propriedades e permissões na classe CLLocationManager :

    • PausesLocationUpdatesAutomatically – Este é um booleano que pode ser definido dependendo se o sistema tem permissão para pausar as atualizações de localização. Em alguns dispositivos, o padrão é true, o que pode fazer com que o dispositivo pare de receber atualizações de localização em segundo plano após cerca de 15 minutos.
    • RequestAlwaysAuthorization - Você deve passar esse método para dar ao usuário do aplicativo a opção de permitir que o local seja acessado em segundo plano. RequestWhenInUseAuthorization também pode ser passado se você quiser dar ao usuário a opção de permitir que o local seja acessado somente quando o aplicativo estiver em primeiro plano.
    • AllowsBackgroundLocationUpdates – Esta é uma propriedade booleana, introduzida no iOS 9, que pode ser configurada para permitir que um aplicativo receba atualizações de localização quando suspenso.

    Importante

    O iOS 8 (e superior) também requer uma entrada no arquivo Info.plist para mostrar o usuário como parte da solicitação de autorização.

  5. Adicione chaves Info.plist para os tipos de permissão que o aplicativo requer – NSLocationAlwaysUsageDescription, NSLocationWhenInUseUsageDescriptione/ou NSLocationAlwaysAndWhenInUseUsageDescription – com uma cadeia de caracteres que será exibida para o usuário no alerta que solicita acesso aos dados de localização.

  6. O iOS 9 requer que, ao usar AllowsBackgroundLocationUpdates o Info.plist inclua a chave UIBackgroundModes com o valor location. Se você concluiu a etapa 2 deste passo a passo, isso já deve estar no arquivo Info.plist.

  7. Dentro da LocationManager classe, crie um método chamado StartLocationUpdates com o código a seguir. Este código mostra como começar a receber atualizações de localização do 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();
    }
    

    Há várias coisas importantes acontecendo neste método. Primeiro, realizamos uma verificação para ver se o aplicativo tem acesso aos dados de localização no dispositivo. Verificamos isso chamando LocationServicesEnabled o CLLocationManager. Esse método retornará false se o usuário tiver negado ao aplicativo o acesso às informações de localização.

  8. Em seguida, informe ao gerente de localização com que frequência atualizar. CLLocationManager Fornece muitas opções para filtrar e configurar dados de localização, incluindo a frequência das atualizações. Neste exemplo, defina como DesiredAccuracy atualizar sempre que o local for alterado em um metro. Para obter mais informações sobre como configurar a frequência de atualização de local e outras preferências, consulte a Referência de classe CLLocationManager na documentação da Apple.

  9. Por fim, chame StartUpdatingLocation a CLLocationManager instância. Isso informa ao gerente de localização para obter uma correção inicial no local atual e começar a enviar atualizações

Até agora, o gerenciador de localização foi criado, configurado com os tipos de dados que queremos receber e determinou a localização inicial. Agora, o código precisa renderizar os dados de localização para a interface do usuário. Podemos fazer isso com um evento personalizado que usa a CLLocation como argumento:

// event for the location changing
public event EventHandler<LocationUpdatedEventArgs>LocationUpdated = delegate { };

A próxima etapa é assinar as atualizações de localização do CLLocationManagere gerar o evento personalizado LocationUpdated quando novos dados de localização estiverem disponíveis, passando a localização como um argumento. Para fazer isso, crie um novo LocationUpdateEventArgs.cs de classe. Esse código pode ser acessado no aplicativo principal e retorna a localização do dispositivo quando o evento é gerado:

public class LocationUpdatedEventArgs : EventArgs
{
    CLLocation location;

    public LocationUpdatedEventArgs(CLLocation location)
    {
       this.location = location;
    }

    public CLLocation Location
    {
       get { return location; }
    }
}

Interface do Usuário

  1. Use o Construtor de Interfaces do Xcode para criar a tela que exibirá informações de localização. Clique duas vezes no arquivo Main.storyboard para começar.

    No storyboard, arraste vários rótulos para a tela para atuar como espaços reservados para as informações de localização. Neste exemplo, há rótulos para latitude, longitude, altitude, curso e velocidade.

    Para obter mais informações, consulte Projetando interfaces do usuário com o Xcode.

  2. No Painel de Soluções, clique duas vezes no ViewController.cs arquivo e edite-o para criar uma nova instância do LocationManager e chamá-lo StartLocationUpdates. Altere o código para ficar semelhante ao seguinte:

    #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
    

    Isso iniciará as atualizações de localização na inicialização do aplicativo, embora nenhum dado seja exibido.

  3. Agora que as atualizações de localização foram recebidas, atualize a tela com as informações de localização. O método a seguir obtém o local do nosso LocationUpdated evento e o mostra na interface do usuário:

    #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
    

Ainda precisamos assinar o LocationUpdated evento em nosso AppDelegate e chamar o novo método para atualizar a interface do usuário. Adicione o seguinte código logo ViewDidLoad, após a StartLocationUpdates chamada:

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;

}

Agora, quando o aplicativo é executado, ele deve ser parecido com isto:

Um exemplo de execução de aplicativo

Manipulando estados ativos e em segundo plano

  1. O aplicativo está emitindo atualizações de localização enquanto está em primeiro plano e ativo. Para demonstrar o que acontece quando o aplicativo entra em segundo plano, substitua os métodos que rastreiam as alterações de estado do AppDelegate aplicativo para que o aplicativo grave no console quando fizer a transição entre o primeiro plano e o plano de fundo:

    public override void DidEnterBackground (UIApplication application)
    {
        Console.WriteLine ("App entering background state.");
    }
    
    public override void WillEnterForeground (UIApplication application)
    {
        Console.WriteLine ("App will enter foreground");
    }
    

    Adicione o seguinte código no LocationManager para imprimir continuamente dados de localização atualizados na saída do aplicativo, para verificar se as informações de localização ainda estão disponíveis em 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);
        }
    }
    
  2. Há um problema restante com o código: tentar atualizar a interface do usuário quando o aplicativo estiver em segundo plano fará com que o iOS o encerre. Quando o aplicativo entra em segundo plano, o código precisa cancelar a assinatura das atualizações de localização e parar de atualizar a interface do usuário.

    O iOS nos fornece notificações quando o aplicativo está prestes a fazer a transição para um estado de aplicativo diferente. Nesse caso, podemos assinar a ObserveDidEnterBackground Notificação.

    O snippet de código a seguir mostra como usar uma notificação para permitir que a exibição saiba quando interromper as atualizações da interface do usuário. Isso vai em ViewDidLoad:

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

    Quando o aplicativo estiver em execução, a saída será semelhante a esta:

    Exemplo da saída de localização no console

  3. O aplicativo imprime atualizações de localização na tela ao operar em primeiro plano e continua a imprimir dados na janela de saída do aplicativo enquanto opera em segundo plano.

Resta apenas um problema pendente: a tela inicia as atualizações da interface do usuário quando o aplicativo é carregado pela primeira vez, mas não tem como saber quando o aplicativo entrou novamente em primeiro plano. Se o aplicativo em segundo plano for trazido de volta para o primeiro plano, as atualizações da interface do usuário não serão retomadas.

Para corrigir isso, aninhe uma chamada para iniciar atualizações de interface do usuário dentro de outra notificação, que será acionada quando o aplicativo entrar no estado Ativo:

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

Agora, a interface do usuário começará a ser atualizada quando o aplicativo for iniciado pela primeira vez e retomará a atualização sempre que o aplicativo voltar ao primeiro plano.

Neste passo a passo, criamos um aplicativo iOS bem comportado e com reconhecimento de plano de fundo que imprime dados de localização na tela e na janela de saída do aplicativo.