
使用版面配置自訂功能,控制 Google 提供的檢視畫面和自訂業務檢視畫面的檢視區塊階層和畫面位置。
請實作版面配置委派代表來管理整個畫面的版面配置,而不是將自訂檢視畫面新增至預先定義的版面配置版位。在導航狀態轉換期間,版面配置委派代表會收到 Google 提供的元件,例如轉彎資訊卡、預計到達時間資訊卡和按鈕。請使用標準 iOS 自動版面配置限制,將這些元素與自訂內容並排。
使用這個架構,即可在畫面上精確定位自訂業務資訊 (例如訂單狀態或取貨操作說明),同時避免檢視畫面重疊。
版面配置自訂功能的運作方式
版面配置自訂功能採用委派設計模式。SDK 不會自動在畫面上繪製或定位 UI 元件,而是直接將元件傳遞至您編寫的自訂類別:版面配置委派。
如要自訂版面配置,請建立符合NavigationLayoutDelegate 通訊協定的類別,並將例項傳遞至 GMSMapView 的 setLayoutDelegate(_:) 方法。每當導航狀態轉換時 (例如從基本地圖切換至即時路線指引),SDK 就會對委派物件執行回呼方法,並提供該特定狀態可用的 UI 元件。為提供流暢、整合的使用者體驗,這個架構明確區分了應用程式和 SDK 的責任。使用版面配置委派來自訂下列項目:
- 建構檢視區塊階層:選擇要為每個 UI 狀態新增至畫面的 Google 元件和自訂業務檢視區塊。
- 定位每個元素:設定精確的螢幕錨點、邊界和版面配置位置。請避免對 Google 元件套用自訂寬度或高度限制,因為 Google 元件會自行計算內部尺寸。
- 設定基本地圖的框架:使用可視區域元件的座標,定義地圖攝影機的可見邊界。
- 分層顯示畫面:決定自訂檢視畫面要浮動顯示在 Google 內建控制項的上方、下方或旁邊。
同時,您無法使用版面配置委派來自訂下列 Google 元件:
- 元件尺寸:Google 提供的元件大小和內部尺寸,由 SDK 自動計算。
- 觸發條件:根據即時路線資料,顯示動態快訊或提示。
導入原則
編寫版面配置委派時,請遵守下列規則,避免版面配置錯誤或執行階段當機:
- 預設為空白畫面:只有在委派明確將 Google 提供的元件新增至檢視區塊階層並定位時,這些元件才會顯示。
- 不支援舊版 API:自訂委派代表啟用時,系統不支援控制舊版版位版面配置模型的屬性和方法,且可能無法正常運作。
- 請勿修改內部檢視區塊結構:請勿周遊或修改 Google 提供元件 (例如轉彎資訊卡或預計到達時間資訊卡) 的檢視區塊階層。由於這些內部檢視區塊階層是基礎實作細節,因此可能會隨著 SDK 版本而變更。如果修改這些屬性,可能會導致版面配置在日後更新 SDK 時中斷。
舊版 API 相容性
使用自訂版面配置委派時,為確保版面配置行為可靠,請避免使用下列以版位為基礎的舊版 API,這些 API 預計會淘汰。如要遷移現有應用程式,請將這些 API 的使用方式替換為自訂版面配置委派中的程式碼:
顯示舊版版位 API
| 舊版 API | 版面配置委派代表替代 |
|---|---|
isNavigationHeaderEnabled |
讀取 startGuidance(navMap:newState:oldState:) 中的 turnCard 檢視表。 |
isNavigationFooterEnabled |
讀取 startGuidance(navMap:newState:oldState:) 中的 etaCard 檢視表。 |
isNavigationTripProgressBarEnabled |
讀取 startGuidance(navMap:newState:oldState:) 中的 tripProgressBar 檢視表。 |
isNavigationReportIncidentButtonEnabled |
在 buttons 陣列中找出 .reporting 按鈕。 |
setHeaderAccessoryView(_:)和invalidateLayout(forAccessoryView:) |
在版面配置中加入自己的檢視畫面,並將其錨定至 turnCard。 |
padding和paddingAdjustmentBehavior |
調整 viewport 檢視畫面的位置,讓相機入鏡。 |
navigationHeaderLayoutGuide |
直接將自訂檢視畫面錨定至 turnCard。 |
navigationFooterLayoutGuide |
直接將自訂檢視畫面錨定至 etaCard。 |
bottomTrailingButtonsLayoutGuide |
將按鈕直接錨定至 etaCard 或容器輔助線。 |
bottomTrailingButtonsAlternateLayoutGuide |
將按鈕直接錨定至 .reporting 按鈕或 wideMode 限制。 |
navigatorWillPresentPrompt(_:)和navigatorDidDismissPrompt(_:) |
請依賴 NavigationLayoutDelegate 提示回呼,例如 showPrompt(navMap:newPrompt:)。 |
設定自訂版面配置委派代表後,舊版版面配置指南 (例如 navigationHeaderLayoutGuide 或 bottomTrailingButtonsLayoutGuide) 和標頭附屬檢視畫面方法就不會生效。請改為將檢視畫面錨定至 Google 提供的檢視畫面。
停用導航 UI 時,GMSUISettings 的 compassButton 屬性仍會控制指南針。啟用導航功能後,委派代表會決定是否要顯示 buttons 陣列中的指南針。
基本檢查清單
請按照下列基本步驟和規定,成功導入版面配置委派:
-
在啟用導航前設定委派:請先在
GMSMapView上呼叫setLayoutDelegate(_:),再使用isNavigationEnabled或enableNavigation(with:)啟用導航。如果導航功能已啟用,或您為 CarPlay 畫面建立了地圖檢視,這個方法會擲回錯誤。地圖檢視畫面會保留對委派物件的強參照。如要還原內建版面配置,請在停用導航功能時傳遞nil。// Set the layout delegate before you enable navigation. do { try mapView.setLayoutDelegate(MyLayoutDelegate()) } catch { print("Couldn't set the layout delegate: \(error)") } mapView.isNavigationEnabled = true
-
附加必要檢視畫面:您必須在所有導航狀態下,將 Google 標誌 (
googleLogo) 和可視區域 (viewport) 新增至版面配置 容器。 -
遵循固定尺寸:SDK 會決定多個 Google 提供的元件尺寸。請勿對下列元素套用自訂寬度或高度限制:
- 轉牌
- 預計到達時間資訊卡
- 收到的提示
- Google 標誌
- 速度小工具
-
將元素新增為子檢視畫面:直接將 Google 提供的檢視畫面,新增為自訂容器檢視畫面或
GMSMapView本身的子檢視畫面。 -
使用版面配置指南對齊:Google 提供的檢視畫面包含內建的視覺邊界。如要讓自訂元素與 Google 提供的元件完美對齊,請將檢視畫面限制在 Google 檢視畫面的
layoutMarginsGuide屬性。 -
在狀態結束時清除:在
stopGuidance(navMap:newState:oldState:)中,移除有效的導引檢視畫面並停用其限制,然後再還原導航就緒版面配置。在disableNavigationUI(navMap:oldState:)中,移除您新增的所有檢視畫面和版面配置指南,包括畫面上的任何提示。這可避免某個狀態的檢視畫面與下一個狀態重疊。 -
自行處理無障礙功能和由右至左支援:Google 提供的檢視畫面符合無障礙標準,但委派代表必須負責整體版面配置的無障礙功能。如要支援由右至左的語言,請使用開頭和結尾錨定,而非左側和右側錨定。
-
避免使用舊版版面配置 API:自訂委派代表處於啟用狀態時,請勿在
GMSUISettings上使用舊版版面配置修改屬性,或在GMSMapView上使用舊版版面配置指南。附加自訂委派代表時,SDK 可能會忽略這些舊版通話。
UI 狀態和 Google 元件
導航狀態轉換時,SDK 會將唯讀的 NavigationUIState 物件傳遞至委派回呼。這個物件會將目前的版面配置設定旗標,與轉譯畫面所需的 Google 元件 (例如 UIView 執行個體) 組合在一起。
版面配置委派代表會管理四種作業狀態的檢視區塊階層。下列狀態圖說明 SDK 如何在導航狀態之間轉換,以及在委派物件上執行的回呼方法:
提供給委派代表的特定 Google 元件,取決於導航生命週期的目前階段。
必要元件 (所有導覽狀態)
無論目前的導航階段為何,您都必須在檢視區塊階層中加入、定位及保留下列 Google 元件:
Google 標誌 (
googleLogo):這個元件會顯示必要的 Google 地圖標誌。如果啟用「重新置中」按鈕,當駕駛人捲動地圖,使車輛不在畫面中央時,標誌會自動轉換為這個按鈕。因此,標誌的位置會決定「重新置中」按鈕顯示的位置。建議將標誌放在版面配置的左下角,符合標準地圖 UI 的預期。可視區域 (
viewport): 定義攝影機取景邊界的隱形UIView。SDK 會根據可視區域的位置,將車輛 V 形符號置中,並繪製有效路線。調整可視區域的位置,使其覆蓋螢幕上未遮蔽的開放區域,安全避開自訂底部功能表等不透明疊加層。
個導覽就緒元件
在導航就緒狀態下,UI 會保持精簡,讓使用者專注於基本地圖。NavigationReadyUIState 物件可讓您存取下列項目:
- buttons:為基本地圖設定的浮動動作檢視畫面陣列 (通常只有「指南針」按鈕)。由於 Google 可能會在日後的 SDK 版本中新增或重新排序按鈕,因此請避免依賴固定的陣列索引。請改為疊代陣列,透過比較
type屬性和NavigationButtonKnownType.compass來讀取每個按鈕的類型,並呼叫其view屬性來擷取實體檢視畫面。請注意,這個陣列中的確切按鈕與主動引導期間可用的按鈕不同。
個有效的指引元件
即時路線指引開始後,SDK 會解鎖全套導航控制項。ActiveGuidanceUIState 物件可讓您存取下列 Google 元件:
turnCard:主要標題橫幅,顯示即將進行的行車路線、距離測量和車道指引。請將其置於版面配置頂端,建立熟悉的導覽階層,並將自訂檢視畫面錨定在周圍。etaCard:頁尾橫幅,顯示預計到達時間、剩餘行程時間,以及到目的地的剩餘距離。您可以將其放置在螢幕底部邊緣,或將其座標與自訂工作管理表整合。tripProgressBar:垂直進度列,顯示駕駛人在目前路線上行駛的距離。與舊版版面配置不同,您不必將其固定在靠近地圖邊緣的位置,而是可以自由錨定在任何地方,例如自訂容器的側邊。speedWidget:時速表和速限標示的浮動控制項。視 API 設定 (GMSMapView的shouldDisplaySpeedometer和shouldDisplaySpeedLimit屬性) 和資料可用性而定,這個檢視畫面會在執行階段,動態調整四種視覺狀態的大小 (不顯示任何內容、僅顯示目前速度、僅顯示速限,或同時顯示兩者)。由於小工具可能會在這些大小之間切換,且不會發出通知,因此請一律使用限制錨定周圍的檢視畫面,讓版面配置自動調整,避免空間重疊。
buttons:主動導航狀態的展開浮動動作檢視畫面陣列 (通常包含「指南針」和「事故通報」按鈕)。與導航就緒狀態一樣,您可以依type(.compass或.reporting) 篩選陣列,並使用view屬性擷取實體檢視畫面,藉此找出並擷取個別按鈕檢視畫面。接著,您可以單獨定位這些檢視區塊,或將其新增至GMSNavigationHidingStackView。這個堆疊檢視畫面會垂直排列按鈕,並在空間不足時,從arrangedSubviews陣列的結尾開始隱藏按鈕。請勿在受管理的檢視畫面上設定isHidden。
NavigationStyleValues.headerNominalHeight 為準,足以容納任何轉彎資訊卡內容。如要讓相機畫面保持穩定,請將 viewport 的頂端直接限制在 turnCard 的底部邊緣,如實作範例所示。個動態提示詞元件
在導航期間,事故警示或測速照相警告等提示會獨立觸發。
提示準備好顯示時,SDK 會呼叫委派物件的 showPrompt(navMap:newPrompt:) 回呼,並直接傳遞 newPrompt 檢視畫面。委派代表負責將這則提示順暢地置於版面配置上方 (通常錨定在地圖容器的底部邊緣)。
由於傳入的提示會覆蓋螢幕下半部,因此您必須更新版面配置,以免提示與可視區域、Google 標誌或任何靠底對齊的按鈕重疊。
委派物件也會控制提示進入和離開畫面的方式。詳情請參閱「自訂提示」。
處理螢幕大小和寬螢幕模式
為處理地圖尺寸和螢幕方向的變更,版面配置委派會使用下列功能:
檢視畫面大小調整:每當地圖的實際大小變更時,調整版面配置。
寬模式:地圖夠寬時,切換為寬格式版面配置。
回應檢視畫面大小調整
分割畫面版面配置、容器滑桿和裝置旋轉通常會觸發sizeChanged(navMap:newState:) 回呼。您可以使用這個回呼,對自訂 UI 進行一般回應式調整。實作 sizeChanged(navMap:newState:),根據新的螢幕顯示比例重新定位自訂元素、套用自訂寬度或高度中斷點,以及偵測調整大小事件何時切換寬螢幕模式,以便安全地切換版面配置變化版本。瞭解寬版顯示模式
如果地圖容器寬度足以並排顯示 UI 元件,就會啟用寬版模式。
從狀態物件讀取 wideMode 布林值,重新定位自訂 UI 元素,並確保地圖中心不會遮擋駕駛視線。由於 Google 提供的元件 (例如轉彎資訊卡和預計到達時間資訊卡) 在寬螢幕模式下會自動縮小並重新調整形狀,因此讀取這個布林值可確保版面配置在 Google 元件更新的同時進行調整。
請參考以下範例,瞭解如何在標準和寬螢幕模式下,安排版面配置元件的位置:
標準直向模式:將轉彎資訊卡置於畫面頂端,預計到達時間資訊卡置於底部。
寬螢幕模式:將轉彎資訊卡和預計到達時間資訊卡沿著螢幕前緣排列,轉彎資訊卡在上方,預計到達時間資訊卡在下方。如果將任一卡片放在前緣以外的位置,會導致未定義的行為。您可以將可視區域和自己的內容放在任何位置,常見的做法是放在資訊卡的結尾端。
在狀態轉換期間檢查寬版模式
如果版面配置支援寬模式變化版本,請在每個狀態轉換回呼中評估 wideMode 布林值,而不是只依賴 sizeChanged(navMap:newState:)。
enableNavigationUI(navMap:newState:) 和 startGuidance(navMap:newState:oldState:) 等回呼期間檢查狀態物件,以便處理橫向模式下的初始應用程式啟動。這種做法可確保版面配置評估寬模式狀態,並在新的導覽狀態開始時,啟用正確的標準或寬版面配置變化版本。符合 Google 樣式
為協助自訂檢視區塊符合 Google 提供的元件間距,SDK 提供 NavigationStyleValues 類別。其類別屬性會以點為單位傳回測量值。
舉例來說,如要在螢幕頂端角落 (轉彎資訊卡對面) 放置自訂按鈕,請使用 NavigationStyleValues.headerTopPadding 做為按鈕的上邊界。按鈕會與轉彎資訊卡的頂端邊緣對齊。
可用的測量項目如下:
NavigationStyleValues.buttonHorizontalPaddingNavigationStyleValues.buttonVerticalPaddingNavigationStyleValues.defaultCornerRadiusNavigationStyleValues.elementHorizontalPaddingNavigationStyleValues.headerFooterHorizontalPaddingNavigationStyleValues.headerNominalHeightNavigationStyleValues.headerTopPadding
Google 提供的檢視畫面也包含內建邊界。堆疊兩個 Google 提供的檢視畫面時,請將其中一個檢視畫面限制在另一個檢視畫面的 layoutMarginsGuide,以免邊界累加。舉例來說,如要將速度小工具放在預計到達時間資訊卡上方,請將 speedWidget 的底部錨點限制在 etaCard.layoutMarginsGuide.topAnchor。
自訂提示
版面配置委派代表會控制提示進入和離開畫面的方式,以及提示的哪些角落會圓角化。
動畫提示
SDK 不會為提示加上動畫效果。委派代表會決定每個提示的顯示和消失方式:
- 在
showPrompt(navMap:newPrompt:)中,將提示新增至地圖檢視畫面的起始位置,然後以動畫方式移至定位。舉例來說,提示可以從可見區域下方開始,或從底部功能表後方開始,頂端邊緣與功能表頂端對齊。 - 在
hidePrompt(navMap:oldPrompt:)中,將提示動畫移出檢視畫面,並在動畫結束時從上層檢視畫面中移除。 - 在
changePrompt(navMap:newPrompt:oldPrompt:)中,同時執行隱藏和顯示動畫,將提示替換成另一個提示。
實作範例採用這種方法。
如要關閉路況、更佳路線和事故的提示,請將 GMSNavigator 的 shouldDisplayPrompts 屬性設為 false。
圓角提示
提示符合 NavigationRoundedCornerView 協定,因此您可以選擇要將哪些角落設為圓角。根據預設,提示只會將頂端邊角設為圓角,適合錨定在畫面底部的提示。如果將提示放在其他內容 (例如底部功能表) 上方,請將四個角都設為圓角:
newPrompt.roundedCorners = [
.layerMinXMinYCorner, .layerMaxXMinYCorner,
.layerMinXMaxYCorner, .layerMaxXMaxYCorner,
]
範例:導入限制式版面配置
以下範例顯示基本版面配置委派,使用自動版面配置限制來定位轉彎資訊卡、預計到達時間資訊卡、按鈕、Google 標誌和可視區域。此外,提示也會在畫面上滑入和滑出。為求簡短,這個範例不處理寬螢幕模式、速度小工具或行程進度列,因此僅適用於支援直向模式的應用程式。
import GoogleNavigation
import UIKit
/// A layout delegate that positions the essential navigation UI elements: the
/// turn card, ETA card, buttons, Google logo, and viewport. It also slides
/// prompts on and off the screen.
@MainActor
final class BasicLayoutDelegate: NSObject, NavigationLayoutDelegate {
/// How long prompts take to slide on or off the screen, in seconds.
private static let promptAnimationDuration: TimeInterval = 0.4
/// The views and constraints for one navigation state.
@MainActor
private final class StateElements {
let buttonStack = GMSNavigationHidingStackView(
position: .bottom, alignment: .trailing)
var constraints: [NSLayoutConstraint] = []
private unowned let navMap: GMSMapView
private let buttons: [UIView]
private let topLevelViews: [UIView]
init(
navMap: GMSMapView, buttons: [NavigationUIButton],
topLevelViews: [UIView]
) {
self.navMap = navMap
self.buttons = buttons.map { $0.view }
self.topLevelViews = topLevelViews
}
/// Adds the views to the map view, and then activates the constraints.
func activate() {
topLevelViews.forEach { navMap.addSubview($0) }
buttonStack.arrangedSubviews = buttons
navMap.addSubview(buttonStack)
NSLayoutConstraint.activate(constraints)
}
/// Deactivates the constraints, and then removes the views.
func deactivate() {
NSLayoutConstraint.deactivate(constraints)
buttonStack.removeFromSuperview()
buttonStack.arrangedSubviews = []
topLevelViews.forEach { $0.removeFromSuperview() }
}
}
private var navigationReadyElements: StateElements?
private var activeGuidanceElements: StateElements?
private var currentPrompt: NavigationPromptView?
private var promptBottomConstraint: NSLayoutConstraint?
/// Constraints that apply in both the navigation-ready and active guidance
/// states.
private var sharedConstraints: [NSLayoutConstraint] = []
/// Marks the bottom of the map content area. The guide stays above the ETA
/// card and above any prompt that's on the screen.
private let mapContentsBottomGuide = UILayoutGuide()
// MARK: - Navigation-ready state
func enableNavigationUI(
navMap: GMSMapView, newState: NavigationReadyUIState
) {
let safeArea = navMap.safeAreaLayoutGuide
let bottomGuide = mapContentsBottomGuide
navMap.addLayoutGuide(bottomGuide)
// Keep the guide at the bottom of the safe area unless a higher-priority
// constraint pushes it up.
let lowPriorityBottom = bottomGuide.bottomAnchor.constraint(
equalTo: safeArea.bottomAnchor)
lowPriorityBottom.priority = .defaultLow
sharedConstraints = [
bottomGuide.heightAnchor.constraint(equalToConstant: 0),
lowPriorityBottom,
]
let logo = newState.googleLogo
let viewport = newState.viewport
let elements = StateElements(
navMap: navMap, buttons: newState.buttons,
topLevelViews: [logo, viewport])
let buttonStack = elements.buttonStack
elements.constraints = sharedConstraints + [
buttonStack.trailingAnchor.constraint(equalTo: safeArea.trailingAnchor),
buttonStack.topAnchor.constraint(equalTo: safeArea.topAnchor),
buttonStack.bottomAnchor.constraint(equalTo: bottomGuide.topAnchor),
logo.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor),
logo.bottomAnchor.constraint(equalTo: bottomGuide.topAnchor),
viewport.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor),
viewport.trailingAnchor.constraint(equalTo: safeArea.trailingAnchor),
viewport.topAnchor.constraint(equalTo: safeArea.topAnchor),
viewport.bottomAnchor.constraint(equalTo: bottomGuide.topAnchor),
]
elements.activate()
navigationReadyElements = elements
}
func disableNavigationUI(
navMap: GMSMapView, oldState: NavigationReadyUIState
) {
currentPrompt?.removeFromSuperview()
currentPrompt = nil
promptBottomConstraint = nil
navigationReadyElements?.deactivate()
navigationReadyElements = nil
navMap.removeLayoutGuide(mapContentsBottomGuide)
}
// MARK: - Active guidance state
func startGuidance(
navMap: GMSMapView, newState: ActiveGuidanceUIState,
oldState: NavigationReadyUIState
) {
navigationReadyElements?.deactivate()
let safeArea = navMap.safeAreaLayoutGuide
let bottomGuide = mapContentsBottomGuide
let turnCard = newState.turnCard
let etaCard = newState.etaCard
let logo = newState.googleLogo
let viewport = newState.viewport
let elements = StateElements(
navMap: navMap, buttons: newState.buttons,
topLevelViews: [turnCard, etaCard, viewport, logo])
let buttonStack = elements.buttonStack
elements.constraints = sharedConstraints + [
turnCard.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor),
turnCard.topAnchor.constraint(equalTo: navMap.topAnchor),
etaCard.leadingAnchor.constraint(equalTo: navMap.leadingAnchor),
etaCard.bottomAnchor.constraint(equalTo: navMap.bottomAnchor),
bottomGuide.bottomAnchor.constraint(
lessThanOrEqualTo: etaCard.topAnchor),
buttonStack.trailingAnchor.constraint(equalTo: safeArea.trailingAnchor),
buttonStack.topAnchor.constraint(equalTo: turnCard.bottomAnchor),
buttonStack.bottomAnchor.constraint(equalTo: bottomGuide.topAnchor),
logo.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor),
logo.bottomAnchor.constraint(equalTo: bottomGuide.topAnchor),
// Frame the camera in the open area below the turn card.
viewport.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor),
viewport.trailingAnchor.constraint(equalTo: safeArea.trailingAnchor),
viewport.topAnchor.constraint(equalTo: turnCard.bottomAnchor),
viewport.bottomAnchor.constraint(equalTo: bottomGuide.topAnchor),
]
elements.activate()
activeGuidanceElements = elements
}
func stopGuidance(
navMap: GMSMapView, newState: NavigationReadyUIState,
oldState: ActiveGuidanceUIState
) {
activeGuidanceElements?.deactivate()
activeGuidanceElements = nil
navigationReadyElements?.activate()
}
// MARK: - Prompts
func showPrompt(navMap: GMSMapView, newPrompt: NavigationPromptView) {
navMap.addSubview(newPrompt)
currentPrompt = newPrompt
// Start with the prompt just below the bottom edge of the map view.
let bottomConstraint = newPrompt.bottomAnchor.constraint(
equalTo: navMap.bottomAnchor, constant: newPrompt.bounds.height)
promptBottomConstraint = bottomConstraint
NSLayoutConstraint.activate([
bottomConstraint,
newPrompt.leadingAnchor.constraint(
equalTo: navMap.safeAreaLayoutGuide.leadingAnchor),
// Keep the map content above the prompt.
mapContentsBottomGuide.bottomAnchor.constraint(
lessThanOrEqualTo: newPrompt.topAnchor),
])
// Lay out the prompt in its starting position, and then slide it up.
navMap.layoutIfNeeded()
UIView.animate(withDuration: Self.promptAnimationDuration) {
bottomConstraint.constant = 0
navMap.layoutIfNeeded()
}
}
func hidePrompt(navMap: GMSMapView, oldPrompt: NavigationPromptView) {
guard let bottomConstraint = promptBottomConstraint else { return }
promptBottomConstraint = nil
currentPrompt = nil
// Slide the prompt down, and then remove it.
UIView.animate(withDuration: Self.promptAnimationDuration) {
bottomConstraint.constant = oldPrompt.bounds.height
navMap.layoutIfNeeded()
} completion: { _ in
oldPrompt.removeFromSuperview()
}
}
func changePrompt(
navMap: GMSMapView, newPrompt: NavigationPromptView,
oldPrompt: NavigationPromptView
) {
// Slide the old prompt out and the new prompt in at the same time.
hidePrompt(navMap: navMap, oldPrompt: oldPrompt)
showPrompt(navMap: navMap, newPrompt: newPrompt)
}
}
如要使用委派,請先在地圖檢視上設定委派,再啟用導航功能,詳情請參閱基本檢查清單。
限制與已知問題
CarPlay 使用固定的版面配置範本,因此不適用版面配置自訂功能。如果您在為 CarPlay 畫面建立的地圖檢視上呼叫 setLayoutDelegate(_:),該方法會擲回錯誤。
如要瞭解影響特定 SDK 版本的問題,請參閱版本資訊中的「已知問題」。