8 changed files with 504 additions and 30 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