如何更新显示的 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)
我可能对 cmets 有点过火了,但我希望这会有所帮助。
【讨论】:
以上是关于如何更新显示的 ETA 和距离值? Swift 4 MKDirections MapKit的主要内容,如果未能解决你的问题,请参考以下文章
如何在 swift 中使用 HealthKit 获得步行和跑步距离
如何在 swift 中仅为唯一值更新 TableView 行?