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 con los siguientes fines:

  • Obtén la ubicación actual del dispositivo.
  • Obtén una lista de los lugares donde es probable que se encuentre el dispositivo.
  • Pídele al usuario la mejor coincidencia de lugar.
  • Muestra 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 de Apple Core.

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 el SDK de Maps 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 en el que 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 Podfile, junto con las dependencias que puedan tener.
  5. Abre (doble clic) en el current-place-on-map.xcworkspace del proyecto para abrirlo en Xcode. Debes usar el archivo .xcworkspace para abrir el proyecto.

Para obtener instrucciones de instalación detalladas, consulta Comenzar (Maps) y Comenzar (Places).

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

A fin de completar este instructivo, necesitas una clave de API de Google con autorización para usar el SDK de Maps para iOS y la API de Places.

  1. Sigue las instrucciones para comenzar a usar Google Maps Platform a fin de configurar una cuenta de facturación y un proyecto habilitado con ambos productos.
  2. Sigue las instrucciones en Obtén una clave de API a fin de crear una clave de API para el proyecto de desarrollo que configuraste anteriormente.

Agrega la clave de API a tu aplicación

Swift

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

  1. Ten en cuenta que se agregó la siguiente declaración de importación al archivo:
    import GooglePlaces
    import GoogleMaps
  2. Edita la siguiente línea en 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")

Objective‑C

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

  1. Ten en cuenta que se agregó la siguiente declaración de importación al archivo:
    @import GooglePlaces;
    @import GoogleMaps;
  2. Edita la siguiente línea en 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"]

Compila y ejecuta tu app

  1. Conecta un dispositivo iOS a la computadora o selecciona un simulador del menú emergente de esquema 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 del menú Producto/Ejecutar (o en el ícono del botón de reproducción).

Xcode compila la app y, luego, la ejecuta en el dispositivo o en el simulador.

Deberías ver un mapa con varios marcadores centrados en la ubicación actual, similar a la imagen de esta página.

Solución de problemas:

  • Si no ves un mapa, comprueba que hayas obtenido una clave de API y la hayas agregado a la app, como se describió anteriormente. Consulta la consola de depuración de Xcode para ver los 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 de la app: com.google.examples.current-place-on-map.
  • El mapa no se mostrará correctamente si se rechaza la solicitud de permisos para 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 Wi-Fi 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. Para obtener más información sobre el manejo de permisos, consulta la guía sobre cómo solicitar permisos de ubicación en tu app 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 las partes más importantes de la app current-place-on-map para ayudarte a comprender cómo compilar una app similar.

La app de current-place-on-map cuenta con dos controladores de vista: uno para mostrar un mapa que muestra el lugar seleccionado actualmente por el usuario y uno para mostrar una lista de los lugares que puede elegir. Ten en cuenta que cada controlador de vista tiene las mismas variables para realizar el 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 secuencias.

Solicitando permiso de ubicación

Tu app debe solicitar permiso al usuario 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.

Configuración del administrador de ubicaciones

Usa CLLocationManager para buscar 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 ubicación, la ubicación actual, la vista de mapa, el cliente de lugares 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 ubicación 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 conservar la lista de lugares probables y el lugar seleccionado del 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 ubicación 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 el usuario seleccione un lugar, agrega un marcador al mapa.

  1. Obtén una lista de lugares probables para propagar un UITableView, desde el que el usuario puede seleccionar el lugar en el que se encuentra actualmente.
  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 presentar los lugares probables al usuario. Cuando el usuario presiona "Obtener lugar", pasamos a una vista nueva y le mostramos al usuario una lista de posibles lugares para elegir. La función prepare actualiza PlacesViewController con la lista de lugares probables actuales y se llama automáticamente cuando se realiza una transición.
  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 de delegado 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 de delegado 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 de relajación para volver a la vista anterior y agregar el marcador al mapa. Se llama automáticamente a IBAction unwindToMain cuando se muestra el controlador de vista 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];
}
      

¡Felicitaciones! Compilaste una app para iOS que permite al usuario elegir su lugar actual y muestra 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.