Selecionar o lugar atual e exibir detalhes em um mapa
Este tutorial mostra como criar um aplicativo iOS para:
- Ver a localização atual do dispositivo.
- Veja uma lista dos lugares em que o dispositivo provavelmente está.
- Solicitar ao usuário a melhor correspondência de lugar.
- Mostrar um marcador no mapa.
Siga este tutorial para criar um app iOS usando o SDK do Places para iOS, o SDK do Maps para iOS e o framework de localização principal da Apple.
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:
- Faça o download e instale o Xcode versão 14.0 (link em inglês) ou mais recente.
- Se você ainda não tiver o CocoaPods, instale-o no macOS executando o seguinte comando no terminal:
sudo gem install cocoapods
- No diretório em que você salvou o repositório de amostra (Acessar o código), acesse o diretório
tutorials/current-place-on-map
. - Execute o comando
pod install
. Isso instala as APIs especificadas noPodfile
, com todas as dependências que elas possam ter. - Execute
pod outdated
para comparar a versão do pod instalada com as novas atualizações. Se uma nova versão for detectada, executepod update
para atualizar oPodfile
e instalar o SDK mais recente. Para mais detalhes, consulte o Guia do CocoaPods (em inglês). - Abra (clique duas vezes) o current-place-on-map.xcworkspace do projeto para abri-lo no Xcode. Use 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.
- Siga as instruções em Primeiros passos na Plataforma Google Maps para configurar uma conta de faturamento e um projeto ativado para esses dois produtos.
- Siga as instruções em Conseguir uma chave de API para criar uma chave de API para o projeto de desenvolvimento que você configurou anteriormente.
Como adicionar a chave de API ao aplicativo
Inclua sua chave de API ao AppDelegate.swift
da seguinte maneira:
- Observe que a seguinte instrução de importação foi adicionada ao arquivo:
import GooglePlaces import GoogleMaps
- Edite a seguinte linha no método
application(_:didFinishLaunchingWithOptions:)
, substituindo YOUR_API_KEY pela chave de API:GMSPlacesClient.provideAPIKey("YOUR_API_KEY") GMSServices.provideAPIKey("YOUR_API_KEY")
Como criar e executar o app
- Conecte um dispositivo iOS ao computador ou selecione um simulador no menu pop-up do esquema do Xcode.
- Se você estiver usando um dispositivo, verifique se os serviços de localização estão ativados. Se você estiver usando um simulador, selecione um local no menu Recursos.
- 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.
- Você verá um mapa com vários marcadores centralizados ao redor do seu local atual.
Solução de problemas:
- Se o mapa não aparecer, verifique se você recebeu uma chave de API e a incluiu no app, conforme descrito acima. Verifique se há mensagens de erro no console de depuração do Xcode.
- Se você restringiu a chave de API pelo identificador do pacote do iOS, edite a chave 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 para 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 Simulator/Redefinir conteúdo e configurações...
- Verifique se 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 arquivo Info.plist do seu projeto com as permissões de localização adequadas. Para mais informações sobre o gerenciamento de permissões, consulte o guia abaixo sobre como solicitar permissões de localização no app.
- Use as ferramentas de depuração de Xcode para visualizar registros e depurar o app.
Entender o código
Nesta parte do tutorial, explicamos as partes mais importantes do app current-place-on-map para ajudar você a entender como 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 atualmente selecionado pelo usuário 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 lugares prováveis (likelyPlaces
) e para indicar a seleção do usuário (selectedPlace
). A navegação entre visualizações é realizada usando segues.
Solicitando permissão de localização
O app precisa solicitar ao usuário consentimento 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 os dados de localização.
Como configurar o administrador de local
Use CLLocationManager para encontrar a localização 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 saber a localização do dispositivo. Para mais detalhes, consulte o guia sobre Como conseguir a localização do usuário na Documentação do desenvolvedor da Apple.
- Declare o gerenciador de localização, 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.
- Inicialize o gerenciador de local e o
GMSPlacesClient
emviewDidLoad()
. - Declare variáveis para manter a lista de locais prováveis e o local selecionado pelo usuário.
- Adicione delegados para gerenciar eventos do gerenciador de local usando uma cláusula de extensão.
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;
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];
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;
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 fica oculto até que uma atualização de local seja recebida (as atualizações de local são tratadas 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 localização com base na localização atual do usuário e apresentar a lista em uma UITableView
. Quando o usuário selecionar um lugar, adicione um marcador ao mapa.
- Receba uma lista de lugares prováveis para preencher um
UITableView
, em que o usuário pode selecionar o lugar em que está localizado no momento. - Abra uma nova visualização para apresentar lugares prováveis 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
atualizaPlacesViewController
com a lista de lugares prováveis atuais e é chamada automaticamente quando uma execução é realizada. - Em
PlacesViewController
, preencha a tabela usando a lista de locais mais prováveis, usando a extensão delegadaUITableViewDataSource
. - Gerencie a seleção do usuário usando a extensão delegada
UITableViewDelegate
.
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]; } }]; }
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; } } }
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
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; }
Como adicionar um marcador ao mapa
Quando o usuário fizer uma seleção, use uma sequência de desfazer para retornar à visualização anterior e adicione o marcador ao mapa. A IBAction unwindToMain
é chamada automaticamente ao retornar ao 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 para 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 localização principal da Apple.