Apple MapKit导航:到达目的地时是否提供回调通知?
Apple MapKit导航到达目的地无回调的解决方法
问题描述
我在应用中使用Apple MapKit导航功能,设置起点和目的地后功能运行正常,但到达目的地时,我的ViewController未收到任何回调。请问是否有对应的回调可处理该场景,或有其他解决方法?我需要在到达目的地时切换至下一个ViewController。
当前代码
AppleMap类实现
class AppleMap { var source: CLLocationCoordinate2D? = nil var destination: CLLocationCoordinate2D? = nil init(source: CLLocationCoordinate2D, destination: CLLocationCoordinate2D) { self.source = source self.destination = destination } // If you are calling the coordinate from a Model, don't forgot to pass it in the function parenthesis. func present(in viewController: UIViewController, sourceView: UIView) { if let source = self.source, let destination = self.destination { let source = MKMapItem(placemark: MKPlacemark(coordinate: source)) source.name = "Source" let destination = MKMapItem(placemark: MKPlacemark(coordinate: destination)) destination.name = "Destination" MKMapItem.openMaps( with: [source, destination], launchOptions: [MKLaunchOptionsDirectionsModeKey: MKLaunchOptionsDirectionsModeDriving] ) } } }
调用代码
let appleMap = AppleMap(source: source, destination: pickupLocation) appleMap.present(in: self, sourceView: UIView())
问题根源
你当前使用的MKMapItem.openMaps()是直接跳转到系统地图App进行导航,你的App会进入后台,完全无法接收系统地图的导航状态回调——包括到达目的地的通知。
解决方法
方案一:在App内使用MKMapView实现导航(推荐)
通过在自己的App中嵌入MKMapView,可以全程监听导航状态和用户位置,从而在到达目的地时触发回调。
修改后的AppleMap类示例
import MapKit import CoreLocation protocol AppleMapNavigationDelegate: AnyObject { func didArriveAtDestination() } class AppleMap: NSObject, MKMapViewDelegate, CLLocationManagerDelegate { var source: CLLocationCoordinate2D? var destination: CLLocationCoordinate2D? weak var delegate: AppleMapNavigationDelegate? private var mapView: MKMapView! private var locationManager: CLLocationManager! // 到达目的地的距离阈值(单位:米),可根据需求调整 private let arrivalThreshold: CLLocationDistance = 100 init(source: CLLocationCoordinate2D, destination: CLLocationCoordinate2D) { self.source = source self.destination = destination super.init() setupLocationManager() } private func setupLocationManager() { locationManager = CLLocationManager() locationManager.delegate = self locationManager.desiredAccuracy = kCLLocationAccuracyBest locationManager.requestWhenInUseAuthorization() } func present(in viewController: UIViewController) { guard let source = source, let destination = destination else { return } // 创建并配置MKMapView mapView = MKMapView(frame: viewController.view.bounds) mapView.delegate = self mapView.showsUserLocation = true viewController.view.addSubview(mapView) // 请求导航路线 let request = MKDirections.Request() request.source = MKMapItem(placemark: MKPlacemark(coordinate: source)) request.destination = MKMapItem(placemark: MKPlacemark(coordinate: destination)) request.transportType = .automobile let directions = MKDirections(request: request) directions.calculate { [weak self] response, error in guard let self = self, let response = response else { return } // 添加路线到地图 self.mapView.addOverlay(response.routes[0].polyline) // 调整地图视角以显示路线 self.mapView.setVisibleMapRect(response.routes[0].polyline.boundingMapRect, edgePadding: UIEdgeInsets(top: 50, left: 50, bottom: 50, right: 50), animated: true) // 开始监听用户位置 self.locationManager.startUpdatingLocation() } } // MARK: - CLLocationManagerDelegate func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { guard let currentLocation = locations.last, let destinationCoord = destination else { return } let destinationLocation = CLLocation(latitude: destinationCoord.latitude, longitude: destinationCoord.longitude) let distanceToDestination = currentLocation.distance(from: destinationLocation) // 当距离小于阈值时,判定为到达目的地 if distanceToDestination <= arrivalThreshold { locationManager.stopUpdatingLocation() delegate?.didArriveAtDestination() } } // MARK: - MKMapViewDelegate func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer { guard let polyline = overlay as? MKPolyline else { return MKOverlayRenderer(overlay: overlay) } let renderer = MKPolylineRenderer(polyline: polyline) renderer.strokeColor = .blue renderer.lineWidth = 3 return renderer } }
使用方式(ViewController中)
class YourViewController: UIViewController, AppleMapNavigationDelegate { override func viewDidLoad() { super.viewDidLoad() // 假设source和pickupLocation已获取 let appleMap = AppleMap(source: source, destination: pickupLocation) appleMap.delegate = self appleMap.present(in: self) } // 到达目的地的回调 func didArriveAtDestination() { // 切换到下一个ViewController let nextVC = NextViewController() navigationController?.pushViewController(nextVC, animated: true) } }
方案二:跳转系统地图后,返回时检测位置
如果必须使用系统地图App导航,只能在用户从系统地图返回你的App时,检查当前位置是否接近目的地,再执行页面跳转。
实现步骤
- 在你的ViewController中,监听App进入前台的事件:
override func viewDidLoad() { super.viewDidLoad() NotificationCenter.default.addObserver(self, selector: #selector(checkArrivalStatus), name: UIApplication.willEnterForegroundNotification, object: nil) } @objc private func checkArrivalStatus() { guard let destinationCoord = // 这里获取你的目的地坐标 else { return } let locationManager = CLLocationManager() locationManager.requestWhenInUseAuthorization() // 获取当前位置 if let currentLocation = locationManager.location { let destinationLocation = CLLocation(latitude: destinationCoord.latitude, longitude: destinationCoord.longitude) let distance = currentLocation.distance(from: destinationLocation) if distance <= 100 { // 同样使用距离阈值 // 切换到下一个ViewController let nextVC = NextViewController() navigationController?.pushViewController(nextVC, animated: true) } } } deinit { NotificationCenter.default.removeObserver(self) }
注意事项
- 方案一需要申请位置权限(
NSLocationWhenInUseUsageDescription),并在Info.plist中配置。 - 距离阈值可根据实际场景调整(比如步行导航可以设为50米,驾车设为150米)。
内容的提问来源于stack exchange,提问作者Muhammad Haroon Iqbal
相关产品推荐
相关产品推荐

