22 changed files with 918 additions and 358 deletions
@ -0,0 +1,42 @@ |
|||||
|
|
||||
|
import Foundation |
||||
|
import NotificationCenter |
||||
|
|
||||
|
extension Notification.Name { |
||||
|
static let navigationdEvent = Notification.Name("navigationdEvent") //导航事件 |
||||
|
} |
||||
|
@objc class AmapHelper: NSObject { |
||||
|
private static var isInitialized = false |
||||
|
// MARK: - 初始化监听器(你可以在服务底层初始化时调用这个方法) |
||||
|
@objc static func initializeListener() { |
||||
|
guard !isInitialized else { |
||||
|
print("⚠️ AmapHelper 已经初始化过了,忽略重复调用") |
||||
|
return |
||||
|
} |
||||
|
isInitialized = true |
||||
|
print("✅ AmapHelper 初始化监听导航事件") |
||||
|
NotificationCenter.default.addObserver( |
||||
|
self, |
||||
|
selector: #selector(handleNavigationEvent(_:)), |
||||
|
name: .navigationdEvent, // 自定义事件名 |
||||
|
object: nil |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
// MARK: - 处理导航事件 |
||||
|
@objc static func handleNavigationEvent(_ notification: Notification) { |
||||
|
guard let userInfo = notification.userInfo else { |
||||
|
print("⚠️ 导航事件未携带 userInfo") |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
print("📍 收到导航事件 userInfo: \(userInfo)") |
||||
|
|
||||
|
// 示例处理:读取起点和终点信息 |
||||
|
if let start = userInfo["start"] as? String, |
||||
|
let end = userInfo["end"] as? String { |
||||
|
print("🚗 导航从 \(start) 到 \(end)") |
||||
|
// TODO: 调用实际导航逻辑 |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,165 @@ |
|||||
|
import Foundation |
||||
|
import AMapNaviKit |
||||
|
import AVFoundation |
||||
|
|
||||
|
public class AmapService: NSObject, AMapNaviDriveManagerDelegate { |
||||
|
public static let shared = AmapService() |
||||
|
|
||||
|
private var driveManager: AMapNaviDriveManager? |
||||
|
private var startPoint: AMapNaviPoint? |
||||
|
private var endPoint: AMapNaviPoint? |
||||
|
private var isNavigating = false |
||||
|
|
||||
|
private override init() { |
||||
|
super.init() |
||||
|
setupDriveManager() |
||||
|
} |
||||
|
|
||||
|
private func setupDriveManager() { |
||||
|
// 使用单例模式初始化 |
||||
|
driveManager = AMapNaviDriveManager.sharedInstance() |
||||
|
driveManager?.delegate = self |
||||
|
driveManager?.isUseInternalTTS = true |
||||
|
|
||||
|
// 设置高德地图服务 |
||||
|
// 1. 设置隐私合规 |
||||
|
AMapServices.shared()?.enableHTTPS = true |
||||
|
AMapServices.shared()?.apiKey = "aa60a03be9f6531bcc64029e60f95d03" |
||||
|
} |
||||
|
|
||||
|
public func startNavigation(from start: String, to end: String) { |
||||
|
guard !isNavigating else { |
||||
|
print("导航已在进行中") |
||||
|
return |
||||
|
} |
||||
|
print("开始导航") |
||||
|
// 解析坐标 |
||||
|
let startCoords = start.split(separator: ",").compactMap { Double($0.trimmingCharacters(in: .whitespaces)) } |
||||
|
let endCoords = end.split(separator: ",").compactMap { Double($0.trimmingCharacters(in: .whitespaces)) } |
||||
|
|
||||
|
guard startCoords.count == 2, endCoords.count == 2 else { |
||||
|
print("坐标格式错误,应为'经度,纬度'") |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
// 创建导航点(注意:高德地图使用经纬度顺序) |
||||
|
startPoint = AMapNaviPoint.location(withLatitude: CGFloat(startCoords[1]), |
||||
|
longitude: CGFloat(startCoords[0])) |
||||
|
endPoint = AMapNaviPoint.location(withLatitude: CGFloat(endCoords[1]), |
||||
|
longitude: CGFloat(endCoords[0])) |
||||
|
|
||||
|
if let start = startPoint, let end = endPoint { |
||||
|
isNavigating = true |
||||
|
|
||||
|
// 使用推荐的默认策略(避让拥堵+速度优先+避免收费) |
||||
|
let strategy: AMapNaviDrivingStrategy = .drivingStrategySingleDefault |
||||
|
|
||||
|
// 调用最新API(注意参数是数组) |
||||
|
let success = driveManager?.calculateDriveRoute(withStart: [start], |
||||
|
end: [end], |
||||
|
wayPoints: nil, |
||||
|
drivingStrategy: strategy) ?? false |
||||
|
|
||||
|
if !success { |
||||
|
isNavigating = false |
||||
|
print("路径规划参数校验失败") |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public func stopNavigation() { |
||||
|
driveManager?.stopNavi() |
||||
|
isNavigating = false |
||||
|
} |
||||
|
|
||||
|
// MARK: - AMapNaviDriveManagerDelegate |
||||
|
|
||||
|
public func driveManager(onCalculateRouteSuccess driveManager: AMapNaviDriveManager) { |
||||
|
print("Route calculation succeeded") |
||||
|
driveManager.startEmulatorNavi() // 使用模拟导航(测试用) |
||||
|
// 实际使用时改为: |
||||
|
// driveManager.startGPSNavi() |
||||
|
} |
||||
|
|
||||
|
public func driveManager(_ driveManager: AMapNaviDriveManager, onCalculateRouteFailure error: Error) { |
||||
|
isNavigating = false |
||||
|
print("Route calculation failed: \(error.localizedDescription)") |
||||
|
} |
||||
|
|
||||
|
public func driveManager(_ driveManager: AMapNaviDriveManager, playNaviSound soundString: String, soundStringType: AMapNaviSoundType) { |
||||
|
// 过滤空内容和不需要处理的播报类型 |
||||
|
guard !soundString.isEmpty else { return } |
||||
|
|
||||
|
switch soundStringType { |
||||
|
case .default: |
||||
|
// 默认导航提示(如"前方500米右转"等) |
||||
|
playSystemTTS(soundString) |
||||
|
|
||||
|
case .navigationStart: |
||||
|
// 开始导航提示("开始导航"等) |
||||
|
playSystemTTS("准备出发," + soundString) |
||||
|
|
||||
|
case .navigationEnd: |
||||
|
// 到达目的地提示 |
||||
|
playSystemTTS(soundString + ",导航结束") |
||||
|
|
||||
|
case .yaw: |
||||
|
// 偏航提示("您已偏航,正在重新规划路线"等) |
||||
|
playSystemTTS("注意:" + soundString) |
||||
|
|
||||
|
case .interrupt: |
||||
|
// 重要打断播报(如急转弯警告等) |
||||
|
interruptCurrentSpeech() |
||||
|
playSystemTTS(soundString) |
||||
|
|
||||
|
default: |
||||
|
// 其他未明确处理的类型使用默认播报 |
||||
|
playSystemTTS(soundString) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public func playSystemTTS(_ text: String) { |
||||
|
let utterance = AVSpeechUtterance(string: text) |
||||
|
utterance.voice = AVSpeechSynthesisVoice(language: "zh-CN") |
||||
|
utterance.rate = 0.5 // 适当放慢语速 |
||||
|
|
||||
|
let synthesizer = AVSpeechSynthesizer() |
||||
|
synthesizer.speak(utterance) |
||||
|
} |
||||
|
|
||||
|
public func interruptCurrentSpeech() { |
||||
|
AVSpeechSynthesizer().stopSpeaking(at: .immediate) |
||||
|
} |
||||
|
|
||||
|
public func driveManager(_ driveManager: AMapNaviDriveManager, update naviInfo: AMapNaviInfo?) { |
||||
|
guard let naviInfo = naviInfo else { return } |
||||
|
|
||||
|
let currentRoad = naviInfo.currentRoadName ?? "未知道路" |
||||
|
let nextRoad = naviInfo.nextRoadName ?? "未知道路" |
||||
|
let distance = naviInfo.routeRemainDistance |
||||
|
let time = naviInfo.routeRemainTime |
||||
|
|
||||
|
print(""" |
||||
|
当前道路: \(currentRoad) |
||||
|
下条道路: \(nextRoad) |
||||
|
剩余距离: \(distance)米 |
||||
|
剩余时间: \(time)秒 |
||||
|
""") |
||||
|
} |
||||
|
|
||||
|
public func driveManager(_ driveManager: AMapNaviDriveManager, error: Error) { |
||||
|
isNavigating = false |
||||
|
print("Navigation error occurred: \(error.localizedDescription)") |
||||
|
} |
||||
|
|
||||
|
public func driveManagerDidEndEmulatorNavi(_ driveManager: AMapNaviDriveManager) { |
||||
|
isNavigating = false |
||||
|
print("Emulator navigation ended") |
||||
|
} |
||||
|
|
||||
|
deinit { |
||||
|
driveManager?.stopNavi() |
||||
|
driveManager?.delegate = nil |
||||
|
AMapNaviDriveManager.destroyInstance() |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,231 @@ |
|||||
|
import UIKit |
||||
|
import AVFAudio |
||||
|
import AMapNaviKit |
||||
|
|
||||
|
class AmapViewController: UIViewController { |
||||
|
|
||||
|
// MARK: - 必须声明为强引用 |
||||
|
private var driveManager: AMapNaviDriveManager! |
||||
|
private var driveView: AMapNaviDriveView! |
||||
|
private let speechSynthesizer = AVSpeechSynthesizer() |
||||
|
|
||||
|
private var startPoint: AMapNaviPoint! |
||||
|
private var endPoint: AMapNaviPoint! |
||||
|
// 自定义初始化方法 |
||||
|
init(startpos: String, endpos: String) { |
||||
|
super.init(nibName: nil, bundle: nil) |
||||
|
self.startPoint = AmapViewController.parseCoord(from: startpos) |
||||
|
self.endPoint = AmapViewController.parseCoord(from: endpos) |
||||
|
print("导航---------------------: \(startPoint) \(endPoint)") |
||||
|
} |
||||
|
// 解析 "lat,lng" -> AMapNaviPoint |
||||
|
private static func parseCoord(from string: String) -> AMapNaviPoint? { |
||||
|
let components = string.split(separator: ",") |
||||
|
guard components.count == 2, |
||||
|
let lng = Double(components[0]), |
||||
|
let lat = Double(components[1]) else { |
||||
|
print("❌ 无效坐标格式: \(string)") |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
return AMapNaviPoint.location(withLatitude: lat, longitude: lng) |
||||
|
} |
||||
|
required init?(coder aDecoder: NSCoder) { |
||||
|
fatalError("init(coder:) has not been implemented") |
||||
|
} |
||||
|
|
||||
|
override func viewDidLoad() { |
||||
|
super.viewDidLoad() |
||||
|
setupAMapComponents() |
||||
|
} |
||||
|
|
||||
|
private func setupAMapComponents() { |
||||
|
// 1. 初始化 DriveView(必须在主线程) |
||||
|
print("导航------------------ setupAMapComponents_1") |
||||
|
DispatchQueue.main.async { |
||||
|
print("导航------------------ setupAMapComponents_2") |
||||
|
self.driveView = AMapNaviDriveView(frame: self.view.bounds) |
||||
|
print("导航------------------ setupAMapComponents_3") |
||||
|
self.driveView.autoresizingMask = [.flexibleWidth, .flexibleHeight] |
||||
|
print("导航------------------ setupAMapComponents_4") |
||||
|
self.driveView.delegate = self |
||||
|
print("导航------------------ setupAMapComponents_5") |
||||
|
self.view.addSubview(self.driveView) |
||||
|
print("导航------------------ setupAMapComponents_6") |
||||
|
|
||||
|
// 2. 初始化 DriveManager |
||||
|
self.driveManager = AMapNaviDriveManager.sharedInstance() |
||||
|
print("导航------------------ setupAMapComponents_7") |
||||
|
self.driveManager.delegate = self |
||||
|
print("导航------------------ setupAMapComponents_8") |
||||
|
self.driveManager.allowsBackgroundLocationUpdates = true |
||||
|
print("导航------------------ setupAMapComponents_9") |
||||
|
|
||||
|
// 3. 确保对象非空再关联 |
||||
|
if let manager = self.driveManager, let view = self.driveView { |
||||
|
print("导航------------------ setupAMapComponents_10") |
||||
|
manager.addDataRepresentative(view) |
||||
|
print("导航------------------ setupAMapComponents_11") |
||||
|
self.startNavigation() |
||||
|
print("导航------------------ setupAMapComponents_12") |
||||
|
} else { |
||||
|
print("Error: driveManager or driveView is nil") |
||||
|
} |
||||
|
print("导航------------------ setupAMapComponents_13") |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private func startNavigation() { |
||||
|
// print("导航------------------ startNavigation_1") |
||||
|
// let startPoint = AMapNaviPoint.location(withLatitude: 39.989614, longitude: 116.481763)! |
||||
|
// print("导航------------------ startNavigation_2") |
||||
|
// let endPoint = AMapNaviPoint.location(withLatitude: 39.983456, longitude: 116.315495)! |
||||
|
// print("导航------------------ startNavigation_3") |
||||
|
|
||||
|
driveManager.calculateDriveRoute( |
||||
|
withStart: [startPoint], |
||||
|
end: [endPoint], |
||||
|
wayPoints: nil, |
||||
|
drivingStrategy: .drivingStrategySingleDefault |
||||
|
) |
||||
|
print("导航------------------ startNavigation_4") |
||||
|
} |
||||
|
|
||||
|
@objc private func exitNavigation() { |
||||
|
print("导航------------------ 退出导航,清理资源") |
||||
|
// 停止导航 |
||||
|
driveManager.stopNavi() |
||||
|
driveManager.removeDataRepresentative(driveView) |
||||
|
driveManager.delegate = nil |
||||
|
|
||||
|
// 移除导航视图 |
||||
|
driveView.delegate = nil |
||||
|
driveView.removeFromSuperview() |
||||
|
driveView = nil |
||||
|
|
||||
|
// 销毁导航管理器 |
||||
|
driveManager = nil |
||||
|
|
||||
|
// 关闭当前页面,视情况而定 |
||||
|
// 如果你用的是导航控制器,可以pop |
||||
|
navigationController?.popViewController(animated: true) |
||||
|
// 或者如果是present出来的,dismiss |
||||
|
// dismiss(animated: true, completion: nil) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// MARK: - 代理方法 |
||||
|
extension AmapViewController: AMapNaviDriveViewDelegate, AMapNaviDriveManagerDelegate { |
||||
|
//View回调 |
||||
|
func driveViewCloseButtonClicked(_ driveView: AMapNaviDriveView) { |
||||
|
print("导航界面退出按钮被点击") |
||||
|
// 停止导航并清理 |
||||
|
driveManager.stopNavi() |
||||
|
driveManager.removeDataRepresentative(driveView) |
||||
|
driveManager.delegate = nil |
||||
|
driveView.delegate = nil |
||||
|
driveView.removeFromSuperview() |
||||
|
|
||||
|
// 返回到 Flutter 页面 |
||||
|
self.dismiss(animated: true, completion: nil) |
||||
|
} |
||||
|
|
||||
|
func driveView(_ driveView: AMapNaviDriveView, didChange showMode: AMapNaviDriveViewShowMode) { |
||||
|
print("导航界面显示模式改变:\(showMode.rawValue)") |
||||
|
} |
||||
|
|
||||
|
func driveView(_ driveView: AMapNaviDriveView, didChange trackMode: AMapNaviViewTrackingMode) { |
||||
|
print("导航界面跟随模式改变:\(trackMode.rawValue)") |
||||
|
} |
||||
|
|
||||
|
func driveView(_ driveView: AMapNaviDriveView, didChangeOrientation isLandscape: Bool) { |
||||
|
print("导航界面横竖屏切换:isLandscape = \(isLandscape)") |
||||
|
} |
||||
|
|
||||
|
func driveView(_ driveView: AMapNaviDriveView, didChangeDayNightType showStandardNightType: Bool) { |
||||
|
print("导航界面白天/黑夜模式切换:isNight = \(showStandardNightType)") |
||||
|
} |
||||
|
|
||||
|
func driveViewEdgePadding(_ driveView: AMapNaviDriveView) -> UIEdgeInsets { |
||||
|
// 你可以根据自己 UI 布局返回合适的内边距,防止遮挡 |
||||
|
let padding = UIEdgeInsets(top: 100, left: 50, bottom: 80, right: 60) |
||||
|
print("请求导航界面可视区域边距:\(padding)") |
||||
|
return padding |
||||
|
} |
||||
|
|
||||
|
//导航回调 |
||||
|
func driveManager(_ driveManager: AMapNaviDriveManager, error: Error) { |
||||
|
print("导航------------------ ❌ 发生错误: \(error.localizedDescription)") |
||||
|
} |
||||
|
|
||||
|
func driveManager(onCalculateRouteSuccess driveManager: AMapNaviDriveManager){ |
||||
|
print("导航------------------ ✅ 路线规划成功(旧方法)") |
||||
|
driveManager.startGPSNavi() |
||||
|
} |
||||
|
|
||||
|
func driveManager(_ driveManager: AMapNaviDriveManager, onCalculateRouteSuccessWith type: AMapNaviRoutePlanType) { |
||||
|
print("导航------------------ 路径规划成功,类型: \(type.rawValue)") |
||||
|
driveManager.startGPSNavi() |
||||
|
} |
||||
|
|
||||
|
func driveManager(_ driveManager: AMapNaviDriveManager, onCalculateRouteFailure error: Error) { |
||||
|
print("导航------------------ ❌ 路线规划失败: \(error.localizedDescription)") |
||||
|
} |
||||
|
|
||||
|
func driveManager(_ driveManager: AMapNaviDriveManager, onCalculateRouteFailure error: Error, routePlanType type: AMapNaviRoutePlanType) { |
||||
|
print("导航------------------ ❌ 路线规划失败(新方法): \(error.localizedDescription),类型: \(type.rawValue)") |
||||
|
} |
||||
|
|
||||
|
func driveManager(_ driveManager: AMapNaviDriveManager, didStartNavi naviMode: AMapNaviMode) { |
||||
|
print("导航------------------ 🚗 已开始导航,模式: \(naviMode.rawValue)") |
||||
|
} |
||||
|
|
||||
|
func driveManagerNeedRecalculateRoute(forYaw driveManager: AMapNaviDriveManager) { |
||||
|
print("导航------------------ 出现偏航,需要重新计算路径") |
||||
|
} |
||||
|
|
||||
|
func driveManagerNeedRecalculateRoute(forTrafficJam driveManager: AMapNaviDriveManager) { |
||||
|
print("导航------------------ 遇到拥堵,重新规划路线") |
||||
|
} |
||||
|
|
||||
|
func driveManager(_ driveManager: AMapNaviDriveManager, onArrivedWayPoint wayPointIndex: Int) { |
||||
|
print("导航------------------ 到达途经点索引:\(wayPointIndex)") |
||||
|
} |
||||
|
|
||||
|
func driveManagerIsNaviSoundPlaying(_ driveManager: AMapNaviDriveManager) -> Bool { |
||||
|
print("导航------------------ 🔊 正在判断是否在播报语音") |
||||
|
return false // 如果你有 TTS 系统播报时需返回 true |
||||
|
} |
||||
|
|
||||
|
func driveManager(_ driveManager: AMapNaviDriveManager, playNaviSound soundString: String, soundStringType: AMapNaviSoundType) { |
||||
|
print("导航------------------ 📢 导航播报: \(soundString),类型: \(soundStringType.rawValue)") |
||||
|
// 示例:使用 AVSpeechSynthesizer 播放语音 |
||||
|
let utterance = AVSpeechUtterance(string: soundString) |
||||
|
utterance.voice = AVSpeechSynthesisVoice(language: "zh-CN") |
||||
|
speechSynthesizer.speak(utterance) |
||||
|
} |
||||
|
|
||||
|
func driveManagerDidEndEmulatorNavi(_ driveManager: AMapNaviDriveManager) { |
||||
|
print("导航------------------ 🛑 模拟导航结束") |
||||
|
} |
||||
|
|
||||
|
func driveManager(onArrivedDestination driveManager: AMapNaviDriveManager) { |
||||
|
print("导航------------------ 🎯 到达目的地") |
||||
|
} |
||||
|
|
||||
|
func driveManager(_ driveManager: AMapNaviDriveManager, onNaviPlay ringType: AMapNaviRingType) { |
||||
|
print("导航------------------ 🔔 播放提示音,类型: \(ringType.rawValue)") |
||||
|
} |
||||
|
|
||||
|
func driveManager(_ driveManager: AMapNaviDriveManager, update gpsSignalStrength: AMapNaviGPSSignalStrength) { |
||||
|
print("导航------------------ 📡 GPS信号强度: \(gpsSignalStrength.rawValue)") |
||||
|
} |
||||
|
|
||||
|
func driveManager(_ driveManager: AMapNaviDriveManager, postRouteNotification notifyData: AMapNaviRouteNotifyData) { |
||||
|
print("导航------------------ 📌 路线通知信息: \(notifyData)") |
||||
|
} |
||||
|
|
||||
|
func driveManager(_ driveManager: AMapNaviDriveManager, onSuggestChangeMainNaviRoute suggestChangeMainNaviRouteInfo: AMapNaviSuggestChangeMainNaviRouteInfo) { |
||||
|
print("导航------------------ 🔄 建议切换主导航路线: \(suggestChangeMainNaviRouteInfo)") |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue