Organiza tus páginas con colecciones Guarda y categoriza el contenido según tus preferencias.

Selecciona el lugar actual y muestra información en un mapa

En este instructivo, se muestra cómo compilar una app para iOS a fin de realizar lo siguiente:

  • Obtén la ubicación actual del dispositivo.
  • Obtén una lista de los lugares donde probablemente se encuentre el dispositivo.
  • Solicítale al usuario la mejor coincidencia de lugar.
  • Mostrar un marcador en el mapa.

Sigue este instructivo a fin de compilar una app para iOS con el SDK de Places para iOS, el SDK de Maps para iOS y el framework de Ubicación principal de Apple.

Obtén el código

Clona o descarga el SDK de Google Maps para iOS desde GitHub.

Configura tu proyecto de desarrollo

Sigue estos pasos a fin de instalar el SDK de Places para iOS y Maps SDK para iOS:

  1. Descarga e instala Xcode 13.0 o una versión posterior.
  2. Si todavía no tienes CocoaPods, instálalo en macOS ejecutando el siguiente comando desde la terminal:
    sudo gem install cocoapods
  3. En el directorio donde guardaste el repositorio de muestra (Obtén el código), navega al directorio tutorials/current-place-on-map.
  4. Ejecuta el comando pod install. Esto instala las API especificadas en el Podfile, junto con cualquier dependencia que puedan tener.
  5. Ejecuta pod outdated para comparar la versión del pod instalada con las actualizaciones nuevas. Si se detecta una versión nueva, ejecuta pod update para actualizar Podfile e instalar el SDK más reciente. Para obtener más detalles, consulta la Guía de CocoaPods.
  6. Abre (doble clic) el current-place-on-map.xcworkspace del proyecto para abrirlo en Xcode. Debes usar el archivo .xcworkspace para abrir el proyecto.

Para obtener instrucciones detalladas para la instalación, consulta Cómo comenzar (Maps) y Cómo comenzar (Places).

Habilita las API necesarias y obtén una clave de API

Si deseas completar este instructivo, necesitas una clave de API de Google autorizada a fin de utilizar el SDK de Maps para iOS y la API de Places.

  1. Sigue las instrucciones que se indican en Primeros pasos con Google Maps Platform para configurar una cuenta de facturación y un proyecto habilitado con ambos productos.
  2. Sigue las instrucciones sobre cómo obtener una clave de API a fin de crear una clave de API para el proyecto de desarrollo que configuraste antes.

Agrega la clave de API a tu aplicación

Agrega tu clave de API a AppDelegate.swift de la siguiente manera:

  1. Ten en cuenta que se agregó la siguiente instrucción de importación al archivo:
    import GooglePlaces
    import GoogleMaps
  2. Edita la siguiente línea de tu método application(_:didFinishLaunchingWithOptions:) y reemplaza YOUR_API_KEY por tu clave de API:
    GMSPlacesClient.provideAPIKey("YOUR_API_KEY")
    GMSServices.provideAPIKey("YOUR_API_KEY")

Cómo compilar y ejecutar tu app

  1. Conecta un dispositivo iOS a tu computadora o selecciona un simulador en el menú emergente de esquemas de Xcode.
  2. Si usas un dispositivo, asegúrate de que los servicios de ubicación estén habilitados. Si usas un simulador, selecciona una ubicación en el menú Funciones.
  3. En Xcode, haz clic en la opción Product/Run del menú (o en el ícono del botón de reproducción).
    • Xcode compila la app y la ejecuta en el dispositivo o en el simulador.
    • Deberías ver un mapa con una serie de marcadores centrados cerca de tu ubicación actual.

Solución de problemas:

  • Si no ves un mapa, verifica si obtuviste una clave de API y la agregaste a la app, como se describió anteriormente. Revisa la consola de depuración de Xcode y busca mensajes de error sobre la clave de API.
  • Si restringiste la clave de API mediante el identificador de paquete de iOS, edita la clave para agregar el identificador de paquete para la app: com.google.examples.current-place-on-map.
  • El mapa no se mostrará correctamente si se rechaza la solicitud de permisos de los servicios de ubicación.
    • Si usas un dispositivo, ve a Configuración/General/Privacidad/Servicios de ubicación y vuelve a habilitar los servicios de ubicación.
    • Si usas un simulador, ve a Simulador/Restablecer contenido y configuración...
    La próxima vez que se ejecute la app, asegúrate de aceptar la solicitud de servicios de ubicación.
  • Asegúrate de tener una buena conexión de wifi o GPS.
  • Si se inicia la app, pero no se muestra ningún mapa, asegúrate de haber actualizado el archivo Info.plist de tu proyecto con los permisos de ubicación adecuados. A fin de obtener más información sobre el manejo de permisos, consulta la guía para solicitar permisos de ubicación en tu app, que se encuentra a continuación.
  • Usa las herramientas de depuración de Xcode para ver los registros y depurar la app.

Comprende el código

En esta sección del instructivo, se explican los componentes más importantes de la app current-place-on-map para ayudarte a comprender cómo compilar una app similar.

La app current-place-on-map cuenta con dos controladores de vista: uno para mostrar un mapa que muestra el lugar seleccionado actualmente del usuario y otro para mostrarle al usuario una lista de los posibles lugares que puede elegir. Ten en cuenta que cada controlador de vista tiene las mismas variables para realizar un seguimiento de la lista de lugares probables (likelyPlaces) y para indicar la selección del usuario (selectedPlace). La navegación entre vistas se logra mediante segues.

Solicitud de permiso de ubicación

Tu app debe solicitar al usuario su consentimiento para usar los servicios de ubicación. Para ello, incluye la clave NSLocationAlwaysUsageDescription en el archivo Info.plist de la app y establece el valor de cada clave en una string que describa cómo la app pretende usar los datos de ubicación.

Cómo configurar el administrador de ubicaciones

Usa CLLocationManager para encontrar la ubicación actual del dispositivo y solicitar actualizaciones periódicas cuando este se traslade a una nueva ubicación. En este instructivo, se proporciona el código necesario para obtener la ubicación del dispositivo. Para obtener más detalles, consulta la guía sobre cómo obtener la ubicación del usuario en la documentación para desarrolladores de Apple.

  1. Declara el administrador de ubicaciones, la ubicación actual, la vista de mapa, el cliente de Places y el nivel de zoom predeterminado a nivel de la clase.
  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. Inicializa el administrador de ubicaciones y GMSPlacesClient en 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. Declara variables para contener la lista de lugares posibles y el lugar seleccionado por el usuario.
  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. Agrega delegados que controlen eventos para el administrador de ubicaciones mediante una cláusula de extensión.
  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);
    }
          

Cómo agregar un mapa

Crea un mapa y agrégalo a la vista en viewDidLoad(), en el controlador de vista principal. El mapa permanece oculto hasta que se recibe una actualización de ubicación (las actualizaciones de ubicación se controlan en la extensión 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;
      

Cómo solicitar al usuario que seleccione su lugar actual

Usa el SDK de Places para iOS a fin de obtener las cinco probabilidades más altas de lugares según la ubicación actual del usuario y presenta la lista en un UITableView. Cuando un usuario selecciona un lugar, agrega un marcador al mapa.

  1. Obtén una lista de lugares probables para propagar una UITableView, desde la cual el usuario puede seleccionar el lugar en el que se encuentra.
  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. Abre una vista nueva para mostrar los posibles lugares al usuario. Cuando el usuario presiona "Obtener lugar", nos pasaremos a una vista nueva, que le mostrará una lista de posibles lugares para elegir. La función prepare actualiza PlacesViewController con la lista de lugares probables actuales y se la llama automáticamente cuando se realiza una serie.
  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. En PlacesViewController, completa la tabla con la lista de lugares más probables, mediante la extensión 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. Controla la selección del usuario con la extensión 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;
    }
          

Cómo agregar un marcador al mapa

Cuando el usuario haga una selección, usa una transición para desenrollar a fin de volver a la vista anterior y agrega el marcador al mapa. Cuando se regresa al controlador de vista principal, se llama automáticamente a la IBAction unwindToMain.

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];
}
      

¡Felicitaciones! Compilaste una app para iOS que le permite al usuario elegir su lugar actual y mostrar el resultado en un mapa de Google. En este curso, aprendiste cómo usar el SDK de Places para iOS, el SDK de Maps para iOS y el framework de ubicación de Apple Core.