Advanced Place Details 구성요소

장소 세부정보 구성요소의 고급 버전은 장소 세부정보 Essentials와 동일한 기능을 제공합니다. 또한 장소 이름 표시 여부를 선택하고, 구성요소의 작업 버튼을 구성하고, 표시되는 리뷰와 미디어에 검색 및 필터링 옵션을 적용할 수 있습니다.

고급 장소 세부정보 구성

AdvancedPlaceDetailsView 또는 AdvancedPlaceDetailsCompactView를 사용하여 고급 기능에 액세스합니다.

콘텐츠 표시 옵션

AdvancedPlaceDetailsContent의 열거형을 사용하여 표시할 콘텐츠를 지정할 수 있습니다. 장소 세부정보 필수와 동일한 콘텐츠를 모두 구성할 수 있으며, 장소 이름이 표시되는지 여부를 선택할 수 있습니다.

작업 버튼 구성

AdvancedPlaceDetailsView 또는 AdvancedPlaceDetailsCompactView의 .cornerActions 및 .mainActions 메서드를 사용하여 작업 버튼을 삭제하거나 맞춤설정합니다.

시각적 저작자 표시 요구사항

맞춤 버튼이 있는 장소 세부정보 콤팩트 뷰
맞춤설정된 버튼이 있는 고급 장소 세부정보 컴팩트 뷰

중요: 맞춤 작업 버튼이 Google 이외의 서드 파티와 사용자 정보를 공유하는 경우를 최종 사용자에게 명확하게 알려야 합니다. 시각적 출처 표시 요구사항을 참고하세요.

오른쪽의 예에서는 사용자 정보가 'Altostrat'으로 전송된다는 점을 명확하게 보여주는 맞춤 이미지를 확인할 수 있습니다.

검색 및 필터링 옵션 적용

AdvancedPlaceDetailsView 또는 AdvancedPlaceDetailsCompactView의 .searchMediaOptions 및 .searchReviewOptions 메서드를 사용하여 미디어나 리뷰의 검색 및 필터링을 사용 설정합니다.

맞춤 콘텐츠 슬롯 만들기

맞춤 콘텐츠 슬롯 배치 옵션

맞춤 콘텐츠 슬롯을 사용하면 AI 생성 요약, 일정 타임스탬프, 맞춤 배지와 같은 자체 콘텐츠를 사전 정의된 레이아웃 영역에 직접 추가할 수 있습니다.

맞춤 콘텐츠를 추가하려면 AdvancedPlaceDetailsCompactView의 .placeDetailsCompactContentSlot 메서드를 사용합니다. 사용 가능한 위치는 .basicSectionTop 및 .basicSectionBottom입니다.

콘텐츠 슬롯의 저작자 표시 요구사항 및 콘텐츠 제안

맞춤 콘텐츠 슬롯이 마운트되면 Google에서 제공하는 콘텐츠와 일부 콘텐츠가 서드 파티에서 제공된다는 점을 명확히 하기 위해 공개 정보가 자동으로 업데이트됩니다.

권장사항

정보의 출처에 관한 모호성을 방지하기 위해 Google 장소 세부정보와 맞춤 콘텐츠 간에 명확한 시각적 계층 구조를 유지합니다.

정보 출처에 대한 모호성 방지

권장사항

사용자의 즉각적인 목표에 맞게 현지화된 가치 있는 통계를 고려하세요. 실시간 예약 기간이나 현재 예약 가능 여부와 같은 맞춤 콘텐츠는 사용자가 더 빠르고 정보에 입각한 결정을 내리는 데 도움이 됩니다.

가치 있는 통계 제공

금지사항

Google에서 제공한 콘텐츠를 숨긴 경우에도 평점, 리뷰, 영업시간과 같은 기존 Google 장소 세부정보를 중복해서는 안 됩니다. 이렇게 하면 콘텐츠의 출처가 Google인지 서드 파티인지에 관해 사용자에게 혼동을 줄 수 있습니다.

기존 Google 장소 세부정보를 중복하지 마세요.

금지사항

공식 Google 보증으로 오인될 수 있는 출처가 표시되지 않은 주장이나 배지('추천' 등)를 표시하지 마세요. 맞춤 콘텐츠는 출처를 명시적으로 표시해야 합니다.

출처가 표시되지 않은 주장 또는 배지를 표시하지 않음

금지사항

맞춤 장소 컨테이너 내에 광고, 마케팅 문구, 프로모션 할인 또는 구독 메시지를 포함하지 마세요.

광고, 마케팅 문구, 프로모션 할인 또는 구독 메시지를 포함하지 않습니다.

고급 장소 세부정보 예

장소 이름 생략

장소 이름을 생략하려면 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)
      }
    }
}