Advanced Place Details コンポーネント

Place Details コンポーネントの Advanced バージョンは、Places Details Essentials と同じ機能を提供します。また、ビジネス名を表示するかどうかを選択したり、コンポーネントのアクション ボタンを設定したり、表示されるクチコミやメディアに検索とフィルタリングのオプションを適用したりすることもできます。

高度な Place Details の構成

上級者向け機能にアクセスするには、AdvancedPlaceDetailsView または AdvancedPlaceDetailsCompactView を使用します。

コンテンツの表示オプション

AdvancedPlaceDetailsContent の列挙型を使用して、表示するコンテンツを指定できます。プレイスの詳細の基本と同じコンテンツをすべて設定できるほか、プレイス名を表示するかどうかを選択できます。

アクション ボタンを設定する

AdvancedPlaceDetailsView または AdvancedPlaceDetailsCompactView の .cornerActions メソッドと .mainActions メソッドを使用して、アクション ボタンを削除またはカスタマイズします。

視覚的な表示ルールの要件

カスタマイズされたボタンを含む Place Details のコンパクト ビュー
カスタマイズされたボタンを含む詳細な Place Details のコンパクト ビュー

重要: カスタム操作ボタンが Google 以外の第三者とユーザー情報を共有する場合、エンドユーザーにそのことを明確に伝える必要があります。ビジュアル アトリビューションの要件をご覧ください。

右の例は、ユーザー情報が「Altostrat」に送信されることを明確にするカスタム画像を示しています。

検索とフィルタのオプションを適用する

AdvancedPlaceDetailsView または AdvancedPlaceDetailsCompactView の .searchMediaOptions メソッドと .searchReviewOptions メソッドを使用して、メディアまたはレビューの検索とフィルタリングを有効にします。

カスタム コンテンツ スロットを作成する

カスタム コンテンツ スロットの配置オプション

カスタム コンテンツ スロットを使用すると、独自のコンテンツ(AI 生成の要約、旅程のタイムスタンプ、カスタム バッジなど)を事前定義されたレイアウト ゾーンに直接追加できます。

カスタム コンテンツを追加するには、AdvancedPlaceDetailsCompactView の .placeDetailsCompactContentSlot メソッドを使用します。使用できる位置は .basicSectionTop と .basicSectionBottom です。

コンテンツ スロットの帰属表示の要件とコンテンツの提案

カスタム コンテンツ スロットがマウントされると、開示情報が自動的に更新され、どのコンテンツが Google によって提供され、どのコンテンツがサードパーティによって提供されるかが明確になります。

すべきこと

Google のプレイスの詳細とカスタム コンテンツの間に明確な視覚的階層を維持し、情報のソースに関する曖昧さを防ぎます。

情報源の曖昧さを解消する

すべきこと

ユーザーの直近の目標に合わせてローカライズされた、価値の高い分析情報を検討します。リアルタイムの予約ウィンドウや現在の空き状況などのカスタマイズされたコンテンツは、ユーザーがより迅速かつ十分な情報に基づいて判断を下すのに役立ちます。

価値の高い分析情報を提供する

悪い例

Google 提供のコンテンツを非表示にした場合でも、評価、レビュー、営業時間など、既存の Google プレイスの詳細情報を複製しないでください。そうすると、コンテンツのソースや、Google から提供されたものか第三者から提供されたものかについて、ユーザーが混乱する可能性があります。

既存の Google プレイスの詳細を重複させない

悪い例

Google の公式な推奨と誤解される可能性のある、帰属先のない主張やバッジ(「おすすめ!」など)を表示しないでください。カスタム コンテンツでは、出典を明示的に明記する必要があります。

帰属先のない主張やバッジを表示しない

悪い例

カスタムのプレース コンテナ内に広告、マーケティング コピー、プロモーション割引、定期購入のプロンプトを含めないでください。

広告、マーケティング コピー、プロモーション割引、定期購入のプロンプトは含めないでください

高度な Place Details の例

場所の名前を省略する

地名を除外するには、AdvancedPlaceDetailsConfiguration の content パラメータに .name を含めないでください。

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))")
        }
      }
    )
  }
}

操作ボタンを削除する

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

カスタム アクションと事前定義アクションを追加する

このサンプルでは、ユーザーが場所をお気に入りに追加またはお気に入りから削除するためのオプションと、事前定義された [地図で開く] アクションを追加します。

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)
    }
  }
}

クチコミとメディアをフィルタする

このコードサンプルは、「コーヒー」に言及しているメディアとレビューのみを表示します。

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)")
      }
    }
  }
}

コンテンツ スロットを作成する

このサンプルでは、basicSectionTop スロットと 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)
      }
    }
}