Componente Advanced Place Details

A versão Advanced do componente Place Details oferece os mesmos recursos do Places Details Essentials. Também é possível escolher se o nome do lugar vai aparecer, configurar os botões de ação do componente e aplicar opções de pesquisa e filtragem às avaliações e mídias exibidas.

Configuração avançada de Place Details

Use AdvancedPlaceDetailsView ou AdvancedPlaceDetailsCompactView para acessar recursos avançados.

Opções de exibição de conteúdo

É possível especificar qual conteúdo mostrar usando as enumerações em AdvancedPlaceDetailsContent. Você pode configurar todo o conteúdo do Places Details Essentials e escolher se o nome do lugar vai aparecer.

Configurar botões de ação

Use os métodos .cornerActions e .mainActions de AdvancedPlaceDetailsView ou AdvancedPlaceDetailsCompactView para remover ou personalizar os botões de ação.

Requisitos de atribuição visual

Visualização compacta de detalhes do lugar com botões personalizados
Visualização compacta do Advanced Place Details com botão personalizado

Importante:você precisa deixar claro para os usuários finais quando um botão de ação personalizada compartilhar informações do usuário com um terceiro que não seja o Google. Consulte os requisitos de atribuição visual.

O exemplo à direita mostra uma imagem personalizada que deixa claro que as informações do usuário serão enviadas para "Altostrat".

Aplicar opções de pesquisa e filtragem

Use os métodos .searchMediaOptions e .searchReviewOptions de AdvancedPlaceDetailsView ou AdvancedPlaceDetailsCompactView para ativar a pesquisa e a filtragem de mídia ou avaliações.

Criar slots de conteúdo personalizados

Opções de posicionamento de espaços de conteúdo personalizados

Com os espaços de conteúdo personalizados, você pode adicionar seu próprio conteúdo (como resumos gerados por IA, carimbos de data/hora de itinerários ou selos personalizados) diretamente em zonas de layout predefinidas.

Para adicionar conteúdo personalizado, use o método .placeDetailsCompactContentSlot de AdvancedPlaceDetailsCompactView. As posições disponíveis são .basicSectionTop e .basicSectionBottom.

Requisitos de atribuição e sugestões de conteúdo para espaços de conteúdo

Quando um slot de conteúdo personalizado é montado, a declaração é atualizada automaticamente para esclarecer qual conteúdo é fornecido pelo Google e que parte dele é fornecida por terceiros.

O que fazer

Mantenha uma hierarquia visual clara entre os detalhes do Google Maps e o conteúdo personalizado para evitar ambiguidades sobre a origem das informações.

Evitar ambiguidade sobre a fonte de informações

O que fazer

Considere insights localizados e de alto valor adaptados às metas imediatas dos usuários. Conteúdo personalizado, como períodos de reserva em tempo real ou disponibilidade atual, ajuda os usuários a tomar decisões mais rápidas e informadas.

Fornecer insights de alto valor

O que não fazer

Não duplique detalhes de lugares do Google, como classificações, avaliações ou horário de funcionamento, mesmo que você tenha ocultado o conteúdo fornecido pelo Google. Isso pode confundir os usuários sobre a origem do conteúdo e se ele vem do Google ou de terceiros.

Não duplique os detalhes de um lugar no Google

O que não fazer

Não mostre declarações ou selos não atribuídos (por exemplo, "Recomendação principal!") que possam ser confundidos com endossos oficiais do Google. O conteúdo personalizado precisa atribuir explicitamente a fonte.

Não mostrar declarações ou selos sem atribuição

O que não fazer

Não inclua anúncios, textos de marketing, descontos promocionais ou solicitações de assinatura em contêineres de lugar personalizados.

Não inclua anúncios, textos de marketing, descontos promocionais ou solicitações de assinatura.

Exemplos avançados de Place Details

Omitir o nome do lugar

Para omitir o nome do lugar, não inclua .name no 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))")
        }
      }
    )
  }
}

Remover botões de ação

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

Adicionar ações personalizadas e predefinidas

Este exemplo adiciona uma opção para um usuário marcar ou desmarcar um lugar como favorito, além da ação predefinida Abrir no 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)
    }
  }
}

Filtrar avaliações e mídia

Este exemplo de código mostra apenas mídia e avaliações que mencionam "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)")
      }
    }
  }
}

Criar slots de conteúdo

Este exemplo adiciona conteúdo aos slots basicSectionTop e 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)
      }
    }
}