You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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时,检查当前位置是否接近目的地,再执行页面跳转。

实现步骤

  1. 在你的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 02:07:40