如何更新显示的 ETA 和距离值? Swift 4 MKDirections MapKit

Posted

技术标签:

【中文标题】如何更新显示的 ETA 和距离值? Swift 4 MKDirections MapKit【英文标题】:How can I update my displayed ETA and Distance values? Swift 4 MKDirections MapKit 【发布时间】:2017-12-08 14:07:54 【问题描述】:

在我的 viewDidLoad 中,我检索当前位置和所选商店的位置(该位置通过 Geofire 存储在 Firebase 数据库中) 我已经在 viewDidLoad 中显示了计算的 ETA 和距离,但是当我走出去并沿着路线行驶时,它并没有更新。每当用户移动时,如何更新 DirectionsLbl 和 etaLbl?有什么建议么?我已经发布了我的 viewDidLoad 和 didUpdateLocations 函数。提前致谢。

 func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) 

    let numberFormatter = NumberFormatter()
    numberFormatter.minimumFractionDigits = 2
    numberFormatter.maximumFractionDigits = 2
    numberFormatter.minimumIntegerDigits = 1
    let userLat = locationManager.location?.coordinate.latitude
    let userLon = locationManager.location?.coordinate.longitude
    myLocation = CLLocation.init(latitude: userLat!, longitude: userLon!)
    manager.stopUpdatingLocation()

    // Create request
    self.request.destination = self.destinationMapItem
    self.request.source = self.self.sourceMapItem!
    self.request.transportType = MKDirectionsTransportType.walking
    self.request.requestsAlternateRoutes = false
    let directions = MKDirections(request: self.request)
    directions.calculate  response, error in
        if let route = response?.routes.first 
            let time = self.secondsToHoursMinutesSeconds(seconds: route.expectedTravelTime)
            print("Distance: \(route.distance), ETA: \(time)")
            self.etaLbl.text = "\(time)mins"
            let miles = route.distance / 1609.344
            self.distanceLbl.text = "\(String(describing: numberFormatter.string(from: miles as NSNumber)!))m"
            for step in route.steps 
                self.directionslbl.text = step.instructions
            
            self.mapView.camera.altitude = 500

            self.mapView.add(route.polyline)
         else 
            print(error?.localizedDescription)
        
    




override func viewDidLoad() 
    super.viewDidLoad()

    self.mapView.showsBuildings = false
    self.mapView.isRotateEnabled = false
    self.mapView.showsCompass = false
    self.mapView.showsPointsOfInterest = false
    let numberFormatter = NumberFormatter()
    numberFormatter.minimumFractionDigits = 2
    numberFormatter.maximumFractionDigits = 2
    numberFormatter.minimumIntegerDigits = 1
    geoFire = GeoFire(firebaseRef: ref.child("Store Locations"))
    print(store!)
    self.orderIDLbl.text = orderID!
    self.storeLbl.text = store!
    geoFire?.getLocationForKey(store!, withCallback:  (location, error) in

        if error != nil 

            print("ERROR IS: \(String(describing: error?.localizedDescription))")

         else 

            self.destinationStore = location
            print("LOCATION OF STORE IS: \(String(describing: self.destinationStore!))")
            // Get destination position
            let storeLong = self.destinationStore?.coordinate.longitude
            let storeLat = self.destinationStore?.coordinate.latitude
            let storeLocation = CLLocation.init(latitude: storeLat!, longitude: storeLong!)
            let destinationPlacemark = MKPlacemark(coordinate: storeLocation.coordinate, addressDictionary: nil)
            let anno = StoreAnnotation(coordinate: storeLocation.coordinate, storeName: self.store!)
            self.mapView.addAnnotation(anno)
            self.destinationMapItem = MKMapItem(placemark: destinationPlacemark)
            self.request.destination = self.destinationMapItem
            self.request.source = self.self.sourceMapItem!
            self.request.transportType = MKDirectionsTransportType.walking
            self.request.requestsAlternateRoutes = false
            let directions = MKDirections(request: self.request)
            directions.calculate  response, error in
                if let route = response?.routes.first 
                    let time = self.secondsToHoursMinutesSeconds(seconds: route.expectedTravelTime)
                    print("Distance: \(route.distance), ETA: \(time)")
                    self.etaLbl.text = "\(time)mins"
                    let miles = route.distance / 1609.344
                    self.distanceLbl.text = "\(String(describing: numberFormatter.string(from: miles as NSNumber)!))m"
                    self.directionsArray = route.steps
                    print(route.steps)
                    for step in route.steps 
                        self.directionslbl.text = step.instructions
                    
                    //self.mapView.camera.heading = self.directionsArray[0].
                    self.mapView.add(route.polyline)
                 else 
                    print(error?.localizedDescription)
                
            
        

    )

    let userLat = locationManager.location?.coordinate.latitude
    let userLon = locationManager.location?.coordinate.longitude
    myLocation = CLLocation.init(latitude: userLat!, longitude: userLon!)
    mapView.delegate = self
    mapView.userTrackingMode = .followWithHeading
    mapView.camera.altitude = 500
    mapView.camera.pitch = 30



    locationManager.delegate = self;
    locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
    locationManager.requestAlwaysAuthorization()
    locationManager.startUpdatingLocation()


    // Get current position
    let sourcePlacemark = MKPlacemark(coordinate: (myLocation?.coordinate)!, addressDictionary: nil)
    self.sourceMapItem = MKMapItem(placemark: sourcePlacemark)

    let orderStatusRef = ref.child("Users").child(currentUserID!).child("Current Order").child(orderID!)

    orderStatusRef.observe(.value)  (DataSnapshot) in

        let dict = DataSnapshot.value as? NSDictionary
        let orderStatus = dict!["Order Status"] as? Int

        switch orderStatus! 

        case 0:
            self.orderStatusLbl.text = "Received"
            self.orderStatusLbl.textColor = UIColor.red
        case 1:
            self.orderStatusLbl.text = "Being Prepared"
            self.orderStatusLbl.textColor = UIColor.orange


        default:
            break
        

    


【问题讨论】:

我没有运行您的代码,但是您在 DidUpdateLocations 中调用了 StopUpdatingLocations,所以我认为您不会获得多个位置更新? 另外,您对当前位置的设置应该来自“locations”参数(set loc = locations.last!) 【参考方案1】:

我遇到了同样的问题,无法在线找到任何可用的解决方案。可能有更好的方法来解决这个问题,但我会为您提供对我有用的方法。

这是我在 Stack Overflow 上的第一篇文章,因此对于任何清晰度和/或格式问题,我提前道歉。


我在班级顶部定义了两个变量。其中两个用于我们即将创建的计时器。另外两个用于在您完成路线时更新 ETA。

var directionsTimer: Timer? // For updating the turn-by-turn directions
var etaTimer: Timer? // For updating the ETA label


var calculatingETA = false // To make sure we aren't running two ETA calculations at the same time

然后我通过在计算出方向后运行此函数来设置这两个计时器。

 private func setupTimers() 
    self.etaTimer = Timer.scheduledTimer(withTimeInterval: 20.0, repeats: true, block:  (timer) in self.updateETA() )
    self.directionsTimer = Timer.scheduledTimer(withTimeInterval: 5.0, repeats: true)  (timer) in self.updateDirectionLabels() 


“etaTimer”设置为每 20 秒运行一次以下函数。此功能将使您的 UI 中的 ETA 标签保持最新,同时沿路线进行。

private func updateETA() 
        
        // Grab the device's current location (in coordinates)
        guard let currentLocationCoordinate = self.currentCoordinate else  print("Unable To Validate Current Location."); return 
        
        // Create a MKMapItem from the device's current location
        let currentLocationItem = MKMapItem(placemark: MKPlacemark(coordinate: currentLocationCoordinate))
        
        // Grab the location of your route's destination (preferably as a MKMapItem)
        guard let destinationItem = self.destinations?.first else  print("Unable To Validate Destination."); return 
        
        // Create a MKMapItem from the route's destination
        // !! My destination was already saved as an MKMapItem, if yours is not, you can just repeat the same above step !! //
        
        // Make sure the last calculation is finished
        if self.calculatingETA == false 
            
            // Here we will create a new MKDirections item, starting from the device's current location, going to your route's destination
            
            let req = MKDirections.Request()
                req.source = currentLocationItem // Place your current location item here
                req.destination = destinationItem // Place your destination item here
                req.transportType = .automobile
            
            let directions = MKDirections(request: req)
            
            // Set this variable to true
            self.calculatingETA = true
            
            directions.calculateETA  (response, error) in
                if let res = response 
                    
                    // You now have an up-to-date ETA
                    let etaString = res.expectedTravelTime.toETAString()
                    DispatchQueue.main.async  self.etaLabel.text = etaString 
                    
                    // Set this variable to false. We have finished calculating
                    self.calculatingETA = false
                    
                    Print("Updated ETA: \(etaString)", .info)
                
                else if let err = error 
                    self.calculatingETA = false
                    
                    Print("Unable To Update ETA. [MESSAGE] \(err.localizedDescription)", .error)
                
            
        
    

“directionsTimer”设置为每五秒运行一次以下函数。此功能将更新您的 UI 中的“距离下一回合”标签。

private func updateDirectionLabels() 

    // Make sure the current step is within bounds
    if (self.steps.isEmpty == false) && (self.stepCounter < self.steps.count) 

        // Define the current step
        let currentStep = self.steps[self.stepCounter]
        
        // Update the instructions to match the current MKRoute.Step
        DispatchQueue.main.async  self.directionLabel.text = currentStep.instructions 
        
        // Grab the device's current location (in coordinates)
        if let currentCoordinate = self.currentCoordinate 

            // Create a CLLocation object from the device's coordinates
            let currentLocation = CLLocation(latitude: currentCoordinate.latitude, longitude: currentCoordinate.longitude)
            
            // Grab the coordinates for the current MKRoute.Step
            let currentStepCoordinate = currentStep.polyline.coordinate

            // Create a CLLocation object from the step's coordinates
            let currentStepLocation = CLLocation(latitude: currentStepCoordinate.latitude, longitude: currentStepCoordinate.longitude)
            
            // Get the distance between the device's current location and the location of the next step
            let distanceToStep = currentLocation.distance(from: currentStepLocation)

            // Format the result
            let distanceInMiles = Double(distanceToStep * 0.00062).rounded()
            
            // Update label displaying the distance until next turn
            DispatchQueue.main.async  self.directionDistanceLabel.text = String(describing: distanceInMiles) 
        
    


我可能对 cme​​ts 有点过火了,但我希望这会有所帮助。

【讨论】:

以上是关于如何更新显示的 ETA 和距离值? Swift 4 MKDirections MapKit的主要内容,如果未能解决你的问题,请参考以下文章

如何在 swift 中使用 HealthKit 获得步行和跑步距离

如何更新 Celery Task ETA?

如何在 swift 中仅为唯一值更新 TableView 行?

如何使用 Swift 4.2 创建应用商店应用更新通知机制?

无法从循环内的switch语句接收值

我正在尝试按位置对用户的帖子进行排序并使用 PFGeopoint 和 Swift 显示距离