NAV Navbar

Map8 Navigation SDK for iOS

Map8 Navigation SDK 提供一個完整的導航畫面:只要給目的地,就能在您的 App 裡開啟逐向導航,不需要自己處理路線規劃、GPS 貼路與畫面。

內建功能:

畫面版面是固定的,您可以調整配色、接收導航事件,並決定導航畫面要如何呈現在您的 App 中。

系統需求

版本相容性

Navigation SDK 搭配的 Map8 Maps SDK(Map8.xcframework)
1.1.1 1.0.4 以上

語言

導航畫面的文字與語音播報目前只提供繁體中文。

安裝

加入 framework

Navigation SDK 由以下三個 framework 組成,請一併加入您的 Xcode 專案:

Framework 說明
Map8.xcframework Map8 地圖 SDK(安裝方式見 Maps SDK 安裝指南)
Map8Navigation.xcframework 導航畫面與導航流程
M8TurnLib.xcframework 導航引擎

下載 Navigation SDK:https://api.map8.zone/download/navigation_sdk/map8-navigation-sdk-ios.zip?key=您的key

將解壓縮後的 Map8Navigation.xcframework 與 M8TurnLib.xcframework 拖曳加入您的專案,前往 General 頁籤,在 Frameworks, Libraries, and Embedded Content 區塊確認三個 framework 都已加入,並將 Embed 選項設為 Embed & Sign。

設定 Map8 Key

在 App 啟動時設定一次 Map8 Key(地圖與導航共用同一把 Key):

import UIKit
import Map8

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
    func application(_ application: UIApplication,
                     didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        MGLAccountManager.map8Key = "您的 Map8 Key"
        return true
    }
}

也可以沿用地圖 SDK 的方式,在 Info.plist 加入 Map8Key,兩者擇一即可。

Info.plist 設定

定位權限(必要)

加入 Privacy - Location When In Use Usage Description,說明 App 為什麼需要定位,例如:

導航需要取得您的位置

背景導航(建議)

加入 Required background modes(UIBackgroundModes),並勾選以下兩項:

項目 用途
App registers for location updates(location) 鎖屏或切換到其他 App 時繼續定位
App plays audio or streams audio/video using AirPlay(audio) 在背景播報語音導航

快速開始

建立 Map8NavigationViewController,設定目的地後呈現出來,再呼叫 startNavigation():

import UIKit
import CoreLocation
import Map8Navigation

class ViewController: UIViewController, Map8NavigationViewControllerDelegate {

    func startNavigation() {
        let destination = Map8NavigationDestination(
            coordinate: CLLocationCoordinate2D(latitude: 25.102355, longitude: 121.548493),
            name: "國立故宮博物院",
            address: "台北市士林區至善路二段221號"
        )

        let navigation = Map8NavigationViewController(destination: destination)
        navigation.delegate = self
        navigation.modalPresentationStyle = .fullScreen
        present(navigation, animated: true)
        navigation.startNavigation()
    }

    // 使用者按了結束導航(抵達時會先顯示抵達畫面,按「完成」後 SDK 預設會自動關閉)
    func navigationViewController(_ controller: Map8NavigationViewController,
                                  didEndWith reason: Map8NavigationEndReason) {
        if reason == .cancelled {
            controller.dismiss(animated: true)
        }
    }

    // 無法導航(沒有定位權限、路線規劃失敗):一定要處理,否則畫面會停在原地
    func navigationViewController(_ controller: Map8NavigationViewController,
                                  didFailWith error: Map8NavigationError) {
        controller.dismiss(animated: true)
    }
}

執行後的流程:

  1. 取得使用者目前位置,規劃到目的地的路線
  2. 顯示路線預覽:整條路線、總時間、距離、主要道路、路線列表
  3. 使用者按下「開始導航」,進入逐向導航
  4. 抵達目的地時顯示抵達畫面,使用者按「完成」後關閉導航畫面

呈現方式

請以全螢幕(.fullScreen)呈現導航畫面。使用預設的卡片式(sheet)呈現時,使用者往下滑就能關掉畫面;這種情況 SDK 會自動結束導航並呼叫 didEndWith: .cancelled,但導航會被意外中斷。

在 SwiftUI 中使用

用 UIViewControllerRepresentable 包裝,並以 fullScreenCover 呈現:

import SwiftUI
import CoreLocation
import Map8Navigation

// 不要命名為 NavigationView,會跟 SwiftUI 內建的 NavigationView 衝突。
struct Map8NavigationScreen: UIViewControllerRepresentable {
    let destination: Map8NavigationDestination
    let onClose: () -> Void

    func makeUIViewController(context: Context) -> Map8NavigationViewController {
        let controller = Map8NavigationViewController(destination: destination)
        controller.delegate = context.coordinator
        controller.startNavigation()
        return controller
    }

    func updateUIViewController(_ controller: Map8NavigationViewController, context: Context) {}

    func makeCoordinator() -> Coordinator { Coordinator(onClose: onClose) }

    final class Coordinator: Map8NavigationViewControllerDelegate {
        let onClose: () -> Void
        init(onClose: @escaping () -> Void) { self.onClose = onClose }

        func navigationViewController(_ controller: Map8NavigationViewController,
                                      didEndWith reason: Map8NavigationEndReason) {
            if reason == .cancelled { onClose() }
        }

        // 抵達畫面按「完成」:由 SwiftUI 關閉,不要讓 SDK 自己 dismiss
        func navigationViewControllerDidFinish(_ controller: Map8NavigationViewController) {
            onClose()
        }

        func navigationViewController(_ controller: Map8NavigationViewController,
                                      didFailWith error: Map8NavigationError) {
            onClose()
        }
    }
}

struct ContentView: View {
    @State private var isNavigating = false

    var body: some View {
        Button("開始導航") { isNavigating = true }
            .fullScreenCover(isPresented: $isNavigating) {
                Map8NavigationScreen(
                    destination: Map8NavigationDestination(
                        coordinate: CLLocationCoordinate2D(latitude: 25.102355, longitude: 121.548493),
                        name: "國立故宮博物院"
                    ),
                    onClose: { isNavigating = false }
                )
                .ignoresSafeArea()
            }
    }
}

範例 App

我們提供一個完整的範例 App,示範目的地搜尋、導航、模擬導航,以及路線偏好、日夜間、導航視角、路線預覽、自訂配色等設定。

下載範例 App:https://api.map8.zone/download/navigation_sdk/map8-navigation-sdk-ios-sample.zip?key=您的key

壓縮檔內已附所需的 framework,解壓縮後:

  1. 開啟 Map8NavigationSample.xcodeproj
  2. 在 SampleApp.swift 把 YOUR_MAP8_KEY 換成您的 Map8 Key
  3. 在 Signing & Capabilities 選擇您的開發團隊(Team),必要時修改 Bundle Identifier
  4. 接上 iPhone 或選擇模擬器執行
檔案 內容
SampleApp.swift 設定 Map8 Key
ContentView.swift 首頁:目的地、開始導航、模擬導航、各項設定
NavigationScreen.swift 在 SwiftUI 中使用 Map8NavigationViewController、處理導航事件
PlaceSearch.swift 用 Map8 地點搜尋 API 找目的地

路線預覽

預設在規劃完成後先顯示路線預覽,使用者確認後才開始導航。

// 關掉預覽:規劃完成後直接開始導航
navigation.showsRoutePreview = false

如果您想自己設計預覽畫面,可以關掉內建預覽,從 didPlanRoute 取得路線摘要,使用者確認後再呼叫 startGuidance():

func navigationViewController(_ controller: Map8NavigationViewController,
                              didPlanRoute summary: Map8NavigationRouteSummary) {
    print("總距離 \(summary.distance) 公尺,約 \(Int(summary.duration / 60)) 分鐘")
    print("經 \(summary.mainRoads().joined(separator: "、"))")
    for step in summary.steps {
        print("\(step.roadName) \(step.distance) 公尺")
    }
}

// 使用者在您的預覽畫面按下開始
navigation.startGuidance()

路線偏好

在 startNavigation() 之前設定,導航中偏離路線重新規劃時也會沿用:

navigation.routePreference = .avoidHighways
值 說明
.fastest 最佳路徑(預設)
.shortest 最短距離
.preferFreeway1 國道一號優先
.preferFreeway3 國道三號優先
.avoidHighways 避走高速公路

導航畫面操作

鏡頭模式

模式 說明
跟隨 預設,鏡頭跟著車子移動
自由 使用者拖曳、縮放、旋轉地圖後進入;畫面出現「回到導航」按鈕,停止操作 10 秒後自動回到跟隨
全覽 使用者按下全覽按鈕後進入,顯示剩下的路線;按「回到導航」回到跟隨
// 自動回到導航的秒數,nil = 不自動回來
navigation.autoRecenterDelay = 15

// 用程式碼切換
navigation.showRouteOverview()
navigation.recenter()

導航視角

導航中的鏡頭有兩種視角:

視角 說明
.tilted 立體 預設,鏡頭往前傾斜,看得到前方較遠的路
.topDown 平面 從正上方往下看,車頭朝上
navigation.cameraPerspective = .topDown

// 在畫面上顯示切換按鈕(預設不顯示),讓使用者在導航中自己切換
navigation.showsPerspectiveButton = true

導航途中也可以修改 cameraPerspective,鏡頭會用動畫轉過去。路線預覽與全覽固定是平面,不受這個設定影響。

使用者按切換按鈕時會呼叫 delegate,可以在這裡記住使用者的選擇,下次導航時設定回 cameraPerspective:

func navigationViewController(_ controller: Map8NavigationViewController,
                              didChange cameraPerspective: Map8NavigationCameraPerspective) {
    // 例如存到 UserDefaults
}

語音

navigation.isVoiceMuted = true

畫面上的語音按鈕也可以切換靜音。

語音播報時會自動降低其他 App 的音樂音量、暫停 Podcast,播完再恢復。

結束導航

畫面左下角內建結束按鈕。如果您想自己提供結束方式,可以關掉內建按鈕:

navigation.showsEndButton = false

// 在您自己的按鈕裡
navigation.stopNavigation()

抵達

抵達目的地時預設顯示抵達畫面(已抵達目的地、名稱、地址、「完成」按鈕)。使用者按「完成」後會呼叫 navigationViewControllerDidFinish,沒有實作時預設會關閉導航畫面(dismiss)。

// 想自己處理抵達後的畫面
navigation.showsArrivalScreen = false

func navigationViewController(_ controller: Map8NavigationViewController,
                              didEndWith reason: Map8NavigationEndReason) {
    if reason == .arrived {
        // 顯示您自己的抵達畫面
    }
}

外觀

日間/夜間

預設依目的地的日出日落時間自動切換,夜間會改用深色地圖與較暗的面板。

navigation.appearance = .automatic  // 預設,依日出日落切換
navigation.appearance = .light      // 一律日間
navigation.appearance = .dark       // 一律夜間

配色

可以把導航畫面調整成您的品牌色。日間與夜間各一組配色,隨時修改都會立即套用。

var scheme = Map8NavigationColorScheme.default
scheme.routeLine = .systemGreen
scheme.accent = .systemGreen
navigation.colorScheme = scheme

// 夜間另外設定,不設定則使用內建的夜間配色
navigation.nightColorScheme = .night
欄位 說明
panelBackground 主要面板底色:轉彎卡片、下方資訊列、測速警示
secondaryPanelBackground 次要面板底色:「接著」那一列、狀態提示
panelText 面板文字與轉彎圖示、車道箭頭
warning 接近測速照相、超速時閃爍的警示色
routeLine 尚未走過的路線(外框會自動調暗)
traveledRouteLine 已經走過的路線(外框會自動調暗)
accent 強調色:車標箭頭、浮動按鈕圖示
floatingButtonBackground 浮動按鈕底色

橫向畫面

導航畫面支援直向與橫向:橫向時資訊集中在左側,右側留給地圖。您的 App 需要在 General → Deployment Info 開啟橫向(Landscape Left / Right),導航畫面才會跟著轉向;只支援直向的 App 則維持直向顯示。

模擬導航

開發或測試時,可以不用實際移動,讓 SDK 沿著路線自動產生定位。模擬的定位與真實 GPS 走同一套流程。

navigation.simulation = Map8NavigationSimulation(
    origin: CLLocationCoordinate2D(latitude: 25.075828, longitude: 121.574365),
    speedKmh: 50,
    playbackRate: 4   // 4 倍速
)
navigation.startNavigation()

導航事件

實作 Map8NavigationViewControllerDelegate 接收導航事件,所有方法都是選用的,只實作需要的即可。所有方法都在主執行緒呼叫,可以直接更新 UI。

// ViewController 已在宣告處加上 Map8NavigationViewControllerDelegate(見「快速開始」),
// 這裡只是把方法放在 extension 裡整理,不要再重複宣告一次協定。
extension ViewController {

    func navigationViewController(_ controller: Map8NavigationViewController,
                                  didPlanRoute summary: Map8NavigationRouteSummary) {
        // 路線規劃完成
    }

    func navigationViewControllerDidStartNavigation(_ controller: Map8NavigationViewController) {
        // 使用者按下開始導航
    }

    func navigationViewController(_ controller: Map8NavigationViewController,
                                  didUpdate progress: Map8NavigationProgress) {
        // 每收到一筆定位呼叫一次(通常約每秒一次):剩餘距離、剩餘時間、目前路名、車速
    }

    func navigationViewControllerDidReroute(_ controller: Map8NavigationViewController) {
        // 偏離路線後已重新規劃
    }

    func navigationViewController(_ controller: Map8NavigationViewController,
                                  didEndWith reason: Map8NavigationEndReason) {
        // 抵達(.arrived)或使用者結束(.cancelled)
    }

    func navigationViewControllerDidFinish(_ controller: Map8NavigationViewController) {
        // 使用者在抵達畫面按下「完成」
        controller.dismiss(animated: true)
    }

    func navigationViewController(_ controller: Map8NavigationViewController,
                                  didFailWith error: Map8NavigationError) {
        // 無法導航:沒有定位權限,或路線規劃失敗
        controller.dismiss(animated: true)
    }

    func navigationViewController(_ controller: Map8NavigationViewController,
                                  didChange cameraMode: Map8NavigationCameraMode) {
        // 鏡頭模式改變(跟隨/自由/全覽)
    }

    func navigationViewController(_ controller: Map8NavigationViewController,
                                  didChange cameraPerspective: Map8NavigationCameraPerspective) {
        // 使用者按切換按鈕,改變了導航視角(立體/平面);用程式設定不會呼叫
    }
}

API 參考

Map8NavigationViewController

建立

介面 說明
init(destination:) 以目的地建立導航畫面

屬性

屬性 型別 預設 說明
delegate Map8NavigationViewControllerDelegate? nil 導航事件
destination Map8NavigationDestination — 目的地,下次 startNavigation() 生效
routePreference Map8NavigationRoutePreference .fastest 路線偏好
showsRoutePreview Bool true 規劃完先顯示路線預覽
showsArrivalScreen Bool true 抵達時顯示抵達畫面
showsEndButton Bool true 顯示內建的結束導航按鈕
cameraPerspective Map8NavigationCameraPerspective .tilted 導航視角
showsPerspectiveButton Bool false 顯示立體/平面切換按鈕
isVoiceMuted Bool false 語音靜音
autoRecenterDelay TimeInterval? 10 自由模式停止操作後自動回到導航的秒數
appearance Map8NavigationAppearance .automatic 日間/夜間外觀
colorScheme Map8NavigationColorScheme .default 日間配色
nightColorScheme Map8NavigationColorScheme .night 夜間配色
styleURL / nightStyleURL URL Map8 預設樣式 日間/夜間地圖樣式
simulation Map8NavigationSimulation? nil 模擬導航(開發測試用)
routeSummary Map8NavigationRouteSummary? 唯讀 目前路線摘要
cameraMode Map8NavigationCameraMode 唯讀 目前鏡頭模式
isNavigating Bool 唯讀 是否正在導航中(含規劃路線中)
isNightAppearance Bool 唯讀 目前是否為夜間外觀

方法

方法 說明
startNavigation() 開始:規劃路線,依設定顯示預覽或直接導航
startGuidance() 從路線預覽進入導航(自行設計預覽畫面時使用)
stopNavigation() 結束導航,通知 didEndWith: .cancelled
recenter() 回到導航視角
showRouteOverview() 全覽剩下的路線

資料型別

Map8NavigationDestination

欄位 型別 說明
coordinate CLLocationCoordinate2D 目的地座標
name String 名稱,選填(顯示在抵達畫面,不影響選路)
address String 地址,選填(顯示在抵達畫面,不影響選路)

Map8NavigationRouteSummary

欄位 型別 說明
distance Int 總距離(公尺)
duration TimeInterval 預估總時間(秒)
steps [Map8NavigationRouteStep] 每一個步驟
mainRoads(limit:) [String] 主要道路(距離最長的幾條路名)

Map8NavigationRouteStep

欄位 型別 說明
roadName String 道路名稱
distance Int 這一段的長度(公尺)
maneuver Map8NavigationManeuver 這一段結束時的轉彎

Map8NavigationProgress

欄位 型別 說明
distanceRemaining Int 剩餘距離(公尺)
durationRemaining TimeInterval 剩餘時間(秒)
currentRoadName String 目前道路名稱
speedKmh Int 目前車速(km/h)

Map8NavigationSimulation

欄位 型別 預設 說明
origin CLLocationCoordinate2D — 模擬起點
speedKmh Double 50 模擬車速
playbackRate Double 1 播放倍速

列舉

型別 值
Map8NavigationEndReason .arrived 抵達、.cancelled 使用者結束
Map8NavigationError .locationPermissionDenied 沒有定位權限、.routeFailed(Error) 路線規劃失敗
Map8NavigationCameraMode .following 跟隨、.free 自由、.overview 全覽
Map8NavigationCameraPerspective .tilted 立體、.topDown 平面
Map8NavigationAppearance .automatic 自動、.light 日間、.dark 夜間
Map8NavigationManeuver .turnLeft、.turnRight、.uTurnLeft、.uTurnRight、.keepLeft、.keepRight、.straight

網路與隱私

連線網域

Navigation SDK 會連線到以下網域,如果您的網路環境有白名單限制,請一併開放:

網域 用途
api.map8.zone 地圖圖資與樣式
nav8.pakka.ai 路線規劃、路口實景圖

收集的資料

為了規劃路線,SDK 會將以下資料傳送到路線規劃伺服器,僅用於導航功能本身,不做跨 App 追蹤:

資料 用途
精確位置 規劃路線的起點、偏離路線後重新規劃
目的地座標,以及目的地名稱、地址的前 5 個字 規劃路線的終點;名稱與地址僅供伺服器記錄,不影響選路
裝置識別碼 取得路線規劃服務的存取權杖

SDK 已內附 Apple 規定的隱私清單(PrivacyInfo.xcprivacy),Xcode 產生 App 的隱私報告時會自動包含。填寫 App Store 的 App 隱私權資訊時,請將「精確位置」與「裝置 ID」列入,用途為「App 功能」。

常見問題

進入隧道後車標還會動嗎?

會。導航中超過 2 秒收不到 GPS,SDK 會沿著路線依進隧道前的車速推算位置,車標、語音與區間測速倒數照常進行;GPS 恢復後立即改回真實位置。若隧道內減速或塞車,出隧道時車標可能會往回修正一下。

偏離路線時會怎麼樣?

偏離路線超過 35 公尺並持續 5 秒,SDK 會播報「重新規劃路線」並自動規劃新路線,同時沿用原本的路線偏好。

沒有定位權限會怎麼樣?

使用者拒絕定位權限時,導航會結束並呼叫 didFailWith: .locationPermissionDenied,您可以在這裡引導使用者到設定開啟權限。


台灣圖霸感謝您的支持與愛護!

有任何疑問,或是指教,都非常歡迎您找我們詢問。

非常感謝!