Compartilhar via


Passo a passo - Local 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 atualizações de localização enquanto o aplicativo está ativo ou em segundo plano.

Este passo a passo explica alguns conceitos importantes de plano de fundo, incluindo o registro de um aplicativo como um aplicativo necessário em segundo plano, a suspensão de atualizações da interface do usuário quando o aplicativo está em segundo plano e o trabalho com os WillEnterBackground métodos e WillEnterForegroundAppDelegate .

Configuração do aplicativo

  1. Primeiro, crie um novo aplicativo iOS >> Single View Application (C#). Chame-o de Localização e verifique se 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 local editando o arquivo Info.plist para o projeto.

    No 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 Habilitar modos em segundo plano e Atualizações de local.

    No Visual Studio para Mac, ele terá a seguinte aparência:

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

    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 que 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 um número de propriedades e permissões na classe CLLocationManager :

    • PausesLocationUpdatesAutomatically – Este é um Boolean que pode ser definido dependendo se o sistema tem permissão para pausar 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 este 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 apenas 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 ao 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 ao 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 desta explicação 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 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 de atualizações. Neste exemplo, defina o DesiredAccuracy para atualizar sempre que o local for alterado por 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, recorra StartUpdatingLocation à CLLocationManager instância. Isso informa ao gerenciador de local para obter uma correção inicial no local atual e para 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 um CLLocation como argumento:

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

A próxima etapa é assinar as atualizações de local do CLLocationManager, e gerar o evento personalizado LocationUpdated quando novos dados de local estiverem disponíveis, passando o local como um argumento. Para fazer isso, crie uma nova classe LocationUpdateEventArgs.cs. Esse código é acessível no aplicativo principal e retorna o local 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 Xcode Interface Builder 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 Criando interfaces de usuário com o Xcode.

  2. No Solution Pad, 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 ter a seguinte aparência:

    #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 local 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 nos inscrever no 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 ter a seguinte aparência:

Uma execução de aplicativo de exemplo

Manipulando estados Ativo e em Segundo Plano

  1. O aplicativo está emitindo atualizações de local enquanto está em primeiro plano e ativo. Para demonstrar o que acontece quando o aplicativo entra em segundo plano, substitua os métodos que controlam as AppDelegate alterações de estado do 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 para a saída do aplicativo, para verificar se as informações de local 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 de 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. Neste caso, podemos subscrever a ObserveDidEnterBackground Notificação.

    O trecho de código a seguir mostra como usar uma notificação para informar ao modo de exibição quando interromper as atualizações da interface do usuário. Isso irá em ViewDidLoad:

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

    Quando o aplicativo estiver em execução, a saída terá a seguinte aparência:

    Exemplo da saída de local no console

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

Apenas um problema pendente permanece: 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, aninhar uma chamada para iniciar atualizações da 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.