Componente Advanced Place Details

La versión Advanced del componente Place Details ofrece las mismas funciones que Place Details Essentials. También te permite elegir si se muestra el nombre del lugar, configurar los botones de acción del componente y aplicar opciones de búsqueda y filtrado a las opiniones y el contenido multimedia que se muestran.

Configuración avanzada de Place Details

Usa AdvancedPlaceDetailsView o AdvancedPlaceDetailsCompactView para acceder a las funciones avanzadas.

Opciones de visualización de contenido

Puedes especificar qué contenido mostrar con las enumeraciones en AdvancedPlaceDetailsContent. Puedes configurar todo el contenido de Places Details Essentials y elegir si se muestra el nombre del lugar.

Cómo configurar botones de acción

Usa los métodos .cornerActions y .mainActions de AdvancedPlaceDetailsView o AdvancedPlaceDetailsCompactView para quitar o personalizar los botones de acción.

Requisitos de atribución visual

Vista compacta de Place Details con botones personalizados
Vista compacta de Advanced Place Details con un botón personalizado

Importante: Debes dejar en claro a los usuarios finales cuando un botón de acción personalizada comparta información del usuario con un tercero que no sea Google. Consulta los requisitos de atribución visual.

En el ejemplo de la derecha, se muestra una imagen personalizada que deja en claro que la información del usuario se enviará a "Altostrat".

Aplicar opciones de búsqueda y filtrado

Usa los métodos .searchMediaOptions y .searchReviewOptions de AdvancedPlaceDetailsView o AdvancedPlaceDetailsCompactView para habilitar la búsqueda y el filtrado de contenido multimedia o de opiniones.

Crea espacios de contenido personalizados

Opciones de posición de espacios publicitarios de contenido personalizado

Los espacios de contenido personalizado te permiten agregar tu propio contenido (como resúmenes generados por IA, marcas de tiempo de itinerarios o insignias personalizadas) directamente en zonas de diseño predefinidas.

Para agregar contenido personalizado, usa el método .placeDetailsCompactContentSlot de AdvancedPlaceDetailsCompactView. Las posiciones disponibles son .basicSectionTop y .basicSectionBottom.

Requisitos de atribución y sugerencias de contenido para los espacios de contenido

Cuando se activa un espacio de contenido personalizado, la divulgación se actualiza automáticamente para aclarar qué contenido proporciona Google y qué contenido proporcionan terceros.

Qué debes hacer

Mantén una jerarquía visual clara entre los detalles de Google Place y el contenido personalizado para evitar cualquier ambigüedad sobre la fuente de la información.

Evita la ambigüedad sobre la fuente de información

Qué debes hacer

Considera proporcionar estadísticas localizadas y de gran valor adaptadas a los objetivos inmediatos de tus usuarios. El contenido personalizado, como los períodos de reserva en tiempo real o la disponibilidad actual, ayuda a los usuarios a tomar decisiones más rápidas y fundamentadas.

Proporciona estadísticas de gran valor

Qué no debes hacer

No dupliques los detalles existentes de Google Place, como las calificaciones, las opiniones o el horario de atención, incluso si ocultaste el contenido proporcionado por Google. Si lo haces, los usuarios podrían confundirse sobre la fuente del contenido y si proviene de Google o de un tercero.

No dupliques los detalles de un lugar de Google existente

Qué no debes hacer

No muestres afirmaciones ni insignias sin atribución (por ejemplo, "¡La mejor recomendación!") que puedan confundirse con respaldos oficiales de Google. El contenido personalizado debe atribuir explícitamente su fuente.

No muestres afirmaciones o insignias sin atribución

Qué no debes hacer

No incluyas anuncios, textos de marketing, descuentos promocionales ni mensajes de suscripción en los contenedores de lugares personalizados.

No incluyas anuncios, textos de marketing, descuentos promocionales ni mensajes de suscripción

Ejemplos avanzados de Place Details

Omitir el nombre del lugar

Para omitir el nombre del lugar, no incluyas .name en el parámetro content de AdvancedPlaceDetailsConfiguration.

Swift

struct DemoView: View {
  static let peetsCoffeePlaceID = "ChIJT7FdmYiAhYAROFOvrIxRJDU"

  // Query for loading the place details component.
  @State var query: PlaceDetailsQuery = PlaceDetailsQuery(
    identifier: .placeID(peetsCoffeePlaceID)
  )
  @State private var favoritePlaces: Set<String> = []

  var body: some View {
    AdvancedPlaceDetailsView(
      query: $query,
      configuration: AdvancedPlaceDetailsConfiguration(
        content:[.address()],
        theme: PlacesMaterialTheme()
      ),
      placeDetailsCallback: { result in
        if let place = result.place {
          print("Place: \(place.description)")
        } else {
          print("Error: \(String(describing: result.error))")
        }
      }
    )
  }
}

Quita los botones de acción

Swift

struct DemoView: View {
  static let peetsCoffeePlaceID = "ChIJT7FdmYiAhYAROFOvrIxRJDU"

  @State var query: PlaceDetailsQuery = PlaceDetailsQuery(
    identifier: .placeID(peetsCoffeePlaceID)
  )

  var body: some View {
    AdvancedPlaceDetailsView(
      query: $query,
      configuration: AdvancedPlaceDetailsConfiguration(
        content: AdvancedPlaceDetailsView.allContent,
        theme: PlacesMaterialTheme()
      ),
      placeDetailsCallback: { result in
        if let place = result.place {
          print("Place: \(place.description)")
        } else {
          print("Error: \(String(describing: result.error))")
        }
      }
    )
    .mainActions{ _ in
      return []
    }
  }
}

Agrega acciones personalizadas y predefinidas

En este ejemplo, se agrega una opción para que un usuario marque o quite de favoritos un lugar, junto con la acción predefinida Abrir en Maps.

Swift

struct DemoView: View {
  static let peetsCoffeePlaceID = "ChIJT7FdmYiAhYAROFOvrIxRJDU"

  @State var query: PlaceDetailsQuery = PlaceDetailsQuery(
    identifier: .placeID(peetsCoffeePlaceID)
  )
  
  @State var favoritePlaces: Set<String> = []

  var body: some View {
    AdvancedPlaceDetailsView(
      query: $query,
      configuration: AdvancedPlaceDetailsConfiguration(
        content: AdvancedPlaceDetailsView.allContent,
        theme: PlacesMaterialTheme()
      ),
      placeDetailsCallback: { result in
        if let place = result.place {
          print("Place: \(place.description)")
        } else {
          print("Error: \(String(describing: result.error))")
        }
      }
    )
    .mainActions{ place in
      var actions: [MainPlaceActionElement] = []
      let placeID = place.placeID!
      if self.favoritePlaces.contains(placeID) {
        actions.append(
          .action(
            MainPlaceAction(
              image: Image(systemName: "heart.fill"),
              label: "Unfavorite"
            ) { _ in self.toggleFavorite(placeID) }
          )
        )
      } else {
        actions.append(
          .action(
            MainPlaceAction(
              image: Image(systemName: "heart"),
              label: "Favorite"
            ) { _ in self.toggleFavorite(placeID) }
          )
        )
      }
      actions.append(.actionId(.openMap(style: .primary)))
      return actions
    }
    .cornerActions { place in
      var actions: [CornerPlaceActionElement] = []
      let placeID = place.placeID!
      if self.favoritePlaces.contains(placeID) {
        actions.append(
          .action(
            CornerPlaceAction(
              image: Image(systemName: "heart.fill"),
              accessibilityLabel: "Unfavorite",
            ) { _ in self.toggleFavorite(placeID) }
          )
        )
      } else {
        actions.append(
          .action(
            CornerPlaceAction(
              image: Image(systemName: "heart"),
              accessibilityLabel: "Favorite",
            ) { _ in self.toggleFavorite(placeID) }
          )
        )
      }
      actions.append(.actionId(.openMap(style: .primary)))
      return actions
    }
  }

  func toggleFavorite(_ placeID: String) {
    if favoritePlaces.contains(placeID) {
      favoritePlaces.remove(placeID)
    } else {
      favoritePlaces.insert(placeID)
    }
  }
}

Cómo filtrar opiniones y contenido multimedia

En esta muestra de código, solo se incluyen fotos, videos y opiniones que mencionan "café".

Swift

struct DemoView: View {
  static let peetsCoffeePlaceID = "ChIJT7FdmYiAhYAROFOvrIxRJDU"

  @State var query: PlaceDetailsQuery = PlaceDetailsQuery(
    identifier: .placeID(peetsCoffeePlaceID)
  )
  let searchMediaOptions = SearchMediaOptions(query: "coffee")
  let searchReviewsOptions = SearchReviewsOptions(query: "coffee")

  var body: some View {
    AdvancedPlaceDetailsView(
      query: $query,
      configuration: AdvancedPlaceDetailsConfiguration(
        content: [.media(), .reviews()],
        theme: PlacesMaterialTheme()
      ),
      placeDetailsCallback: { _ in }
    )
    .searchMediaOptions(searchMediaOptions) { results in
      guard let result = results.first else { return }
      switch result {
      case .success(let summary):
        print("Summary: \(summary)")
      case .failure(let error):
        print("Error: \(error)")
      }
    }
    .searchReviewsOptions(searchReviewsOptions) { results in
      guard let result = results.first else { return }
      switch result {
      case .success(let summary):
        print("Summary: \(summary)")
      case .failure(let error):
        print("Error: \(error)")
      }
    }
  }
}

Cómo crear espacios de contenido

En este ejemplo, se agrega contenido a los espacios basicSectionTop y basicSectionBottom.

Swift

import SwiftUI
import PlacesSwift

struct ItineraryCard: View {
    static let peetsCoffeePlaceID = "ChIJT7FdmYiAhYAROFOvrIxRJDU"

    @State var query: PlaceDetailsQuery = PlaceDetailsQuery(
      identifier: .placeID(peetsCoffeePlaceID)
    )

    var body: some View {
      AdvancedPlaceDetailsCompactView(
        orientation: .vertical,
        query: $query,
        configuration: AdvancedPlaceDetailsCompactConfiguration(
          content: AdvancedPlaceDetailsCompactView.allContent,
          theme: PlacesMaterialTheme()
        ),
        placeDetailsCallback: { _ in }
      )
      .placeDetailsCompactContentSlot(position: .basicSectionTop) { place in
        Text("Day 2 • 2:30 PM")
          .font(.caption)
          .fontWeight(.bold)
          .foregroundColor(.accentColor)
      }
      .placeDetailsCompactContentSlot(position: .basicSectionBottom) { place in
        VStack(alignment: .leading, spacing: 4) {
          Text("AI Notes")
            .font(.caption)
            .bold()
          Text("Great quiet atmosphere for morning meetings. Famous for house-roasted pour-overs.")
            .font(.footnote)
            .foregroundColor(.secondary)
        }
        .padding(8)
        .background(Color(.systemGray6))
        .cornerRadius(8)
      }
    }
}