Mantenha tudo organizado com as coleções Salve e categorize o conteúdo com base nas suas preferências.

Selecionar o lugar atual e exibir detalhes em um mapa

Neste tutorial, mostramos como criar um app iOS para:

  • Acessar o local atual do dispositivo.
  • Veja uma lista de onde o dispositivo provavelmente está localizado.
  • Solicitar ao usuário a melhor correspondência de lugar.
  • Mostrar um marcador no mapa.

Siga este tutorial se você quiser criar um app iOS usando o SDK do Places para iOS, o SDK do Maps para iOS e o framework de local do Apple Core.

Como conseguir o código

Clone ou faça o download do SDK do Google Maps para iOS no GitHub.

Como configurar o projeto de desenvolvimento

Siga estas etapas para instalar o SDK do Places para iOS e o SDK do Maps para iOS:

  1. Faça o download e instale o Xcode versão 13.0 ou posterior.
  2. Se você ainda não tem o CocoaPods, instale-o no macOS executando o seguinte comando no terminal:
    sudo gem install cocoapods
  3. No diretório em que você salvou o repositório de amostra (acesse o código), acesse o diretório tutorials/current-place-on-map.
  4. Execute o comando pod install. Isso instala as APIs especificadas no Podfile, além de todas as dependências que possam ter.
  5. Abra (clique duas vezes) o current-place-on-map.xcworkspace do projeto para abri-lo no Xcode. É necessário usar o arquivo .xcworkspace para abrir o projeto.

Para instruções detalhadas de instalação, consulte Primeiros passos (Maps) e Primeiros passos (Places).

Como ativar as APIs necessárias e gerar uma chave de API

Para concluir este tutorial, você precisa de uma chave de API do Google que possa usar o SDK do Maps para iOS e a API Places.

  1. Siga as instruções em Como começar a usar a Plataforma Google Maps para configurar uma conta de faturamento e um projeto ativado com ambos os produtos.
  2. Siga as instruções em Acessar uma chave de API se quiser criar uma chave de API para o projeto de desenvolvimento configurado anteriormente.

Adicionar a chave de API ao aplicativo

Swift

Inclua sua chave de API ao AppDelegate.swift da seguinte maneira:

  1. A seguinte instrução de importação foi adicionada ao arquivo:
    import GooglePlaces
    import GoogleMaps
  2. Edite a seguinte linha no método application(_:didFinishLaunchingWithOptions:), substituindo YOUR_API_KEY pela sua chave de API:
    GMSPlacesClient.provideAPIKey("YOUR_API_KEY")
    GMSServices.provideAPIKey("YOUR_API_KEY")

Objective-C

Inclua sua chave de API ao AppDelegate.m da seguinte maneira:

  1. A seguinte instrução de importação foi adicionada ao arquivo:
    @import GooglePlaces;
    @import GoogleMaps;
  2. Edite a seguinte linha no método application(_:didFinishLaunchingWithOptions:), substituindo YOUR_API_KEY pela sua chave de API:
    [GMSPlacesClient provideAPIKey: @"YOUR_API_KEY"]
    [GMSServices provideAPIKey: @"YOUR_API_KEY"]

Como criar e executar o app

  1. Conecte um dispositivo iOS ao seu computador ou selecione um simulador no menu pop-up de esquema do Xcode.
  2. Se você estiver usando um dispositivo, confira se os Serviços de localização estão ativados. Se você estiver usando um simulador, selecione um local no menu Recursos.
  3. No Xcode, clique na opção de menu Product/Run ou no ícone do botão de reprodução.

O Xcode cria o app e o executa no dispositivo ou no simulador.

Vai aparecer um mapa com vários marcadores centralizados ao redor do seu lugar atual, semelhante à imagem desta página.

Solução de problemas:

  • Se o mapa não aparecer, confirme se você recebeu uma chave de API e a incluiu no app, como descrito acima. Verifique se há mensagens de erro sobre essa chave no console de depuração do Xcode.
  • Se você restringiu a chave de API pelo identificador do pacote iOS, edite-a para adicionar o identificador do pacote ao app: com.google.examples.current-place-on-map.
  • O mapa não será exibido corretamente se a solicitação de permissões dos serviços de localização for recusada.
    • Se você estiver usando um dispositivo, acesse Configurações/Geral/Privacidade/Serviços de localização e reative os Serviços de localização.
    • Se você estiver usando um simulador, acesse Simulador/Redefinir conteúdo e configurações...
    Na próxima vez que o app for executado, aceite o prompt de Serviços de localização.
  • Certifique-se de que você tem uma boa conexão Wi-Fi ou GPS.
  • Se o app for iniciado, mas nenhum mapa for exibido, verifique se você atualizou o Info.plist do projeto com as permissões de localização apropriadas. Para mais informações sobre como lidar com permissões, consulte o guia sobre como solicitar permissão de localização no seu app abaixo.
  • Use as ferramentas de depuração do Xcode para ver os registros e depurar o app.

Noções básicas sobre o código

Nesta seção do tutorial, explicamos as partes mais importantes do app current-place-on-map para ajudar você a criar um app semelhante.

O app current-place-on-map apresenta dois controladores de visualização: um para exibir um mapa que mostra o lugar selecionado pelo usuário no momento e outro para apresentar ao usuário uma lista de possíveis lugares para escolher. Cada controlador de visualização tem as mesmas variáveis para rastrear a lista de possíveis lugares (likelyPlaces) e indicar a seleção do usuário (selectedPlace). A navegação entre visualizações é realizada usando segues.

Solicitar permissão de localização

Seu app precisa solicitar o consentimento do usuário para usar os Serviços de localização. Para fazer isso, inclua a chave NSLocationAlwaysUsageDescription no arquivo Info.plist do app e defina o valor de cada chave como uma string que descreve como o app pretende usar dados de local.

Como configurar o Gerenciador de local

Use CLLocationManager para encontrar o local atual do dispositivo e solicitar atualizações regulares quando o dispositivo se mover para um novo local. Neste tutorial, fornecemos o código necessário para receber a localização do dispositivo. Para mais detalhes, consulte o guia sobre Como determinar o local do usuário na documentação do desenvolvedor da Apple.

  1. Declare o gerenciador de local, a localização atual, a visualização de mapa, o cliente do Places e o nível de zoom padrão no nível da classe.
  2. Swift

    var locationManager: CLLocationManager!
    var currentLocation: CLLocation?
    var mapView: GMSMapView!
    var placesClient: GMSPlacesClient!
    var preciseLocationZoomLevel: Float = 15.0
    var approximateLocationZoomLevel: Float = 10.0
          

    Objective-C

    CLLocationManager *locationManager;
    CLLocation * _Nullable currentLocation;
    GMSMapView *mapView;
    GMSPlacesClient *placesClient;
    float preciseLocationZoomLevel;
    float approximateLocationZoomLevel;
          
  3. Inicialize o gerenciador de local e o GMSPlacesClient no viewDidLoad().
  4. Swift

    // Initialize the location manager.
    locationManager = CLLocationManager()
    locationManager.desiredAccuracy = kCLLocationAccuracyBest
    locationManager.requestWhenInUseAuthorization()
    locationManager.distanceFilter = 50
    locationManager.startUpdatingLocation()
    locationManager.delegate = self
    
    placesClient = GMSPlacesClient.shared()
          

    Objective-C

    // Initialize the location manager.
    locationManager = [[CLLocationManager alloc] init];
    locationManager.desiredAccuracy = kCLLocationAccuracyBest;
    [locationManager requestWhenInUseAuthorization];
    locationManager.distanceFilter = 50;
    [locationManager startUpdatingLocation];
    locationManager.delegate = self;
    
    placesClient = [GMSPlacesClient sharedClient];
          
  5. Declare variáveis para armazenar a lista de possíveis lugares e o lugar selecionado pelo usuário.
  6. Swift

    // An array to hold the list of likely places.
    var likelyPlaces: [GMSPlace] = []
    
    // The currently selected place.
    var selectedPlace: GMSPlace?
          

    Objective-C

    // An array to hold the list of likely places.
    NSMutableArray<GMSPlace *> *likelyPlaces;
    
    // The currently selected place.
    GMSPlace * _Nullable selectedPlace;
          
  7. Adicione delegados para tratar eventos do gerenciador de local usando uma cláusula de extensão.
  8. Swift

    // Delegates to handle events for the location manager.
    extension MapViewController: CLLocationManagerDelegate {
    
      // Handle incoming location events.
      func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        let location: CLLocation = locations.last!
        print("Location: \(location)")
    
        let zoomLevel = locationManager.accuracyAuthorization == .fullAccuracy ? preciseLocationZoomLevel : approximateLocationZoomLevel
        let camera = GMSCameraPosition.camera(withLatitude: location.coordinate.latitude,
                                              longitude: location.coordinate.longitude,
                                              zoom: zoomLevel)
    
        if mapView.isHidden {
          mapView.isHidden = false
          mapView.camera = camera
        } else {
          mapView.animate(to: camera)
        }
    
        listLikelyPlaces()
      }
    
      // Handle authorization for the location manager.
      func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
        // Check accuracy authorization
        let accuracy = manager.accuracyAuthorization
        switch accuracy {
        case .fullAccuracy:
            print("Location accuracy is precise.")
        case .reducedAccuracy:
            print("Location accuracy is not precise.")
        @unknown default:
          fatalError()
        }
    
        // Handle authorization status
        switch status {
        case .restricted:
          print("Location access was restricted.")
        case .denied:
          print("User denied access to location.")
          // Display the map using the default location.
          mapView.isHidden = false
        case .notDetermined:
          print("Location status not determined.")
        case .authorizedAlways: fallthrough
        case .authorizedWhenInUse:
          print("Location status is OK.")
        @unknown default:
          fatalError()
        }
      }
    
      // Handle location manager errors.
      func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
        locationManager.stopUpdatingLocation()
        print("Error: \(error)")
      }
    }
          

    Objective-C

    // Delegates to handle events for the location manager.
    #pragma mark - CLLocationManagerDelegate
    
    // Handle incoming location events.
    - (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations
    {
      CLLocation *location = locations.lastObject;
      NSLog(@"Location: %@", location);
    
      float zoomLevel = locationManager.accuracyAuthorization == CLAccuracyAuthorizationFullAccuracy ? preciseLocationZoomLevel : approximateLocationZoomLevel;
      GMSCameraPosition * camera = [GMSCameraPosition cameraWithLatitude:location.coordinate.latitude
                                                               longitude:location.coordinate.longitude
                                                                    zoom:zoomLevel];
    
      if (mapView.isHidden) {
        mapView.hidden = NO;
        mapView.camera = camera;
      } else {
        [mapView animateToCameraPosition:camera];
      }
    
      [self listLikelyPlaces];
    }
    
    // Handle authorization for the location manager.
    - (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status
    {
      // Check accuracy authorization
      CLAccuracyAuthorization accuracy = manager.accuracyAuthorization;
      switch (accuracy) {
        case CLAccuracyAuthorizationFullAccuracy:
          NSLog(@"Location accuracy is precise.");
          break;
        case CLAccuracyAuthorizationReducedAccuracy:
          NSLog(@"Location accuracy is not precise.");
          break;
      }
    
      // Handle authorization status
      switch (status) {
        case kCLAuthorizationStatusRestricted:
          NSLog(@"Location access was restricted.");
          break;
        case kCLAuthorizationStatusDenied:
          NSLog(@"User denied access to location.");
          // Display the map using the default location.
          mapView.hidden = NO;
        case kCLAuthorizationStatusNotDetermined:
          NSLog(@"Location status not determined.");
        case kCLAuthorizationStatusAuthorizedAlways:
        case kCLAuthorizationStatusAuthorizedWhenInUse:
          NSLog(@"Location status is OK.");
      }
    }
    
    // Handle location manager errors.
    - (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
    {
      [manager stopUpdatingLocation];
      NSLog(@"Error: %@", error.localizedDescription);
    }
          

Adicionar um mapa

Crie um mapa e adicione-o à visualização em viewDidLoad() no controlador de visualização principal. O mapa permanece oculto até que uma atualização de local seja recebida. As atualizações de local são processadas na extensão CLLocationManagerDelegate.

Swift

// A default location to use when location permission is not granted.
let defaultLocation = CLLocation(latitude: -33.869405, longitude: 151.199)

// Create a map.
let zoomLevel = locationManager.accuracyAuthorization == .fullAccuracy ? preciseLocationZoomLevel : approximateLocationZoomLevel
let camera = GMSCameraPosition.camera(withLatitude: defaultLocation.coordinate.latitude,
                                      longitude: defaultLocation.coordinate.longitude,
                                      zoom: zoomLevel)
mapView = GMSMapView.map(withFrame: view.bounds, camera: camera)
mapView.settings.myLocationButton = true
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
mapView.isMyLocationEnabled = true

// Add the map to the view, hide it until we've got a location update.
view.addSubview(mapView)
mapView.isHidden = true
      

Objective-C

// A default location to use when location permission is not granted.
CLLocationCoordinate2D defaultLocation = CLLocationCoordinate2DMake(-33.869405, 151.199);

// Create a map.
float zoomLevel = locationManager.accuracyAuthorization == CLAccuracyAuthorizationFullAccuracy ? preciseLocationZoomLevel : approximateLocationZoomLevel;
GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:defaultLocation.latitude
                                                        longitude:defaultLocation.longitude
                                                             zoom:zoomLevel];
mapView = [GMSMapView mapWithFrame:self.view.bounds camera:camera];
mapView.settings.myLocationButton = YES;
mapView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
mapView.myLocationEnabled = YES;

// Add the map to the view, hide it until we've got a location update.
[self.view addSubview:mapView];
mapView.hidden = YES;
      

Solicitar que o usuário selecione o lugar atual

Use o SDK do Places para iOS para ver as cinco principais probabilidades de lugar com base na localização atual do usuário e apresente a lista em um UITableView. Quando o usuário selecionar um lugar, adicione um marcador ao mapa.

  1. Veja uma lista de possíveis lugares para preencher um UITableView, em que o usuário pode selecionar o lugar em que está localizado no momento.
  2. Swift

    // Populate the array with the list of likely places.
    func listLikelyPlaces() {
      // Clean up from previous sessions.
      likelyPlaces.removeAll()
    
      let placeFields: GMSPlaceField = [.name, .coordinate]
      placesClient.findPlaceLikelihoodsFromCurrentLocation(withPlaceFields: placeFields) { (placeLikelihoods, error) in
        guard error == nil else {
          // TODO: Handle the error.
          print("Current Place error: \(error!.localizedDescription)")
          return
        }
    
        guard let placeLikelihoods = placeLikelihoods else {
          print("No places found.")
          return
        }
    
        // Get likely places and add to the list.
        for likelihood in placeLikelihoods {
          let place = likelihood.place
          self.likelyPlaces.append(place)
        }
      }
    }
          

    Objective-C

    // Populate the array with the list of likely places.
    - (void) listLikelyPlaces
    {
      // Clean up from previous sessions.
      likelyPlaces = [NSMutableArray array];
    
      GMSPlaceField placeFields = GMSPlaceFieldName | GMSPlaceFieldCoordinate;
      [placesClient findPlaceLikelihoodsFromCurrentLocationWithPlaceFields:placeFields callback:^(NSArray<GMSPlaceLikelihood *> * _Nullable likelihoods, NSError * _Nullable error) {
        if (error != nil) {
          // TODO: Handle the error.
          NSLog(@"Current Place error: %@", error.localizedDescription);
          return;
        }
    
        if (likelihoods == nil) {
          NSLog(@"No places found.");
          return;
        }
    
        for (GMSPlaceLikelihood *likelihood in likelihoods) {
          GMSPlace *place = likelihood.place;
          [likelyPlaces addObject:place];
        }
      }];
    }
          
  3. Abra uma nova visualização para apresentar possíveis lugares ao usuário. Quando o usuário toca em "Get Place", passamos para uma nova visualização e mostramos a ele uma lista de possíveis lugares para escolher. A função prepare atualiza PlacesViewController com a lista de lugares prováveis atuais e é chamada automaticamente quando uma execução é realizada.
  4. Swift

    // Prepare the segue.
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
      if segue.identifier == "segueToSelect" {
        if let nextViewController = segue.destination as? PlacesViewController {
          nextViewController.likelyPlaces = likelyPlaces
        }
      }
    }
          

    Objective-C

    // Prepare the segue.
    - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
    {
      if ([segue.identifier isEqualToString:@"segueToSelect"]) {
        if ([segue.destinationViewController isKindOfClass:[PlacesViewController class]]) {
          PlacesViewController *placesViewController = (PlacesViewController *)segue.destinationViewController;
          placesViewController.likelyPlaces = likelyPlaces;
        }
      }
    }
          
  5. Em PlacesViewController, preencha a tabela usando a lista de locais mais prováveis, usando a extensão delegada UITableViewDataSource.
  6. Swift

    // Populate the table with the list of most likely places.
    extension PlacesViewController: UITableViewDataSource {
      func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return likelyPlaces.count
      }
    
      func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier, for: indexPath)
        let collectionItem = likelyPlaces[indexPath.row]
    
        cell.textLabel?.text = collectionItem.name
    
        return cell
      }
    }
          

    Objective-C

    #pragma mark - UITableViewDataSource
    
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    {
      return self.likelyPlaces.count;
    }
    
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
      return [tableView dequeueReusableCellWithIdentifier:cellReuseIdentifier forIndexPath:indexPath];
    }
    @end
          
  7. Gerencie a seleção do usuário usando a extensão delegada UITableViewDelegate.
  8. Swift

    class PlacesViewController: UIViewController {
    
      // ...
    
      // Pass the selected place to the new view controller.
      override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "unwindToMain" {
          if let nextViewController = segue.destination as? MapViewController {
            nextViewController.selectedPlace = selectedPlace
          }
        }
      }
    }
    
    // Respond when a user selects a place.
    extension PlacesViewController: UITableViewDelegate {
      func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        selectedPlace = likelyPlaces[indexPath.row]
        performSegue(withIdentifier: "unwindToMain", sender: self)
      }
    
      // Adjust cell height to only show the first five items in the table
      // (scrolling is disabled in IB).
      func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return self.tableView.frame.size.height/5
      }
    
      // Make table rows display at proper height if there are less than 5 items.
      func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
        if (section == tableView.numberOfSections - 1) {
          return 1
        }
        return 0
      }
    }
          

    Objective-C

    @interface PlacesViewController () <UITableViewDataSource, UITableViewDelegate>
    // ...
    
    -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
    {
    
    }
    
    #pragma mark - UITableViewDelegate
    
    // Respond when a user selects a place.
    -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
      self.selectedPlace = [self.likelyPlaces objectAtIndex:indexPath.row];
      [self performSegueWithIdentifier:@"unwindToMain" sender:self];
    }
    
    // Adjust cell height to only show the first five items in the table
    // (scrolling is disabled in IB).
    -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
    {
      return self.tableView.frame.size.height/5;
    }
    
    // Make table rows display at proper height if there are less than 5 items.
    -(CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section
    {
      if (section == tableView.numberOfSections - 1) {
        return 1;
      }
      return 0;
    }
          

Adicionar um marcador ao mapa

Quando o usuário fizer uma seleção, use uma sequência de desenrolamento para retornar à visualização anterior e adicionar o marcador ao mapa. A IBAction unwindToMain é chamada automaticamente ao retornar para o controlador de visualização principal.

Swift

// Update the map once the user has made their selection.
@IBAction func unwindToMain(segue: UIStoryboardSegue) {
  // Clear the map.
  mapView.clear()

  // Add a marker to the map.
  if let place = selectedPlace {
    let marker = GMSMarker(position: place.coordinate)
    marker.title = selectedPlace?.name
    marker.snippet = selectedPlace?.formattedAddress
    marker.map = mapView
  }

  listLikelyPlaces()
}
      

Objective-C

// Update the map once the user has made their selection.
- (void) unwindToMain:(UIStoryboardSegue *)segue
{
  // Clear the map.
  [mapView clear];

  // Add a marker to the map.
  if (selectedPlace != nil) {
    GMSMarker *marker = [GMSMarker markerWithPosition:selectedPlace.coordinate];
    marker.title = selectedPlace.name;
    marker.snippet = selectedPlace.formattedAddress;
    marker.map = mapView;
  }

  [self listLikelyPlaces];
}
      

Parabéns! Você criou um app iOS que permite ao usuário escolher o lugar atual e mostra o resultado em um mapa do Google. Ao fazer isso, você aprendeu a usar o SDK do Places para iOS, o SDK do Maps para iOS e o framework de local do Apple Core.